###### tags: `社團` # Unity + [Unity小遊戲教學](https://www.youtube.com/watch?v=kzukcEaXTjw&list=PLYGd-m5DMaRZWiv_UeYqUxQZkpKGqvtKo) + [Unity Script 常用語法教學(unity課程入門學習筆記)](https://www.gameislearning.url.tw/article_content.php?getb=2&foog=9997) + [Unity開發筆記 - GameObject & Transform 的關係](http://glimmerfish.blogspot.com/2016/01/unity-gameobject-transform-1.html) <br /> ## 一、Unity 安裝 + [Unity](https://unity.com/download) (2020.3.20f1) + [visual studio community](https://visualstudio.microsoft.com/zh-hant/thank-you-downloading-visual-studio/?sku=Community&rel=16)(2019) + Unity/Edit/Preferences/External Tools/External Script Editor/Visual Studio Community 2019 <br /> ## 二、3D入門 + [Unity筆記 — 遊戲製作基本概念](http://misdemo.nkust.org/fy/2020/03/10/unity-note0/) + [Unity筆記 — 基礎操作(1)](http://misdemo.nkust.org/fy/2020/03/15/unity-note1/) + [Unity筆記 — 基礎操作(2)](http://misdemo.nkust.org/fy/2020/03/15/unity-note2/) ### 1.版面設定 + Layout/2by3 ### 2.物件、場景基本觀念 + 新增物件 - GameObject/3D Object/Plane * 更名為「Floor」 - GameObject/3D Object/Cube * 更名為「Player」 * Inspector/Transform/Position(X:-2,Y:3,Z:0) - GameObject/3D Object/Sphere * 更名為「Ball」。 * Inspector/Transform/Position(X:5,Y:2,Z:0) - Scene視窗上「十字符號」(move tool),可移動物件。 - 「選轉工具」(Rotate Tool),可用來調整物件角度。 - GameObject/Create Empty,可建立最基本的物件「GameObject」,為沒有實體的空物件,只有 Transform元件(Position、Rotation、Scale屬性)。物件是因為Mesh Renderer(網格渲染)元件,才能看見形狀。 - 3D Object 這些物件都有實體,在建立時除了 Transform 外,還會建立 Mesh Renderer、Mesh Filter、Collider 這三個元件。 * Mesh Renderer(網格渲染):Renderer 動作類似「粉刷」。元件會從 Mesh Filter 獲取形狀模型,然後粉刷(渲染)出形狀。 * Mesh Filter:立體形狀模型的資源(.fbx檔)。 * Collider:碰撞體,讓物件無法穿透,即能被碰到。 - Windows/Assets Store:Unity 內的 Assets Store,提供許多 2D、3D物件和音檔等遊戲資源。 + 新增場景 - Project/Assets/Scenes/空白處右鍵/Create/Scene - 切換不同場景 + 場景(Scene)視窗視角操作 - alt:鼠標從手掌變成眼睛圖示,功能從「移動」變成以座標中心(0,0,0)為圓心,「旋轉」移動視角。 - 滑鼠右鍵:以原本的視角位置為圓心,旋轉移動視角。 - 滑鼠滾輪:立體座標固定不動,視角前後移動。 - 調整 z 軸,使攝影機在下方,讓 Scene 視窗和 Game 視窗的視角相似。 - 以上方「Move Tool」、「Rotate Tool」調整 Camera、Directional Light 位置、角度,看看 Game 視窗的變化。 * Hierarchy/「Main Camera」/Inspector/ Transform/Position(X:0,Y:3,Z:-10)/Rotation(X:6,Y:0,Z:0) + Game視窗 - Display1:Display1 是 Main Camera 拍攝畫面的代號。當遊戲有多個攝影機時,可在這邊切換畫面。 - Free Aspect:調整執行遊戲時預覽的畫面尺寸 * Game/Free Aspect/+/Width & Height(640,480) * 最終輸出的遊戲畫面尺寸需要在File/Build Settings/Player Settings/Player/Resolution設定 ### 3.Player物件新增元件 + Player 物件需要以滑鼠左鍵控制跳躍,因此要新增Rigidbody(剛體元件),模擬物理運動;以及 Script(腳本),撰寫感應滑鼠左鍵被按下並跳躍的程式。 + Hierarchy/「Player」/Inspector/Add Component/Physics/ Rigidbody + 按下畫面中央上方的播放鍵執行遊戲 Player 物件會往下掉落,為模擬物理運動的效果。再按一次播放鍵,停止執行遊戲。 + Hierarchy/「Player」/Inspector/Add Component/New Script/name:Player/Create and Add/在「Player」上點2下編輯 ```C= public class Player : MonoBehaviour { protected float jump_speed = 8.0f; // 不公開變數,f代表float,一定要加。 void Start() { } void Update() { if (Input.GetMouseButtonDown(0)) //如果滑鼠被按下(左鍵) { this.GetComponent<Rigidbody>().velocity = Vector3.up * this.jump_speed; //取得<剛體>元件,給剛體定一個移動速度 = 向上運動*jump_speed //Vector3是指三維度(X,Y,Z),另外有Vector2,表示二維(X,Y) } } } ``` + 加上顏色 - 先建立顏色的材質(Material)元件,再套用到物件上。 - Project/Assets/Create/Material/取名為「red」/Inspector/Main Maps/Albedo/選取紅色 - Hierarchy/「Player」/Inspector/Materials/Elements/選紅色材質 + 測試執行,試試連點滑鼠左鍵。 + 取消編輯器 Unity Message 0個參考:visual studio/工具/選項/文字編輯器/所有語言/CodeLens/啟用CodeLens、顯示Unity提供者,均取消打勾 ### 4.Ball物件新增元件 + Hierarchy/「Ball」/Inspector/Add Component/Physics/ Rigidbody + Hierarchy/「Ball」/Inspector/Add Component/New Script/name:Player/Create and Add/在「Ball」上點2下編輯 ```C= void Start() { this.GetComponent<Rigidbody>().velocity = new Vector3(-7.0f, 6.0f, 0.0f); // 設定球體往左上角飛的速度 : x座標-7(往左) y座標 6(向上) // 寫在 start 方法內,所以當球被初始化就會執行(往左上飛躍的動作) } ``` ### 5.Prefab預製件 + [Unity學習筆記#6 : Prefab 使用須知](https://kendevlog.wordpress.com/2017/09/24/unity%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%986-prefab-%E4%BD%BF%E7%94%A8%E9%A0%88%E7%9F%A5/) + 預製件類似設計圖,能重複產生同款的物件。為了不斷製造出 Ball 物件,要先建立 Ball 的預製件。 + 將 Hierarchy/「Ball」物件/拖曳到Project/Assets視窗下,就會在 Assets 資料夾內建立 Ball Prefab。 - 建立好該物件的 Prefab 後,Hierarchy 視窗下的物件名字會變藍色。 - Project/Assets/「Ball」/Inspector/Transform/Position(X:5,Y:2,Z:0) + GameOject/Create Empty/取名為「Launcher」 - 可以把 Launcher 物件想成一個透明的發射台 - Hierarchy/「Launcher」/Inspector/Add Component/New Script/name:Player/Create and Add/在「Launcher」上點2下編輯 - 當變數被設定成 public 時,Inspector 視窗會顯示出這個變數 - 將「Ball」預製件拉進Hierarchy/「Launcher」/Inspector/Launcher(Script)/Ball Prefab/None(Game Object) ```C= public class Launcher : MonoBehaviour { public GameObject ballPrefab; //建立「ballPrefab」物件變數 void Start() { } void Update() { Instantiate(this.ballPrefab); } } ``` ```C= void Update() { if (Input.GetMouseButtonDown(1)) // 如果按下滑鼠(右鍵) { Instantiate(this.ballPrefab); // 生成 ballPrefab } } ``` + 先測試執行不加if程式,看看會不會產生球。 - Launcher Script 感應玩家是否有按下右鍵,有的話就建立 Ball 物件。球會有發出去的效果是因為 Ball Script 內寫著「初始化時往左上方彈出」的指令。 + 每發射一次球體,Hierarchy 視窗下就多了一個「Ball(Clone)」物件。 - 表示球即使飛出遊戲畫面,還是會一直存在著,不斷消耗裝置的資源。 - 在 Ball Script 新增 OnBecameInvisible 方法,當物件不可視時就執行 Destroy。 ```C= private void OnBecameInvisible() //離開畫面時的方法 { Destroy(this.gameObject); //銷毀(這個物件),如果括號內只寫 this,那就只會銷毀這個腳本 } ``` + 測試執行,球離開畫面時 Hierarchy 視窗裏的Ball(Clone)會消失。 ### 6.玩家、球剛體元件(Rigidbody)調整 + Player 物件會被 Ball 撞飛、旋轉 - Hierarchy/「Player」/Inspector/Rigidbody/Constraints/Freeze Position(X,Z打勾)/Freeze Rotation(X,Y,Z打勾) + Ball 不會被彈飛 - Project/Asset/「Ball」預製件/Inspector/Rigidbody/Mass(0.01) ### 7.建立物理材質(Physic Material) + 設定磨擦、反彈等和物理材質相關的部分。 + Project/Assets/空白處右鍵/Create/Physic Material/更名為「Ball Physic Material」/Inspector/Bounciness(1) + Project/Assets/「Ball」預製件/Inspector/Sphere Collider/Material選「Ball Pyhsic Material」 ### 8.設定遊戲環境的重力 + Unity 遊戲環境的 Gravity(引力)預設為 9.81。 + Edit/Project Settings/Physics/Gravity(-20) + 更改引力設定後,物件的運動軌道也會變化,再微調物件的運動速度 - Player Script 的 jump_speed 參數:12.0f - Ball Script 的 Start 方法內的 Vector3 參數:(-10.0f, 9.0f, 0.0f) ### 9.讓 Player 物件不可連續跳躍 + 在 Player Script 新增 is_landing 變數,當感應到碰撞時(碰到地板時),is_landing 改為 true,才能再執行跳躍運動。 ```C= public class Player : MonoBehaviour { protected float jump_speed = 12.0f; // 不公開變數,f代表float,一定要加。 public bool is_landing = false; // 新增 is_landing 變數,當感應到碰撞時(碰到地板時),is_landing 改為 true,才能再執行跳躍運動。 void Start() { this.is_landing = false; // 初始化為 false,因為 Player 初始化時會先在地板上方建立,然後再落到地面。 } void Update() { if (this.is_landing) //如果 is_landing 是 true { if (Input.GetMouseButtonDown(0)) //感應到滑鼠(左鍵)被按下 { this.is_landing = false; //is_landing 就等於 false this.GetComponent<Rigidbody>().velocity = Vector3.up * this.jump_speed; //執行向上跳躍運動 } } } private void OnCollisionEnter(Collision collision) //偵測是否有碰撞 { this.is_landing = true; // 發生碰撞(落地時)時 is_landing 等於 true } } ``` + 測試執行,當 Player 物件成功撞到球體時,is_landing 也會變成 true(還能再跳一次)。 - 觀察 Hierarchy/「Player」/Inspector/Player(Script)/is_landing的變化(打勾為true) ### 10.使用標籤功能(Tag) + Hierarchy/「Floor」/Inspector/Tag/Add Tag/+/New Tag name:「地板」/Save/重新在Tag選「地板」 + 修改 Player Script 裏 OnCollisionEnter() ```C= private void OnCollisionEnter(Collision collision) { if (collision.gameObject.tag == "地板") // 如果碰撞到的物體標籤"地板" { this.is_landing = true; } } ``` <br /> ## 三、小朋友下樓梯(2D) + [【Unity】3小時製作一個遊戲 | Unity 遊戲開發初學者教學](https://www.youtube.com/watch?v=nPW6tKeapsM) ### 1.版面設定 + Scene/Game視窗分離 + Game/Free Aspect/+/Label(9:16),Type(Aspect Ratio),Width & Height(9,16) ### 2.製作一個地板 + Project/Assets/Scenes/SampleScene 改名為「DownStair」 + Project/Assets/空白處右鍵/Create/2D/Sprites/Square - 拉到 Scene - Hierarchy/Square改名為「地板」 - Hierarchy/「地板」/Inspector/Transform/Scale(X:2,Y:0.4)/Color(任意) ### 3.自動產生地板(Prefab) + Project/Assets/空白處右鍵/Create/Folder/New Folder改名為「Resources」 + 將2.的「地板」,從Hierarchy視窗拉進上面的資料夾。 + GameObject/Create Empty/更名為「管理」 - Inspector/Position(X:0,Y:0,Z:0) - Inspector/Add Component/New script/取名為「GroundManager」/Create and Add/在「GroundManager」上點2下編輯 ```C= void Start() { GameObject newGround = Instantiate(Resources.Load<GameObject>("地板")); } ``` + 先將原先的地板從場景移除,測試執行,場景中會出現地板,且 Hierarchy 視窗會出現「地板(Clone)」。 ### 4.自動產生多個地板(垂直等距) + GroundManager(Script) ```C= public class GroundManager : MonoBehaviour { readonly float initPositionY = 0; [Range(2, 6)] public float spacingY; // Inspector 參數欄位修飾 [Range(int, int)] 限制變數的範圍,並增加一個拉桿 UI。 void Start() { for (int i = 0; i < 3; i++) { GameObject newGround=Instantiate(Resources.Load<GameObject>("地板")); float newGroundPositionY = initPositionY - spacingY * i; newGround.transform.position = new Vector3(0, newGroundPositionY, 0); } } } ``` + 「管理」/Inspector/GroundManager(Script)/Spacing Y/0->3 + 測試執行,查看3個「地板(Clone)」的坐標。 ### 5.設定牆壁,並自動產生多個亂數地板 + 「Main Camera」/Inspector/Camera/Size(5.36) 設定遊戲畫面大小。 + GameObject/Create Empty/更名為「右邊界」 - Inspector/Position(X:3,Y:0,Z:0) - Inspector/Add Component/Physics 2D/Box Collider 2D/Size(Y:20)/Offset(X:0.5) + 複製「右邊界」,「右邊界(1)」 更名為「左邊界」 - Inspector/Position(X:-3,Y:0,Z:0) - Inspector/Add Component/Physics 2D/Box Collider 2D/Size(Y:20)/Offset(X:-0.5) ```C= public class GroundManager : MonoBehaviour { readonly float leftBorder = -3; // 左邊界 readonly float rightBorder = 3; // 右邊界 readonly float initPositionY = 0; [Range(2, 6)] public float spacingY; // Inspector 參數欄位修飾 [Range(int, int)] 限制變數的範圍,並增加一個拉桿 UI。 void Start() { for (int i = 0; i < 3; i++) { GameObject newGround=Instantiate(Resources.Load<GameObject>("地板")); float newGroundPositionY = initPositionY - spacingY * i; newGround.transform.position = new Vector3(NewGroundPositionX(), newGroundPositionY, 0); } } float NewGroundPositionX() { return Random.Range(leftBorder, rightBorder); } } ``` + 測試執行可看到三個 X 軸位子不同的地板。 ### 6.玩家移動 + Project/Assets/Scenes/Square 拉到 Scene,取名為「玩家」。 - Inspector/Transform/Position(X:0,Y:3) - Inspector/Add Component/Physics 2D/Rigidbody 2D/Constrains/Freeze Rotation(Z打勾,可避免旋轉)。(剛體元件模擬物理特質,如不可穿透、碰撞、重力等,還可以固定物件位置) - Inspector/Add Component/Physics 2D/Circle Collider 2D - Inspector/Add Component/New script/取名為「Player」/Create and Add/在「Player」上點2下編輯 ```C= public class Player : MonoBehaviour { public float forceX; // 水平推力 Rigidbody2D playerRigidBody2D; // 玩家身上的剛體物件 readonly float toLeft = -1; // 往左 readonly float toRight = 1; // 往右 readonly float stop = 0; // 停止 float directionX; // 實際方向 void Start() { playerRigidBody2D = GetComponent<Rigidbody2D>(); } void Update() { if (Input.GetKey(KeyCode.LeftArrow)) directionX = toLeft; else if (Input.GetKey(KeyCode.RightArrow)) directionX = toRight; else directionX = stop; Vector2 newDirection = new Vector2(directionX,0); playerRigidBody2D.AddForce(newDirection*forceX); } } ``` + Inspector/Player(Script)/ForceX:15 + 測試執行時,玩家會直接往下掉(地板只是一張圖)。 - Project/Assets/Scenes/Resources/點地板/Inspector/Open prefab/Add Component/Physics 2D/Box Collider 2D ### 7.攝影機定速下降 + 「Main Camera」/Inspector/Add Component/New script/取名為「CameraManager」/Create and Add/在「CameraManager」上點2下編輯 ```C= public class CameraManager : MonoBehaviour { public float downSpeed; void FixedUpdate() //FixedUpdate 每秒執行50次 { transform.Translate(0, -downSpeed * Time.deltaTime, 0); // transform.Translate(x, y, z); 移動。攝影機往下移動->場景往上移動 } } ``` + Inspector/CameraManager(Script)/Down Speed:1 + 測試執行,攝影機往下移動,造成場景往上移動。 ### 8.地板改寫 + 增加地板 List,儲存地板。 + 改寫 Start(),呼叫 SpawnGround(),產生地板。 + 改寫 NewGroundPositionX(),讓第一塊地板置中,可以接住玩家。 + 新增 NewGroundPositionY(),計算新地板的 Y 座標。 + 新增 SpawnGround(),將產生地板程式從Start()移至此,並將新地板加入 List。 ```C= public class GroundManager : MonoBehaviour { readonly float leftBorder = -3; // 左邊界 readonly float rightBorder = 3; // 右邊界 readonly float initPositionY = 0; static int groundNumber = -1; // 地板流水號 [Range(2, 6)] public float spacingY; // Inspector 參數欄位修飾 [Range(int, int)] 限制變數的範圍,並增加一個拉桿 UI。 public List<Transform> grounds; void Start() { grounds = new List<Transform>(); for (int i = 0; i < 10; i++) { SpawnGround(); } } float NewGroundPositionX() { if (grounds.Count == 0) return 0; return Random.Range(leftBorder, rightBorder); } float NewGroundPositionY() // 計算新地板的 Y 座標 { if (grounds.Count == 0) // 地板數量為 0 return initPositionY; int lowerIndex = grounds.Count - 1; // 最下面地板的編號 return grounds[lowerIndex].transform.position.y - spacingY; // 最下面地板的 Y 再減地板間距為新的地板 Y 座標 } void SpawnGround() //產生單一地板 { GameObject newGround = Instantiate(Resources.Load<GameObject>("地板")); newGround.transform.position = new Vector3(NewGroundPositionX(), NewGroundPositionY(), 0); grounds.Add(newGround.transform); groundNumber++;//地板編號+1 newGround.name = "地板" + groundNumber;//修改物件名稱為地板+流水編號 } } ``` + 測試執行可產生10塊地板,觀察 Hierarchy/「管理」/Grounds/地板物件名稱。 + Q:往下幾層後,玩家一直往左或右會飛出邊界? - 將「左邊界」、「右邊界」拉至「Main Camera」下,讓他們跟著 Camera移動。 ### 9.控制地板數量 + 新增 ControlSpanGround(),控制地板數量。 ```C= readonly int MAX_GROUND_COUNT = 10; // 最大地板數量 public void ControlSpawnGround() // 控制產生地板 { SpawnGround(); if (grounds.Count > MAX_GROUND_COUNT) // 地板數量超過最大值 { Destroy(grounds[0].gameObject); // 摧毀畫面上的地板物件 grounds.RemoveAt(0); // 從list刪除 } } ``` + 測式執行 - GameObject/UI/Button/Inspector/Button/On Click()/+/將「管理」拉到None (Object)/No function 選 GroundManager > ControlSpawnGround - Main Camera/Inspector/Down Speed 改為 0。 - 按Button後,觀察 Hierarchy/「管理」/ Inspector/Grounds 地板Clone的狀況,大於10之後,會摧毀最上層的地板。 - 測式完將 Camera 速度改回 1。 - 測式完將 Button/Inspector/勾勾取消,隱藏起來。 ### 10.自動計算何時要產生新地板 + 修改 ControlSpanGround() ,玩家下方地板太少時就要產生新的地板。 ```C= readonly int MIN_GROUND_COUNT_UNDER_PLAYER = 3; // 玩家下方最少地板數量 public Transform player; void Start() { grounds = new List<Transform>(); for (int i = 0; i < MAX_GROUND_COUNT; i++) { SpawnGround(); } } public void ControlSpawnGround() // 控制產生地板 { int groundsCountUnderPlayer = 0; // 先計算玩家下方的地板數量 foreach (Transform ground in grounds) if (ground.position.y < player.transform.position.y) groundsCountUnderPlayer++; if (groundsCountUnderPlayer < MIN_GROUND_COUNT_UNDER_PLAYER) // 如果地板數量不足 { SpawnGround(); if (grounds.Count > MAX_GROUND_COUNT) // 地板數量超過最大值 { Destroy(grounds[0].gameObject); grounds.RemoveAt(0); } } } void Update() { ControlSpawnGround(); } ``` + 「管理」/Inspector/Grounds/Player None(Transform) 選「玩家」,才能在GroundManager script中,存取玩家 Y 座標。 + 測式執行,玩家下方地板少於3時,會增加地板。地板數量超過最大值,會摧毀最上層的地板。 ### 11.加入死亡機制 + Project/Assets/空白處右鍵/Create/2D/Sprites/Isometric Diamond/ - 拉到 Scene,Inspector/Transform/Position(Y:5.4),Scale(X:2,Y:3) - 複製 2次,調整 X 位子 。 + GameObject/Create Empty/更名為「陷阱上」 - 將3個Isometric Diamond拉進「陷阱上」。 - Inspector/Add Component/Physics 2D/Box Collider 2D - 將「陷阱上」拉至「Main Camera」,讓陷阱跟著Camera移動。 + 修改Player Script ```C= public static bool isDead; void Start() { isDead = false; playerRigidBody2D = GetComponent<Rigidbody2D>(); } ``` + 「陷阱上」/Inspector/Add Component/New script/取名為「DeadZone」/Create and Add/在「DeadZone」上點2下編輯 ```C= private void OnCollisionEnter2D(Collision2D other) // 傳入碰撞對象,取名為 other { if (other.gameObject.CompareTag("玩家")) { Player.isDead = true; Debug.Break(); // 讓遊戲暫停,方便測試 } } ``` + 「玩家」/Inspector/Tag/Add Tag/+/New Tag name:玩家/Save/Tag選「玩家」 + 測試完,刪除 Debug.Break() + 「管理」/Inspector/Add Component/New script/取名為「GameManager」/Create and Add/在「GameManager」上點2下編輯 ```C= using UnityEngine.UI; using UnityEngine.SceneManagement; public class GameManager : MonoBehaviour { public Button restartButton; public GameObject player; void Start() { restartButton.gameObject.SetActive(false); } void Update() { if (Player.isDead) { player.SetActive(false); restartButton.gameObject.SetActive(true); } } public void ReloadScene() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); } } ``` + Hierarchy/「Button」 - 按右鍵更名為「重新開始遊戲」 - Inspector/打勾 - Rect Transform/PosX:0,PosY:-15 - On Click() 選 GameManager > ReloadScene - Herarchy/Canvas/Button/Text/Inspector/Text:重新開始 + 連結GameManager(Script)需要控制的元件 - 「管理」/GameManager(Script)/RestartButton 選「重新開始遊戲」 - 「管理」/GameManager(Script)/Player 選「玩家」 + GameObject/Create Empty/更名為「陷阱下」 - 將「陷阱下」拉至「Main Camera」,讓陷阱跟著Camera移動。 - Inspector/Transform/Position(X:0,Y:-5.9) - Inspector/Add Component/Physics 2D/Box Collider 2D/Size(X:7,Y:1) - Inspector/Add Component/Scripts/DeadZone ### 12.樓層計算 + GameObject/UI/Text/更名為「樓層」 - Inspector/Rect Transform/PosX:50,PosY:120 + GroundManager(Script) ```C= using UnityEngine.UI; public class GroundManager : MonoBehaviour { [Range(1, 20)] public float singleFloorHeight; public Text displayCountFloor; float CountLowerGroundFloor() { float playerPositionY = player.transform.position.y; float deep = Mathf.Abs(initPositionY-playerPositionY); return (deep / singleFloorHeight)+1; } void DisplayCountFloor() { displayCountFloor.text = "地下" + CountLowerGroundFloor().ToString("0000") + "樓"; } void Update() { ControlSpawnGround(); DisplayCountFloor(); } ``` + 「管理」/GroundManager(Script)/Single Floor Height(1.7) + 「管理」/GroundManager(Script)/DisplayCountFloor選「樓層」 ### 13.細項調整 + Project/Assets/空白處右鍵/Create/Folder/New Folder改名為「Scripts」/將所有的 Script 移入 + 解決可以靠在左、右邊界的問題 - Project/Assets/空白處右鍵/Create/2D/Physic Material 2D/改名為「牆壁材料」 - Inspector/Friction設為 0.1 - 「左邊界」、「右邊界」/Inspector/Box Collider 2D/Material 選「牆壁材料」 + 解決可以靠在地板的問題 - 點「牆壁材料」/Ctrl+D複製/更名為「地板材料」/ - Inspector/Friction:0.1,Bounciness:0.1 - Project/Assets/Resources/地板//Inspector/Box Collider 2D/Material 選「地板材料」 + File/Build and Run