一·前言
在游戏的浩瀚星空中,《坦克大战》无疑是一颗璀璨且经典的恒星。自问世以来,它凭借紧张刺激的对战玩法、丰富多变的关卡设计,深深烙印在无数玩家的记忆深处,成为了电子游戏发展历程中的一座里程碑。对于广大游戏开发爱好者而言,复刻这样一款经典游戏,不仅是一次向经典致敬的情怀之旅,更是一个全方位提升自身技能、深入探索游戏开发奥秘的绝佳契机。

二·视频效果展示
坦克大战
三·源项目以及素材
通过网盘分享的文件:坦克大战
链接: https://pan.baidu.com/s/1_WgxiXTH-WjKVT3pQwrJwA?pwd=1209 提取码: 1209
--来自百度网盘超级会员v5的分享
四·详细制作步骤
1.工程创建和素材导入
(1)打开Unity,创建2D工程

(2)导入给予的素材




2.场景搭建和使用素材
(1)调整MainCamera距离和背景颜色

(2)修改界面比例大小为5:4


(3)检查资源:是否切割,Texture Type一定要是2D精灵,单张还是多张取决于是否是单张图片还是图集



(4)图中资源为图集(且已经切割好,可以直接用了——点一个资源会展开很多图片)
(5)如果发现有的图片没有进行切割,可以按照下面图示进行切割

(6)创建项目游戏场景Scene,并且命名为 TankBattleScene

3.制作预制体
(1)把Player1第一张图片Hierarchy窗口,并修改名字为Player,并且调整大小如下图所示:


(2)创建一个文件夹名为Prefabs,用来存放预制体,并把Player拖入其中

(3)同理:把Map图集的每个图片按照上方的操作也是创建预制体并方法Prefabs文件夹中
(这里补充一下:两个河流图片是后面用于做动画的,这里不需要拖入)

(4)创建两个文件夹Animation和AnimationController

(5)按住Shift,选中Born图集的全部图集,一起拖放到Hierarchy窗口下


(6)调整动画大小,然后点击播放按钮显示


(7)修改名字,然后分别把产生的动画以及动画控制器放入相应的文件夹

(8)把Born也拖入Prefabs文件夹中,然后在Prefabs中创建子文件夹对其进行分类,如图:

(9)同理,创建Explode,Shield,River等效果,步骤同上
(补充一下:这里需要把河流效果也给加到这哦)

4.实现玩家移动
(1)创建一个文件夹Scripts用来存放脚本,在该文件下创建一个脚本,然后把脚本挂载到Player游戏物体上

(2)双击打开脚本,编辑脚本内容如下:(已经写好了代码的详细注释)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 定义一个名为 Player 的公共类,继承自 MonoBehaviour 类,这是 Unity 中所有脚本的基类
public class Player : MonoBehaviour
{
// 定义一个公共的浮点型变量 speed,用于控制玩家的移动速度,初始值设为 3
public float speed = 3;
// Start 方法在脚本实例被启用时调用,通常用于初始化操作
// 该方法在脚本生命周期中只会被调用一次,且在第一个 Update 方法调用之前执行
void Start()
{
// 这里目前没有具体的初始化操作,可以根据需求添加相关代码
}
// Update 方法在每一帧都会被调用,用于处理游戏对象的逻辑更新
void Update()
{
// 获取水平方向的输入值,使用 Input.GetAxisRaw 方法,它返回 -1、0 或 1
// "Horizontal" 是 Unity 中预定义的输入轴名称,通常对应键盘上的左右箭头键或 A、D 键
float x = Input.GetAxisRaw("Horizontal");
// 使用 transform.Translate 方法移动游戏对象
// Vector3.right 表示世界坐标系中的右方向,即 X 轴正方向
// x * speed * Time.deltaTime 计算出在当前帧中应该移动的距离
// Space.World 表示使用世界坐标系进行移动
transform.Translate(Vector3.right * x * speed * Time.deltaTime, Space.World);
// 获取垂直方向的输入值,使用 Input.GetAxisRaw 方法,它返回 -1、0 或 1
// "Vertical" 是 Unity 中预定义的输入轴名称,通常对应键盘上的上下箭头键或 W、S 键
float y = Input.GetAxisRaw("Vertical");
// 使用 transform.Translate 方法移动游戏对象
// Vector3.up 表示世界坐标系中的上方向,即 Y 轴正方向
// y * speed * Time.deltaTime 计算出在当前帧中应该移动的距离
// Space.World 表示使用世界坐标系进行移动
transform.Translate(Vector3.up * y * speed * Time.deltaTime, Space.World);
}
}
(3)保存代码,启动unity项目,使用wasd键即可移动(方向键也可以)
5.控制坦克移动朝向进行切换
(1)打开Player脚本进行编辑
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 3;
// 定义一个 SpriteRenderer 类型的私有变量 picture,用于获取和控制该游戏对象上的 SpriteRenderer 组件
private SpriteRenderer picture;
// 定义一个公共的 Sprite 数组 TankDirection,用于存储坦克不同方向的精灵图片,按照上、右、下、左的顺序排列
public Sprite[] TankDirection;//上右下左
private void Awake()
{
// 在 Awake 方法中获取该游戏对象上的 SpriteRenderer 组件,并赋值给 picture 变量
picture = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
float x = Input.GetAxisRaw("Horizontal");
transform.Translate(Vector3.right * x*speed*Time.deltaTime,Space.World);
// 判断水平输入值 x 是否小于 0,若小于 0 表示玩家按下了向左的按键
if(x<0)
{
// 将 SpriteRenderer 组件的 sprite 属性设置为 TankDirection 数组中索引为 3 的精灵图片,即坦克向左的图片
picture.sprite = TankDirection[3];//左边
}
// 判断水平输入值 x 是否大于 0,若大于 0 表示玩家按下了向右的按键
else if (x > 0)
{
// 将 SpriteRenderer 组件的 sprite 属性设置为 TankDirection 数组中索引为 1 的精灵图片,即坦克向右的图片
picture.sprite = TankDirection[1];//右边
}
float y = Input.GetAxisRaw("Vertical");
transform.Translate(Vector3.up * y * speed * Time.deltaTime, Space.World);
// 判断垂直输入值 y 是否小于 0,若小于 0 表示玩家按下了向下的按键
if (y < 0)
{
// 将 SpriteRenderer 组件的 sprite 属性设置为 TankDirection 数组中索引为 2 的精灵图片,即坦克向下的图片
picture.sprite = TankDirection[2];//下边
}
// 判断垂直输入值 y 是否大于 0,若大于 0 表示玩家按下了向上的按键
else if (y > 0)
{
// 将 SpriteRenderer 组件的 sprite 属性设置为 TankDirection 数组中索引为 0 的精灵图片,即坦克向上的图片
picture.sprite = TankDirection[0];//上边
}
}
}
(2)选中脚本挂载的对象Player,把图片朝向的图片分别拖入到如图位置

6.碰撞器的添加(解决抖动问题)
(1)给prefabs文件中预制体Player添加Box Collider 2D碰撞器组件和Rigidbody组件,同时调节组件参数如下:



(2)给Map地图上作为障碍过不去的预制体添加Box Collider 2D碰撞器组件即可,如下:

(3)修复Player碰撞偏移Bug

(4)解决坦克碰撞鬼畜现象(修改代码)
总结:把Update的代码全部放入Fixedupdate里面,同时time属性也改一下,如下图

7.移动优先级的添加(2D渲染层级的问题)
(1)修改移动的优先级(避免同时按下两个键就会导致斜着移动)增添圈出来的代码即可

(2)修改代码 整理代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 3;
private SpriteRenderer picture;
public Sprite[] TankDirection;//上右下左
private void Awake()
{
picture = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void FixedUpdate()
{
Move();
}
public void Move()
{
float x = Input.GetAxisRaw("Horizontal");
transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
if (x < 0)
{
picture.sprite = TankDirection[3];//左边
}
else if (x > 0)
{
picture.sprite = TankDirection[1];//右边
}
if (x != 0)
{
return;//保证同时按着水平和垂直方向上 水平优先
}
float y = Input.GetAxisRaw("Vertical");
transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
if (y < 0)
{
picture.sprite = TankDirection[2];//下边
}
else if (y > 0)
{
picture.sprite = TankDirection[0];//上边
}
}
}
(3)设置优先级:特效>tank
数值越大,越后渲染,覆盖最上面
Born 和 Shield 和 Bullet 等渲染数值为1
例如;我们需要Tank可以从 Grass下面窜过去
坦克大战穿越草坪


(4)制作子弹,拖入bullet的一张照片,然后大小为 3 3 1,命名为Bullet,制作预制体

8.坦克的攻击方法
(1)打开Player脚本,写一个攻击方法 Attack()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 3;
private SpriteRenderer picture;
public Sprite[] TankDirection;//上右下左
public GameObject bulletPre;
private void Awake()
{
picture = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Attack();
}
private void FixedUpdate()
{
Move();
}
private void Attack()
{
if ((Input.GetKeyDown(KeyCode.Space)))
{
Instantiate(bulletPre, transform.position, transform.rotation);
}
}
public void Move()
{
float x = Input.GetAxisRaw("Horizontal");
transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
if (x < 0)
{
picture.sprite = TankDirection[3];//左边
}
else if (x > 0)
{
picture.sprite = TankDirection[1];//右边
}
if (x != 0)
{
return;//保证同时按着水平和垂直方向上 水平优先
}
float y = Input.GetAxisRaw("Vertical");
transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
if (y < 0)
{
picture.sprite = TankDirection[2];//下边
}
else if (y > 0)
{
picture.sprite = TankDirection[0];//上边
}
}
}
(2)把子弹预制体拖入到脚本上

(3)测试
按空格键可以生成子弹
9.子弹朝向
(1)实例化子弹函数修改生成角度
(2)代码如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 3;
private Vector3 bulletEulerAngles;
private SpriteRenderer picture;
public Sprite[] TankDirection;//上右下左
public GameObject bulletPre;
private void Awake()
{
picture = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Attack();
}
private void FixedUpdate()
{
Move();
}
private void Attack()
{
if ((Input.GetKeyDown(KeyCode.Space)))
{
//子弹产生的角度:当前坦克角度+子弹应该旋转的角度
Instantiate(bulletPre, transform.position, Quaternion.Euler(transform.eulerAngles+bulletEulerAngles));
}
}
public void Move()
{
float x = Input.GetAxisRaw("Horizontal");
transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
if (x < 0)
{
picture.sprite = TankDirection[3];//左边
bulletEulerAngles = new Vector3(0, 0, 90);
}
else if (x > 0)
{
picture.sprite = TankDirection[1];//右边
bulletEulerAngles = new Vector3(0, 0, -90);
}
if (x != 0)
{
return;//保证同时按着水平和垂直方向上 水平优先
}
float y = Input.GetAxisRaw("Vertical");
transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
if (y < 0)
{
picture.sprite = TankDirection[2];//下边
bulletEulerAngles = new Vector3(0, 0, -180);
}
else if (y > 0)
{
picture.sprite = TankDirection[0];//上边
bulletEulerAngles = new Vector3(0, 0, 0);
}
}
}
10.让子弹动起来,攻击CD
(1)创建子弹脚本Bullet,并挂载到子弹预制体上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public float movespeed = 5;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.Translate(transform.up * movespeed * Time.deltaTime, Space.World);
}
}
(2)增加攻击间隔
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
private float timeval;
public float speed = 3;
private Vector3 bulletEulerAngles;
private SpriteRenderer picture;
public Sprite[] TankDirection;//上右下左
public GameObject bulletPre;
private void Awake()
{
picture = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(timeval>=0.4f)
{
Attack();
}
else
{
timeval += Time.deltaTime;
}
}
private void FixedUpdate()
{
Move();
}
private void Attack()
{
if ((Input.GetKeyDown(KeyCode.Space)))
{
//子弹产生的角度:当前坦克角度+子弹应该旋转的角度
Instantiate(bulletPre, transform.position, Quaternion.Euler(transform.eulerAngles+bulletEulerAngles));
timeval = 0;
}
}
public void Move()
{
float x = Input.GetAxisRaw("Horizontal");
transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
if (x < 0)
{
picture.sprite = TankDirection[3];//左边
bulletEulerAngles = new Vector3(0, 0, 90);
}
else if (x > 0)
{
picture.sprite = TankDirection[1];//右边
bulletEulerAngles = new Vector3(0, 0, -90);
}
if (x != 0)
{
return;//保证同时按着水平和垂直方向上 水平优先
}
float y = Input.GetAxisRaw("Vertical");
transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
if (y < 0)
{
picture.sprite = TankDirection[2];//下边
bulletEulerAngles = new Vector3(0, 0, -180);
}
else if (y > 0)
{
picture.sprite = TankDirection[0];//上边
bulletEulerAngles = new Vector3(0, 0, 0);
}
}
}
(3)给子弹Bullet添加Box Collider2D和rigidbody ,然后选择 Is trigger,取消重力


11.空气墙的制作--添加标签区分
(1)新建标签tag


(2)制作空气墙AirWall

12.玩家无敌方法与死亡方法的编写
(1)在player脚本中创建玩家死亡方法
private void Die()
{
if(isDefend)
{
return;
}
//产生爆炸特效
Instantiate(explosionpre,transform.position,transform.rotation);
//死亡
Destroy(gameObject);
}
(2)创建Explosion脚本,然后挂载到爆炸效果上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Explosion : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Destroy(gameObject,0.167f);
}
// Update is called once per frame
void Update()
{
}
}
(3)打开bullet脚本
private void OnTriggerEnter2D(Collider2D collision)
{
switch(collision.tag)
{
case "Tank":
collision.SendMessage("Die");
break;
case "Wall":
break;
case "Enemy":
break;
case "Iron":
break;
case "Heart":
break;
}
}
(4)挂载脚本需要的物体

(5)把保护盾挂载到player下面作为子物体(根据需要启动或者关闭保护,减少性能)

(6)开局限时保护
if (isDefend)// 保护状态
{
defeneffectpre.SetActive(true);
defendtimeval-=Time.deltaTime;
if (defendtimeval <=0 )
{
defeneffectpre.SetActive(false);
isDefend = false;
}
13.区别玩家的子弹与敌人的子弹
(1)打开bullet脚本完善代码
private void OnTriggerEnter2D(Collider2D collision)
{
switch(collision.tag)
{
case "Tank":
if(!isPlayerBullet)
{
collision.SendMessage("Die");
}
break;
case "Wall":
Destroy(collision.gameObject);
Destroy(gameObject);
break;
case "Enemy":
collision.SendMessage("Die");
Destroy(gameObject);
break;
case "Iron":
Destroy(gameObject);
break;
case "Heart":
collision.SendMessage("Die");
break;
}
}
(2)创建一个Heart脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Heart : MonoBehaviour
{
private SpriteRenderer sr;
public Sprite pic;
// Start is called before the first frame update
void Start()
{
sr = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
}
public void Die()
{
sr.sprite = pic;
}
}
14.制作敌人的子弹
(1)复制子弹预制体,修改名字,并且取消 是玩家子弹这一项
15.制作敌人,添加玩家产生效果
(1)复制Player预制体,修改为Enemy,删除Player脚本,修改渲染图片

(2)添加一个Enemy脚本,先复制Player脚本的内容,暂且不做处理
(3)增加一个Heart死亡后,爆炸效果,并且挂载到Heart上面
public void Die()
{
sr.sprite = pic;
Instantiate(Explodeeffctpre, transform.position, transform.rotation);
}
(4)增加一个Born出生特效
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Born : MonoBehaviour
{
public GameObject playerpre;
// Start is called before the first frame update
void Start()
{
Invoke("BornTank", 0.8f);
Destroy(gameObject,0.8f);
}
// Update is called once per frame
void Update()
{
}
private void BornTank()
{
Instantiate(playerpre,transform.position,Quaternion.identity);
}
}
16.使用15的特效产生敌人
(1)创建两个敌人(图片不同的两种敌人),挂载Enemy脚本,然后给脚本拖拽需要的资源图片

(2)添加资源和修改参数

(3)打开Born脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Born : MonoBehaviour
{
public GameObject playerpre;
public GameObject[] Enemy;
public bool CreatPlayer;
// Start is called before the first frame update
void Start()
{
Invoke("BornTank", 0.8f);
Destroy(gameObject,0.8f);
}
// Update is called once per frame
void Update()
{
}
private void BornTank()
{
if(CreatPlayer)
{
Instantiate(playerpre, transform.position, Quaternion.identity);
}
else
{
int num=Random.Range(0, 2);
Instantiate(Enemy[num], transform.position, Quaternion.identity);
}
}
}
(4)添加脚本需要的资源,然后创建多个Bron物体,运行结果


17.编写敌人ai
(1)删除Enemy脚本的所有保护效果代码
(2)添加自动发射子弹代码
void Update()
{
if (timeval >= 3f)
{
Attack();
}
else
{
timeval += Time.deltaTime;
}
}
private void Attack()
{
Instantiate(bulletPre, transform.position, Quaternion.Euler(transform.eulerAngles + bulletEulerAngles));
timeval = 0;
}
(3)添加自动移动代码
public void Move()
{
if(changedirctiontime>=4)
{
int num=Random.Range(0, 8);
if (num > 5)
{
y = -1; x = 0;
}
else if (num == 0)
{
y = 1; x = 0;
}
else if (num > 0 && num <= 2)
{
y = 0; x = -1;
}
else if(num>2&&num<=4)
{
y = 0; x = 1;
}
changedirctiontime = 0;
}
else
{
changedirctiontime += Time.fixedDeltaTime;
}
transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
if (x < 0)
{
picture.sprite = TankDirection[3];//左边
bulletEulerAngles = new Vector3(0, 0, 90);
}
else if (x > 0)
{
picture.sprite = TankDirection[1];//右边
bulletEulerAngles = new Vector3(0, 0, -90);
}
transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
if (y < 0)
{
picture.sprite = TankDirection[2];//下边
bulletEulerAngles = new Vector3(0, 0, -180);
}
else if (y > 0)
{
picture.sprite = TankDirection[0];//上边
bulletEulerAngles = new Vector3(0, 0, 0);
}
}
18.初始化地图工具的创建
(1)创建一个空物体MapCreate,创建一个Map脚本,挂载到MapCreate上,创建基地和周围的围墙
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Map : MonoBehaviour
{
public GameObject[] item;//初始化地图物体数组
private void Awake()
{
//基地以及周围围墙
CreateItem(item[0],new Vector3(0,-8,0),Quaternion.identity);//实例老家
CreateItem(item[1], new Vector3(-1, -8, 0), Quaternion.identity);//实例wall
CreateItem(item[1], new Vector3(1, -8, 0), Quaternion.identity);//实例wall
CreateItem(item[1], new Vector3(1, -7, 0), Quaternion.identity);//实例wall
CreateItem(item[1], new Vector3(-1, -7, 0), Quaternion.identity);//实例wall
CreateItem(item[1], new Vector3(0, -7, 0), Quaternion.identity);//实例wall
}
private void CreateItem(GameObject creatgameobject,Vector3 creatposition,Quaternion ro)
{
GameObject itemGo=Instantiate(creatgameobject,creatposition,ro);
itemGo.transform.SetParent(gameObject.transform);
}
}
(2)实例化地图
//实例化地图
for (int i = 0; i < 60; i++)
{
CreateItem(item[1], CreatRandomPosition(), Quaternion.identity);
}
for (int i = 0; i < 20; i++)
{
CreateItem(item[2], CreatRandomPosition(), Quaternion.identity);
}
for (int i = 0; i < 20; i++)
{
CreateItem(item[4], CreatRandomPosition(), Quaternion.identity);
}
for (int i = 0; i < 20; i++)
{
CreateItem(item[5], CreatRandomPosition(), Quaternion.identity);
}
19.编写产生随机位置的方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Map : MonoBehaviour
{
public GameObject[] item;//初始化地图物体数组
private List<Vector3> itempositonlist = new List<Vector3>();//已经有东西的位置列表
private void Awake()
{
//基地以及周围围墙
CreateItem(item[0], new Vector3(0, -8, 0), Quaternion.identity);//实例老家
CreateItem(item[1], new Vector3(-1, -8, 0), Quaternion.identity);//实例wall
CreateItem(item[1], new Vector3(1, -8, 0), Quaternion.identity);//实例wall
CreateItem(item[1], new Vector3(1, -7, 0), Quaternion.identity);//实例wall
CreateItem(item[1], new Vector3(-1, -7, 0), Quaternion.identity);//实例wall
CreateItem(item[1], new Vector3(0, -7, 0), Quaternion.identity);//实例wall
//实例化外围墙
for (int i = -11; i < 12; i++)
{
CreateItem(item[6], new Vector3(i, -9, 0), Quaternion.identity);
}
for (int i = -11; i < 12; i++)
{
CreateItem(item[6], new Vector3(i, 9, 0), Quaternion.identity);
}
for (int i = -8; i < 9; i++)
{
CreateItem(item[6], new Vector3(-11, i, 0), Quaternion.identity);
}
for (int i = -8; i < 9; i++)
{
CreateItem(item[6], new Vector3(11, i, 0), Quaternion.identity);
}
//实例化地图
for (int i = 0; i < 60; i++)
{
CreateItem(item[1], CreatRandomPosition(), Quaternion.identity);
}
for (int i = 0; i < 20; i++)
{
CreateItem(item[2], CreatRandomPosition(), Quaternion.identity);
}
for (int i = 0; i < 20; i++)
{
CreateItem(item[4], CreatRandomPosition(), Quaternion.identity);
}
for (int i = 0; i < 20; i++)
{
CreateItem(item[5], CreatRandomPosition(), Quaternion.identity);
}
// 初始化玩家
GameObject go = Instantiate(item[3], new Vector3(-2, -8, 0), Quaternion.identity);
go.GetComponent<Born>().CreatPlayer = true;
//初始化敌人;
CreateItem(item[3], new Vector3(-10, 8, 0), Quaternion.identity);
CreateItem(item[3], new Vector3(0, 8, 0), Quaternion.identity);
CreateItem(item[3], new Vector3(10, 8, 0), Quaternion.identity);
InvokeRepeating("CreateEnemy",4,5);
}
private void CreateItem(GameObject creatgameobject, Vector3 creatposition, Quaternion ro)
{
GameObject itemGo = Instantiate(creatgameobject, creatposition, ro);
itemGo.transform.SetParent(gameObject.transform);
itempositonlist.Add(creatposition);
}
private Vector3 CreatRandomPosition()
{
while (true)
{
Vector3 createposiyion = new Vector3(Random.Range(-9, 10), Random.Range(-7, 8), 0);
if (!HasthePosition(createposiyion))
{
return createposiyion;
}
}
}
private bool HasthePosition(Vector3 createpos)
{
for (int i = 0; i < itempositonlist.Count; i++)
{
if (createpos == itempositonlist[i])
{
return true;
}
}
return false;
}
private void CreateEnemy()
{
int num = Random.Range(0, 3);
Vector3 EnemyPos=new Vector3();
if(num==0)
{
EnemyPos = new Vector3(-10,8,0);
}
else if(num==1)
{
EnemyPos=new Vector3(0,8,0);
}
else
{
EnemyPos=new Vector3(10,8,0);
}
CreateItem(item[3], EnemyPos, Quaternion.identity);
}
}
20.添加音效
(1)添加开头音乐(创建一个空物体,挂载该组件,然后挂载音乐)


(2)添加发射子弹音效


五·结语
在这个过程中,我们不仅掌握了 Unity 引擎诸多核心功能的运用,更对游戏开发流程有了系统性的认知。希望这份指南能为你的游戏开发之旅点亮一盏灯,激励你在此基础上继续拓展创新,为玩家带来更精彩、更具沉浸感的坦克大战体验。

2697





