一、實驗概述
1.1實驗名稱:
Roll a ball
1.2實驗目的:
本次實驗的總的目的是通過具體的程序的編寫與unity軟件相結合,將所學的知識內化,即將在課堂上學到的知識集成在一起,並實現相關功能,從而鍛煉自己的程序編寫、程序調試能力以及對unity的掌握能力。
1.3實驗內容:
實驗內容是使用Unity軟件完成一個小球滾動吃掉小立方體的游戲。
本次實驗是以windows操作系統為平台,通過unity軟件進行本次實驗的項目實現,unity是一個十分優秀的全面整合的專業游戲引擎。通過unity軟件和編程工具的結合可以輕松創建各種3D模型,模擬模型動態以及插入互動內容。
1.4實驗要求:
基本要求:
1.構建一個小球滾動的游戲場景;
2.創建一個小球,按鍵盤上的上下左右鍵,小球會朝相應的方向移動,小球移動的時候相機也要相應移動。
3.在場景中創建多個立方體,每個立方體都在旋轉;小球與立方體發生碰撞的時候,立方體消失,計分板上得分加“1”。
4.當得分達到“5”分時,在屏幕上顯示“XXX同學,你贏了!”,如果不能輸出中文,可以用英文代替。
加分項目:
- 添加小球和立方體發生碰撞的特效,添加立方體隨機生成,添加小球撞擊阻礙物的物理效果。
- 你能想到的可以實現的其他效果。
1.4實驗開發步驟指導
l 創建地面並貼上紋理
l 創建player
l 設置攝像機
l 隨機創建食物
l 設置碰撞
l 得分音效
l 計分板與獲勝
l 隨機阻礙物
l player和food觸發特效
l 設置退出和重新開始
二、實驗過程
提前導入unitychan的包
1.創建地面並貼上紋理
(1) 創建plane並修改大小至合適
(2) 創建材質GroundMaterial並在Albedo處添加貼圖
2.創建player
(1) 設置player,放入場景
(2) 創建Animation Controller(player),添加Animator中的controller
(3) 添加rigidbody 並取消Use Gravity。
否則由於重力原因會使unitychan下墜
(4) 設置animator
A.創建BlendTree,添加動作
B.調整PosX和PosY

C.設置參數inputH和inputY,分別代表水平方向和豎直方向,通過這兩個個參數的改變,可以調整動作的狀態。

(5) 添加腳本使人物可以進行移動
代碼思路:從鍵盤的上下左右鍵獲取用戶的輸入,通過blendTree控制向前向后向左向右的四個運動狀態,同時發生一定的旋轉,改變方向。
1. //從鍵盤獲取獲取inputH和inputV
2. inputH = Input.GetAxis("Horizontal");
3. inputV = Input.GetAxis("Vertical");
4. //設置值
5. anim.SetFloat("inputH", inputH);
6. anim.SetFloat("inputV", inputV);
7. //計算位移
8. Vector3 moveX = transform.right * inputH * 100f * Time.deltaTime *speed;
9. Vector3 moveZ = transform.forward *inputV * 100f * Time.deltaTime *speed;
10. float rotey = inputH * 30f * Time.deltaTime * speed;
11. //進行移動
12. rbody.velocity = moveX + moveZ;
13. //rbody.velocity = new Vector3(moveX, 0f, moveZ);
14. this.transform.Rotate(new Vector3(0, rotey, 0));
(6) 設置角色與場景間位移
取消勾選Apply Root Motion。勾上apply root motion對象的Transfrom變動會基於動畫移動,不受腳本控制。 勾掉則Transform受到腳本控制。
3.設置攝像機
(1)跟隨player移動
將main camera 設置為unitychan的子對象,則可以跟隨player的移動而移動。
(2)第三人稱視角。
設置相機的位置,使其位於player的背后

(3)設置camera
最終效果:

4.隨機創建食物
在plane上隨機位置隨機時間間隔生成一個cube,如果沒有被觸發則在15s后銷毀。
(1) 創建prefab
我們會在游戲中產生許多屬性相同的小Cube作為食物,如果要控制每一個食物隨機間隔時間出現,通過將其設為預制體可以減輕很多工作量。比如,給預制體添加一個腳本,則由預制體而來的游戲對象都有該腳本。
(2) 貼圖
創建一個 Cube Material,賦給food
(3) 設置旋轉
給預制體food添加腳本CubeRotate控制立方體的旋轉。
選用transform.Rotate函數
【function Rotate (axis : Vector3, angle : float, relativeTo : Space = Space.Self) : void】
transform.Rotate(new Vector3(0f, 1f, 1f));
(4) 隨機出現
通過腳本設置食物在plane中隨機時間,隨機位置產生。並且在15s沒有被吃時銷毀。
A.新建一個空的GameObject,添加腳本FoodControl
代碼如下:
void Start () {
float groundwidth=ground.GetComponent<Renderer>().bounds.extents.x;
float foodwidth = myfood.GetComponent<Renderer>().bounds.extents.x;
maxWidth = groundwidth - foodwidth;
// Vector3 screnpos = new Vector3(Screen.width, 0, 0);
// Vector3 moveWidth = Camera.main.ScreenToWorldPoint(screnpos);
//食物自身寬度
//maxWidth = moveWidth.x - foodwidth;
}
// Update is called once per frame
void Update () {
time -= Time.deltaTime;
if (time < 0)
{
//產生一個隨機數,代表實例化下一個food所需要的時間
time = Random.Range(1.5f, 2.0f);
//產生隨機數Posx,PosY,代表實例化food的坐標
float posX = Random.Range(-maxWidth, maxWidth);
float posY = Random.Range(-maxWidth, maxWidth);
Vector3 spawnPositon = new Vector3(posX, 2f, posY);
//實例化food,15秒后銷毀
newmyfood = (GameObject)Instantiate(myfood, spawnPositon, Quaternion.identity);
Destroy(newmyfood, 15);
}
}
B.設置參數
運行效果:

5.設置碰撞
(1)為player添加Box Collider組件,編輯范圍

(2) 碰撞檢測
A.在MonoBehaviour類中,OnCollisionEnter、OnCollisionExit和OnCollisionStay是碰撞時的回調方法,可以在腳本中重載它們。當綁定了Collision腳本組件的游戲物體發生碰撞時,OnCollisionEnter便會被觸發調用一次。然后,在整個碰撞過程中會持續調用OnCollisionStay方法,直到碰撞接觸被解除時,OnCollisionExit被觸發一次。這三個方法都有一個Collision類型的參數,用於保存碰撞信息。
撞檢測 /* void OnCollisionEnter(Collision collision) { //獲取碰撞到的游戲物體上的Collider // string name = collision.collider.name; // print(name); if (collision.collider.tag == "food") { Destroy(collision.collider.gameObject); } } */
B.添加腳本后,給player的RigidBody設置Constraints

(3)觸發檢測
使用碰撞檢測,發生碰撞的游戲物體之間會有碰撞模擬,例如撞到東西會反彈或者停頓一下。如果只想要檢測物體與物體之間是否發生接觸,但是不要產生碰撞的效果,可以使用觸發器來進行接觸檢測。
這樣在游戲物體發生接觸的時候就不會有碰撞的效果了,而是會直接穿過去。Unity的碰撞器有很多類型,Cube的碰撞器類型是盒子碰撞器,另外還有球形碰撞器、膠囊體碰撞器等。
A.將food設為觸發器類型
B.設置腳本
使用MonoBehaviour類的OnTriggerEnter、OnTriggerExit和OnTriggerStay是觸發檢測的三個回調方法,OnTriggerEnter在游戲物體發生接觸時調用一次,OnTriggerExit在游戲物體完全分離時調用一次,而OnTriggerStay在游戲物體接觸過程中持續調用。 觸發器回調的這三個方法的參數都是Collider類型,表示的就是被碰撞的游戲物體的觸發器組件對象。
在本次實驗中使用的是OnTriggerEnter。
void OnTriggerEnter(Collider collider) { if (collider.tag == "food") { GetComponent<AudioSource>().Play(); GameObject neweffect = (GameObject)Instantiate(effect, transform.position, effect.transform.rotation); neweffect.transform.parent = transform; Destroy(neweffect, 1.0f); score++; if (score == 5) { wintext.SetActive(true); wininmage.SetActive(true); var z=GameObject.FindGameObjectsWithTag("food"); for(int i=1;i<z.Length;i++) { Destroy(z[i]); } got.GetComponent<FoodControl>().enabled = false; } Destroy(collider.gameObject); text.text = "your score:"+score.ToString(); } }
(5)觸發后cube消失
當檢測到觸發后,說明player吃到了食物,就要將食物銷毀。通過調用Destroy(),可以銷毀想要銷毀的游戲對象。
觸發器回調的參數都是Collider類型,表示的就是被碰撞的游戲物體的觸發器組件對象。因此在Destroy()中傳入collider.gameObject即可。
6.得分音效
(1)添加AudioSource
為避免在游戲啟動時開啟音效,取消勾選Play On Awake
(2)播放
7.計分板與獲勝
(1)計分
當player吃到food后分數會上升
這一功能主要在觸發檢測中實現,通過定義一個int型score,來記錄吃到food的個數,每當有觸發被檢測到,說明player吃到了food,則score加一。
(2)計分板
在Unity中使用UI可以通過GUI和UGUI。在這里使用的是UGUI。但在操作過程中確實感受到了如果要想設置成自己想要的界面狀態,需要大量的調整。
添加畫布,添加text對象,通過在代碼中控制文本來顯示分數。
void OnGUI() { GUIStyle style = new GUIStyle(); style.normal.background = null; style.normal.textColor = new Color(0, 0, 0); style.fontSize = 15; GUI.Label(new Rect(100, 20, 100, 40), "your score:" + score, style); }
(3)youwin!
在canvas中添加text和image,但並不顯示,當score達到5后,通過代碼進行顯示。
效果截圖:

(4)勝利后清理環境
當吃到五個食物后,清理環境中剩下的食物,並且停止產生新的食物。
A.清理剩下的食物
通過GameObject.FindGameObjectsWithTag()方法來獲取所有標簽為“food”的食物,並一一銷毀。
var z=GameObject.FindGameObjectsWithTag("food"); for(int i=1;i<z.Length;i++) { Destroy(z[i]); }
B.停止產生新食物
通過關閉產生食物的腳本來停止產生新食物。
got.GetComponent<FoodControl>().enabled = false;
8.隨機阻礙物
隨即阻礙物的實現類似於隨機生成食物。
(1) 創建prefabs![]()
(2) 添加腳本
隨機生成一個數字a,在場景中生成相應個數的障礙物,player碰到障礙物時會被阻擋。
void Start () { a = Random.Range(1, 4); //生成 [1,4) 的隨機整數 float groundwidth = ground.GetComponent<Renderer>().bounds.extents.x; float foodwidth = myban.GetComponent<Renderer>().bounds.extents.x; maxWidth = groundwidth - foodwidth; } // Update is called once per frame void Update () { if (a > 0) { float posX = Random.Range(-maxWidth, maxWidth); float posY = Random.Range(-maxWidth, maxWidth); Vector3 spawnPositon = new Vector3(posX, 1f, posY); //實例化阻礙物 newmyban = (GameObject)Instantiate(myban, spawnPositon, Quaternion.identity); a--; } }
效果截圖

9.player和food觸發特效
(1) 導入effects資源
(2) 編寫代碼
在觸發檢測中添加以下代碼,使得出發時播放effct效果,1s后銷![]()
GameObject neweffect = (GameObject)Instantiate(effect, transform.position, effect.transform.rotation); neweffect.transform.parent = transform; Destroy(neweffect, 1.0f);
效果截圖:

10.設置退出和重新開始
(1)利用UGUI,設置兩個button:exit、restart。
(2)添加響應事件
代碼如下:
1. public void ReStart()
2. {
3. score = 0;
4. wintext.SetActive(false);
5. wininmage.SetActive(false);
6. exitbutton.SetActive(false);
7. restartbutton.SetActive(false);
8. got.GetComponent<FoodControl>().enabled = true;
9. }
1. public void GameExit()
2. {
3. Application.Quit();
4. }
10.設置空氣牆
(1)添加4個cube,分別調整至周圍
(2)勾除mesh render
最后的效果~~

