解析度資訊
您的螢幕解析度為:
您的瀏覽器內部目前寬高為:
User Agent:
您的螢幕解析度為:
您的瀏覽器內部目前寬高為:
User Agent:
ScriptableObject 可以用來作為事件系統的一部分。這種方法可以幫助我們在不同的 MonoBehaviour 之間進行通信,而不需要它們彼此知道對方的存在,從而實現鬆耦合。
首先要建立 GameEvent 與 GameEventListener 。它們兩個互相依賴
GameEvent 
GameEventListener 列表用來保存要聽取該事件的聽眾,RegisterListener 來註冊聽眾UnregisterListener 將聽眾移出Raise 觸發聽眾註冊的事件1  | []  | 
GameEventListener
Event ResponseOnEnable 中將自己註冊到 Event 中OnDisable 中將自己從 Event 中移出OnEventRaised 中呼叫回應的方法1  | public class GameEventListener : MonoBehaviour  | 
接著在 Unity 編輯器中建立一個 ScriptableObject 實體,並命名為 ClickEvent
建立一個 DoSomethingWhenClick 的遊戲物件(GameObject) ,並添加以下腳本,這個遊戲物件有一個公開方法 JustDoIt,當事件發生時,要被呼叫。
1  | public class DoSomethingWhenClick : MonoBehaviour  | 

建立一個 EventListener 的 GameObject,將 GameEventListener 腳本添加到這個 EventListener GameObject 中。
ScriptableObject 實體 ClickEvent ,拖放到,EventListener 中的 Event 欄位,表示這個 EventListener 要監聽的事件是 ClickEventDoSomethingWhenClick 遊戲物件放到 Response 欄位,並設定要執行該物件的公開方法 JustDoIt
接著建立事件發起的物件,這邊建立兩個, RaiseClickableCircle 與 RaiseClickableButWithUnityEvent 
RaiseClickableCircle 的腳本如下,它主要是直接使用 GameEvent 作為參考,其缺點是只能用一個 Event
1  | public class RaiseClickableCircle : MonoBehaviour  | 

RaiseClickableButWithUnityEvent 幾乎與 RaiseClickableCircle 一模一樣,差別在於是使用 UnityEvent ,這樣可以放置多個 Event,當觸發 Raise 時在裡面的 Event 都會觸發。
1  | public class RaiseClickableButWithUnityEvent : MonoBehaviour  | 

最後執行,並點擊畫面上的圓形或是方形,可以發現都會觸發 DoSomethingWhenClick 的 JustDoIt 方法,而 RaiseClickableButWithUnityEvent , RaiseClickableCircle 與 DoSomethingWhenClick 完全不知曉對方的存在。
使用基於事件架構(event-based architecture)的好處是,它不會每一幀都在執行,只有在當事件發生時才會執行,因此效率會比在 MonoBehaviour 的 Update 方法中執行好。
Reference:
ScriptableObject as shared variable
ScriptableObject 可以作為遊戲變數在各個 MonoBehaviour 之間分享資料,此外,透過這種方式還可以減少 MonoBehaviour 物件之間互相參考的耦合度。
下面將使用 ScriptableObject 建立玩家生命數值,並在不同的 MonoBehaviour之間使用 
首先建立一個繼承 ScriptableObject 的類 FloatVariable 
1  | []  | 
接下來建立兩個 FloatVariable 實體, HP 與 MaxHP

然後建立繼承 MonoBehaviour 的 Player 類
1  | public class Player : MonoBehaviour  | 
在 Unity 編輯器中,將剛剛建立的 HP 與 MaxHP 設定到 Player 上
接著建立一個 HPView
1  | public class HPView : MonoBehaviour  | 
在 Unity 編輯器中,將 HP 與 MaxHP 設定到 HPView 上,
將 Player.Hurt() 設定給按鈕的 On Click 。
最後執行,在畫面上可以按下按鈕,觸發 Player.Hurt() ,可以觀察到 CurrentHPText 的變化
並且可以發現這兩個 MonoBehaviour: Player 和 HPView 互相不知道彼此的存在,實現了鬆耦合(loosely coupled)。
Reference:
ScriptableObject 是一個資料容器(Data container),它透過共享通用資料的方式來減少對重複資料的記憶體佔用,進而降低你APP的記憶體使用量。
如果你的預製物件(Prefab)中儲存了一些不會改變的資料,那麼很適合把它們改為使用 ScriptableObject ,因為每當你實體化(instantiate)一個預製物件(Prefab)時,這個實體會保存自己的一份資料,當實體化很多個實體時,會有很多重複的資料佔用很多記憶體,因此你可以使用 ScriptableObject 可以把資料儲存起來,然後讓所有的預製物件(Prefab)存取這個 ScriptableObject 。 
ScriptableObject 和 MonoBehaviour 一樣,繼承了 UnityEngine.Object ,但是與 MonoBehaviour 不同在於你不能把 ScriptableObject 附加(attach)到一個 GameObject 上,也不能進行 GatComponent 等對 GameObject 的操作,相反你需要把它儲存為資源(Asset)。
在使用Unity編輯器時,你可以把資料放在 ScriptableObject ,在執行時也可以調整其中的資料,但是你不可以把它當作遊戲存檔的工具。
使用 ScriptableObject 主要情況是在編輯時期修改 ScriptableObject 的資料,在執行時這些資料會被當作資源(Asset)使用。
建立一個類並繼承 ScriptableObject,你可以使用 CreateAssetMenu 屬性(attribute),讓你方便在編輯器中使用,以下是一個範例:
1  | []  | 
之後,你可以在 Assets -> Create -> ScriptableObjects -> SpawnManagerScriptableObject 中建立新的 ScriptableObject 實體。
你可以更改該實體的名稱與它的屬性。
接下來你可以在 MonoBehaviour 中使用這個 ScriptableObject 實體,如下
1  | using UnityEngine;  | 
在Unity編輯器中,使用拖拉的方式將剛剛建立好的 ScriptableObject 實體拖拉到對應的位置上,另外 Box Prefab 是一個簡單的Square
最後在執行Unity,就可以看到Unity使用剛剛建立出來 ScriptableObject 資料去產生 Box 物件。
此外除了在編輯器中建立 實體,在執行期間也可以透過 CreateInstance 來建立。
1  | ScriptableObject.CreateInstance<MyScriptableObjectClass>();  | 
MonoBehaviour 與 ScriptableObject 的比較
| MonoBehaviour | ScriptableObject | 
|---|---|
MonoBehaviour 接收所有來自 Unity 的 callback,像是 Start , Awake , Update , OnEnable ,OnDisabl , OnCollisonEnter 等 | 
ScriptableObject 只接收一些: Awake , OnEnable , OnDestroy 與 OnDisable 。 在 Editor 中的話還有 OnValidate 與 Reset | 
MonoBehaviour 必須要 附加(attach)到 GameObject 上 | 
ScriptableObject 不能附加到 GameObject 上,需要在專案(Project Level)中將它們儲存為 asset 檔案,在其他腳本中參考這些 ScriptableObject asset | 
當儲存 MonoBehaviour 時,會將它們的資料儲存到 Scenes 與 Prefabs 中 | 
每個 ScriptableObject 實體都會被保存為專案層級(Project level)的獨立檔案中 | 
一般在 Play Mode 中修改 MonoBehaviour 中的值之後離開 Play Mode 的話,這些值會被重新設定(Reset) | 
當離開 Play Mode 後, ScriptableObject 則不會重新設定。此外當編譯發布之後,執行期間 ScriptableObject 修改的值不會被保存,而是為發布時的值 | 
在 ScriptableObject 中只有以下的 callback 會被呼叫
Awake : 與 MonoBehaviour 的類似,只有在 ScriptableObject 開始時會被呼叫。當遊戲執行(launched)或是有參考到這個 ScriptableObject asset 的 Scene 被載入時會呼叫。OnEnable : 在 Awake 之後呼叫,當 ScriptableObject 被載入(Loaded)或是實體化(Instantiated)時會被呼叫。當 ScriptableObject.CreateInstance 或是 script 被重新編譯(recompilation)時 OnEnable 會被呼叫。OnDisable : 當載入的 Scene 不再參考這個 ScriptableObject asset 時,就會呼叫 OnDisable , 它會在 OnDestory 之前被呼叫。此外在 script 重新編譯進入 Play Mode 時,也會呼叫,此時會出現 OnDisable 出現在 OnEnable 的情況。 OnDestory : 當 ScriptableObject 在 編輯器中刪除,或是重程式碼中刪除時,會呼叫 OnDestory 。如果是在執行期間建立的 ScriptableObject 在APP離開或是離開 Play Mode 時也會呼叫。以下 callback 只在編輯器中呼叫
OnValidate : 當值在 Inspector 中改變值,才會呼叫。可以在這邊確保你的編輯器輸入的資料是合適的範圍(如 0 ~ 1)。Reset : 當點擊 Inspector 中的 Reset 按鈕時,會呼叫。Reference:
Unity 中的預製物件(Prefab),讓你可以儲存一個遊戲物件(GameObject),這個預製物件(Prefab)會包含該遊戲物件的所有組件(Component),屬性值(property values),以及它的子遊戲物件,並且可以建立,更改它的設定,就像是一個可重用的資源(Asset)。預製物件資源(Prefab Asset)就像是一個模板(template),讓你在場景中(Scene)添加預製物件實體(Prefab instance)
Prefab Asset 的更改,都會自動反應(reflected)到該預製物件的實體上。Nest PrefabsNest Prefabsoverride),讓每個預製物件的實體可以有不同的行為runtime)才加入的遊戲物件,那麼這個遊戲物件可以把它變為預製物件,例如子彈,NPC等只會在遊戲期間出現的物件當你想要在你的遊戲專案中重用遊戲物件(GameObject),例如非玩家角色(non-player character (NPC)),或是一些道具,應該把這個遊戲物件變為預製物件(Prefab),因為Unity的Prefab System會自動幫你同步遊戲物件的資料,讓這些有相同預製物件的實體有同樣的資料。以下常見使用預製物件的例子
Environmental Assets):例如在關卡中心顯示多次的樹。override)在移動速度,發出聲音上有所不同。Projectiles):如子彈,箭等,像是海盜船的砲台開火時,要產生一個砲彈實體在 Unity 中建立預製物件資源(Prefab Asset)的方式非常簡單,在Unity編輯器中,將在 Hierarchy window 中的物件使用滑鼠左鍵拖曳到下方的 Project window 即可。操作完成的同時會發現原先的遊戲物件變為淡藍色的,表示它變為預製物件(Prefab instance)了
Reference:
FormerlySerializedAs 是 Unity 中的一個屬性(attribute),用於處理欄位的重新命名。當重新命名一個序列化的欄位時,Unity 通常會失去對舊名的參考,導致序列化出現問題。
使用 FormerlySerializedAs 可以告诉 Unity 欄位之前的名稱,從而正確地反序列化。
以下為修改範例:
在 BaseEntity 類別中有一個 HP 欄位以及一個 ItemData 欄位,並且已經在 Inspector 上更改過它們的值
1  | public class BaseEntity : MonoBehaviour  | 

此時,想要更改欄位的名稱,如果直接重新命名這些欄位的話,在 Inspector 會丟失這些值,

使用 FormerlySerializedAs 告訴 Unity 欄位之前叫什麼。當編譯後,Unity 會幫我們將值對應到對應到新的欄位上。
1  | public class BaseEntity : MonoBehaviour  | 

確認更新之後沒問題,可將 FormerlySerializedAs 刪除,但記得要在大家都更新完之後再做刪除的動作。
Reference:
UV 是一種紋理坐標系統,與物體的空間坐標 (x, y, z) 屬於不同的坐標系。 UV 座標是用來映射紋理到物體表面,它與物體坐標之間存在映射關係,但這種關係不一定是線性或唯一的。
假設紋理大小是 512x512 但我們不會用 512x512 去做映射,因為 UV 座標的範圍是 0 到 1 之間,所以 512x512 大小的圖片就會被轉換為 uv(0/512,0/512) 至 (512/512,512/512)。這麼做的話即使你的紋理不是 512x512,而是 1024x1024 或其他大小,UV 仍然適用,因為它們代表的是 相對位置,而不是像素座標。
當我們想要檢查 Mesh 的頂點索引、 UV 座標,或三角形組成方式,我們可以使用 MeshFilter 來取得 Mesh
接下來我們建立一個 Quad 並設定他的 Materail 試試看。

Unlit/Texture ,在把 512x512 的圖片放到這個 Material 中。1  | public class Test : MonoBehaviour  | 
(0,0.75) , (0.25,0.75) , (0,1) , (0.25,1) 就是 紅色1 的區塊。
1  | for (int i = 0; i < mesh.triangles.Length; i += 3)  | 
1  | for (int i = 0; i < mesh.vertexCount; i++)  | 

參考
Power 對 UV 的影響
當你對 UV 使用 Power,會導致貼圖的變形,因為 UV 不是線性的了。
舉例:UV 使用 Power
UV.xy → Power(UV.xy, 2)
Power(UV, 1) 👉 不變
Power(UV, 2) 👉 使貼圖集中在 (0,0),朝右上角壓縮
在前後端分離的狀況下,很容易就會遇到跨域的問題,為了開發測試方便,我們可以暫時禁用Chrome的安全設定,以方便我們測試開發。
在Mac上使用以下指令來關閉 Chrome的安全設定
1  | open -n -a "Google Chrome" --args --user-data-dir=/tmp/temp_chrome_user_data_dir http://localhost:8080/ --disable-web-security  | 
當出現 你正在使用不受支援的命令列標嫩:--disable-web-security•這可能會危及穩定性與安全性。 字樣,表示你已經成功關閉Chrome的安全設定了。
Reference: https://stackoverflow.com/questions/3102819/disable-same-origin-policy-in-chrome
在自己架設的伺服器中,如果要把檔案上傳到AWS S3,一種常見的做法是先將檔案上傳到自身伺服器然後再上傳到AWS S3,但是這樣太耗費資源了。
幸好AWS S3有方式可以讓你在前端直接將檔案上傳到S3中,以下說明如何設定前端的HTML,讓檔案直接上傳。
首先你要有可以存取Bucket的 AWSAccessKeyId 與 AWSSecretAccessKey
以下資料皆來自https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
| AWSAccessKeyId | AKIAIOSFODNN7EXAMPLE | 
| AWSSecretAccessKey | wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY | 
| Bucket | sigv4examplebucket | 
以下是HTML Form的例子
1  | <html>  | 
Policy 應由後端產生,他是一個JSON物件,以下是一個例子
1  | { "expiration": "2015-12-30T12:00:00.000Z",  | 
| expiration | 表示這個上傳的Request可用的期限 | |
| conditions | 設定條件,在檔案上傳前AWS S3會檢查一下,是否和HTML Form中的相符合 | |
| bucket | Bucket 名稱,此範例為 sigv4examplebucket | 
|
| “starts-with”, “$key” | 要放在這個Bucket的 Prefix,此範例為user/user1/ ,在HTML Form中的 key 需要符合在此設定的前綴祠才可以,如 user/user1/myfile.csv | 
|
| acl | 設定 S3 ACL, 如設為:public-read 表示可以被公開讀取,也可以設為 private ,表示是私有的 | |
| success_action_redirect | 上傳成功之後,要導向到哪個網頁,如設為:http://localhost:8080/uploaded.html | |
| x-amz-meta-uuid | 後端產生的UUID | |
| x-amz-server-side-encryption | 說明使用哪種加密方式, AES256 | |
| “starts-with”, “$Content-Type” | 上傳的 content type,可以不設定 | |
| x-amz-credential | 由  | 
|
| x-amz-algorithm | 說明建立這個Request的簽名使用哪種演算法,例如 AWS4-HMAC-SHA256 | |
| x-amz-date | 建立這個Request的簽名的日期,為可選的,需要為 ISO 8601 basic format (YYYYMMDD’T’HHMMSS’Z’) 格式 | 
注意,如果你在HTML Form中沒有設定某些參數,那麼在Policy中也不要設定,例如我在HTML Form中沒有設定 ["starts-with", "$Content-Type", "image/"] 和 ["starts-with", "$x-amz-meta-tag", ""],那麼在 Policy中也不要設定。 
之後由後端使用Base64將這段字串編碼,這個Base64字串編碼就是要放在 HTML Form 中的 Policy,以下是Java產生的範例
1  | JSONObject j = new JSONObject();  | 
最後產生 Signature ,Java 可以直接使用有人提供的產生方式,來自 https://gist.github.com/phstudy/3523576726d74a0410f8
1  | // 在 AccessSecretKey前面要加上 AWS4  | 
以上動作完成之後,還需要為你的Bucket設定 Cross-origin resource sharing (CORS)
1  | [  | 
Bucket沒有設定Cross-origin resource sharing (CORS)會出現 CORS policy 錯誤
1  | Access to XMLHttpRequest at 'https://<你的bucket>.s3.amazonaws.com/' from origin 'https://你的網站.com ' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.  | 
如果是還在測試開發的話,可以先將Chrome的web-security關閉,Mac使用以下指令,其他設定可以參考: disable-same-origin-policy-in-chrome
1  | open -n -a "Google Chrome" --args --user-data-dir=/tmp/temp_chrome_user_data_dir http://localhost:8080/ --disable-web-security  | 
Reference:
2D樞紐/鉸鏈關節(Hinge Joint 2D) 是 Unity 中的一個 2D 物理組件。
GameObject)圍繞一個特定的點旋轉Rigidbody 2D)物件,或是Connected Rigidbody 設為 Nonelinear force)2D樞紐/鉸鏈關節(Hinge Joint 2D)具有三個約束條件,這些條件都是可選的
Rigidbody 2D)物件上的兩個錨點(anchor point)之間的相對線性距離(relative linear distance)Rigidbody 2D)遊戲物件上的兩個錨點之間的角速度(angular speed)Maximum Motor Force屬性限制最大扭矩(maximum torque)來限制角速度。angle)在指定的弧度(arc)範圍內可以使用這個關節來建構需要像旋轉樞軸行為的物理遊戲物件。例如:
see-saw pivot):水平部分連接到基座。使用關節的角度(Angle)限制來模擬翹翹板的最高點和最低點。scissors)的樞軸:剪刀用鉸鏈連接在一起。使用關節的角度限制來模擬剪刀的閉合和最大打開。Hinge Joint 2D) 的馬達(motor)來旋轉車輪。| 屬性 | 功能 | |
|---|---|---|
| Enable Collision | 啟用這個屬性後,可以偵測碰撞。 | |
| Connected Rigidbody | 指定此關節連接到的其他遊戲物件(GameObject)。如果將此設置為「None」,則關節的另一端固定在由「Connected Anchor」設置定義的空間點。 | |
| Auto Configure Connected Anchor | 啟用此屬性以自動設置此2D鉸鏈關節(Hinge Joint 2D)連接到的遊戲物件的錨點(Anchor)位置。如果啟用此屬性,則不需要為「Connected Anchor」屬性輸入座標。 | 
|
| Anchor | 定義此遊戲物件的2D剛體(Rigidbody2D)上的關節(joint)端點連接的位置(以x、y座標表示)。 | 
|
| Connected Anchor | 定義這個關節(joint)端點要連接到另一個遊戲物件的2D剛體(Rigidbody2D)上的位置(以x、y座標表示)。 | 
|
| Use Motor | 啟用此選項以對關節應用馬達動力。 | |
| Motor | 選擇此選項以展開此屬性的設置。 | |
| Motor Speed | 設定馬達要達到的目標速度(每秒度數)。 | |
| Maximum Motor Force | 設定馬達在嘗試達到目標速度時可以施加的最大扭矩(torque)(或旋轉力(rotation))。 | 
|
| Use Limits | 啟用此選項以限制旋轉的角度(rotation angle)。 | 
|
| Angle Limits | 選擇此選項以展開對角度限制的設置。當Use Limits啟用時,就會應用這些限制。 | 
|
| Lower Angle | 設定旋轉圓弧(rotation arc)下端(lower end)允許的極限。 | 
|
| Upper Angle | 設定旋轉圓弧(rotation arc)上端(upper end)允許的極限。 | 
|
| Break Action | 設置當超過力(force)臨界值或達到扭矩臨界值(torque threshold)時採取的動作。 | 
|
| Break Force | 設置力的臨界值,當超過時這個關節就會執行選擇的Break Action。預設值為Infinity,表示不執行Break Action。 | 
|
| Break Torque | 設置扭矩的臨界值,當超過時這個關節就會執行選擇的Break Action。預設值為Infinity,表示不執行Break Action。 | 
Reference