【Unity3D實戰】零基礎一步一步教你製作跑酷類遊戲(填坑完整版)
阿新 • • 發佈:2019-01-06
在兩個月前曾寫了一篇《【Unity3D實戰】零基礎一步一步教你製作跑酷類遊戲(1)》,裡面一步一步演示了製作跑酷類遊戲,然而由於時間原因,只寫到了讓角色往前移動為止。這個坑一直沒有時間去填,(雖然也沒多少人看啦),今天剛好有時間完成了一個跑酷類遊戲的Demo。放上來給有興趣的朋友看看。
Demo原始碼及對應素材下載:連結: https://pan.baidu.com/s/1smnQFt7 密碼: ptcw
遊戲簡要說明
遊戲型別:跑酷類遊戲(Demo,非完整遊戲)
操作方式:左右方向鍵(可自己移植到手機端)
遊戲要素:
1.遊戲角色會自動向前跑,玩家可通過左右方向鍵讓其左右移動
2.遊戲中存在障礙物,玩家需避開這些障礙物,否則會因為被障礙物阻擋的原因無法前進
3.當遊戲角色因為被阻擋而消失在視野中時,視為失敗
4.當遊戲角色因為被阻擋而處於偏後方時,會提高移動速度直到回到原本所處的螢幕位置
遊戲場景搭建
使用準備好的素材(路面、人物、障礙物),將這些素材製作成Prefab,然後根據自己喜好搭建好場景(如何搭建請看上一篇教程:《【Unity3D實戰】零基礎一步一步教你製作跑酷類遊戲(1)》)。如下圖:
遊戲指令碼編寫
遊戲角色控制器moveController: 新建一個C#檔案,命名為moveController,然後將其開啟。 由於角色需要向前、左、右三個方向移動,所以我們需要有其在前進方向上的速度與左右方向上的速度,分別命名為:moveVSpeed、moveHSpeed, 同時由於玩家在落後的情況下需要加速,所以我們宣告兩個變數:前進方向上的最小移動速度minVSpeed// 前進移動速度
float moveVSpeed;
// 水平移動速度
public float moveHSpeed = 5.0f;
// 最大速度
public float maxVSpeed = 10.0f;
// 最小速度
public float minVSpeed = 5.0f;
其中moveHSpeed、maxVSpeed、minVSpeed宣告為public,方便在面板上修改。
錯誤修改:感謝 jewis123 朋友提出的,這裡漏了jumpHeight與m_jumpState的定義,前者代表最大高度,後者代表當前是向上跳躍,還是從高處落下,詳細可檢視原始碼。moveVSpeed = minVSpeed; 在Update()中使人物能移動起來:
float h = Input.GetAxis("Horizontal");
Vector3 vSpeed = new Vector3(this.transform.forward.x, this.transform.forward.y, this.transform.forward.z) * moveVSpeed ;
Vector3 hSpeed = new Vector3(this.transform.right.x, this.transform.right.y, this.transform.right.z) * moveHSpeed * h;
Vector3 jumpSpeed = new Vector3(this.transform.up.x, this.transform.up.y, this.transform.up.z) * jumpHeight * m_jumpState;
this.transform.position += (vSpeed + hSpeed + jumpSpeed) * Time.deltaTime;
儲存一下cs檔案,切換到Unity,將該指令碼掛載在角色物件的身上,保留預設值或手動設定:
運行遊戲,看看是否能成功跑起來,並且能通過左右鍵控制人物左右移動。
看著人物越跑越遠越跑越遠,最後消失在遠方......誒!教練,這和說好的不一樣啊!人物咋不見了?
咳咳,這是因為我們沒有讓攝像機跟隨它的原因,接下來,我們讓攝像機與人物一起移動~
開啟剛才的C#檔案,宣告一個public的變數// 攝像機位置
public Transform cameraTransform; 在Update()函式中,新增以下程式碼: // 設定攝像機移動速度 Vector3 vCameraSpeed = new Vector3(this.transform.forward.x, this.transform.forward.y, this.transform.forward.z) * minVSpeed; // 讓攝像機跑起來 cameraTransform.position += (vCameraSpeed) * Time.deltaTime; 注意到沒,這裡我們所定義的攝像機的移動速度與人物移動速度有點差別: 1.攝像機沒有左右移動 2.攝像機的速度恆定為minVSpeed,也就是我們所定義的人物的最小移動速度(當然這個時候人物也一直是以這個速度在移動) 轉到Unity,檢視人物身上的Move Controller元件,現在這裡應該多了一個變數等你設定: 我們將攝像機拖動到camera Transform處,再運行遊戲。這時候你應該能看到人物在不斷往前走,但在螢幕上的位置是沒有變化的,因為攝像機一起移動了。 人物走著走著 哎呀 前面怎麼沒路了?別急,讓我們來讓路無限延長起來~ 首先我們將道路的GameObject複製幾個,我這裡是總共有3個道路的GameObject,分別命名為Road1,Road2,Road3 然後在每一個Road下,新增一個Cube,將Cube的Mesh Renderer關閉,並將其Box Collider的Is Trigger勾上。命名為ArrivePos。(我才不會告訴你們這一步應該在上一行之前做呢!) 將多條道路拼好,連成一條筆直的公路。 然後新建一個空物體,命名為GameManager,為其新建C#Script GameManager.cs,然後開啟該指令碼。 宣告一下多個變數:(注意引用名稱空間using System.Collections.Generic;
// 生成障礙物點列表
public List<Transform> bornPosList = new List<Transform>();
// 道路列表
public List<Transform> roadList = new List<Transform>();
// 抵達點列表
public List<Transform> arrivePosList = new List<Transform>();
// 障礙物列表
public List<GameObject> objPrefabList = new List<GameObject>();
// 目前的障礙物
Dictionary<string, List<GameObject>> objDict = new Dictionary<string, List<GameObject>>();
// 道路間隔距離
public int roadDistance;
並定義函式:
// 切出新的道路
public void changeRoad(Transform arrivePos)
{
int index = arrivePosList.IndexOf(arrivePos);
if(index >= 0)
{
int lastIndex = index - 1;
if (lastIndex < 0)
lastIndex = roadList.Count - 1;
// 移動道路
roadList[index].position = roadList[lastIndex].position + new Vector3(roadDistance, 0, 0);
initRoad(index);
}
else
{
Debug.LogError("arrivePos index is error");
return;
}
}
void initRoad(int index)
{
string roadName = roadList[index].name;
// 清空已有障礙物
foreach(GameObject obj in objDict[roadName])
{
Destroy(obj);
}
objDict[roadName].Clear();
// 新增障礙物
foreach(Transform pos in bornPosList[index])
{
GameObject prefab = objPrefabList[Random.Range(0, objPrefabList.Count)];
Vector3 eulerAngle = new Vector3(0, Random.Range(0, 360), 0);
GameObject obj = Instantiate(prefab, pos.position, Quaternion.EulerAngles(eulerAngle)) as GameObject;
obj.tag = "Obstacle";
objDict[roadName].Add(obj);
}
}
在Start()中:
void Start () {
foreach(Transform road in roadList)
{
List<GameObject> objList = new List<GameObject>();
objDict.Add(road.name, objList);
}
initRoad(0);
initRoad(1);
}
然後開啟之前的moveController.cs,宣告變數:// 遊戲管理器
public GameManager gameManager; 定義函式:
void OnTriggerEnter(Collider other)
{
// 如果是抵達點
if (other.name.Equals("ArrivePos"))
{
gameManager.changeRoad(other.transform);
}
// 如果是透明牆
else if (other.tag.Equals("AlphaWall"))
{
// 沒啥事情
}
// 如果是障礙物
else if(other.tag.Equals("Obstacle"))
{
}
}
呼,一大串程式碼,大家敲的累不累,什麼!你是copy過去的?太過分了!我要拿刀子了!
嗯,切換回Unity中,點選GameManager這個物體,設定其GameManager元件的值:
這裡的BornPos指的是障礙物出生點,以下圖所示為每一條道路定義一個或多個出生點,每條路的出生點用一個BornPos的空物體進行管理: 然後將出生點按其所處道路的序號一一拖入(先設定size的值,3條就設定為3) RoadList也是一樣,將道路按序號一一拖入。 這裡的ArrivePosList要注意一下,並不是直接按道路序號拖入,而是往後一位,即: road2 road3 ........ roadn road1
這樣的順序將其對應的ArrivePos拖入列表 然後將需要生成的障礙物的Prefab檔案拖入ObjPrefabList 設定道路的間隔距離(即一條道路的中心點到接下來一條道路中心點的距離 distance = road1.length / 2 + road2.length / 2 大概這麼計算) 到這一步為止,GameManager的設定基本完成。點選人物的GameObject,設定moveController,將GameManager的遊戲物件拖入到指定位置: 對了,還有一步非常重要的設定: 為人物新增Collider與RightBody,為所有障礙物和路面新增Collider(注意不要勾上Is Trigger) 然後運行遊戲。 呼,這時候沒有問題的話應該是能看到有障礙物出現了,人物走到障礙物處會被擋住,並且道路會自動拼接移動,無止境的走下去、走下去、走下去。。。 這個Demo也基本進入尾聲了,接下來,做最後的遊戲失敗判斷和讓角色趕回正常位置。 開啟GameManager.cs,宣告變數:
public bool isEnd = false; 開啟moveController.cs 宣告變數:
// 攝像機距離人物的距離
public float cameraDistance; 在Update()函式中 新增以下程式碼:
// 當人物與攝像機距離小於cameraDistance時 讓其加速
if(this.transform.position.x - cameraTransform.position.x < cameraDistance)
{
moveVSpeed += 0.1f;
if (moveVSpeed > maxVSpeed)
{
moveVSpeed = maxVSpeed;
}
}
// 超過時 讓攝像機趕上
else if(this.transform.position.x - cameraTransform.position.x > cameraDistance)
{
moveVSpeed = minVSpeed;
cameraTransform.position = new Vector3(this.transform.position.x - cameraDistance, cameraTransform.position.y, cameraTransform.position.z);
}
// 攝像機超過人物
if(cameraTransform.position.x - this.transform.position.x > 0.0001f)
{
Debug.Log("你輸啦!!!!!!!!!!");
gameManager.isEnd = true;
}
定義OnGUI()函式:
void OnGUI()
{
if (gameManager.isEnd)
{
GUIStyle style = new GUIStyle();
style.alignment = TextAnchor.MiddleCenter;
style.fontSize = 40;
style.normal.textColor = Color.red;
GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 50, 200, 100), "你輸了~", style);
}
}
到這一步,Demo編寫完成。(由於現在是凌晨4點 實在太疲憊,所以本篇基本都是直接貼程式碼,如果有朋友有什麼問題的話 可以直接留言哈~)