Unity游戏开发之双人坦克
作者:
虚拟喵
来源
: https://blog.csdn.net/qq_35361471/article/details/79321304
开发版本:unity 5.3.5f
适合人群:初学Unity者
源文件链接请见文末!
开启学习之旅吧!
玩法:双方分别用WSAD键或上下左右键来控制两个坦克移动,用空格键或回车键来控制发射炮弹,当一方血条为0,则重新开始
场景搭建
新建3D工程TankWar,新建MainScene场景,导入Assets资源,将Prefabs文件夹中LevelArt预制体拖入场景中。
场景预制体中已经包含方向光,可以将场景原有的方向光删除。
如果出现如下场景,由于渲染原因
Window-lighting窗口 build一下场景 等右下的进度结束就可了,时间稍微有点长~
开发坦克
将Models中Tank模型拖入场景,并设为预制体,标签设置为Play
注意,对场景中的Tank进行一些操作后,需要Apply一下,才可以将这些操作映射到其他预制体上。
给Tank添加BoxCollider,并对Collider大小进行调节,可以切换到正交视图进行调节
刚体需要冻结Y轴移动,X轴和Z轴的旋转
为Tank添加拖尾效果,将Prefabs文件夹中的DustTrail粒子特效,放在Tank左右后轮
将拖尾粒子特效分别作为左右后轮的子物体,方便随Tank一起移动
在Tank身上挂载TankController.cs脚本
添加Move方法,在FixedUpdate()内调用,FixedUpdate是固定帧时间调用
void Move()
{
//Input.GetAxis("Vertical")在键盘按下W/S键或上下键时候,会返回范围在-1到1的值
float v = Input.GetAxis("Vertical");
//修改刚体速度,transform.forward控制物体在Z轴移动
_rigidbody.velocity = transform.forward * v * speed;
//Input.GetAxis("Horizontal")在键盘按下A/D键或左右键时候,会返回范围在-1到1的值
float h = Input.GetAxis("Horizontal");
//angularVelocity是刚体角速度向量,可以控制物体旋转,transform.up是物体旋转的轴
_rigidbody.angularVelocity = transform.up * h * rotateSpeed;
}
开发子弹
控制Tank子弹发射
Models文件夹中的Shell拖入场景,添加Rigidbody和CapsuleCollider,并设为预制体
为坦克添加一个发射子弹的位置ShootPos,设为Tank的子物体,要注意这个位置不要在坦克自身的碰撞体内
在PlayerController.cs中添加发射子弹的方法Attack(),在FixedUpdate()方法内调用
void Attack()
{
//发射键_keyCode可以在Inspector面板设置
if (Input.GetKeyDown(_keyCode))
{
//在发射位置ShootPos实例化子弹
GameObject go = GameObject.Instantiate(shellPrefab, shootPos.position, shootPos.rotation) as GameObject;
//方法一:可以通过修改刚体速度
//go.GetComponent<Rigidbody>().velocity = go.transform.forward * shellSpeed;
//方法二:可以给物体添加一个方向的力
go.GetComponent<Rigidbody>().AddForce(go.transform.forward * shellSpeed);
}
}
可以玩家一设置发射键为空格键,玩家二设置发射键为回车键
在子弹身上挂载脚本ShellController.cs
public class ShellController : MonoBehaviour
{
//prefabs文件夹中的爆炸预设
public GameObject explosionPrefabs;
private void OnTriggerEnter(Collider other)
{
//实例化爆炸效果,并赋值给go
GameObject go = GameObject.Instantiate(explosionPrefabs, transform.position, transform.rotation) as GameObject;
//在爆炸效果实例化1.5s后销毁,因为这个爆炸效果的时长就是1.5s
Destroy(go, 1.5f);
//立即销毁该子弹
Destroy(this.gameObject);
//如果子弹碰到的是坦克,则调用对象身上的TakeDamage()方法
if (other.CompareTag("Player"))
{
//SendMessage()消息推送,可以调用该对象身上的私有或公有方法
other.SendMessage("TakeDamage");
}
}
}
TakeDamage()方法请看下文。微信搜索公众号 [爱上游戏开发],回复 “资料”,免费领取 200G 学习资料!
优化坦克控制
由于游戏中需要有两个坦克,目前的代码会让两个Tank控制方式一致
我们希望一台Tank由WSAD键控制,另一台由上下左右来控制
在InputManager里添加两个新的虚拟键
Edit-Project Setting-Input
将原有的Horizontal右键复制两个新的,并修改名称和监听按键
Vertical同样的操作
我们根据不同的虚拟键名来分别控制两个Tank
新建一个枚举类型,用来区别操作方式
public enum Player
{
Player01,
Player02
}
//public可以外部设置,默认为玩家一
public Player player = Player.Player01;
private int PlayerNum = 1;
private Rigidbody _rigidbody;
private void Start()
{
_rigidbody = gameObject.GetComponent<Rigidbody>();
switch (player)
{
case Player.Player01:
PlayerNum = 1;
_keyCode = KeyCode.Space;
break;
case Player.Player02:
PlayerNum = 2;
_keyCode = KeyCode.Return;
break;
}
}
private void FixedUpdate()
{
Move();
//Attack();
}
void Move()
{
float v = Input.GetAxis("VerticalPlayer0" + PlayerNum);
float h = Input.GetAxis("HorizontalPlayer0" + PlayerNum);
_rigidbody.velocity = transform.forward * v * speed;
_rigidbody.angularVelocity = transform.up * h * rotateSpeed;
}
在Inspector面板,玩家一可以设置为Player01,玩家二可以设置为Player02
修改两个坦克的颜色,可以将Materials文件夹中的不同颜色材质直接拖到场景中坦克对象上即可
摄像机跟随
public class CamFellowPlayer : MonoBehaviour
{
public Transform player1;
public Transform player2;
public float maxSize = 20f;
public float minSize = 6.5f;
private Vector3 offset;
private float ratio;
private float distance;
private void Start()
{
//计算摄像机距离两个坦克中心点的偏差值
offset = transform.position - (player1.position + player2.position) / 2f;
//计算初始的两个坦克的距离
distance = Vector3.Distance(player1.position, player2.position);
//计算初始正交大小和初始Tank距离的比例
ratio = minSize / distance;
}
private void Update()
{
//防止因为坦克销毁后报错
if (player1!=null&&player2!=null)
{
transform.position = (player1.position + player2.position) / 2f + offset;
distance = Vector3.Distance(player1.position, player2.position);
//计算当前的坦克距离和比例的乘积,则得到当前的正交大小
float size = distance * ratio;
//Mathf.Clamp可以限制Size的值在minSize和maxSize之间
size = Mathf.Clamp(size, minSize, maxSize);
//将得到的size赋值给主摄像机的正交大小
//Camera.main返回的是主摄像机
Camera.main.orthographicSize = size;
}
}
}
计算伤害并设计血条
为tank添加脚本TankHealth.cs
我们使用UGUI的滑动条来设计血条
因为涉及到UGUI一些知识,我先将血条做成了预制体
注意别忘记在Inspector面板,把血条hpSlider赋值
public class TankHealth : MonoBehaviour
{
//设计血量为100,可以在Inspector面板调整
public int hp = 100;
//prefabs文件夹中的坦克爆炸效果预设
public GameObject tankExplosion;
public Slider hpSlider;
private int hpTotal;
private void Start()
{
//初始化hpTotal
hpTotal = hp;
}
private void TakeDamage()
{
//如果当前hp值已经低于0,则返回
if (hp <= 0) return;
hp -= Random.Range(10, 20);
//根据当前的hp和总血量hpTotal的比值还赋予滑动条的值
hpSlider.value = (float)hp / hpTotal;
//受到伤害之后,血量为零,则实例化爆炸效果,并销毁该坦克
if (hp<=0)
{
//transform.position + Vector3.up因为坦克的中心点在坦克的底部,可以向上偏移一米,爆炸效果更好
GameObject.Instantiate(tankExplosion, transform.position + Vector3.up, transform.rotation);
//销毁该坦克对象
Destroy(this.gameObject);
//重新加载当前场景
UnityEngine.SceneManagement.SceneManager.LoadScene("MainScene");
}
}
}
至此,游戏基本开发完成,如果觉得单调可以加一些音乐音效,可以参照我之前的案例,就不在此赘述啦!
喜欢我的,就关注我吧,你的关注,就是我的动力!
素材及工程链接:百度云盘链接 密码:ly74
-- END --
公众号后台回复「资料」获取超多学习福利
>>> 点击进入技术讨论群 <<< ▽想深入了解么?
长按/扫码关注我吧↑↑↑
觉得不错就点个在看吧!