雙人3D坦克實現
player1: WSAD控制上下左右 空格鍵發射炮彈
player2: IKJL可控制上下左右 B鍵發射炮彈
每個坦克只有100hp,子彈擊中1次扣30hp,hp時時顯示在坦克上
當一輛坦克hp低於0時,游戲結束
Main Camera聚焦兩輛坦克中心點
游戲項目已托管到github上(里面有個32bit可執行文件) 傳送門
(官方demo 素材包有點大。。)
游戲界面
(覺得子彈射程短,可以適當提高游戲子彈速度)

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class FollowCamera : MonoBehaviour { public GameObject tank1; public GameObject tank2; public float size= 0.58f; Vector3 p; // Use this for initialization void Start () { Vector3 target=(tank1.transform.position + tank2.transform.position) / 2; p = target - transform.position; } // Update is called once per frame void Update () { if (tank2 != null&&tank1!=null) { //計算出兩個坦克之間的距離 transform.position = (tank1.transform.position + tank2.transform.position) / 2 - p; //找到兩個坦克的中點 //讓攝像機對准中心點 //根據兩點之間的距離,調整攝像機的尺寸 float distance = Vector3.Distance(tank1.transform.position, tank2.transform.position); GetComponent<Camera>().orthographicSize = distance * size + 2f; } if(tank2 == null || tank1 == null) { SceneManager.LoadScene("SampleScene"); } } }

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class TankHealth : MonoBehaviour { public int hp = 100; public GameObject ex; public Slider slider; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } public void TakeDamage() { hp -= 30; if (hp < 0) { Destroy(gameObject); GameObject go = Instantiate(ex, transform.position, transform.rotation); } slider.value = hp; } }

using System.Collections; using System.Collections.Generic; using UnityEngine; public class DestroyForTime : MonoBehaviour { // Use this for initialization void Start () { Destroy(gameObject,2); } // Update is called once per frame void Update () { } }

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Shell : MonoBehaviour { public GameObject ex; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } private void OnCollisionEnter(Collision collision) { Destroy(gameObject); GameObject go = Instantiate(ex,transform.position,transform.rotation); //判斷是否打中坦克 if(collision.gameObject.tag == "Tank") { collision.gameObject.GetComponent<TankHealth>().TakeDamage(); } //扣除坦克的血量 } }

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Shoot : MonoBehaviour { public GameObject shell; Transform pos; public KeyCode key = KeyCode.Space; public float speed = 180; // Use this for initialization void Start () { //找到子對象 pos = transform.Find("FirstPosition"); } // Update is called once per frame void Update () { //按下一個按鍵 if (Input.GetKeyDown(key)) { GameObject go = Instantiate(shell,pos.position,pos.rotation); go.GetComponent<Rigidbody>().velocity = go.transform.forward * speed; //go.GetComponent<Rigidbody>().velocity = Vector3.forward * speed; } } }

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Movement : MonoBehaviour { public float speed = 10; public float angularSpeed = 5; public string player = "1"; // Use this for initialization void Start () { } // Update is called once per frame void Update () { //獲得玩家的鍵盤輸入 float v = Input.GetAxis("Vertical"+player); float h = Input.GetAxis("Horizontal"+ player); //讓坦克前后移動 GetComponent<Rigidbody>().velocity = transform.forward * v * speed; GetComponent<Rigidbody>().angularVelocity = transform.up * h * angularSpeed; } }
實現過程
創建場景
導入坦克資源場景

Lightmapping光照貼圖技術是一種增強靜態場景光照效果的技術,其優點是可以通過較少的性能消耗使靜態場景看上去更加真實,豐富,更加具有立體感;缺點是不能用來實時地處理動態光照。當游戲場景包含了大量的多邊形時,實時光源和陰影對游戲的性能的影響會很大。這時使用Lightmapping技術,將光線效果預渲染成貼圖使用到多邊形上模擬光影效果。
加載光照貼圖有時候會造成場景卡頓
Auto Generate :設置成不自動生成
Generate Lighting :生成一次場景光照
坦克的前后移動
添加坦克預設體進入游戲場景,DustTrail粒子特效預設體放到坦克上
(GameObject ->Align With View 將Game視圖對准當前Scene視圖)
給坦克添加碰撞器和剛體
(碰撞器慢慢移,拖動數值調整到剛好包圍完坦克)
添加移動坦克腳本

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Movement : MonoBehaviour { public float speed = 5; // Use this for initialization void Start () { } // Update is called once per frame void Update () { //獲得玩家的鍵盤輸入 float v = Input.GetAxis("Vertical"); //讓坦克前后移動 GetComponent<Rigidbody>().velocity = transform.forward * v * speed; } }
設置坦克移動速度
public float speed = 5;
獲得玩家的鍵盤輸入
float v = Input.GetAxis("Vertical");
讓坦克前后移動
GetComponent<Rigidbody>().velocity = transform.forward * v * speed;
(將相機放到坦克里面,實現相機和坦克一起移動)
坦克的旋轉
查看Unity輸入按鍵值
水平移動:Vertical
左右移動:Horizontal

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Movement : MonoBehaviour { public float speed = 10; public float angularSpeed = 5; // Use this for initialization void Start () { } // Update is called once per frame void Update () { //獲得玩家的鍵盤輸入 float v = Input.GetAxis("Vertical"); float h = Input.GetAxis("Horizontal"); //讓坦克前后移動 GetComponent<Rigidbody>().velocity = transform.forward * v * speed; GetComponent<Rigidbody>().angularVelocity = transform.up * h * angularSpeed; } }
設置坦克移動轉動角速度
public float angularSpeed = 5;
獲得玩家的鍵盤輸入
float h = Input.GetAxis("Horizontal");
讓坦克前后移動
GetComponent<Rigidbody>().angularVelocity = transform.up * h * angularSpeed;
為實現雙人坦克效果,提高按鍵靈活性,在InputManager管理器中添加兩個Axes
修改Axes值Horizontal1、Horizontal2、Vertical1、Vertical2
我把玩家二上下左右WSAD按鍵修改為IKJL,實現雙人笑游戲效果
默認角色是1號玩家
public string player = "1"; //獲得玩家的鍵盤輸入 float v = Input.GetAxis("Vertical"+player); float h = Input.GetAxis("Horizontal"+ player);
新建雙人坦克修改Player值
坦克子彈
給子彈添加剛體和碰撞器
(將子彈設置為預設體,保存到Gary文件夾中)
給坦克添加一個空物體,每次坦克都是從這個點動態生成子彈發射出去的
添加Shoot腳本,掛載到坦克物體上

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Shoot : MonoBehaviour { public GameObject shell; Transform pos; // Use this for initialization void Start () { //找到子對象 pos = transform.Find("FirstPosition"); } // Update is called once per frame void Update () { //按下一個按鍵 if (Input.GetKeyDown(KeyCode.Space)) { //發射子彈 實例化 Instantiate(shell,pos.position,pos.rotation); } } }
引用子彈實例化
public GameObject shell;
找到子彈發射的位置
pos = transform.Find("FirstPosition");
按下一個按鍵,,將子彈實例化
if (Input.GetKeyDown(KeyCode.Space)) { Instantiate(shell,pos.position,pos.rotation); }

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Shoot : MonoBehaviour { public GameObject shell; Transform pos; public KeyCode key = KeyCode.Space; // Use this for initialization void Start () { //找到子對象 pos = transform.Find("FirstPosition"); } // Update is called once per frame void Update () { //按下一個按鍵 if (Input.GetKeyDown(key)) { Instantiate(shell,pos.position,pos.rotation); } } }
在外添加一個key,按任意鍵可以射出子彈
public KeyCode key = KeyCode.Space; //按下一個按鍵 if (Input.GetKeyDown(key)) { Instantiate(shell,pos.position,pos.rotation); }
定義子彈發射速度
public float speed = 180;
按下一個按鍵,子彈發射出去
if (Input.GetKeyDown(key)) { GameObject go = Instantiate(shell,pos.position,pos.rotation); go.GetComponent<Rigidbody>().velocity = go.transform.forward * speed; go.GetComponent<Rigidbody>().velocity = Vector3.forward * speed; }
子彈爆炸動畫
添加腳本shell,掛載到子彈物體對象上
private void OnCollisionEnter(Collision collision) { Destroy(gameObject); }
當子彈碰到剛體時銷毀自己
添加子彈碰到物體觸發爆炸效果
引用粒子特效
public GameObject ex;
private void OnCollisionEnter(Collision collision) { Destroy(gameObject); GameObject go = Instantiate(ex,transform.position,transform.rotation); }

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Shell : MonoBehaviour { public GameObject ex; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } private void OnCollisionEnter(Collision collision) { Destroy(gameObject); GameObject go = Instantiate(ex,transform.position,transform.rotation); } }
子彈血量的減少
給坦克添加標簽

using System.Collections; using System.Collections.Generic; using UnityEngine; public class TankHealth : MonoBehaviour { public int hp = 100; public GameObject ex; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } public void TakeDamage() { hp -= 30; if (hp < 0) { Destroy(gameObject); GameObject go = Instantiate(ex, transform.position, transform.rotation); } } }
初始化血量hp=100
public int hp = 100;
設置坦克爆炸粒子效果
public GameObject ex;
當血量低於0時,坦克銷毀,爆炸粒子效果出現
public void TakeDamage() { hp -= 30; if (hp < 0) { Destroy(gameObject); GameObject go = Instantiate(ex, transform.position, transform.rotation); } }
相機跟隨
將照相機改為正交
在攝像機中添加並掛載FollowCameral腳本
兩個坦克的引用
public GameObject tank1; public GameObject tank2;
兩個坦克之間的中點
Vector3 p; Vector3 target=(tank1.transform.position + tank2.transform.position) / 2; p = target - transform.position;
計算出兩個坦克之間的距離
transform .position= (tank1.transform.position + tank2.transform.position) / 2 - p;
找到兩個坦克的中點並讓攝像機對准中心點,根據兩點之間的距離,調整攝像機的尺寸
float distance = Vector3.Distance(tank1.transform.position,tank2.transform.position); GetComponent<Camera>().orthographicSize = distance * size+ 2f;

using System.Collections; using System.Collections.Generic; using UnityEngine; public class FollowCamera : MonoBehaviour { public GameObject tank1; public GameObject tank2; public float size= 0.58f; Vector3 p; // Use this for initialization void Start () { Vector3 target=(tank1.transform.position + tank2.transform.position) / 2; p = target - transform.position; } // Update is called once per frame void Update () { //計算出兩個坦克之間的距離 transform .position= (tank1.transform.position + tank2.transform.position) / 2 - p; //找到兩個坦克的中點 //讓攝像機對准中心點 //根據兩點之間的距離,調整攝像機的尺寸 float distance = Vector3.Distance(tank1.transform.position,tank2.transform.position); GetComponent<Camera>().orthographicSize = distance * size+ 2f; } }
添加音效
在Main Camera上綁定Audio Source音樂播放器
血條
創建一個Slider,把Canvas移動到坦克上
設置Slider控件的 PosX=0 PosX=0 PosZ=0
參數下如下配置
設置完成后效果如下
設置成垂直

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class TankHealth : MonoBehaviour { public int hp = 100; public GameObject ex; public Slider slider; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } public void TakeDamage() { hp -= 30; if (hp < 0) { Destroy(gameObject); GameObject go = Instantiate(ex, transform.position, transform.rotation); } slider.value = hp; } }
添加Slider控件
public Slider slider;
坦克攻擊時,時時刷新slider.value值
public void TakeDamage() { hp -= 30; if (hp < 0) { Destroy(gameObject); GameObject go = Instantiate(ex, transform.position, transform.rotation); } slider.value = hp; }
完結!