Github項目地址:Pacmangit
Pixels Per Unit:多少像素至關於Unity一個單位,迷宮Maze大小232x256,github
Pivot:設置貼圖的零點,Bettom Left左下角數組
物理化:牆,import package->custom package,導入已經設置好碰撞體的牆dom
pacman切圖,動畫片斷:Sprite Mode->multiple,Pixels Per Unit=8,進行Sprite Editor,顯示其窗口。選擇Slice切片,Type爲Grid By Cell Count,切割參數3行4列,Apply後可在Pacman下面看到切割好的12張照片。ide
動做製做:12張照片每3張爲一個動做,分別是右,左,上,下,每次將3張拖入Hierarchy面板,保存在Animations文檔下,各自命名。可在Project面板Animations文件夾下包含4個動畫文件,說明每次保存的3張圖片生成一個動畫,還包含4個動畫機(但只須要設置一個。其他可刪除)函數
狀態機:在主角Pacman內添加Animator組件,添加上述留下的動畫機,打開Animator頁面,看到4個組件,初始狀況爲:當遊戲物體進入狀態機,默認狀態轉變爲PacmanRight;後拖拽其餘3個狀態到狀態機頁面測試
分析主角移動:僅僅能橫x縱y向移動,當持續按住某方向鍵位,速度爲每0.3s移動 1 Unit
Any State:切換鏈接4個狀態,點擊連線可看到說明:不管在任何狀態只要達到連線內條件,即轉變狀態到所指對象狀態字體
Any State 切換條件:在Parameters內添加float型DirX,DirY值用來判斷(持續按鍵產生的是浮點數)。例如PacmanRight的判斷,添加DirY,當DirY>0.1(浮點數不精確性質,留必定範圍)。而且取消狀態機Settings內Can Transition To(考慮到幀數問題,和重複播放初始動做問題),其次2D動畫將融合調0動畫
其餘:能夠調節動畫的speed以調節播放該動做的速度ui
吃豆人的實體化:
吃豆人的移動方法:
具體實現移動過程:
Vector2.MoveTowards(transform.position,dest,speed)
,使得返回起始點到目標點的中間值,另設 temp
接收這個值;再對剛體進行移動操做GetComponent<Rigidbody2D>().MovePosition(temp);
transform.position = test
,故不會移動,所以須要按鍵檢測以改變 dest
的值:
Input.GetKey(KeyCode.UpArrow)
或者 Input.GetKey(KeyCode.W)
實現讀取鍵位dest:(Vector2)transform.position + Vector2.up;
實現吃豆人每次單位移動:(Vector2)transform.position + Vector2.up
,表示 當前座標position 加 向上一個單位量;每次讀取鍵盤方向信息,將當前座標 + 某方向單位量 = 目的地位置
產生問題1:此時移動會形成吃豆人旋轉問題
緣由:Pacman 與牆壁碰撞時Z軸座標改變形成旋轉
解決:凍結Z軸 Rigdibody2D->Constraints->Freeze Rotation Z
產生問題2:卡槽間移動容易卡住,非規則移動
緣由:問題在於按鍵過程時刻改變dest ,形成 temp = Vector2.MoveTowards()的時刻改變
解決:判斷當上一個dest抵達時纔讀取新的鍵位if ((Vector2)transform.position == dest)
產生問題3:首次測試按鍵移動發現撞牆後就不可再移動
緣由:抵達牆邊時,鍵盤讀取的dest到了牆之外,if判斷永遠沒法transform.position==dest,沒法在鍵盤讀取
解決:檢測目的地合法性
//檢查目的地是否合法 dir方向值(上述的Vector.XXX) private bool Valid(Vector2 dir) { //pos 存儲當前位置(牆內的合法位置) Vector2 pos = transform.position; //從 當前值pos+方向值dir 的位置發射一條射線到Pacman 當前的位置pos RaycastHit2D hit = Physics2D.Linecast(pos + dir, pos); //射線打到的碰撞器 是否等於 吃豆人的碰撞器: //若射線從牆中心(不合法位置)射出,hit.collider爲牆的,不等於Pacman的,返回fault //若射線從路面(合法位置)射出,hit.collider等於Pacman的,返回true return (hit.collider == GetComponent<Collider2D>()); }
實現不一樣動做狀態機的切換:
Vector2 dir = dest - (Vector2)transform.position;
GetComponent<Animator>().SetFloat("DirX", dir.x);
2D遊戲Z軸問題:若在不一樣層級碰撞功能失效,若在同一層級,則存在渲染順序問題(誰覆蓋誰)
渲染順序問題:Sprite Renderer->Order in Layer
豆子:
Pacdot.cs
OntriggerEnter2D(Collider2D collision)
函數用來檢測觸發 Pacdot 的物體是否爲Pacman ,是則銷燬Pacdot敵人的建立:
敵人的移動(單路徑):
EnemyMove.cs
Transform[] WayPoint
,及index
標記敵人在前往哪一個路徑點的途中;FixedeUpdata()
,判斷:若怪物沒抵達目標位置,則從當前位置持續移動直到抵達(同Pacman的移動,但沒有輸入檢測),若抵達,則index++,前往下一個點;此外動畫狀態的檢測、切換也同Pacman的;敵人的移動(多路徑):高級的方法是AI,但本例採用多路線隨機分配實現
wayPointsGo
用來接收爲預製體的路線 Way
;wayPoints
,在 start() 內調用foreach方法,將 wayPointGo
內的組件取出到 t
,將 t.position
座標順序添加到wayPoints
表內(還需修改FixedUpdate()函數內: wayPoints[index] 此時爲表,存儲的是座標,無需再.position,後續的wayPoints.Length也改成了wayPoints.Count),由此實現了一條路徑;private void LoadApath(GameObject go) { //將wayPointsGo數組內某一路徑的子物體(路徑點)的Transform組件取出,依次將其position賦值到Ways表中 //修改成多路徑後隨機從5條路徑走 foreach (Transform t in wayPointsGo[Random.Range(0, 4)].transform) { wayPoints.Add(t.position); } }
產生問題1:不一樣怪物出門都與 Blinky紅色敵人 同一點問題
緣由:由於作預製體way1,way2,...,wayn 時,路徑始末兩點座標都是 Blinky紅色敵人 上方3個單位,因此其餘敵人起始移動就會先進行穿牆到那個點
解決:修改 EnemyMove.cs
,建立一個座標變量 startPos
用在存放每一個敵人路徑的始末位置點,在 Start()
函數內初始化設置 satrtPos
爲怪物起始座標+向上3個單位;再後續 foreach()
內插入該點到List表頭,及在List末尾添加該點,但注意每次要調用LoadApath()函數要清除上一次路徑信息
//清空List內前次路徑的信息 wayPoints.Clear(); //添加首末路徑點到List內 wayPoints.Insert(0, startPos); wayPoints.Add(startPos);
產生問題2:即使隨機路徑下不一樣怪物選到同路徑問題
緣由:每一個敵人進行 Random.Range(0,n)
隨機分配路徑時可能抽到同樣的隨機數
解決:添加 GameManager.cs
,調用以下代碼
public class GameManager : MonoBehaviour { private static GameManager _instance; public static GameManager Instance { get { return _instance; } } public List<int> usingIndex = new List<int>(); public List<int> rawIndex = new List<int> { 0, 1, 2, 3}; private void Awake() { _instance = this; int tempCount = rawIndex.Count; for (int i = 0; i < tempCount; i++) { int tempIndex = Random.Range(0, rawIndex.Count); usingIndex.Add(rawIndex[tempIndex]); rawIndex.RemoveAt(tempIndex); } } } //再修改EnemyMove,Start內 LoadApath(wayPointsGo[GameManager.Instance.usingIndex[GetComponent<SpriteRenderer>().sortingOrder - 2]]);
超級豆子的生成:
超級豆子帶來的超級吃豆人狀態:敵人靜止,且能夠被吃掉
GameManager.cs
內添加布爾變量 isSuperPacman
斷定是否超級狀態isSuperPacman = true
FreeEnemy()
(下有說明)StartCoroutine(Recover());
(下面說明)Blinky.GetComponent<EnemyMove>().enabled = false;
禁用怪物移動腳本的update方法Blinky.GetComponent<SpriteRenderer>().color = new Color(0.7f, 0.7f, 0.7f, 0.7f);
敵人圖標變暗淡EnemyMove.cs
內檢測,若碰撞檢測到的 Pacman 是超級狀態 GameManager.Instance.isSuperPacman
則自身回到出生點協程延時:
OnEatSuperPacdot()
變動爲超級狀態狀態時,啓用協程函數 StartCoroutine(Recover())
,表示該協程函數與 OnEateSuperPacdot()
同時啓用,並行運行Dis_FreezeEnemy()
及恢復吃豆人狀態 isSuperPacman = false
yield return new WaitForSeconds(4f);
吃豆過程概述:
Start 與 Exit 圖標:
UI->Canvas
,做爲UI工做區域;UI->text,start和exit
,修改字體,調整位置創建 Button 按鍵跳轉:
Start、Exit
兩UI添加 Button(script)
組件,設置 Target Graphic->Start,Exit
;GameManager.cs
內添加 OnStartButton()
和 OnExitButton()
對接按鍵腳本,以下圖代碼:On Click->GameManager->OnStartButton
啓用該函數//當點擊 Start public void OnStartButton() { //與點擊開始按鈕後同步進行的函數 StartCoroutine(PlayStartCountDown()); //Start聲音,聲音源在原點位置 AudioSource.PlayClipAtPoint(startClip, Vector3.zero); //隱藏開始按鈕的頁面 startPanel.SetActive(false); } //點擊Exit後退出遊戲 public void OnExitButton() { Application.Quit(); }
網頁跳轉:
調用 Application.OpenURL("https://www.cnblogs.com/SouthBegonia/");
便可實現,可用在Button 也可用在其餘觸發時間