【Unity】製作AndroidPlugin的重點與可能遇到的坑



有時候使用unity的時候會想要更改或使用到android內建的東西,像是android的鍵盤、android的通知欄(Toast)
這個時候單單使用unity能做到的事是有限的,所以必須使用android的plugin才能解決這類的問題


PluginsForAndroid 官方這邊就可以看到了
https://docs.unity3d.com/Manual/PluginsForAndroid.html

android plugin 的兩種格式


一個是用jar檔一個是aar檔,兩個都可以在unity內使用

兩者區別是:
jar檔只包含純code的模式,以下示意圖,每個jar檔可能都不一樣,當然還包含一些目錄的文件夾









aar檔則是有包含布局阿、圖檔阿...等這個plugin用到的資源,看起來像這樣







Unity官方則是推薦使用aar來當作plugin使用


製作android plugin的文章已經很多了,這邊講一下比較重要跟比較容易遇坑的幾點


導入classes (UNITY內的)


unity有提供一個jar檔作為android 與 unity的橋樑,
大致路徑是C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes

ps.如果最後輸出的是aar檔,最後要將aar檔內的libs/classes.jar刪除,以免與unity重複打包

app資料夾內build.gradle內


1. apply plugin: 'com.android.application'改成apply plugin: 'com.android.library' //因為是要當成plugin使用,所以要將application改成library,不然會輸出成apk

2. applicationId "com.xx.xxxxx" //這一行刪除

3. dependencies內新增compile files('libs/classes.jar')  //後面這個路徑是unity plugin接入的路徑跟檔名

4. minSdkVersion 21 對應到 unity BuildSetting內的MinimumAPI  LEVEL (下面有)

AndroidManifest


這個是一個xml檔案,用來指向unity連接android的入口與一些app屬性的操作,像是
裡面比較重要的是packgeName ,unity BuildSetting內的packgeName要跟AndroidManifest內的packgeName一樣,minSdkVersion 也必須跟BuildSetting內MinimumAPI Level一樣 (見下圖)







<meta-data android:name="unityplayer.UnityActivity" android:value="true"/>

放在</intent-filter>下面 </activity>上面

完整版:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.u2a.testconnect" >


  <application
     
      android:label="@string/app_name"  // 如果你打沒這行,你在unity build出來的app名字會跟你在productName設定的不相符
      android:theme="@android:style/Theme.NoTitleBar">
    <activity android:name="com.u2a.testconnect.MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>

  </application>

</manifest>

如果製作jar檔,可能會遇到的坑

上面一經贅述了很多,至於怎麼製作jar檔,參考連結應該會有,筆者爬了很多製作jar檔的方式,發現有個小坑,會讓你按export jar之後,資料夾與jar檔都沒出現在目錄上






















對,就是gradle的版本,默認情況下會使用2.3.3版本,可是這樣照著網路上的資料來做的話,並不會出現想要的jar檔,我是改成2.2.3之後重build一次才出現jar檔的。




Extra: ((由於不多,所以不想多占一篇))

接上unity的plugin後,通常都會在MainActivity繼承UnityPlayerActivity (( 當然其他的Activity也可以,但是記得AndroidMainfest上的入口要修改為你用UnityPlayerActivity的那個 )),那這時已經建立連接口了,創建其他Activity的時候就不用再import UnityPlayerActivity了,除非有特殊需求,而如果要在unity內讀取其他Activity的話,則只需要使用AndroidJavaClass來指定讀取的路徑就可以了,像是我的packgeName為com.u2a.testconnect,第二個Activity 叫做SecondActivity,裡面有個靜態方GetSecondActivityNumber,回傳一個int,則會寫成這樣

Code:

  public Text log;
    int secondActivityNum = 0;

    private void OnGUI()
    {
      

        if (GUI.Button(new Rect(50, 500, 200, 100), "呼叫 SecondActivity的GetNumber"))
        {
           
            using ( AndroidJavaClass unity = new AndroidJavaClass("com.u2a.testconnect.SecondActivity")) //找出packge內的SecondActivity類別
            {
                secondActivityNum =   unity.CallStatic<int>("GetSecondActivityNumber");
                ShowSecondActivityNum();
            }

        }
        
    }

    public void ShowSecondActivityNum()
    {
        log.text = "顯示的數字(預設0) :"+secondActivityNum;
    }



關於 UnityPlayer.UnitySendMessage 看一下文件使用方法就知道了,這邊就不再多說。


成品:GOOGLE雲端


如果上述文章有任何疑問或錯誤理解,請不用懷疑的留言告知我,感謝。


參考資料與延伸閱讀:

Android Activity與 UnityPlayer Activity
http://eppz.eu/blog/unity-android-plugin-tutorial-1/


Android和Unity混合开发——Activity和Unity脚本交互和信息传递
http://www.jianshu.com/p/a7d82b3ac0c4

Android studio中如何生成引用 .aar 和 .jar
http://www.jianshu.com/p/76559da9ab39


Android Studio导出并使用aar和jar
http://chiahaolu.github.io/2016/06/27/Android-Studio%E5%AF%BC%E5%87%BA%E5%B9%B6%E4%BD%BF%E7%94%A8aar%E5%92%8Cjar/


Android Studio 打包及引用 aar
http://www.androidchina.net/2467.html






Share:
Read More
,

【C# 讀書會簡報】

由於前一陣子有朋友幾個人開讀書會複習
做了挺多個的
用完擺著不看也是浪費
所以就分享出來啦~
有錯的煩請下面留言說一下
一起學習~


===

類別、封裝

多載、屬性

匿名委派、Lambda

這邊有許多錯字,基本上看英文比較實在XD,我那時候查也一堆序、續、緒搞不清楚
執行序

LinQ
Share:
Read More
,

【Unity】xml文件讀取

之前做了一個用csv存取對話讀檔的系統,但是最近要用到大量的事件表

發現用csv會一大堆空格,不夠彈性,所以又跑回去以前不怎麼喜歡的xml格式

一開始想說xml好麻煩還要學,才跑去用csv比較直覺的

結果最近幾天看才發現,嗯?! 完全不會到入門其實蠻短的

保存用:
http://stanleyhlng.com/prettify-js/

https://rawgit.com/
========

<?xml version="1.0" encoding="utf-8"?>  
<DailyEvent>
    <Day id="1">
        <Event>
            event1
        </Event>
        <Dialog>
            "第一天"
        </Dialog>

    </Day>
    <Day id="2">
        <Event>
            event2
        </Event>
        <Dialog>
            "第二天"
        </Dialog>
    </Day>
</DailyEvent>

撰寫xml有幾個重點

1.宣告
<?xml version="1.0" encoding="utf-8"?>  
通常出現在XML文件的第一行,可寫可不寫
如果宣告了,version是必要寫上去的,然後後面的encoding則是選填
宣告了之後就可識別使用哪個編碼來代表文件中的字元了
像是
<?xml version="1.0" encoding="Shift-JIS"?>
就是使用日文編碼

2.先進後出
格式很像html那樣的寫法
基本上就是< >跟</ > 成雙成對出現
並且回文,意思就是先進後出,先寫的< >,最後再用</ >把它封起來
像上面的<DailyEvent> 一開始就寫了,如果裡面還要用別的<>就先擱著,如果沒有其他要寫的標籤,就可以封起來了
用文字表達就是 <1> <2> </2></1> 的感覺


3.樹狀結構
就跟樹一樣,只會有一個Root(父節點),指的就是上面範例的DailyEvent
不會有其他的Root了,然後再用這個Root去往下延伸
上面範例的第一個子節點是
<Day id="1">
同階層的另外一個子節點就是
<Day id="2">
而這兩個子節點又分別有兩個子節點

那資料就是可以儲存在某個節點的 <> 與 </> 中間那段空白,就像上面寫的第一天,第二天那樣

4.屬性
<>內可以填屬性,像是上面
<Day id="1">
內的 id="1",就是他的屬性,在unity內等等會用到


====

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Xml.Linq;
using System;
public class ReadXML : MonoBehaviour {
    public string id;
    public string eventContent;
    public string DialogContent;
    
    void Start () {

        var AllDays = XDocument.Load("Assets/DailyEvent_1.xml").Element("DailyEvent").Elements("Day");

        foreach (var day in AllDays)
        {
            id            = day.Attribute("id").Value.Trim();// 屬性
            eventContent  = day.Element("Event").Value.Trim();  // 內容                      
            DialogContent = day.Element("Dialog").Value.Trim(); //也是內容

            Debug.Log("day ID = " + id);
            Debug.Log("eventContent" + eventContent);
            Debug.Log("DialogContent" + DialogContent);
          
        }
    }
    
}

在Unity要讀取到xml檔案有兩種方法

一種是以前的XmlDocument
因為我用的不是這種的,所以這邊就先跳過了
有興趣的參考以下網頁

XML的簡單讀取與寫入

XML檔案基本操作-XmlDocument

第二種是比較新的XDocuments
可以用比較直覺地去讀取xml中的資料

要使用前先加入標頭檔
using System.Xml.Linq;

接下來就是開起xml檔了
先把輸入好的xml丟到unity內的Asset資料夾裡面,我是直接丟外面,當然也可以創建資料夾放到裡面,以下示範直接丟在外面的方式
XDocument AllDays = XDocument.Load("Assets/DailyEvent_1.xml");

那讀取完之後就是拿取裡面資料了
首先要先獲得它的節點
獲取節點有兩種方法
一種是你有很多個節點,使用的就是
根節點.Elements("XXXX");
另外一種就是只有單一節點
根節點.Element("XXXX");
基本上兩種方法就可以獲取到不同深度的節點了

拿取節點後要獲取內容的話就在後面.value就能拿到<與>之間的值了


拿取根節點的時候有比較步驟的方法,以下兩行都是相等的
 var AllDays = XDocument.Load("Assets/DailyEvent_1.xml").Element("DailyEvent");
 var AllDays = XDocument.Load("Assets/DailyEvent_1.xml").Root;

然後還有讀取節點的屬性
day.Attribute("id").Value

然後大致上就可以使用啦~ 還蠻直覺的
晚點放git



Share:
Read More

【C#】ref與out // 傳值與傳址

傳值Call by Value / 傳址 Call by Reference

學過c++的一定會遇到
傳值與傳址
一開始聽到整個
wtf 這兩個是尛阿

到最近看了一下ref跟out之後才真的了解
======

一般資料型別分為兩種
一個是實質型別
一個是參考型別

實質型別就是 int float char...等基礎的型別
而他變數記憶體存放的"內容"是值 所以稱為實"值"型別
也就是"傳值",當你在使用他的時候
他們之間傳遞的方式都是把值copy一份後再傳進去
像是以下
一開始宣告
int A=5, B=10;
並且設一個方法
void ChangeValue(int a,int b)
{
    a = 1;
    b = 2;
}

之後在Start()方法內調用
Debug.Log(" A  :" + A + " b :" + B);
 ChangeValue(A, B);
 Debug.Log(" A  :" + A + " b :" + B);
分別是將AB傳進去前跟傳進去後





可以看到傳進去後值都沒改變,這是因為使用的方式只是傳值(call by value)而已
意思就是把"值"複製給對方,而複製完改變的東西並不會影響到本身的值,因此不會做任何改變

而下面可以對照看看
 Car car11 = new Car("car11");
 Car car22 = new Car("car22");

    void ChangeNewCarValue(Car _car11, Car _car22)
    {
        _car11._carName = "ChangeCAR11";
        _car22._carName = "ChangeCAR22";

    }

同樣的宣告 同樣的方法 同樣在Start內打上
Debug.Log(" car11 :" + car11._carName + " car22 :" + car22._carName);
ChangeNewCarValue(car11, car22);
Debug.Log(" car11 :" + car11._carName + " car22 :" + car22._carName);
出來的結果則為






結果卻改變了,因此從上可以推論出來Car是一個傳址Call by Reference
意思就是,它傳遞的不是複製一份出來的值,而是傳遞丟入物件的記憶體位址
然後間接透過它來操作物件,跟指標很類似。

其實在C/C++的時候 正確是有三個名詞的
call by value 傳值、call by reference 傳參考、call by address傳址
但由於這裡要講的是C#,所以用官方的翻譯來敘述 ->詳細點我
如果要詳細了解這三個有什麼差別 可以再去google

ref、out

ref 關鍵字
會導致引數由參考加以傳遞,而非透過值。
參考程式碼 :
class RefExample
    {
        static void Method(ref int i)
        {
            // Rest the mouse pointer over i to verify that it is an int.
            // The following statement would cause a compiler error if i
            // were boxed as an object.
            i = i + 44;
        }

        static void Main()
        {
            int val = 1;
            Method(ref val);
            Console.WriteLine(val);

            // Output: 45
        }
    }


可以發現實質類型使用ref關鍵字之後,就會變得像是上面參考類型一樣
可以修改傳入的參數了
class RefExample
    {
        static void Method(ref int i)
        {
            // Rest the mouse pointer over i to verify that it is an int.
            // The following statement would cause a compiler error if i
            // were boxed as an object.
            i = i + 44;
        }

        static void Main()
        {
            int val = 1;
            Method(ref val);
            Console.WriteLine(val);

            // Output: 45
        }
    }

那問題來了,參考類型本身傳遞就可以直接修改值了
為何還會使用ref關鍵字加到參考型別身上呢?
1.明確提示
加了之後 不管給別人用還是自己用 都可以知道 傳入的參數是會被改變值得
不管傳入實質或參考都一樣
2.string的不變性
參考這裡
3. 為了修改默認值類型

ref重點:(msdn)
1.傳遞至 ref 參數的引數,在傳遞之前必須先初始化。
2.不能將ref和out用於async修飾詞定義的非同步方法。
3.有進有出

out關鍵字

基本上跟ref一樣
想要多個回傳值的時候,可以使用out

    class OutReturnExample
    {
        static void Method(out int i, out string s1, out string s2)
        {
            i = 44;
            s1 = "I've been returned";
            s2 = null;
        }
        static void Main()
        {
            int value;
            string str1, str2;
            Method(out value, out str1, out str2);
            // value is now 44
            // str1 is now "I've been returned"
            // str2 is (still) null;
        }
    }


ref跟out在使用上有很微小的差距

使用out的話,他不需要在被調用前初始化
但是調用者需要在返回之前指定輸出的參數

另一方面,可以用這樣去想
out類似於將方法附加返回值,也就是回傳多個值啦



Share:
Read More

【C#】特性

特性是什麼

簡單來說 
Unity最常看到的就是在屬性或字段前面加上
[Serializefield] private int Something = 5; 
而前面這個框框內的就是特性
它的功用就是賦予後面那一串陳述某種技能
像上面那個,就是Unity內的特性 
一般我們要在外面的editor調整數值,要將字段的層級調為pubic
但是有時候又不能讓他在其他腳本調用的時候被看見
這時候就可以用到它了→[Serializefield]  (點擊可看文件)
可以強制將私有字段設為在編輯器上可見






看起來就會變成這樣

然後還有,unity在更新它的api的時候 舊方法常常會被新方法取代
但一下把它移除,用戶難免會覺得很莫名 「常用的東西不見的 你在跟我開玩笑嘛!!」的感覺
於是他就使用了Obsolete這個特性,這個特性主要就是【宣告過期功能】
恩 , 超簡略 ,看起來就是這樣




其他還有很多各種不同的特性
這裡只是先簡短介紹一下 其他就慢慢去查唄
Share:
Read More