2015年9月12日 星期六

C# equals

之前考試遇到的題目,覺得==、equlas這兩者到底預設使用什麼比較蠻重要的觀念,所以還是來記錄一下。

class Test
{
public int field = 10;
}
Test test1 = new Test();
Test test2 = new Test();

  1. test1 == test2  => false
    因為Reference type的==,根據預設會藉由判斷兩個參考是否表示相同的物件,以測試參考是否相等,所以代表預設應該是使用類似object.ReferenceEquals來比較兩者的reference是否相等。
  2. test1.Equals(test2) => false
    同1,預設也是檢查參考
  3. object.ReferenceEquals(test1, test2) => false

知道reference的==和equlas預設是使用什麼,接下來只要分別你的data type是value type還是reference type就好。 而大家一般搞混string的是它其實是reference type,只是它implements ==和Equals function會比較字串的值。 基本上上面就解決大部分的問題,後來看到這篇所提到的boxing,也是相當需要注意的。Boxing概念主要就是把數值轉成object,所以就看轉成的object,因為轉成object就是reference type了,也因為==必須標明實際的data type,所以如果是object==object,則當然就是預設檢查reference。但注意如果Unboxing的話==則自然就變成value==value了。

最後唯一的例外一樣是同一篇提到的,字串的常數,照理說字串是reference,宣告兩個相同值的字串常數用成object type,檢查==應該就是檢查reference,可是CLR存在著一種機制,當它初始化時,它會創建一個內部hash,其中key是我們所要存放的字串,而value是誰引用。當定義一個新的string 時,系統會檢查是否有相同的。如果找不到,就建立一個新的,如果找到就直接引用。所以object type兩個字串的常數,reference會是相同的,真是麻煩………..

2015年6月13日 星期六

Warning when Resolving Conflicts in Git

因為在公司出過兩次包了,覺得自己實在太智障了,為了嚴逞自己的錯誤,所以來這邊紀錄一下,希望有看到的人不要再跟我一樣。
基本上處裡Conflict的過程可以看到"連猴子都能懂的Git入門指南"的這兩篇 合併修改紀錄解決衝突
簡單說明一下情境:

  1. 大概有一段時間沒有跟遠端伺服器做合併的動作,只有Commit自己修改的東西到Local Reposity。
  2. 突然某天想Pull和Push跟遠端伺服器同步一下,結果發現有衝突了,基本上應該是Local Reposity和遠端伺服器上的Reposity兩邊有修改到相同的檔案,Git不知道該怎麼自動把遠端伺服器上的紀錄合併到自己的Local Reposity?
  3. 這時就得自己手動合併,重點來了,手動合併老實說是相當危險的事。然後注意這時在處理衝突的動作,都是在Working Copy的動作。衝突Resolve後,必須再Commit到Local Reposity上,最後再Push到遠端伺服器。
    注意:這時要解決衝突,一定會打開修改過檔案的列表視窗(TortoiseGit叫Check for modification),這時會發現莫名一堆檔案是被修改過的,而且不是自己改的。這些就是你當前Local Reposity和遠端伺服器上的Reposity的差異檔案,
    千萬不要把他們Revert!!千萬不要把他們Revert!!千萬不要把他們Revert~~~~
    我因為有潔癖,覺得Git怎麼會亂改我Working Copy的東西,覺得很不爽的全都Revert了之後,Commit加Push。隔天就出現哀嚎聲……
    照理說這些差異檔案,一般沒出現衝突的話,其實Git會自動幫你合併並Commit到你的Local reposity,如果Pull完後再看Git Log的話,就會發現一個Commit的log紀錄。所以如果當出現衝突後,Git無法知道怎麼合併,自然就只能把遠端檔案修改到Working Copy之後,由使用者自己決定哪些資料要合併,並且由使用者自己Commit合併之後的差異檔案到Local Reposity。
    如果在這時你把合併的資料Revert或亂搞,Commit並Push,因為你做了合併的動作,所以Git認為你的資料才是最新的,所以當你Push到遠端伺服器後,遠端伺服器的Reposity自然就會以你的更動為主,所以自然而然的遠端伺服器的資料也跟著被Revert或亂搞了。
  4. 最後結論就是,建議Pull之前先備份Working Copy內修改過的檔案,可以做Stash或者自己手動備份,不然Pull合併後看哪些要Commit就很麻煩,然後只處裡衝突的檔案就好,不要傻傻亂動其他人的東西…………

以上就是我搞爆公司Git的經歷與心得,希望有機會幫到看到這篇的人T_T

Unity render order

基本上Unity render的順序主要由三個地方來依序檢查:

  1. Camera Depth: camera render 順序,Depth越小代表越先畫,而Depth越大的Camera,一律蓋在Camera Depth小的object上面。
    image 
    那Camera會畫哪些東西由Culling Mask決定,所以每個gameObject所選的那些Layer層,只是Camera用來Group哪些gameObject該畫,哪些不該畫。
    image
  2. Render Queue: 代表gameObject在當前Camera內畫的順序,RenderQueue越小的越先畫,而後畫的一定都蓋在先畫的上面。可由Shader或Material指定。
    預設都由Shader指定,Material的RenderQueue會直接以Shader指定的作為預設。
    http://docs.unity3d.com/Manual/SL-SubShaderTags.html
    也可由程式抓到gameObject.render的Material來指定自己的RenderQueue
  3. http://docs.unity3d.com/ScriptReference/Material-renderQueue.html
  4. Transform position的z值: 當以上1和2都相同的話,則就比對Z值。那Z值誰大誰小造成誰先畫,則看你Camera朝向的方向了。反正通常越靠近鏡頭的越先畫。

通常一般UI或3D object其實都可以使用相同的RenderQueue去畫,再調Z值就好。 可是像有些Particle System希望一定要蓋到UI上,而且Unity的Particle System不能控制Particle要移動多少Z值,所以常常穿插在UI或3D object中間,造成閃爍問題(因為Z值靠太近)。這種時候一定要有個Script動態可以去調整RenderQueue,才可以蓋在所有東西的上面。

那目前許多公司都用NGUI(3.x版),雖然每個UIWidget都是用它自己所謂的Widget depth來調整,但是它事實上還是自已用RenderQueue來管理,可以使用它的Drawcall Tool來看它每個Panel底下所畫的Widget的RenderQueue
imageimage

2015年3月24日 星期二

Monobehaviour Execution Order of Event Functions

今天考試又被問到對Unity從來沒有查明的問題,就是Monobehaviour裡面原本被已經決定好的function的執行順序,像是Awake或者Start之類的,原本以為只有一些簡單的Awake、Start、OnEnable、Update、OnDisable、OnDestroy這幾個,查一下Unity官網,想不到還真多。
唯一讓我覺得奇怪的是Start竟然比OnEnable晚,真是奇怪.....

2015年3月9日 星期一

C# value type or reference type

在C# type內有些data type常常搞錯,不知道是value type或者reference type,因為考試又被電一次,在這備註一下。
主要是reference type才有可能為null。

string testValue;
if(testVlaue != null)
{
//是否會進來
}
在這邊因為string是reference type,只是他的operator"="被改寫成是用copy value的方式,看起來很像value type。所以這邊的if不會進來。


decimal testValue;
if(testVlaue != null)
{
//是否會進來
}
decimal是value type所以這個if永遠都會進來。

DateTime testValue;
if(testVlaue != null)
{
//是否會進來
}
DateTime這個data type也是令人意外的type,他是value type,所以這個if永遠都會進來。

int? testValue;
if(testVlaue != null)
{
//是否會進來
}
這個int?就是reference type,所以這個if不會進來。

Tread start problem

今天考試被電了一回,題目是
public class A
{
    public int count = 0;
    public void AddCount(int value)
    {
        count += value;
    }
}

public void test()
{
    A recordA = new A();
    Threand thread = new Thread(delegate()
    {
        recordA.AddCount(1);
    }

    thread.Start();
    recordA.AddCount(1);
}

這邊是因為thread.Start()時,就有可能和下一行recordA.AddCount(1);同時執行,並且在拿count這個變數的值時,有可能都拿到相同的值。所以這邊應該在AddCount function內加lock。

2014年11月4日 星期二

Unity script hash code not compatible

1. prefab attach的script更新之後,將prefab包成Asset bundle,包出去之後。下次包版時,script有異動,但是即使Untiy 做Save project的動作,都不會造成prefab本身有任何異動,所以只有script再包出去,結果新的程式讀到舊的AssetBundle,就會造成錯誤。Untiy報的錯誤是 script hash code not compatible。

2014年10月31日 星期五

Unity mono Awake() and Start() timing

Awake呼叫的時機點在於Component被產生時的那瞬間。
那什麼時候代表Component產生時的那瞬間:

  1. GameObject第一次Active的時候,就會呼叫所有Attached Components的Awake。
  2. 用程式呼叫AddComponent的時候


Start呼叫的時機點在於Component第一次Enable時的那瞬間。
那什麼時候代表Component Enable時的那瞬間:

  1. GameObject上有Implement Start或Update function這種的,在Inspector上就可以看到Script名稱旁有個Check Box,當GameObject第一次Active的時候且Check Box也是打"V"的狀態,就會呼叫Start
  2. 用程式呼叫Component.enabled = true時



Awake一定比Start還早,而且是場景上所有的Awake都結束後,才會呼叫Start。而預設各Component執行Start的順序等同各Component執行Awake的順序,除非在Awake動態改變Component的enable順序。
而Awake和Start最大的差別就在於一種情況:
當GameObject為Active,但是Component是Disabled的狀態,Awake會呼叫的但是Start是不會的。

GameObject上的Components彼此Awake的順序是?
理論上應該是新增Component的順序,但是基本上建議不要依賴這個順序,因為常常會忘記........雖然在Unity工具列的Edit->Project Settings->Script Execution Order來決定,但是一樣不方便。

GameObject之間的Components彼此Awake的順序是?
Unity並未定義GameObject之間的執行順序,一樣建議不要仰賴GameObject的Hierarchy來決定Components之間Awake的順序。


如果真的Components之間或GameObjects之間在Initial之間是有耦合狀態的,建議還是一個Master Script來管理,看是要動態新增GameObject或是動態新增Component,亦或是動態Enable GameObject或Component。




2014年10月15日 星期三

unity render material and shared material

Unity內的render.sharedMaterial,代表的是所有Unity內都使用這份,每次更換屬性時,如果這份是存在Asset內的話,就會發現這份也被修改過(用SVN就可檢查是否被修改)。

用Material xx = render.material,代表是untiy所複製的一份material,每次更換這material的屬性只會改到這份複製的。如果想要讓更改的資料回render,就要做render.material = xx

因為實在太危險了,所以特此記上!!!!!

2014年6月4日 星期三

why grandson class should not call base.base.method?

主要問題是為什麼子類別不能直接呼叫base.base.method?


主要原因就是破壞物件封裝。假設今天有個collection-item的behavior class,有個child class是只能收集red item,而grand child class是只能收集big red item。如果今天grand child class不想透過parent檢查是否為red item,而直接呼叫grand parent add item,是不是有點奇怪?

public class Items
{
    public void add(Item item) { ... }
}

public class RedItems extends Items
{
    @Override
    public void add(Item item)
    {
        if (!item.isRed())
        {
            throw new NotRedItemException();
        }
        super.add(item);
    }
}

public class BigRedItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        if (!item.isBig())
        {
            throw new NotBigItemException();
        }
        super.add(item);
    }
}
public class NaughtyItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        // I don't care if it's red or not. Take that, RedItems!
        super.super.add(item);
    }
}




2013年10月24日 星期四

NGUI Atlas


  1. why use atlas?
    因為atlas只要一個draw call,但是會造成這張atlas內的圖的z值深度都是一樣的
  2. 不會減少圖的size
  3. 壓縮的部分在Texture的import type改成"Advenced",取消掉MipMap(因為是使用平行視角的camera)所以不需要,而圖片壓縮的Default Type改成AutoCompressed,再到各個版本去修改Default值
    (1)IOS的地方勾起Override,並且壓縮格式改成RGBA Compressed PVRTC 4,而壓縮程度改成Best。
    (2)Android的部分勾起Override,這邊根據圖片是否需要帶有Alpha透空的效果,沒有的話選擇RGB Compressed ETC1,有的話則選擇RGBA Compressed DXT5,主要是因為現行還是有部分爛手機沒有支援DXT5,不然理論上還是都選DXT5。Unity已出了新版的選項,可選擇RGB Compressed ETC2(需要OpenGL ES 3.0以上),可讓圖片帶有Alpha的也可做壓縮。如果真的想連舊型手機都要支援的話,得將原圖RGB及Alpha分成兩張圖並使用RGB Compressed ETC1,再改寫NGUI  UITexture的shader傳這兩張圖進去
  4. 最主要的Atlas還是會造成管理上的問題,因為有些會合併,有些會有兩張以上的Atlas,其實相當麻煩,目前還沒有想到好的方式來去做管理,又可以省到效能的方式。

2013年6月23日 星期日

Serialization in ios with Unity

1.         錯誤的Serialize程式碼
[Serializable]
public class MyClass { …………………}

// Same as
XmlSerializer 
MyClass myClass = new MyClass();
IFormatter formatter = new BinaryFormatter();//
MemoryStream stream = new MemoryStream();
formatter.Serialize(stream, myClass);

Error Message in ios:
Unhandled Exception: System.ExecutionEngineException: Attempting to JIT compile method 'UInt32__TypeMetadata:.ctor ()' while running with --aot-only.

2.         What JIT and AOT
   
是兩種不同的compile方式。主要用於在intermediate language(解譯式語言)像是java或者.NET Common Intermediate Language (managed code)
   
因為像這種intermediate language都要經過一道解譯後,執行起來就會比較慢。這兩種方式都是用來改善效能的,AOT是預先將byte code直接與VM compilemachine code,而JIT則是在解譯時動態的預先做一些cache或分析來達到效能最佳化。
AOT: http://en.wikipedia.org/wiki/AOT_compiler
JIT: http://en.wikipedia.org/wiki/Just-in-time_compilation
4.         Another Serialize library
I.          JsonFX: http://www.jsonfx.net/
II.         Protobuf-net: https://code.google.com/p/protobuf-net/
5.         Using Protobuf-net in ios -http://wiki.etc.cmu.edu/unity3d/index.php/Serialization_issue_on_iOS
I.           下載protobuf-net r640版本
II.         將要SerializeData class額外用一個dll library專案,並加上protobuf-net特定的Annotation。這邊Referenceprotobuf-net dll要則選擇core onlyios內的dll
[ProtoContract]
    class MyDataClass {

       
[ProtoMember(1)]
        public int Id {get;set;}
    }
Note:不可有任何不是public的變數或method,否則在下一個Step時會出現non-public Exception.
III.       將輸出的data dllprotobuf-net.dll放在相同資料夾,使用protobuf-net內附帶的precompile.exe產生Serializerdll檔,下cmd指令為precompile {path}\ModelName.dll -o: {path}\SerializerDllName.dll -t:Namespace.SerializerTypeName
IV.      最後將三個dll(data classdllSerializerdllprotobuf-net dll)同時給其它專案Reference即可
Note:這邊要注意dll dependency的問題。因為像現在的Unity專案會使用ResouceLoder來讀const data,但是ResouceLoder class必須要放在Unity專案才行,如果的data classDllreferenceResouceLoder,這樣就會造成dll相互依賴。
6.        Serialize Library比較

C#
BinaryFormatter
protobuf-net
JsonFX
Reference
V
V
X
Extend
V
V
V
Polymorphism
V
V
X
List, Dictionary
V
V
V
In IOS
X
V
V
結論
無法使用在IOS
需要額外使用precompile
無法使用在複雜的資料結構上


2013年2月2日 星期六

Circle shape radial blur

圓狀的放射模糊,越靠近圓心的一定越清楚,離圓心越遠的越模糊。意思是說靠近圓心的貢獻度就較高,離圓心越遠的就越低。

假設一個二維的圓心是O,半徑是r,那在圓的任一點p,我們可以用 得到該點p離圓心佔半徑多少百分比。所以我們可以用(1 - ( |p - o| / r) ) * 點p的某個數值(像是顏色之類的),這樣就可以決定離越遠的點最後的數值要多少了。

實際例子:
圓形的地表筆刷在做模糊的方式就是這樣。假設我們現在要塗的Vertex顏色是紅色,所以如果我們算出來的(1 - ( |p - o| / r) ) = X好了,套用到我們這篇所講的Terrain Vertex Color公式,最後公式結果像這樣
X * strength * color + (1 - strength * X) * last time color = Final Vertex color
這樣就會看到紅色從圓心的中間慢慢的變淡紅,最後變成接近跟背景色是相同的。

Learn new program language skill

  1. 找到好的開發環境,像是Compiler和Editor
  2. 上找些基本的Hello word、Example或Tutorial文件
  3. 學習基本的資料型態使用
  4. 試著建立一個基本的hello word的環境
  5. 找到API文件
  6. 試著看有沒有基本的Debug方式,像是中斷點之類的

2012年12月30日 星期日

Terrain Vertex Color

Vertex color如果我們希望有透明水彩的樣子,會透到舊的Vertex color的話,就會用顏色做lerp,會有強度做為參數,調整與舊有的顏色做加總,公式如下:
strength * color + (1 - strength) * last time color = Final Vertex color
其實就是alpha blending的公式。(上面的數值應該都保持0~1之間)

最後跟我們一般的Diffuse做相乘(應該很少會讓地表的vertex color直接覆蓋掉Diffuse)。

那因為做這種乘色的動作,通常都會降低整個顏色的亮度,所以應該還要有個亮度筆刷,其實這種亮度應該要說是最後顏色的強度,也就是在跟Diffuse做相乘時,還會再乘個數值,讓最後的顏色結果整個再提亮。公式如下:
strength * Brightness + (1 - strength) * last time Brightness  = Final Brightness
FinalVertex color * Diffuse *  Final Brightness = Final Diffuse
這邊從公式就看的出來,如果要提亮的話,Brightness一定要是要大於1的,當然小於1的話還是可以把它刷的更暗。
(如果改成FinalVertex color * Diffuse + Final Brightness這樣的話,看起來會有點像是粉筆筆刷的感覺,兩者就差在粉筆筆刷亮度提高後會容易看不到最後Diffuse的顏色)


這邊要注意的是資料儲存的問題,可以看到Color data in shader這篇。因為通常我們使用地表筆刷刷地表的話,一定會有內外圈,內圈和外圈中間通常是做smooth的動作,所以如果我們使用Unsigned integer來儲存資料的話,那經過smooth(如何做Smooth的動作可看到
Circle shape radial blur這篇)的話,一定會卡掉一些資料,那整個刷出來的感覺就會變成一圈一圈的,不會是一個模糊的放射狀。所以最後儲存的結果就要看到底是要存成一個image還是一個自訂binary格式。如果是image,因為最後一定是存成unsigned integer,一定還是有數值誤差的問題。那如果存成自訂binary格式的缺點就是不能編輯,彈性不夠高。




2012年12月29日 星期六

Color data in shader

一般送進shader的color stream,如果要省顯示卡記憶體空間的話,可以考慮使用unsigned integer的方式送進去。通常空間會有三倍的差距。

目前不知道format在DirectX會叫什麼,但是Gamebryo的話,在使用color stream時element format就叫F_NORMUINT8_4,並且在Gamebryo的預設Material class內會有個SetupPackingRequirements這個function,專門用來設定套用這個Material的Mesh有哪些stream可以送到shader內(這樣的好處是不管Mesh有哪些資料,只要套用到自訂的Material就可以決定說我們只要用Mesh的哪些資料,這樣就不用全部送到shader內浪費顯卡記憶體空間),記得在這邊設定Shader declaration的話要使用SPTYPE_UBYTECOLOR。

然後在shader裡面的color register宣告還是可以宣告成float4,shader會自動幫忙正規化到0~1之間。

這邊要注意使用unsigned integer的動作,可能會有數值誤差的問題。如果color資料需要事先在CPU內計算,並且儲存的格式是unsigned integer,而且color資料會在CPU內先用一些浮點數運算,因為要轉成unsigned int而去掉小數點的數值,然後最後送到GPU內,並且在GPU內又要使用color做運算。注意這邊在GPU的時候拿到的數值,就是已經被去掉部分小數,又轉成float的數值了,這樣的值一定會比整個過程都用float資料來做運算處理的值來的有差距,所以最後出來的畫面一定有落差。

2012年12月1日 星期六

C# with Unmanaged c++ debug setting

如果起始專案是C#的話,而用到其他的專案有些是Unmanaged c++的DLL,預設如果想下中斷點在這些Unmanaged c++的專案,是無法使用的。得在起始專案的屬性內設定->Debug頁面->Enabled Unmanaged C++ debugging。

2012年10月9日 星期二

Debug DLL

Compiler DLL還是會產生.pdb檔,這個pdb檔會在compiler時候產生,做為Debug時可看到Source code之用。通常pdb檔放在跟dll檔同一個資料夾,就會自己去抓到。但是如果放在別的資料夾就要去設定symbol file放在哪裡。
設定方式可以參考到這:
http://msdn.microsoft.com/en-us/library/x54fht41.aspx

VS2008的從MSDN的說明來看,是在Tools->Options->Debugging->Symbols裡面設好就可以了。
另外看有沒有設到正確的抓到PDB檔,可以在VS Debug執行時,Debug->Windows->Modules內可以看到所有DLL載入的情況。

2012年9月19日 星期三

NiMesh composition


NiMesh are the array of NiDataStreamRef objects that contain all varying attributes of the mesh (vertices, indices, etc)

Element - An element is an array of data that supplies a single input variable (varying or uniform) to a shader program. Each stream element defines a format and an offset where a particular element may be found for a given stream. Multiple elements may be interleaved(插入) into a single stream.
這邊指的是因為我們的NiDataStream是一連串的資料,假設有十組頂點資料,每組內的資料包含了當前的頂點位置,法向量,貼圖座標,前面這三項我們稱為Element

Region A Region defines a contiguous subset of a stream. The elements must be the same across all regions in a stream. Every region contains the following data:
-the base offset into each stream
-the active number of elements for the Region
Region指的就是data stream內的某部分資料範圍

Mesh裡面是很多個data stream ref所組成的。
似乎一個mesh裡可以用好多個,不過我想通常剛好分成各個Sematic各一個stream。應該很少會有相同的Sematicdata stream。不過看起來好像可以多個相同的Semantic data stream。可能主要是共用頂點資料八。但是一個mesh裡只可以有一個USAGE_VERTEX_INDEX 用途的stream

Semantic就是送到Shader內,他要知道目前送來的資料是頂點位置,法向量,還是貼圖座標,這個information就是Semantic
Semantic index:指的像是TEXCOORDINATE0, 1, 2, 3這種暫存器的index

NiDataStreamCreateDataStream,然後就可以丟NiDataStreamElementSets就可以了。

NiDataStreamElementDescribes an element of a NiDataStream object, by data type, format, and offset.

NiDataStreamElementLock是用來可以從指定的Mesh取得某一個特定的semantic的資料,並且可以用來抓到meshsub-mesh的某特定semantic資料的起始位置和結束位置。所以要注意它是已經被綁定的。

NiDataStreamPrimitiveLock是用來可以從指定的Mesh取得某一個特定的primitive的資料index資料,像是LineTriangle這種,都是Primitive
Element的話應該可以指定單一某種Semantic的資料全都抓出來的樣子。而Primitive是以Primitive來分,所以Primitive可能會有POSITION and COLOR的資料都有。

NiTStridedRandomAccessIterator 就是用來抓element內的資料的。
Semantic - The elements must be the same across all regions in a stream


NiDataStreamRef是用來記錄一個data stream裡面資料代表什麼資料的範圍,及語意,NiDataStreamRef可以多個對一個data stream


NiDataStreamElement::Type 資料型態int or something
NiDataStreamElement::Format 資料擺放格式,可能是xyzw,或者zyxw這種擺放格式。