顯示具有 Unity 標籤的文章。 顯示所有文章
顯示具有 Unity 標籤的文章。 顯示所有文章

2014年12月13日 星期六

[Spine] Spine匯入Unity,使Spine中的Root motion在Unity中實現

Spine進Unity,是不會像FBX一樣可以把FBX中的Root motion直接當作Unity中Game object的Root motion,因此需要額外自己寫script實現,在Spine Forum中有一篇討論串有討論到這個問題:
Applying Root Motion
Mitch 提供了一個解決方案
Had to modify SkeletonAnimation and add a callback between Update and Apply because the Time and LastTime are set to be equivalent by the time UpdateBones is called.
修改了SkeletonAnimation.cs以及SkeletonRootMotion.cs兩個檔案,實現X軸的Root motion。但是我的需求是X及Y軸都需要實現,因此對SkeletonRootMotion.cs做了一些修改,修改後的結果post在Forum中,在這邊補充一下,我不是Programmer所以Code可能不盡完善、正確...
http://esotericsoftware.com/forum/viewtopic.php?f=7&t=1364&start=26
也可以在我的GitHub上查看,檔案內直接保留了Mitch原始的code,可以直接做比對:
https://github.com/januswow/TAG/blob/master/TouchActionGame/Assets/spine-unity/Assets/spine-unity/SkeletonRootMotion.cs
主要的重點是Spine的Frames Data是以[time, x, y, ... time, x, y]的格式寫入,因此在GetXYAtTime()時,frames變數(timeline.frames)的取用需要注意:
float lastFrameX = frames[frameIndex - 2];
float lastFrameY = frames[frameIndex - 1];
另外一個題外話,由於Spine輸出的資訊完全不同於FBX,因此尚無法搭配Mecanim系統,不過nicloay有寫一個Importer,使Spine能夠在Mecanim中運作,可惜的是無法支援全部的資訊,如FFD等...
http://esotericsoftware.com/forum/viewtopic.php?f=11&t=2012

動作流程控制參考:

2014年11月2日 星期日

[Spine] AnimationState Events

在Spine的Runtimes中,AnimationState有幾個Events可以使用。

  • Start
  • Complete
  • End
  • Event
這幾個事件的觸發時機是:
  • Start
    當一個動作開始播放時(在此推測應該是開始Mixing的時候,尚未驗證…)
  • Complete
    當動作(Animation)完成完整的播放時,若是Looping的Animation則會在每個Loop結束時觸發一次。
    • 每個循環結束時觸發
    • 當動作完整的播放完畢時
  • End
    當動作結束時,其他與Complete相同
    • 每個循環結束時觸發
    • 當動作完整的播放完畢時
    • 當動作結束時(例如在動作播放途中被切換成其他動作)
    • 動作被終止或者清除掉時
  • Event
    當Event Track有設置Event時(在此指Spine Editor中設置的Event)(尚未確認)
在使用End事件時需要注意,不可在End事件中做SetAnimation的動作,因為SetAnimation後動作被打斷又會再觸發End事件,造成無限迴圈的狀況。

Complete與End是Pharan在Spine Forum中所做的說明,來源如下

2013年10月7日 星期一

[Unity] Particle System使用Mesh時,在畫面外不會Render的解決方式

  近日在幫忙解決一個Particle System(Shuriken)的particle消失的問題,遇到的情況是當Particle System的Render Mode使用Mesh,且Particle System的pivot是在畫面外時,mesh不會被render。這是Particle System本身的一個bug,理應要有個Always Render之類的選項來避免off-screen的時候被cull掉。(舊版的particle系統中則是沒有這個問題)

  在經過測試之後,得出兩種解決方式:
  1. Simulation Space - World
    在Particle System的基本設定中,把Simulation Space設定為World。而必須使用Local Space的particle就可使用第二種方式。
  2. Sub Emitters
    啟用Sub Emitters Module,在Birth新增一個sub emitter,這個sub emitter可以是deactive的。



2013年9月6日 星期五

[Unity][Mecanim] 角色處理注意事項


以下為作業上的一些紀錄。

角色輸出與輸入


  • 3ds Max的單位設置為 1 Unit = 1 Centimeters
    • 如此在不改變Asset的Scale Factor狀況下(在Asset的Import Settings中,預設為0.01),Max中的一公尺會與Unity的一公尺相等,這是最符合物理的單位設定,可避免物理運算出現奇怪的現象。
  • 讓角色在DCC Tool中面向Front View(從Front View看到的是角色的正面),如此匯入Unity後,角色的正面會與GameObject的Forward方向一致。
  • 輸出骨架模型FBX檔案時,雖然不會勾選Animation項目,但是其子項目Skins必須要是被勾選著的,否則不會帶出Skin資料(3dsMax 9)。
  • 非Biped-based角色(只由Bone/Dummy...組成的Rig)在輸出動作檔案時,需保留一個Skinned mesh一同輸出,否則會發生輸出的FBX檔沒有Root Node的狀況。
  • 骨架請勿帶有negative scale,因為目前的mechanim並不支援negative scale的blending。
  • 當你的角色的root node不是skeleton而是dummy之類的物件時,在Unity的FBX Import Settings需將Animation設置為Store in Nodes,原因尚未探究(參考
  • 物件的命名不要跟輸出的FBX檔名相同(不是Child的物件),否則會出現flagsCount >= mask.m_Count錯誤訊息,而Rig的Root Node若取名與檔名相同則會出現Look rotation viewing vector is zero錯誤訊息(參考)。
  • Twist bone的父物件必須為該部位的主要骨頭
Mechanim系統
  • 在進行FBX Import Setting的設定時,在Animation的部份要注意Root Transform的設定,勾選的項目將不會對角色產生實際的位移旋轉效果(成為假動作的狀態)。
  • 當角色的動作本身有做位移時,如果動作與動作間的Blending設定吃掉了部份的時間區段,將會喪失那個時間區段內的位移距離,可能會導致位移效果有誤差的狀況。

相關文章

[Unity] 效能上需要注意的事項


在此整理一些有關效能上需要注意的問題,主要資訊來源是Unity的Manual,其中可能有許多地方理解錯誤,請多包涵與指正。
  • Mesh

    • Mesh的渲染是由Renderer Component擔當,一個Renderer會消耗一次運算,當一個角色被拆分成N個Meshes,也就會有N次的運算,因此要儘可能的減少Mesh的數量。
    • GameObject上被Deactive的Renderer Component,依舊會佔用記憶體,只是不會被呈像出來。
    • Mesh的Hard Edge上的點,因為Vert的Normal不一致,所以會被視為多個Verts,而UV Border Edge上的Vert也是一樣,在渲染流程上會被視為多個點。在Unity中所顯示的Verts、Tris就是這個真實的數量(送給OpenGLES的點的數量),所以才會有與DCC Tool中所顯示的數據不同的現象。
參考資料


  • Compression Assets
    • Unity在匯入資源時,會自動進行壓縮,而你可以在Preference中取消這項設定讓編輯時可以省下壓縮的等待時間,但在建立遊戲時是一定會進行壓縮的,不受該項設定影響。
  • Mesh Import Settings
    • Mesh Compression可以減少檔案所佔容量,但不能減少運算時的記憶體消耗。
    • Mesh Optimization會優化點的順序增進GPU運算效能。
  • Textures
    • Normals、Tangents:
      若物件沒有使用到Normal map及Real-time Lighting的話,可以取消Normals。而物件沒有使用到Normal map的話,可以取消Tangents。
    • Anisotropic Filtering:
      是針對當Mesh的面與視角的夾角相當小時會獲得較好的貼圖品質(通常用於地板),但也會增加GPU的運算消耗,在Quality Settings中可以針對所有的貼圖進行設定。
    • 在Unity中貼圖必須為二的冪次方,非二的冪次方貼圖通常只用於GUI。
    • Unity沒有限制貼圖必須為正方形,然而在iOS平台有這項要求,因此製作貼圖時須遵守正方貼圖原則。
參考資料




  • Materials:
    • 在一個GameObject、Renderer上使用的Material是越少越好,最好只使用一個,每個Material都會增加一個Draw call。
  • Skinned Mesh:
    • Unity使用1、2、4根骨頭來計算控制一個頂點,因此在DCC Tool中進行綁定時,一個頂點的權重最多包含4根骨頭即可,在遊戲中實際使用幾根骨頭做運算是依據Quality Settings中的設定。
參考資料




  • Bones:
    • PC遊戲通常的骨頭數量是介議於15~60根間,而行動平台建低於30根。(Blade&Soul的玩家角色使用約130根左右,其中包含臉部、附加骨頭)若人物有使用IK,在匯入Unity後因已經過Bake程序,應把IK清除掉必免占用運算。
  • Animation Compression:
    • Keyframe Reduction
      在匯入時減少Keyframs數量,能夠減少檔案尺寸及記憶體消耗,而Compression只能減少檔案尺寸
  • Combine Children:
    • Combine Children這個Script可以進行combine meshes的動作,而要combine的物件得使用同一張圖,這個處理可以有效降低draw calls(將多個使用同一張貼圖同一個材質的Meshes合併為一個Mesh)。

//

Manual中有優化圖像效能的指引

CPU: optimizing Draw call count
GPU: optimizing Model geometry

http://docs.unity3d.com/Documentation/Manual/OptimizingGraphicsPerformance.html

[Unity] Scroll and Turbulence Shader (流動加擾動效果)

這是我的第二個Fragment Shader,在此作紀錄供參考。
參考文章


此Shader的表現效果:


Inputs
MainTex為一張transparent texture,OffsetTex則是一張noise texture,Shader會以OffsetTex擾動MainTex,擾動的程度由Distortion控制,速度則由TurbulenceSpd控制。另外MainTex本身也會做流動,流動速度由ScrollSpd控制。
Inputs
Fragment Shader (without normals)

 Properties 
 {
  _MainTex("_MainTex", 2D) = "black" {}
  _OffsetTex("_Offset", 2D) = "black" {}
  _Distortion("_Distortion", Float ) = 0.1
  _TurbulenceSpd("_TurbulenceSpd", Float) = -50  
  _ScrollSpd("_ScrollSpd", Float) = 30
 }
 
 SubShader 
 {
  Tags
  {
   "Queue"="Transparent"
   "IgnoreProjector"="True"
   "RenderType"="Transparent"
  }
  
  Cull Off
  ZWrite Off
  Blend SrcAlpha OneMinusSrcAlpha

  Pass {
   CGPROGRAM
   #pragma vertex vert
   #pragma fragment frag
   #include "UnityCG.cginc"
   
   struct appdata {
    fixed4 vertex : POSITION;
    fixed2 texcoord : TEXCOORD0;
   };
   
   struct v2f {
    fixed4 pos : SV_POSITION;
    fixed2 uv : TEXCOORD0;
    fixed2 uv2 : TEXCOORD1;
   };
   
   fixed4 _MainTex_ST;
   fixed4 _OffsetTex_ST;
   fixed _Distortion;
   fixed _TurbulenceSpd;
   fixed _ScrollSpd;
   
   v2f vert (appdata_base v) {
    v2f o;
    
    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    
    // Offset _MainTex uv
    fixed mainTexOffset = _Time * _ScrollSpd;
    //o.uv.x = v.texcoord.x * _MainTex_ST.x + _MainTex_ST.z;
    //o.uv.y = v.texcoord.y * _MainTex_ST.y + _MainTex_ST.w + mainTexOffset;
    o.uv.x = v.texcoord.x;
    o.uv.y = v.texcoord.y + mainTexOffset;
    
    // Offset _OffsetTex uv
    fixed offsetTexOffset = _Time * _TurbulenceSpd;
    //o.uv2.x = v.texcoord.x * _OffsetTex_ST.x + _OffsetTex_ST.z + offsetTexOffset;
    //o.uv2.y = v.texcoord.y * _OffsetTex_ST.y + _OffsetTex_ST.w;
    o.uv2.x = v.texcoord.x + offsetTexOffset;
    o.uv2.y = v.texcoord.y;
    
    return o;
   }
   
   sampler2D _MainTex;
   sampler2D _OffsetTex;
   
   fixed4 frag(v2f i) : COLOR {
    // convert OffsetTex's color to luminance to distort MainTex's uv
    fixed4 offsetTexColor = tex2D(_OffsetTex, i.uv2);
    fixed lum = Luminance(offsetTexColor);
    fixed2 texCord = ( lum, lum );
    
    fixed4 mainTexColor = tex2D( _MainTex, i.uv + texCord * _Distortion );
    
    return mainTexColor;
   }
   
   ENDCG
  }
 }

  • tex2D返回的是color值(r, g, b, a)


Surface Shader ( Need Normals)

  CGPROGRAM
  #pragma surface surf NoLighting 
  #pragma target 2.0
  
  sampler2D _MainTex;
  sampler2D _OffsetTex;
  float _Distortion;
  float _Speed;
  float _MoveSpeed;

  struct EditorSurfaceOutput {
   half3 Albedo;
   half3 Normal;
   half3 Emission;
   half3 Gloss;
   half Specular;
   half Alpha;
  };
  
  fixed4 LightingNoLighting(EditorSurfaceOutput s, fixed3 lightDir, fixed atten)
     {
         fixed4 c;
         c.rgb = s.Albedo; 
         c.a = s.Alpha;
         return c;
     }
   
  struct Input {
   float2 uv_MainTex;
   float2 uv_OffsetTex;
  };

  void surf (Input IN, inout EditorSurfaceOutput o) {
   
   // pan OffsetTex
   fixed time = _Time * _Speed;
   fixed2 UV_PanY = fixed2( IN.uv_OffsetTex.x + time, IN.uv_OffsetTex.y );
   
   // pan MainTex
   fixed moveTime = _Time * _MoveSpeed;
   fixed2 UV_PanMove = fixed2( IN.uv_OffsetTex.x, IN.uv_OffsetTex.y + moveTime );
   
   fixed2 OffsetTex2D = tex2D( _OffsetTex, UV_PanY.xy );
   fixed2 distorted = OffsetTex2D * _Distortion;
   fixed2 Add1 = UV_PanMove + distorted;
   
   fixed4 MainTex2D = tex2D( _MainTex, Add1.xy );
   
   o.Albedo = MainTex2D;
   o.Alpha = MainTex2D.a;
  }
  ENDCG

2013年9月5日 星期四

[Unity] Read/Write Enabled

Unity匯入的Asset中,Texture與Model的Import Settings裡都會有Read/Write Enabled的選項,這個選項的作用是:
  • Texture:
    • Select this to enable access to the texture data from scripts (GetPixels, SetPixels and other Texture2D functions). Note however that a copy of the texture data will be made, doubling the amount of memory required for texture asset. Use only if absolutely necessary. This is only valid for uncompressed and DTX compressed textures, other types of compressed textures cannot be read from. Disabled by default.
    • 當有scripts會從Texture存取texture data時,會需要勾選Read/Write Enabled,但這個選項會使Unity需要一份額外的copy,增加記憶體的消耗。此選項預設是關閉的,也只適用於未壓縮或DXT貼圖格式的貼圖。
  • Model:
    • Enables the mesh to be written at runtime so you can modify the data; it makes a copy in memory. When this option is turned off, it saves memory since Unity can unload a copy of mesh data in the game. However, if you are scaling or instantiating meshes at runtime with a non-uniform scale, you may have to enable "Read/Write Enabled" in their import settings. The reason is that non-uniform scaling requires the mesh data to be kept in memory. Normally this is detected at build time, but when meshes are scaled or instantiated at runtime you need to set this manually. Otherwise they might not be rendered in game builds correctly.
    • 當你需要Runtime修改Model資料時,會需要開啟Read/Write Enabled,同樣會需要一份額外的記憶體消耗,而值得注意的是當Model在Runtime有被做非等比縮放時,也會需要開啟此選項。
    • 使用Mesh Combine...功能時需要開啟Read/Write Enabled。
參考資料
參考文章

2013年9月4日 星期三

[Unity][Mecanim] flagsCount >= mask.m_Count 錯誤訊息的解決方法



  在處理Mecanim的角色時,有時候會遇到上面的錯誤訊息,經過一番搜尋在Unity Answers上找到了錯誤的原因:


  FBX檔案內的物件命名跟FBX檔名命名相同。另外也發現如果Rig Root Node的命名跟FBX檔名相同的話會產生以下的錯誤:


  因此在DCC端處理物件的時候,要注意物件的命名都不可以跟FBX檔名重複,雖然child物件並不會造成這個問題,但還是應該嚴謹一點依照此原則做命名。


  而當要修正此問題時,似乎要把原有有問題的檔案先刪除之後再Import修正過的檔案才能正確運作,若是直接覆蓋錯誤訊息依然會存在。

2013年9月3日 星期二

[Unity] Collider的Physics效能比較

  最近專案在進行效能調校時,為了要減少Physics的效能消耗,查到了freshcut在Unity Community中發表的實驗數據

在只考量Physics效能的時候,Static的Collider無論是哪一類型的Collider消耗都是非常低的,但在dynamic的狀況下,三種Collider對效能的消耗量則有相當的差距:
Box Collider > Capsule Collider >> Sphere Collider
Box Collider稍稍大於Capsule Collider的消耗,而Sphere Collider的消耗則是明顯的少了很多,因此在許可的狀況之下應盡量使用Sphere Collider。

參考文章


2013年8月29日 星期四

[Unity] 更改Asset的Import Settings後,執行ImportAsset以正確更新

  當利用script進行批次的Import Settings設定時,需注意設定參數完成之後得要重新Import Asset才能夠讓Unity成功判讀到你的更改,不然會發生在Inspector中的資訊是對的,但執行Save Project後卻還是沒有正確寫入到Prefab或meta檔案之中。

舉例:
// 設定貼圖檔案的圖片壓縮格式
textureImporter.textureFormat = TextureImporterFormat.PVRTC_RGBA4;
// 設定了Import Settings之後再使用AssetDatabase.ImportAsset進行asset的更新
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate );

參考資料

2013年8月1日 星期四

[Unity] Shader試作 可調整Rim Light效果的Fragment Shader

嘗試撰寫Shader的紀錄。
這是一個帶有類似Rim Light效果及Tint Color,並且可以與原始的Texture做Blending的不受光Fragment Shader,用於角色受擊效果。

Shader "Custom/Unlit Transparent Cutout With Color" {
 Properties {
  _Color ("Color (RGB) Blending (A)", Color) = (1,0,0,0)
  _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
  _Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
 }
 
 // Fragment shader by Janus Huang
 SubShader {
 
  Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
  Blend SrcAlpha OneMinusSrcAlpha
  Lighting Off
  
  Pass {
   CGPROGRAM
   #pragma vertex vert
   #pragma fragment frag
   #include "UnityCG.cginc"
   
   struct appdata {
    fixed4 vertex : POSITION;
    fixed3 normal : NORMAL;
    fixed2 texcoord : TEXCOORD0;
   };
   
   struct v2f {
    fixed4 pos : SV_POSITION;
    fixed2 uv : TEXCOORD0;
    fixed4 color : COLOR;
   };
   
   fixed4 _MainTex_ST;
   fixed4 _Color;
   fixed _Cutoff;
   
   v2f vert (appdata_base v) {
    v2f o;
    
    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    fixed3 viewDir = normalize(ObjSpaceViewDir(v.vertex));
    // 越是面向camera的vertex在dot計算後會獲得越大的計算結果
    fixed dotProduct = dot(v.normal, viewDir);
    // smoothstep用法同lerp, 但其曲線在頭尾都會趨緩
    o.color = smoothstep(0.5, 1, dotProduct);
    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    
    return o;
   }
   
   sampler2D _MainTex;
   
   fixed4 frag(v2f i) : COLOR {
    fixed4 texcol = tex2D(_MainTex, i.uv);
    // clip同AlphaTest
    clip( texcol.a - _Cutoff );
    // 受擊時的color計算 - 將貼圖加亮1.5倍後乘上dot計算後的黑白色(外黑內白)再加上_Color
    fixed3 hitColor = texcol.rgb * 1.5 * i.color.rgb + _Color.rgb;
    // 以_Color的alpha控制貼圖與受擊時的混合程度
    texcol.rgb = lerp( texcol.rgb, hitColor, _Color.a);

    return texcol;
   }   
   ENDCG
  }
 }
}


參考文章

2013年7月30日 星期二

[Unity] Assign Texture and Material (Editor Script)

複製已存在材質並該改貼圖後賦予至模型

// create material
Material newMaterial = new Material( Shader.Find("Diffuse"));
// assign shader
newMaterial.shader = sourceMaterial.shader;
// copy properties from existing material
newMaterial.CopyPropertiesFromMaterial(sourceMaterial);
// create texture from asset(asset path)
Texture texture = (Texture)AssetDatabase.LoadAssetAtPath( textureRelativePath, typeof(Texture));
// assign texture to material
newMaterial.SetTexture("_MainTex", texture);
// create material asset
AssetDatabase.CreateAsset(newMaterial, newMaterialAssetRelativePath);
// assign material to gameObject
AssignMaterial( gameObject, newMaterial );

在建立texture時需注意,在editor部份可以使用AssetDatabase.LoadAssetAtPath,而runtime則需使用Resource.Load

2013年6月27日 星期四

[Unity] 使用 Mecanim 需要注意的事情

此處紀錄我在使用Mecanim過程中所發現需要注意的一些地方,有些可能不正確或解說錯誤,僅供參考!

  • 骨架檔的root的x,y需要對正至原點
骨架檔的root的x,y需要對正至原點,否則動作blending時會有位移的誤差。
  • (Humanoid Rig)如果想要動作在blending前後不會移動到collider,除了在Import Settings裡把動作位移鎖住之外,要注意若是進行blending的時間區段中有較大的位移會容易造成collider的移動,因為blending時會吃掉一部分的位移量導致發生誤差。
  • Any State可以立即執行動作
想要立刻執行的動作(例如死亡)可以利用Any State,Any State代表任何時機點且在任何State之上,所以只要觸發條件成立就會立即執行,但其conditions必須是一次性的命令發送。詳細內容:[Unity] Mecanim 強制進入某個State的方法
Any State的觸發時機是任何時候,只要觸發條件成立就會立刻執行
  • 骨架由無實體物件組成時,需要給骨架的root綁定一個mesh
如果骨架是由bone, dummy等無實體的物件組成,且採取骨架、動態分離的檔案規則時,骨架的root需要綁定一個mesh,且動態檔內也必須要留著這個mesh,如此骨架檔才能成功套用到動態。
  • Humanoid Rig會消耗較多的效能
角色的Rig選擇Humanoid會比起Generic消耗更多的效能,而Legacy則是依據開發者所撰寫的controller,當角色越簡單時,使用Legacy會比較省效能。詳細內容:Mecanim Performance and Optimization
  • 當不使用IK功能時,Rig選擇Generic可以避免一些問題
角色需求較單純時使用Generic可以避免一些因為Humanoid的IK所造成的表現性上的誤差(如transition時的位移誤差)。當Rig選擇Generic時可看到Clip設定中的Root Transform Position(XZ)的Based Upon可選項目除了Original(依照動作檔中原始所key的位置)外就是Root Node Position,選擇Root Node Position時會以骨架的root對正(0,0),而Humanoid則是Center of Mass,以骨架體積中心點對正(0,0),因此當角色的腳挪動時僅管hip沒有移動仍然會使角色的位置偏移。
  • Humanoid Rig最好只使用在人類角色上
Humanoid是針對人類型骨架角色設計的,例如因Foot IK是針對單關節(人類的膝蓋)所設計,所以當腿部結構有雙關節時(如羊人的腳),就會發生一些不正常的現象(如滑步明顯、腳步抖動...)。
  • Transition的conditions為bool時,無法調整blending timing
當條件為bool時,代表該transition可以在任何時間發生,所以就只能調整duration而無法調整timing,如果去調整它會發現重新點選該transition時,會被自動歸回中央點。
當conditions為bool時是無法設定transition timing的
  • 承上,transition的conditions可以不只一個
譬如已有一個bool的條件再加上一個exit time條件,可以使transition依照bool判斷觸發,但transition發生的timing會是固定的,也就是你在transition的timeline中所調整的timing。 參考文章:


bool搭配exit time可以指定transition的timing

延伸閱讀:

[Unity] 清除Console的Log - Clear Console

參考文章:

如果直接
using System

會造成UnityEnging.Object與object的衝突

using sys = System;

static void clearConsoleLog ()
{
 Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
    sys.Type type = assembly.GetType("UnityEditorInternal.LogEntries");
    MethodInfo method = type.GetMethod ("Clear");
    method.Invoke (new object (), null);
}

2013年6月21日 星期五

[Unity] 以ARGB4444搭配Dithering進行貼圖減量

  之前一直受ARGB Texture所困擾,在Mobile平台沒有合適的壓縮格式能在品質與容量之間取得較好的平衡,下面列出幾個格式的缺點
  • RGBA PVRTC 4 bits - iOS推薦的壓縮格式,但是4bits對於圖像品質的破壞太大
  • ARGB 32 bit - 雖能保有圖片品質,但所消耗的容量實在太龐大
  • ARGB 16 bit - 雖能降低貼圖容量,但是顏色有明顯的帶狀瑕疵(color banding)
  Android平台由於各家GPU所支援的壓縮格式各有不同,因此ARGB 16 bit也就是降低texture容量最適合的選項,但16 bit所造成的帶狀瑕疵十分明顯,若真的要使用也不太可能,正好前一陣子看到Unity IN上有Cytus開發者之一的syyang所發表的文章



  • 使用 RGBA4444 搭配 Dithering 減少記憶體用量 by syyang @ UNITYIN

  •   這篇文章正好解決了這個問題,利用Dithering在16bit color depth獲得較佳的圖像呈現,消除帶狀瑕疵。進行Dither處理之後,圖片的顏色資訊會被重新處理,檢視圖片可發現變的有顆粒狀感(抖色),當圖片在眼中的呈現越小時,就會發現看起來跟truecolor的圖片品質頗接近。由於是對顏色資訊做了重新的演算,所以並不受到Unity的轉檔所影響,只要在Import Settings中設定Format為16 bit的壓縮,即可省下容量又能保有還能接受的圖片品質。

      此外需要注意的是經過dither處理的texture若又被Unity重新壓縮到不同尺寸的圖片時,會加重顆粒感使圖片品質降低(因為顏色訊息被重新處理過了),因此輸出給Unity的texture asset必須要輸出成遊戲主要使用的texture size。此外,若處理過的貼圖是用於3D模型的話,經過UV的拉扯也會使圖片失真更加明顯。目前Unity本身並不支援dithering,在網上也還沒看到有人做出支援dither處理的script/plug-in,因此得要自行預先處理這些textures。

    經過UV拉扯、尺寸放大後失真變的相當明顯
    但dithering還是有其諸多限制,而最原始的作法當然就是使用RGB444的安全色系來繪製texture,及在各個channel中只使用:0, 15, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255。但除了顏色數便少之外,還得少用漸層表現,要犧牲的也是相當多,也許能在不影響美術風格的前提下選擇性使用。

      在以Gimp進行dithering處理時,須先將貼圖縮成最終使用的尺寸,在縮小圖片時三種內插法看起來效果幾乎相同,然後再執行Dither to ARGB,此時會多出一個圖檔視窗就是dithering後的圖檔,進行匯出之後即可。匯出PNG的設定所有選項皆可取消,但當texture需要再alpha邊緣作細緻的過度時,透明像素的色彩是必須要被儲存的。Compression Level 0跟9在色系較單純的圖片上是幾乎沒有差別。


    Gimp在安裝script之後可進行dithering處理
    Gimp匯出PNG時的設定

    參考資料


    延伸閱讀

    2013年6月6日 星期四

    [Unity] Dynamic Camera FOV

    遭遇情境

      今天想達成一個攝影機的效果,就是當有特定的物體越來越接近畫面邊緣時,就要動態的增減Camera的FOV,讓該特定物體不要跑出畫面。

    解法

      大致上很簡單,只有兩個步驟:

    1. 求出物件距離畫面邊緣的距離
    2. 利用Mathf.Lerp達成相對應的FOV調整

      先利用Camera.WorldToViewportPoint( Vector3 )求出物件在畫面上的位置,返回的是normalized value(0至1之間),如此就能得知物件相對於畫面邊緣的距離,0代表是在畫面左方邊緣,1則是在畫面右方邊緣。

      再來則是利用Mathf.Lerp來調整FOV,Lerp實際上的運作是
    public static float Lerp(float start, float end, float value)
    {
        return ((1.0f - value) * start) + (value * end);
    }
    
    在其3個arguments中,value是一種類似權重的概念,當他為0時返回的值會是start,為1時返回的即是end,為中間的數值時就是返回按照比例計算後的結果。

      因此我只要把WorldToViewportPoint求得的值,以想要的數值範圍作normalize後,讓距離變成0至1之間的數值,傳入Lerp之中就可達成動態FOV的效果。範例如下:

    // amount : Value between 0 and 1 indicating the weight of value2.
    // 為距離螢幕邊緣比重的值
    Camera.fieldOfView = Mathf.Lerp( float CameraMinFOV, float CameraMaxFOV , float amount );
    


    參考資料


    延伸閱讀

    2013年6月5日 星期三

    [Unity] Mecanim Animator .controller 文件結構紀錄

    此文章紀錄Mecanim Animator Controller檔案的內容結構,當角色數量龐大時,眾多的Animator Controller會造成極大的管理上的困擾,因此若能直接解析內部的訊息的話,即可利用Script做外部的直接修改,做出更方便的管理工具。

    而在初次看過其內容後,獲得了一個重點即是,各地方的命名十分重要,Controller內部的目標來源都是以fileID的形式運作,但同時有些東西也可以在Unity中做命名,雖然會需要多一個命名規則的訂制與管理的工,但是直接利用Name做修改會比用fileID方便許多,譬如說Transition的命名。

    參考文件
    在開始看文件內容之前,先說明fileID的運作方式,fileID實際上是由兩組數字組成,首先是該物件的Class ID Number,然後是一組五位數物件在所在檔案內的序號(偶數),舉個例子,現在有一個帶有3個Animation Clip的FBX檔案,則這3個Clip的fileID就是:( 7400000, 7400001, 7400002 ),74為Animation Clip的ID Number。

    以下開始文件內容:
    在每個區段(term,代表一個object)的一開始,都會先有一串的數據資訊
    • --- !u!91 &9100000
      !u!後代表的是物件類型
      &(ampersand)後的數字是該區段資訊獨有的fileID
    AnimatorController此部份包含了Animator的基本屬性,如:
    • m_Name
      Animator的名稱
    • m_AnimatorEvents
      Flags
    • m_Layers
      Layers
    在來是各個Transition的參數(State與State間的Transition),
    • m_Name
      Transition的命名,預設狀況下是無命名的。
      如果想要手動修改.controller文件的話,對所有的Transition做命名會方便非常多,可快速的知道各個Transition資訊的位置。
    • m_SrcState / m_DstState
      來源跟目的State,填入的資訊為fileID。
    • m_TransitionDuration
      Transition的時間長度,應該是以SrcState的Motion的時間長度為基準的趴數。
      例如來源動作的長度為2秒,而m_TransitionDuration的值為0.5時就是使用1杪做過渡。
    • m_TransitionOffset
      DstState相對於TransitionDuration起始點的offset。
      當DstState的起始點與TransitionDuration的起始點相同時,則m_TransitionOffset的值為0。
      當DstState的結束點與TransitionDuration的起始點相同時,則m_TransitionOffset的值為1。
    • m_Conditions
      進行Transition的條件,以下的參數皆為Condition的條件式
      • m_ConditionMode
        條件判斷的模式
        • 1,Bool的True
        • 2,Bool的False
        • 3,Float/Int的Greater
        • 4,Float/Int的Less
        • 5,ExitTime
        • 6,Int的Equals
        • 7,Int的NotEqual
      •  m_ConditionEvent
        要參照的Flag,m_ConditionMode為ExitTime時此欄位為空
      • m_EventTreshold
        條件式的參考值,Bool時不管是True或False值都填0
      • m_ExitTime
        ExitTime的float值
    再來是State的資訊

    最後是StateMachine的資訊


    [Unity] Mecanim 強制進入某個State的方法(ex立刻執行死亡動作)

    How to change to a specific state immediately.

    參考資料
      在Mecanim Animator Controller中有個叫做Any State的State,是一個一直處在作用中的State,只能連接給其他的State,其他的State是不能連接給他的。

    Animator - Any State

      假使想要有一個在任何State規則之上的動作流程時(即條件成立時能立即進入該流程),譬如說人物的死亡動作,當程式下達死亡指令時,你會想要死亡動作是立刻表演,不希望這個流程會被其他的State卡住。
      這時候Any State就能達到這個效果,在這裡以一個範例作解說:在Animator Parameters中新增一個IsDead的Bool,並新增一個接收Any State的死亡動作State,且Transition Conditions為IsDead == true,如此當死亡時程式下達指令給Animator(animator.SetBool("dead",true)),就會讓dead這個State立刻執行。
      但是因為Any State是一直在作用狀態中的,所以並不能一直讓觸發條件成立,必須要是一次性的命令,否則就會發生一直反覆進入dead State的狀況,舉例來說:IsDead被設為true且Animator已經進入dead State的時候,就必須要把IsDead設回false。

    防止重複觸發Any State的範例Code如下:
    static int deadState = Animator.StringToHash("Base Layer.dead");
    if ( animator.GetCurrentAnimatorStateInfo(0).nameHash == deadState )
    {
        animator.SetBool("IsDead", false);
    }
    
    補充說明,animator.GetCurrentAnimatorStateInfo(int IDX)所返回的是Hash,且Hash並不能轉回String,因此得要先把你所知道的State名稱轉成Hash(.StringToHash())再做比對。
      在這樣的需求之下代表State的命名必須是有規則的、固定的,為了方便作業流程,動作片段的取名與State相同也可以省去某些操作上的步驟(直接將動作片段拉入Animator生成state時會自動以動作片段名稱為state命名)。
      另外直接從Any State做一個Transition到預設動作上,也能用來跳出任何的State。

    Unity 4.3 更新

    在Unity 4.3中新增了一個新的Parameter類型:Trigger,其作用是達成一次性的判斷條件,非常實用。其功能就是當Animator.SetTrigger("ParameterName")命令下達之後,該parameter會被設為True,並在transition完成時會被設為False,以往都需要自己撰寫設回False的程序,現在利用Trigger這個Parameter就會方便很多。
    另外在4.3中Mecanim有大幅的優化,譬如Mecanim已幾乎適用所有的component、property...,可以利用Mecanim控制你的相機FOV、控制你的Script的參數...,再配上新支援的Event(以往ImportedAnimation不支援Event)以及優化過的Animation Window(Dope Sheet + Curves),Mecanim的實用程度已大幅提高。


    參考文章
    延伸




    2013年5月27日 星期一

    [Unity] Shader - ShaderLab


    參考資料:
    相關資料:
    參考範例:


    人生第一次的shader撰寫XD,超陽春Unlit受擊變色用shader,透過shaderLab簡單的功能只要少少幾行就能完成!呈現的效果為,不受光影響,單一貼圖表現,可以透過Color property的alpha來控制貼圖與color間的混合

    ● Opaque版本
    Shader "Custom/Unlit Opaque With Color" {
    	Properties {
    		_Color ("Color (RGB) Blending (A)", Color) = (1,0,0,0)
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    	}
    	SubShader {
    		Tags {"RenderType"="Opaque" "IgnoreProjector"="True"}
    		Lighting Off
    		Cull back
    		ZWrite On
    		
    		Pass {
    			SetTexture [_MainTex] {
    				ConstantColor [_Color]
    				combine constant lerp(constant) texture
    			}
    		}
    		
    	}
    	FallBack "Unlit/Diffuse"
    }
    

    ● Transparent Cutout版本
    Shader "Custom/Unlit Transparent Cutout With Color" {
    	Properties {
    		_Color ("Color (RGB) Blending (A)", Color) = (1,0,0,0)
    		_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
    		_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
    	}
    	SubShader {
    		Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
    		Lighting Off
    		Cull back
    		ZWrite On
    		Blend SrcAlpha OneMinusSrcAlpha
    		
    		Pass {
    			Alphatest Greater [_Cutoff]
    			SetTexture [_MainTex] {
    				ConstantColor [_Color]
    				combine constant lerp(constant) texture, texture
    			}
    		}
    		
    	}
    	FallBack "Unlit/TransparentCutout"
    }