今天我们要学习的是小时候大家都玩过的坦克大战的游戏,采用的是Unity3D进行制作这款2D平面游戏。
首先我们新建一个2D的新项目,这里不做介绍了。
然后是资源的导入,地址:https://pan.baidu.com/s/1eGVzYU-pQLZntuGcFfm0Ww,密码:exqi。
下载好资源后将ReasourcePackage资源直接导入Assets即可。
然后设置一下摄像头的一些参数,把游戏的窗口比例设置为5:4,利于最后的效果。
然后我们调整一下资源里面的图片属性,单张图片为Single,图集则为Mutiple,类型都为Sprite。
图集可任意通过SpriteEditor进行切割为一个个的小图片,模式有自动和按照一定大小切割。
我们将资源中的坦克1拖入到场景中,设置大小为3,3,重命名为Player。
接着在资源中添加Prefabs文件夹用于存放预制体,将上面的Player玩家拖入。
然后将Map资源里面的图片都拖入进来,设置合适的大小,或者直接复制Player,将其Sprite的属性改为相应的图片即可。改成相应的名字后都拖入到Prefabs中待使用。
接着新建Animation和Animatoe Controller两个文件夹分别存放动作和动作控制器。
创建动画有很多种方式,这里简单介绍一种,我们以出生动画为例,选中出生的资源图片图集,选中图集中的四个图片,点击第一张图片按住shift不松,然后选中最后一张,往场景里面拖就可以创建动画了。设置一下动画的大小为3,3。将创建出来的东西拖放到对应文件夹下:
然后将出生的特效拖放到预制体中,这里我们对预制体进行一个分类,分为Map的预制体和Effect的预制体。把出生拖入到Effect中。同理我们做出爆炸动画、护盾动画以及河流的动画。最后效果如图所示:
控制玩家的移动需要用到代码了,所以我们新建一个Scripts文件夹用来存放所有的代码,然后新建脚本PlayerAI用来控制玩家的移动,然后将该脚本挂载到玩家上,进行编辑:
public float moveSpeed = 3;//坦克的移动速度
void Update()
{
float h = Input.GetAxisRaw("Horizontal");//获得玩家的水平轴输入
float v = Input.GetAxisRaw("Vertical");//获得玩家的垂直轴输入
//开始移动
transform.Translate(Vector3.right * h * moveSpeed * Time.deltaTime, Space.World);//水平移动
transform.Translate(Vector3.up * v * moveSpeed * Time.deltaTime, Space.World);//垂直移动
}
然后就是移动的效果:
上一小节玩家可以移动了,但是移动的时候坦克的朝向一直没有变化,这显然是不合理的,所以我们需要设置坦克移动的时候朝向也会跟着方向一起变化。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAI : MonoBehaviour
{
public float moveSpeed = 3;//坦克的移动速度
private SpriteRenderer spriteRender;//坦克的渲染组件
public Sprite[] tankSprite;//坦克的精灵 上右下左
private void Awake()
{
spriteRender = GetComponent();//获取坦克的渲染组件
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
float h = Input.GetAxisRaw("Horizontal");//获得玩家的水平轴输入
float v = Input.GetAxisRaw("Vertical");//获得玩家的垂直轴输入
//开始移动
transform.Translate(Vector3.right * h * moveSpeed * Time.deltaTime, Space.World);//水平移动
transform.Translate(Vector3.up * v * moveSpeed * Time.deltaTime, Space.World);//垂直移动
//调整坦克的朝向图片
if (h < 0)
spriteRender.sprite = tankSprite[3];
else if (h > 0)
spriteRender.sprite = tankSprite[1];
if (v < 0)
spriteRender.sprite = tankSprite[2];
else if (v > 0)
spriteRender.sprite = tankSprite[0];
}
}
我们还需要拖入相对应的图片对公有变量tankSprite进行赋值
然后是效果:
碰撞器是设计游戏中经常用到得组件,只要需要判断物体之间是否发生了碰撞都要用到这个组件。我们给每个物体都添加一个碰撞器,在移动的物体上还要添加刚体,需要注意的是因为这是个2D游戏,所以添加的都是2D的组件。
我们给玩家和地图的预制体都添加上BoxCollider2D的组件,然后给玩家额外添加一个Rigidbody2D,这样就可以进行碰撞了,需要注意的是设置重力Gravity Scale为0,勾选上Freeze Rotation锁定Z轴方向的旋转。
但是碰撞时会一直抖动,为了解决这个问题我们需要代码操作,我们将Update中的所有代码移到FixedUpdate中就可以了
private void FixedUpdate()//生命周期函数,在Update之后执行,固定了每一帧的时间
{
float h = Input.GetAxisRaw("Horizontal");//获得玩家的水平轴输入
float v = Input.GetAxisRaw("Vertical");//获得玩家的垂直轴输入
//开始移动
transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World);//水平移动
transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World);//垂直移动
//调整坦克的朝向图片
if (h < 0)
spriteRender.sprite = tankSprite[3];
else if (h > 0)
spriteRender.sprite = tankSprite[1];
if (v < 0)
spriteRender.sprite = tankSprite[2];
else if (v > 0)
spriteRender.sprite = tankSprite[0];
}
还有一个问题是,当我们按多个方向键时,坦克会斜着走,这显然是不合理的,为了解决这个问题,我们添加了移动的优先级,让他只能往上下左右一个方向走。我们修改一下代码就可以了,把移动封装到一个函数里面放到FixedUpdate调用:
private void FixedUpdate()//生命周期函数,在Update之后执行,固定了每一帧的时间
{
TankMove();
}
private void TankMove()//坦克移动控制函数
{
float h = Input.GetAxisRaw("Horizontal");//获得玩家的水平轴输入
float v = Input.GetAxisRaw("Vertical");//获得玩家的垂直轴输入
//开始移动
transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World);//水平移动
//调整坦克的朝向图片
if (h < 0)
spriteRender.sprite = tankSprite[3];
else if (h > 0)
spriteRender.sprite = tankSprite[1];
if (h != 0)//如果已经按下了水平方向,则不允许有垂直操作,直接返回
return;
transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World);//垂直移动
//调整坦克的朝向图片
if (v < 0)
spriteRender.sprite = tankSprite[2];
else if (v > 0)
spriteRender.sprite = tankSprite[0];
}
还有一个需要实现的是渲染的优先级,简单来说就是坦克在通过草丛时我们需要草丛显示在上方,子弹通过和河流时我们希望子弹在上方,类似于这种。渲染的优先级我们是通过Sprite Renderer组件里面的Order in Layer这个值来设置的,值大的物体会覆盖显示在值小的物体上。
攻击主要涉及的是子弹的实例化,我们添加一个攻击函数,然后在FixedUpdate中调用。
private void TankAttack()//坦克攻击函数
{
if(Input.GetKeyDown(KeyCode.Space))//如果玩家按下了空格键
{
Instantiate(bulletPrefab, transform.position, transform.rotation);//子弹进行实例化
}
}
下面是效果,可以看到子弹出来了,但是子弹的方向只有一个方向的:
我们可以通过添加不同的子弹方向图片来改变朝向,像之前坦克的朝向改变一样,还可以直接改变子弹的方向,下面我们直接用这种方法试试。
我们定义一个子弹需要旋转的角度bulletEulerangles,在坦克移动时进行赋值。
private Vector3 bulletEulerangles;//子弹应该旋转的欧拉角
private void TankMove()//坦克移动控制函数
{
float h = Input.GetAxisRaw("Horizontal");//获得玩家的水平轴输入
float v = Input.GetAxisRaw("Vertical");//获得玩家的垂直轴输入
//开始移动
transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World);//垂直移动
//调整坦克的朝向图片
if (v < 0)
{
spriteRender.sprite = tankSprite[2];
bulletEulerangles = new Vector3(0, 0, 180);
}
else if (v > 0)
{
spriteRender.sprite = tankSprite[0];
bulletEulerangles = new Vector3(0, 0, 0);
}
if (v != 0)//如果已经按下了水平方向,则不允许有垂直操作,直接返回
return;
//开始移动
transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World);//水平移动
//调整坦克的朝向图片
if (h < 0)
{
spriteRender.sprite = tankSprite[3];
bulletEulerangles = new Vector3(0, 0, 90);
}
else if (h > 0)
{
spriteRender.sprite = tankSprite[1];
bulletEulerangles = new Vector3(0, 0, -90);
}
}
下面是效果,可以看到子弹的方向正确了: