來吧,用Unity當(dāng)一個套馬的漢子

作者:繁華如夢
前言
《瘋狂動物園》(不是《瘋狂動物城》)是一款將“和動物互動”與跑酷元素結(jié)合起來的游戲。雖然游戲本身多年前便已推出,但由于機(jī)緣巧合最近才接觸到。
作為一個(自認(rèn)為)涉獵廣泛的游戲開發(fā)人員,怎么不來搞一下試試呢?
地圖復(fù)用
之前做過跑酷游戲的教程,地圖是使用方塊自動生成的。而就這款游戲而言,不需要這么復(fù)雜的地圖生成。這里我們先做一個簡單的地圖,復(fù)用一下就行。
新建3個Plane物體,修改物體的縮放值,使其能夠作為跑道使用。參數(shù)為(3,1,10)。然后將3個Plane平鋪組合成一個較長的跑道。如下圖:

接下來新建一個GameMode腳本,通過玩家的位置來控制地圖移動。原理如下:

代碼實現(xiàn)如下:
public class GameMode : MonoBehaviour {
...
??? public Transform player;//玩家的位置
??? public Transform floorOne;
??? public Transform floorTwo;?????
??? public Transform floorThree;//3個地板
??? int maxVaule = 70;//觸發(fā)點的位置
??? int floorIndex = 1;//當(dāng)前應(yīng)該移動第幾塊地板
??? public void CheckBackMove()??? // 檢測地板是否移動
??? {
??????? float zValue = player.position.z;//獲取玩家的位置的Z值用于計算
??????? if (zValue> maxVaule)
??????? {
??????????? maxVaule += 100;//觸發(fā)點位置移動
??????????? if (floorIndex == 1)
??????????? {
??????????????? floorOne.transform.position = new Vector3(0, 0, maxVaule + 130);//通過觸發(fā)點位置計算新的地板坐標(biāo)位置
??????????????? floorIndex = 2;
??????????? }
??????????? else if (floorIndex == 2)
??????????? {
??????????????? floorTwo.transform.position = new Vector3(0, 0, maxVaule + 130);
??????????????? floorIndex = 3;
??????????? }
??????????? else
??????????? {
??????????????? floorThree.transform.position = new Vector3(0, 0, maxVaule + 130);
??????????????? floorIndex = 1;
??????????? }
??????? }
??? }
}
?
玩家控制
由于是套牛游戲,玩家總是在牛身上,于是移動相關(guān)的代碼大部分都是在動物身上,于是玩家身上只需要控制套索的旋轉(zhuǎn),以及切換動物時的跳躍移動就行了。
套索旋轉(zhuǎn)
套索自身旋轉(zhuǎn)較為簡單,只需要使用Rotate函數(shù)就可以達(dá)成目標(biāo)。但是在游戲中,套索要跟隨玩家的手來進(jìn)行移動并旋轉(zhuǎn),這樣才有玩家控制套索旋轉(zhuǎn)的感覺。實現(xiàn)這個方法有2種方式:
1. 將套索物體綁定在玩家手臂的骨骼節(jié)點下面,使其作為子物體跟隨玩家移動并旋轉(zhuǎn)。
2. 通過根骨骼節(jié)點的位置,每一幀計算套索的位置,并讓套索自轉(zhuǎn)。
此處選擇了第二種方式,代碼如下:
public class Player : MonoBehaviour {
...
??? public GameObject lassoObj;???????? //套索物體
??? public float lassoSpeed;??????????? //套索旋轉(zhuǎn)速度
??? public Transform lassRootPoint;???? //套索的骨骼節(jié)點
??? public void LassRotate()??? // 套索旋轉(zhuǎn)
??? {
??????? lassoObj.transform.position = offestPoint + lassRootPoint.position;
??????? lassoObj.transform.Rotate(Vector3.up, lassoSpeed * Time.deltaTime);
??? }
}
?
效果如下:

跳躍移動
游戲中玩家需要自己控制從動物上彈起,并在空中切換騎乘動物。其空中的移動就是一邊向前移動,一邊受重力緩慢下降了。代碼如下:
public class Player : MonoBehaviour {
...
??? public void JumpMove()
??? {
??????? transform.position += transform.forward * moveSpeed * Time.deltaTime;
??????? transform.position += Vector3.down * 9.8f*0.4f * Time.deltaTime;//乘以0.4f 是讓重力減少,增加在空中的時間
??? }
}
?
動物控制
動物有2種移動方式。一種是未騎乘下的直線奔跑。2是騎乘狀態(tài)下有玩家控制的移動。于是動物的移動代碼如下:
public class AnimalColler : MonoBehaviour {
...
??? public float moveSpeed;//動物移動速度
??? public void Move()
??? {
??????? transform.position += transform.forward * moveSpeed * Time.deltaTime*-1;//素材原因,移動方向為Z軸的反方向
??? }
??? public void RideMove()
??? {
??????? if (Input.GetKey(KeyCode.A))//騎乘狀態(tài)下移動主要修改移動方向來進(jìn)行實現(xiàn)的
??????? {
??????????? transform.Rotate(new Vector3(0, -30, 0) * Time.deltaTime);
??????? }
??????? else if (Input.GetKey(KeyCode.D))
??????? {
??????????? transform.Rotate(new Vector3(0, 30, 0) * Time.deltaTime);
??????? }
??????? else
??????? {
??????????? transform.rotation = startQuat;//停止按鍵旋轉(zhuǎn)復(fù)位
??????? }
??? }
}
?
動物在套索范圍內(nèi),還有一個改變自身顏色的功能。此處我們是通過更換當(dāng)前動物的材質(zhì)來實現(xiàn)的對應(yīng)功能。代碼如下:
public class AnimalColler : MonoBehaviour {
...
??? public Material startMater;?? //一開始動物的材質(zhì)
??? public SkinnedMeshRenderer rendererTest; //動物自己的渲染組件
??? public Material yellowMater;????? //編輯器賦值
??? public void SwitchColor()
??? {
??????? rendererTest.material = GameMode.Instance.yellowMater;
??? }
??? public void RestColor()
??? {
??????? rendererTest.material = startMater;
??? }
}
?
動物生成
由于地圖是復(fù)用的,于是我們只需要通過玩家的位置,在玩家前方的一定范圍內(nèi)生成出新的動物即可。代碼如下:
public class GameMode : MonoBehaviour {
...
??? public GameObject[] animlObjs;????? //所用的能夠生成的怪物數(shù)組
??? void Start () {
...
??????? animlObjs = Resources.LoadAll<GameObject>("Prefabs"); //所有動物預(yù)制體存放在Resources文件夾下的Prefabs文件夾中
??? }
??? public void CreatAnimal()
??? {
??????? int arrayIndex = Random.Range(0, animlObjs.Length);//從數(shù)組隨機(jī)選取一只動物進(jìn)行生成
??????? GameObject tempObj = Instantiate(animlObjs[arrayIndex], Vector3.zero,Quaternion.identity);
??????? tempObj.transform.position = RandomPoint();
??????? tempObj.transform.Rotate(new Vector3(0, 180, 0));//素材問題,生成后需要先旋轉(zhuǎn)180度才能夠正常使用
??? }
?
??? public Vector3 RandomPoint()
??? {
??????? float xVaule = Random.Range(-14, 14);//隨機(jī)X軸? 游戲固定
??????? //通過玩家的位置來確定動物的生成的范圍,并進(jìn)行生成
??????? float zVaule = Random.Range((player.transform.position.z + 100),(player.transform.position.z + 150));
??????? Vector3 tempPoint = new Vector3(xVaule, 0, zVaule);
?????? ?return tempPoint;
??? }
}
?
套索范圍圈
游戲中套索的范圍光圈,我們這簡陋的使用一個2D物體來進(jìn)行替代。使其做為玩家的子物體,跟隨玩家移動。然后代碼中控制其位置,使其緊貼地面。并通過上面的碰撞器來確定范圍內(nèi)的動物,然后使其改變顏色,并進(jìn)行后續(xù)的操作。代碼如下:
public class CirceColler : MonoBehaviour {
??? public float startY;??????? //開始的Y值
??? void Start () {
??????? startY = transform.position.y;
??? }
??? void Update () {
??????? transform.position = new Vector3(transform.position.x, startY, transform.position.z);?? //固定Y值
??? }
??? private void OnTriggerEnter(Collider other)
??? {
??????? AnimalColler animal = other.GetComponent<AnimalColler>();
??????? if (animal!=null)
??????? {
??????????? GameMode.Instance.animal = animal;? //確定當(dāng)前被選中的動物
??????????? GameMode.Instance.isCanRide = true; //現(xiàn)在能夠騎乘
??????????? animal.SwitchColor();//切換動物顏色
??????? }
??? }
}
?
效果如下:

騎乘
通過之前的步驟,我們已經(jīng)能夠確定玩家是否能夠滿足騎乘條件了。現(xiàn)在就是來實現(xiàn)騎乘的效果了。
首先,我們需要在動物預(yù)制體上新建一個空物體,并命名為ridePoint(方便代碼進(jìn)行讀?。?,并調(diào)整至合適位置。用于玩家騎乘。如下圖:

然后在玩家代碼中實現(xiàn)騎乘邏輯。代碼如下:
public class Player : MonoBehaviour {
...
??? void Update () {
??????? if (Input.GetKeyDown(KeyCode.Space) && !isMove)//按下了空格鍵并且沒有在空中進(jìn)行移動
??????? {
??????????? transform.SetParent(null);//離開騎乘的動物
??????????? Time.timeScale = timeScale;//設(shè)置時間縮放,制作時間變慢的效果
??????????? isMove = true;//開始在空中進(jìn)行移動
??????????? circleObj.SetActive(true);//打開套索光圈物體
??????? }
??????? else if(Input.GetKeyDown(KeyCode.Space))//在空中按下了空格鍵
??????? {
??????????? if (GameMode.Instance.animal!=null)//現(xiàn)在光圈中有動物可以被騎乘
??????????? {
??????????????? transform.SetParent(GameMode.Instance.animal.ridePoint);//玩家的父物體設(shè)置為騎乘動物的騎乘點
??????????????? GameMode.Instance.animal.RestColor();//顏色重置
??????????????? GameMode.Instance.animal.ChangSpeed(moveSpeed);//改變速度
??????????????? GameMode.Instance.animal.SetRideState(true);//設(shè)置騎乘狀態(tài)
??????????????? StartCoroutine(MoveToTarget());//開啟協(xié)程移動到騎乘點
??????????????? isMove = false;//關(guān)閉空中移動
??????????????? circleObj.SetActive(false);//關(guān)閉光圈物體顯示
??????????????? animalRide = GameMode.Instance.animal;//當(dāng)前騎乘的動物賦值
??????????????? GameMode.Instance.animal = null;//
??????????? }
??????? }
??? }
??? IEnumerator MoveToTarget()
??? {
??????? for (int i=0;i<20;i++)//20幀移動到騎乘點
??????? {
??????????? transform.localPosition = Vector3.Lerp(transform.localPosition, Vector3.zero, 0.2f);
??????????? yield return null;
??????? }
??????? transform.localPosition = Vector3.zero;
??????? Time.timeScale = 1;//時間縮放回滾? 結(jié)束時間變慢效果
??? }
}
?
效果如下:

距離顯示
由于我們是直線移動,只需要每一幀計算與開始移動位置的距離,就可以得到我們現(xiàn)在移動的距離。代碼如下:
public class GameMode : MonoBehaviour {
...
??? public Text fractionText;?????????? //用于顯示分?jǐn)?shù)的文本
??? private Vector3 startPoint;???????? //玩家一開始的起始點 用于計算距離
??? void Start () {
...
??????? startPoint = player.position;
??? }
??? public void UpdataDistance()
??? {
??????? moveDistance = Vector3.Distance(new Vector3(startPoint.x, player.position.y, startPoint.z), player.position);
??????? fractionText.text = "當(dāng)前的奔跑的距離: " + ((int)moveDistance).ToString();
??? }
}
?
結(jié)語
這樣一個簡單的套牛跑酷游戲就算完成了。雖然有點簡陋,不過追求完美的童鞋可以在這個基礎(chǔ)上進(jìn)行美化操作,并繼續(xù)填充游戲的內(nèi)容。(PS:動畫功能已經(jīng)實現(xiàn),未在文章中進(jìn)行講解,歡迎參考工程食用?。?/p>
相關(guān)鏈接
工程鏈接:https://pan.baidu.com/s/1yo6KwcnT-f5r4rICcFALCQ?errno=0&errmsg=Auth%20Login%20Sucess&&bduss=&ssnerror=0&traceid=
慣例時間
有線下學(xué)習(xí)游戲開發(fā)打算的童鞋,歡迎訪問http://levelpp.com/。
線上課程的傳送門如下:https://study.163.com/course/introduction/1006462005.htm
另有專業(yè)開發(fā)交(gao)流(ji)群等待大家強(qiáng)勢插入:869551769