Unity3D_(游戏)双人3D坦克_简易版
双人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; }
完结!