,

【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

【演算法實作筆記】二分搜尋法 C#實作in Unity

用以搜尋已排序的一串資料,由小排到大

輸入:正整數n
輸出 :n在S中的位置

原理
輸入值與S陣列的中間值比大小
當輸入值比較大時,接下來只需要判斷S陣列中間值的右邊那一串陣列即可
反之亦然。


using UnityEngine;
using System.Collections;

public class BinarySearch_ : MonoBehaviour {

    public int middle;
    int[] hi = new int[5]{1,2,3,4,5};
    void Start()
    {
        middle = Location (hi, 0, 5, 3);
        binarysearch (hi, 2);
    }
    //遞迴版 二元搜尋
    //遞迴呼叫時如果不改變質卻傳進去會消耗很多變數的地址
    int Location(int[] data,int low , int high,int search)
    {

        int mid;

        if (low > high)
            return 0;
        else {
            mid = (low + high) / 2;

            if (search == data [mid])
                return mid;
            else if (search <= data [mid])
                return Location (data, low, mid - 1, search);
            else
                return Location (data, mid + 1, high,search);
                
        }

    }
    //普通版
    void binarysearch(int[] data,int search)
    {
        int low = 0;
        int high = data.Length;

        while (low <= high) {
            int mid = (low+high) / 2;
            if (data [mid] == search) {
                Debug.Log ("key" + mid);
                return;
            } else if (data [mid] > search) {
                high = mid - 1;
            } else if (data [mid] < search) {
                low = mid + 1;
            }
        }
    }
}

用UNITY實作,分遞迴跟普通版,多研究就能看出來了。
Share:
Read More

【Unity】使用動畫曲線 — AnimationCruve


有使用過Unity動畫狀態機的想必會覺得可以隨意新增Key來調整曲線這些功能很方便
那如果有天我只是想簡單做個事,像是把一張圖片放大,我個人會比較想能少一點東西就少一點東西,因此就可以使用到 AnimationCruve ,跟動畫機一樣可以調整曲線的類別。

關於AnimationCruve 在官方文檔中有詳細說明
https://docs.unity3d.com/ScriptReference/AnimationCurve.html

使用的時候就直接在腳本內宣告AnimationCruve

public AnimationCurve Curve;

在script外看來是這樣





夠簡單吧! 點開就能跟Animator一樣的使用了
點進去後可以操作keyframe,對某個keyframe點右鍵->editKey...可以設定他的值如下圖



















仔細看會看到上面的value跟time分別是旁邊的y軸跟x軸可以手動輸入比較精準

那要取出這些值呢,就是使用一個Evaluate方法(官方文檔),該方法就是丟入一個(time),他會返回一個(value)

那如果你要讓他像個動畫,想當然就是放在update裡面啦,不然你放在Start裡只會執行一次,也就是只拿到一個對應值而已
.
.
.
在這裡使用IEnumerator是因為我不想把放在Update裡面,然後又要每次停留1偵,不然while迴圈執行太快導致你看不清楚他有沒有變化

IEnumerator PlayAnimation()
    {

        float curveTime = 0f; 
        float curveAmount = Curve.Evaluate (curveTime);

        while (curveAmount<1.0f) {
        
            curveTime +=Time.deltaTime * Speed;
            curveAmount = Curve.Evaluate (curveTime);

            transform.localScale = new Vector3(curveAmount,curveAmount,curveAmount); //這裡我是把本身物件大小縮放

            yield return null;
        }
    }

先定義一個curveTime 將他當作曲線的time值(就上面講過的X軸的值)傳入
並且將curveTime隨時間增加,然後speed是我想比較方便控制他的速度才乘上去的
curveAmount = Curve.Evaluate (curveTime);這句就是把curveTime去查出對應的value是多少
之後就可以使用curveAmount來做動畫了,你可以改變物體大小...等很多事情
最後當然要等待下一偵啦,不然太快會看不到阿~

完整程式碼:

using UnityEngine;
using System.Collections;

public class WaveControl : MonoBehaviour {

    public AnimationCurve Curve;
    public float Speed;

    void Start () {
        StartCoroutine ("PlayAnimation");

    }

    IEnumerator PlayAnimation()
    {

        float curveTime = 0f;
        float curveAmount = Curve.Evaluate (curveTime);

        while (curveAmount<1.0f) {
        
            curveTime +=Time.deltaTime * Speed;
            curveAmount = Curve.Evaluate (curveTime);

            transform.localScale = new Vector3(curveAmount,curveAmount,curveAmount);

            yield return null;
        }
    }
}

====
16/10/5更:
忘記IEnumerator不能同時播放了XD
最後還是用回Update裡去了QQ
Share:
Read More

【UE4】Unreal 與HTC vive的結合 - 環境設置

題外話: 上次發布時間是5月27... 原本想說每個月發一篇的,但有時候做出的東西就真的很小很不足拿出來紀錄,而且有些也是自己隨便弄弄做出來的東西,半桶水而已也不好發XD
小弟玻璃心,如果說改正哪裡什麼的,哪邊錯誤的留言我會很開心,但如果出現:『像這種程度就自己紀錄就好別發出來的話』我的玻璃心會碎的QQ
不過最近因為實習,有研究unreal的引擎,想說可能有些人入門的時候會跟我一樣會遇到些困難,所以就還是PO了 XD,太深入的我也不太了解,我只能講出我所看到,所查到的各種問題,拜託鞭小力一點。
接下來也是一連串的Unreal,因為unity最近比較少碰XD

____________________


由於官方已經有怎樣設置環境的教學了,這邊只是將他補充說明與製作上會遇到的困難部分講解一下而已

官方也有個故障排除可以參考
點這裡

1. 要先把SteamVR套件下載下來 設置手上的htcVIVE

官方教學
因為htc vive硬體搭配的是SteamVR軟體
如果是什麼board之類的就是其他的 每款硬體都會搭配不等的軟體

在這步上會遇到的困難大概就是【頭戴顯示器】沒辦法搜尋到、【手把】無法搜尋、基地台((那兩顆正方形的))沒辦法偵測


【定位器】

線確定要連接好,記得頻道要設定,後面有顆按鈕可以按,一部設置成B,一部設置成C,然後設置成對角線((左上右下)),而且距離要在五公尺內


【頭戴顯示器】

定位器設定好後,如果偵測不到的話,你可以更新你的顯示卡驅動程式、重開機,記得頭戴顯示器要在你兩個基地台以內

【手把】

設定定位器後,長按電源鍵打開,建議一個一個打開,這部分比較沒什麼困難,當初也是一按就偵測到
--問題參考1


如果以上偵測不到,把SteamVR關掉,然後重開再試一次,大部分都是定位器有問題,因為那個頻道設置與偵測比較困難,參考以下





































2. UE4專案設置

官方教學

這部分相較比較簡單,比較要注意的是這個地方


看那個SteamVR那邊,預設是打勾的


















如果你打勾後,SteamVR開啟後也都連結了,還是灰色的,你就要把它取消打勾後重啟,在打勾一次重啟就會變可以使用了。

基本上只要設置Plugin就能完成與硬體的連接了,接下來就是連接手把、連接顯示器方面的事了~等下一篇唄

Share:
Read More

[C#] 使用Mathf.Sin與Cos取得圓上點座標


最近要使用到算圓,因此想到以前常用的三角函數,sin、cos



Mathf.sin 官方的api裡面註明了
Returns the sine of angle f in radians.
他輸入的是弧度,所以如果想求 圓上某個點的座標,要先把弧度轉換成角度

什麼是弧度呢?
看下圖應該很明顯了XD




(取自維基百科)


兩者關係就是

1弧度 = 360°/2π ≒ 57.29° 

1° = 2π/360° ≒ 0.0174533(弧度) 




這是比較公式化的,如果自己去算的話,就是照上面的圖,180°,分成180份

然後除以長度,就會得出0.017453....

這樣就知道1度等於0.0174533弧度,假如想求90度的話,就*90就好了

Mathf裡面已經定義好常數給你使用了

分別是
Mathf.Deg2Rad跟Mathf.Rad2Deg

以下簡單示範一下
float angleY = Mathf.Sin(60*Mathf.Deg2Rad);
float angleX = Mathf.Cos(60*Mathf.Deg2Rad);
Point1 = new Vector2(angleX, angleY);


Debug.DrawLine(Vector2.zero, Point1,Color.green);


結果: (( 其他線是測試用,上述代碼出現的是綠色那條))



而使用Sin可以使用拿來移動物體
使用上像是這樣

private Vector3 _startPosition;

    void Start()
    {
        _startPosition = transform.position;
    }

    void Update()
    {
        transform.position = _startPosition + new Vector3(Mathf.Sin(Time.time), 0.0f, 0.0f);
    }



這樣就會有平滑平滑的感覺啦~
Share:
Read More

【UGUI】不規則圖片觸發範圍修改

NGUI有boxcollider可以自己調整觸發的範圍
但UGUI由於不是用boxcollider來觸發的,所以要修改也要有另外個方法

基本上用雨松的方法就行了-->連結在此

但找到另外一種方法,可以少寫比較多的代碼,但比較不靈活(?,沒辦法自己控制想要的範圍

首先有這麼一張圖,做成UGUI的按鈕的話,透明的地方也會被觸發到

而今天我想消除透明的地方,只有有顏色的地方才能觸發


先把匯入的圖檔先調一下,調成可讀可寫的


然後+入一個腳本
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class UGUI_Button_alpha : MonoBehaviour {

    public Image image;
    public float threshold = 0.5f;
    
    void Start () {
        image.eventAlphaThreshold = threshold;
    }
    
}

原理其實Unity API裡面寫得很清楚了

讀取Image的eventAlphaThreshold值,來判斷圖片的Alpha值(所以要把圖片設定為可讀可寫)

1的話就是總是會通過,調低就會忽略透明度,所以到最後點擊圖片才會響應。

Image就放你想套用的那張圖片,然後再調一下threshold 到你滿意的值就好

Share:
Read More

[Unity] NGUI與UGUI分別如何攔截事件


由於常常會發生點擊了介面,遊戲內的物件也會被觸發的關係
所以會用到攔截事件,而NGUI的方面比較簡單,只要添加

if (UICamera.isOverUI)
    return;

當我們在射線檢測的時候,如果點擊到UI就不會發生反應了


UGUI方面的話則是

#if IPHONE || ANDROID
            if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
            return;
#else
            if (EventSystem.current.IsPointerOverGameObject())
                return;
#endif

由於要判斷平台,所以比較長
下面那句則是PC使用的
Share:
Read More

【C#】continue跟return與break用法

很多書都有提到,但最近寫的專題裡面用到就順便記下來了


continue :

跳過這次迴圈(i),並執行下一次(i+1)的迴圈
然後執行完迴圈後會回到上一層繼續執行未完成部分
Ex:
void Start () {




for (int j=0; j<5; j++) {
  

   if(j==3)
    continue;
   Debug.Log("continue: "+j);
  }

  Debug.Log ("我在外面");

 }


假設今天有個for循環 印出0~4數字
這時候如果我們想說,不印出3
這時候只要像上面那樣
就可以略過3了


並且,他會繼續執行略過後的方法


return :
回傳、中斷

一碰到return ,"函數" 就終止然後return 值後(也可以直接return),直接結束目前函式,不會繼續執行迴圈外的程式
void Start () {

Debug.Log ("進入Start");

TestReturn ();

Debug.Log ("我在Start外面");

}


void TestReturn()
{
for(int i=0;i<5;i++)
  {
   if(i==2)
    return;
   Debug.Log("Test Return:"+i);
  }

  Debug.Log ("in testReturn");
 }



看看TestReturn函式,return的話,會直接跳出目前的函式,不會繼續執行迴圈內外的函式,然後回到上一層繼續執行。



break :
直接跳出迴圈後,會繼續執行迴圈外的程式

void Start () {

Debug.Log ("進入Start");

TestBreak ();

Debug.Log ("我在Start外面");

}


void TestBreak()
{
for(int i=0;i<5;i++)
  {
   if(i==2)
    break;
   Debug.Log("Test break:"+i);
  }

  Debug.Log ("in break");
 }









跟上面差別就是 會執行迴圈後的程式
Share:
Read More