当前位置:   article > 正文

Unity3D | 经典小游戏Battle City_unity简单小游戏案例

unity简单小游戏案例

Cheer Up

游戏说明:

除了音效,游戏地图上的元素有:

草丛(玩家可以躲进去,敌人攻击不到) 
河流(双方都过不去,但是子弹可以穿过) 
铁墙(坦克和子弹都过不去) 
砖墙(一发子弹摧毁后坦克可以过去)
空气墙(围在地图周围,防止出界) 
敌方大坦克(打两下才死)
敌方小坦克(打一下就死)
有玩家1和玩家2,可以选择单人/双人模式

地图是随机生成的,每次打开都不一样,丰富了游戏的多样性和可玩性。敌方坦克(包括大小坦克)一次最多存在3个,被摧毁后间隔3s在地图的左上/右上/上面中间 随机生成相应数量的。

❥输赢的判断:

敌方坦克数目一定,每消灭一个获得1分,消灭满20个,也就就是得分20分获得胜利,玩家自己有3生命值(即3条生命),被击中一次损失1条生命并且在出生点复活,获得3s的无敌效果。生命耗尽或者老家被摧毁,就game over。

可以理解为有10个类:


☀️项目效果展示

  • 这里的gif是用了一款ScreenToGif的软件录制的,处理的比较短
  • 右侧图标分别表示消灭的敌人数量(上) 和 当前的生命值(下),被击中一次生命值-1,变为0的时候玩家死亡,游戏结束

目录

☀️项目效果展示

☀️前言

⭐️相关背景

⭐️相关知识

⭐️版本说明

⭐️许可证激活

⭐️及时保存

☀️项目概况

⭐️项目整体布局

⭐️猜想验证

⭐️分步介绍(结合代码)

❀Audio.cs

❀Born.cs

❀Bullet.cs

❀Enemy.cs

❀Explosion.cs

❀Heart.cs

❀MapCreation.cs

❀Option.cs

❀Player.cs

❀PlayerManager.cs


☀️前言

⭐️相关背景

  • 博主在大二暑期接触了unity,借鉴实战视频上手花两天复刻了 “坦克大战” 这一经典小游戏,发现做游戏这种过程真的比玩儿游戏还要爽啊,hh,看着一些功能逐渐被实现,游戏越来越完整,真的是挺令人舒适和爽快。由于那时候零基础直接上手,对于一些基础操作真的是不太熟练,很不灵活,幸好有一些前辈的帮助(比如CSDN小伙伴们熟悉的y哥),解决了一些自己难以解决的问题,帮助小虾在迷茫的夜海上拨开神秘的迷雾。

⭐️相关知识

  • 在学习的过程中,几个比较重要的知识点就是:预制体、克隆体、精灵渲染器、渲染层级、脚本、碰撞检测、触发检测、AI设计、UI设计、固定物理帧...

⭐️版本说明

  • 小虾采用的是Unity一个较新的版本-2021.1.16,这里建议小伙伴们下载和教程相同版本的,不然真的会有可能遇到卡点消耗心情哈。
  • 可以和我一样在Unity Hub里面安装,Hub感觉挺好用的。

⭐️许可证激活

  • 这个...有段时间不登好像就要重新激活许可证,遇到同样问题的小伙伴可以进入下图的网址:Unity - Activation 按提示进行操作即可

⭐️及时保存

  • 打开的时候总是激动又煎熬,U3D启动有点慢并且操作过程中很可能就会报废,所以项目过程中一定要及时保存,不然一不小心可能就要重新来过啦!


☀️项目概况

⭐️项目整体布局

  • 整个项目工程的一个概况就在下面了,Hierarchy菜单下是放 预制体(Prefab) 的 克隆体(Clone) 的,这些克隆体可以拖到左上方的大界面中,通过点击播放按钮进行效果检验。左下方的就是整体的动画效果显示了。
  • 做一个小游戏项目,我们首先需要素材,这里推荐初学的小伙伴去siki学院学习,里面会有相应的完整初级案例教程。

⭐️猜想验证

  • 学习的过程就是不断验证猜想并接收新知识的过程,我本来想坦克大战里面的地图就是把预先处理好的 “砖墙、水泥墙、草坪、河流、空气墙(防止Object越过边界的)” 按照一定规律和自己的喜好摆放开来作为整张地图的,但事实打脸了(觉得自己真傻)。
  •  地图实例化(MapCreation) 中,我们使用了 “产生随机位置” 的方法,在避开已有位置的基础上,自动生成其它的地图元素。

代码如下:

  1. // 产生随机位置的方法
  2. private Vector3 CreateRandomPosition() // 列表中没有这个位置,才能产生
  3. {
  4. while(true)
  5. {
  6. // 不生成x=-13,13(变为10)这两列,y=-8,8这两行的位置
  7. Vector3 createPosition = new Vector3( Random.Range(-9,10), Random.Range(-7,8), 0); // x y z,Random.Range(a,b)中b的实际值是b-1
  8. if(!HasThePosition(createPosition))
  9. {
  10. return createPosition; // 列表中没有的位置就返回
  11. }
  12. // false 则继续循环
  13. }
  14. }

⭐️分步介绍(结合代码)

Audio.cs

  • 目的:调用音效
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Audio : MonoBehaviour
  5. {
  6. public AudioClip hitAudio;
  7. public void PlayAudio()
  8. {
  9. AudioSource.PlayClipAtPoint(hitAudio, transform.position);
  10. }
  11. }

Born.cs

  • 目的:让特效播放一段时间后自我销毁,产生玩家
  • 思路:首先引用玩家playerPrefab,新建一个敌人的数组作为敌人预制体的列表,并设置bool变量判断子弹是谁产生的。游戏一开始时,调用延时Born方法,经过延时销毁出生特效。判断是否是玩家,是则产生玩家,不是就定义一个随机数,随机生成2种敌人中的1种。
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Born : MonoBehaviour
  5. {
  6. public GameObject playerPrefab; // 先拿一下玩家的引用
  7. public GameObject[] enemyPrefabList; // 新建一个敌人的数组:敌人预制体的列表
  8. public bool createPlayer; // 为了判断子弹是谁产生的(初始默认为敌人的)
  9. // Start is called before the first frame update
  10. void Start()
  11. {
  12. Invoke("BornTank", 1.0f); // 让游戏一开始调用一下延时Born方法
  13. Destroy(gameObject, 1.0f); // 经过0.8f延时0.8f销毁出生特效
  14. }
  15. private void BornTank()
  16. {
  17. if(createPlayer) // ture则产生玩家
  18. {
  19. Instantiate(playerPrefab, transform.position, Quaternion.identity);
  20. }
  21. else // 敌人有两种,定义一个随机数
  22. {
  23. int num = Random.Range(0,2); // 0 1(整型情况包含前两个)
  24. Instantiate(enemyPrefabList[num], transform.position, Quaternion.identity);
  25. }
  26. }
  27. }

Bullet.cs

  • 目的:模拟子弹和相关物体的碰撞效果
  • 思路:首先给子弹一个速度,这个速度要适中设置,建议和玩家移动速度相同。设置一个bool变量 isPlayerBullet(默认false,不是敌人的子弹),再分6类完善子弹碰撞检测的方法
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class bullet : MonoBehaviour
  5. {
  6. public float moveSpeed = 10;
  7. public bool isPlayerBullet; // 默认false,不是敌人的子弹
  8. void Update()
  9. {
  10. transform.Translate(transform.up * moveSpeed * Time.deltaTime, Space.World); //让子弹沿着自身所在Y轴移动
  11. }
  12. //子弹碰撞检测的方法
  13. private void OnTriggerEnter2D(Collider2D collision)
  14. {
  15. switch (collision.tag)
  16. {
  17. case "Tank":
  18. if (!isPlayerBullet) // 玩家碰到敌人的子弹
  19. {
  20. collision.SendMessage("Die"); // 玩家死亡一次
  21. Destroy(gameObject); // 玩家死亡,自身子弹销毁
  22. }
  23. break;
  24. case "Heart":
  25. collision.SendMessage("Die"); // 老家死亡
  26. Destroy(gameObject); // 子弹碰老家,子弹销毁
  27. break;
  28. case "Enemy":
  29. if(isPlayerBullet) // 敌人碰到玩家的子弹
  30. {
  31. collision.SendMessage("Die"); // 敌人死亡一次
  32. Destroy(gameObject); // 敌人死亡,自身子弹销毁
  33. }
  34. break;
  35. case "Wall":
  36. Destroy(collision.gameObject); // 子弹碰到,墙就销毁
  37. Destroy(gameObject); // 子弹碰墙,子弹也销毁
  38. break;
  39. case "Barrier":
  40. if(isPlayerBullet) // 玩家的子弹才有音效
  41. {
  42. collision.SendMessage("PlayAudio");
  43. }
  44. Destroy(gameObject); // 子弹碰到不可销毁的障碍(铁墙和空气墙),子弹自身销毁
  45. break;
  46. default:
  47. break;
  48. }
  49. }
  50. }

Enemy.cs

  • 目的:实现敌方坦克的攻击、移动、死亡操作并优化 敌人(Enemy) 的AI
  • 思路:这里要用到精灵渲染器,我们要考虑子弹的旋转角度、敌方坦克上下左右的移动操作,攻击的CD、改变方向的时间计时器以及攻击、移动和死亡的具体方法的实现。最后,为了使玩家获得更加流畅舒适的体验,我们要优化 敌人(Enemy) 的AI。
  • Tips:我们要用到 void FixedUpdate() 这个生命周期函数,它也叫固定物理帧,保证在每一秒都匀称的情况下执行...
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Enemy : MonoBehaviour
  5. {
  6. //属性值
  7. public float moveSpeed = 3;
  8. private Vector3 bulletEulerAngles; // 子弹应该旋转的角度
  9. private float v = -1;
  10. private float h;
  11. //引用
  12. private SpriteRenderer sr;
  13. public Sprite[] tankSprite; // 表示(上 右 下 左)方向;因为要改变精灵的值,所以设一个精灵数组(也就是拿一个和精灵值同类型的属性)
  14. public GameObject bulletPrefab;
  15. public GameObject explosionPrefab;
  16. //计时器
  17. private float timeVal; // 攻击CD(计时器)
  18. private float timeValChangeDirection; // 改变方向的时间计时器(一开始若设为4是为了使其已出现就移动)
  19. private void Awake() // 一般的引用在awake中赋值比较好,因为所有属性的确定和引用都应该在游戏物体一开始的时候就拿到
  20. {
  21. sr = GetComponent<SpriteRenderer>(); // 拿到了精灵渲染的组件
  22. // 接下来去控制它的图片属性就可以了
  23. }
  24. void Update()
  25. {
  26. // 攻击的时间间隔
  27. if (timeVal >= 3f)
  28. {
  29. }
  30. else
  31. {
  32. }
  33. }
  34. void FixedUpdate() // 生命周期函数,也叫固定物理帧,是在每一秒都匀称的情况下执行的
  35. {
  36. Move();
  37. }
  38. //坦克的攻击方法
  39. public void Attack()
  40. {
  41. //子弹产生的角度:当前坦克的角度+子弹应该旋转的角度(rotation中的欧拉角要转为四元数的形式表示角度)
  42. //子弹预制体 坦克位置 旋转角
  43. timeVal = 0;
  44. }
  45. // 坦克的移动方法
  46. private void Move() // 把移动代码封装成 Move()函数
  47. {
  48. // 下面的敌人AI只是为了控制h和v的值,下面和player一样根据值选择图片
  49. if(timeValChangeDirection >= 4) // 攻击一次后改变方向
  50. {
  51. int num = Random.Range(0, 8); // 多定义4个,为了让朝下的概率变大
  52. if(num > 5) // 向下(确保概率较大)
  53. {
  54. }
  55. else if(num == 0) // 向上(确保概率较小)
  56. {
  57. }
  58. else if (num > 0 && num <=2) // 向左
  59. {
  60. }
  61. else if (num > 2 && num <= 4) // 向右
  62. {
  63. }
  64. timeValChangeDirection = 0; // 归零防止鬼畜运动
  65. }
  66. else
  67. {
  68. // 因为move()的方法是放在void FixedUpdate()里面的
  69. }
  70. //控制玩家的移动,Vector3.right 表示X轴方向的移动,Y轴是up,Z轴是forward
  71. if (v < 0)
  72. {
  73. sr.sprite = tankSprite[2];
  74. bulletEulerAngles = new Vector3(0, 0, -180);
  75. }
  76. else if (v > 0)
  77. {
  78. sr.sprite = tankSprite[0];
  79. bulletEulerAngles = new Vector3(0, 0, 0);
  80. }
  81. if (v != 0) // 确保按照上下移动的同时不能进行左右移动
  82. {
  83. return;
  84. }
  85. transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World); //Time.deltaTime:按每秒移动;逗号右边的第二个方向是以世界坐标轴的方向
  86. if (h < 0)
  87. {
  88. }
  89. else if (h > 0)
  90. {
  91. }
  92. }
  93. // 坦克的死亡方法
  94. private void Die()
  95. {
  96. // 敌人每次死亡,就让得分+1
  97. PlayerManager.Instance.playerScore++;
  98. // 产生爆炸特效
  99. Instantiate(explosionPrefab, transform.position, transform.rotation);
  100. //死亡
  101. Destroy(gameObject);
  102. }
  103. //优化Enemy的AI:若敌人相碰,顺时针旋转一个角度,这样能避免扎堆
  104. private void OnCollisionEnter2D(Collision2D collision) // 坦克是碰撞检测
  105. {
  106. if(collision.gameObject.tag=="Enemy")
  107. {
  108. // 时间瞬间达到4,即刻旋转
  109. }
  110. }
  111. }

Explosion.cs

  • 目的:实现爆炸效果
  • 思路:这里的爆炸效果是刚出生的爆炸特效,持续短暂时间后消失就好,也就是只作用一小段时间
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Explosion : MonoBehaviour
  5. {
  6. void Start()
  7. {
  8. Destroy(gameObject, 0.167f);
  9. }
  10. }

Heart.cs

  • 目的:模拟 老家(Heart)
  • 思路:开始的时候就获取一下精灵渲染器组件,一旦触发老家死亡效果,就更换为老家被破坏时的图片,并附加爆炸特效。被击中的时候要调用一下 dieAudio音效。
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Heart : MonoBehaviour
  5. {
  6. private SpriteRenderer sr;
  7. public Sprite BrokenSprite; // 为了获取老家(Heart)被破坏时的图片
  8. public GameObject explosionPrefab; // 拿一下爆炸效果的引用
  9. public AudioClip dieAudio;
  10. void Start()
  11. {
  12. sr = GetComponent<SpriteRenderer>(); // 开始的时候就获取一下精灵渲染器组件
  13. }
  14. public void Die()
  15. {
  16. // 更换图片:一旦触发老家死亡效果,就更换为老家被破坏时的图片
  17. // 产生爆炸特效
  18. PlayerManager.Instance.isDefeat = true; // 被击中
  19. // 在某个点调用一下dieAudio
  20. }
  21. }

MapCreation.cs

  • 目的:地图实例化
  • 思路:我们在已知固定位置的地方手动生成,并将它们放在一个列表中,其他的位置用随机生成位置的方法生成地图,这样每次玩家进入游戏的时候,就是一张崭新的地图了,这就丰富了游戏的多样性和可玩性。我们将敌人位置的生成也放在这部分代码中。
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class MapCreation : MonoBehaviour // 地图实例化
  5. {
  6. // 用来装饰初始化地图所需物体的数组
  7. // 0.老家 1.墙 2.障碍 3.出生效果 4.河流 5.草 6.空气墙
  8. public GameObject[] item;
  9. // 已经有东西的位置列表
  10. private List<Vector3> itemPositionList = new List<Vector3>();
  11. private void Awake()
  12. {
  13. InitMap();
  14. }
  15. private void InitMap()
  16. {
  17. // 实例化老家(CreateItem其实还是用的Instantiate的方法)
  18. CreateItem(item[0], new Vector3(0, -8, 0), Quaternion.identity); // Quaternion.identity:无旋转
  19. //用墙把老家围起来
  20. CreateItem(item[1], new Vector3(-1, -8, 0), Quaternion.identity);
  21. CreateItem(item[1], new Vector3(1, -8, 0), Quaternion.identity);
  22. for (int i = -1; i < 2; i++)
  23. {
  24. CreateItem(item[1], new Vector3(i, -7, 0), Quaternion.identity);
  25. }
  26. // 实例化外围墙 x=-14,14(变为11),y=-9,9
  27. for (int i = -11; i < 12; i++) //最上面一行
  28. {
  29. CreateItem(item[6], new Vector3(i, 9, 0), Quaternion.identity);
  30. }
  31. for (int i = -11; i < 12; i++) //最下面一行
  32. {
  33. CreateItem(item[6], new Vector3(i, -9, 0), Quaternion.identity);
  34. }
  35. for (int i = -8; i < 9; i++) //最左面一行
  36. {
  37. CreateItem(item[6], new Vector3(-11, i, 0), Quaternion.identity);
  38. }
  39. for (int i = -8; i < 9; i++) //最右面一行
  40. {
  41. CreateItem(item[6], new Vector3(11, i, 0), Quaternion.identity);
  42. }
  43. // 初始化玩家(通过实例化出生特效实例化玩家)
  44. GameObject go = Instantiate(item[3], new Vector3(-2, -8, 0), Quaternion.identity); // 产生出生特效
  45. go.GetComponent<Born>().createPlayer = true;// 特效上的bool值设置为true:产生玩家
  46. // 产生敌人(在固定的三个位置先产生,之后每隔一段时间在三个位置随机产生)
  47. CreateItem(item[3], new Vector3(-10, 8, 0), Quaternion.identity);
  48. CreateItem(item[3], new Vector3(0, 8, 0), Quaternion.identity);
  49. CreateItem(item[3], new Vector3(10, 8, 0), Quaternion.identity);
  50. InvokeRepeating("CreateEnemy", 4, 5);
  51. // 实例化地图
  52. for (int i = 0; i < 60; i++)
  53. {
  54. CreateItem(item[1], CreateRandomPosition(), Quaternion.identity);
  55. }
  56. for (int i = 0; i < 20; i++)
  57. {
  58. CreateItem(item[2], CreateRandomPosition(), Quaternion.identity);
  59. }
  60. for (int i = 0; i < 20; i++)
  61. {
  62. CreateItem(item[4], CreateRandomPosition(), Quaternion.identity);
  63. }
  64. for (int i = 0; i < 20; i++)
  65. {
  66. CreateItem(item[5], CreateRandomPosition(), Quaternion.identity);
  67. }
  68. }
  69. private void CreateItem(GameObject createGameObject,Vector3 createPosition,Quaternion createRotation) // 封装一个方法:使播放时Hierarchy菜单下的东西不散落
  70. {
  71. // 根据上面的参数实例化游戏物体
  72. // Parent是当前的游戏物体
  73. // 把所有出现过的物体位置存进位置列表
  74. }
  75. // 产生随机位置的方法
  76. private Vector3 CreateRandomPosition() // 列表中没有这个位置,才能产生
  77. {
  78. while(true)
  79. {
  80. // 不生成x=-13,13(变为10)这两列,y=-8,8这两行的位置
  81. Vector3 createPosition = new Vector3( Random.Range(-9,10), Random.Range(-7,8), 0); // x y z,Random.Range(a,b)中b的实际值是b-1
  82. if(!HasThePosition(createPosition))
  83. {
  84. return createPosition; // 列表中没有的位置就返回
  85. }
  86. // false 则继续循环
  87. }
  88. }
  89. // 用来判断位置列表中是否有这个位置
  90. private bool HasThePosition(Vector3 createPos)
  91. {
  92. for (int i = 0; i < itemPositionList.Count; i++) // 遍历当前的位置列表
  93. {
  94. if(createPos == itemPositionList[i]) // 如果随机产生位置的值等于当前列表某个位置的值
  95. {
  96. return true;
  97. }
  98. }
  99. return false;
  100. }
  101. // 产生敌人的方法
  102. private void CreateEnemy()
  103. {
  104. int num = Random.Range(0, 3); // 0 1 2
  105. Vector3 EnemyPos = new Vector3(); // 装随机产生的位置:向量/坐标
  106. if (num == 0)
  107. {
  108. }
  109. else if (num == 1)
  110. {
  111. }
  112. else
  113. {
  114. }
  115. CreateItem(item[3], EnemyPos, Quaternion.identity);
  116. }
  117. }

Option.cs

  • 目的:选项监听实现
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.SceneManagement;
  5. public class Option : MonoBehaviour
  6. {
  7. private int choice = 1;
  8. public Transform posOne;
  9. public Transform posTwo;
  10. // Update is called once per frame
  11. void Update()
  12. {
  13. // 监听开始界面上下移动的选项
  14. if (Input.GetKeyDown(KeyCode.W))
  15. {
  16. choice = 1;
  17. transform.position = posOne.position;
  18. }
  19. else if (Input.GetKeyDown(KeyCode.S))
  20. {
  21. choice = 2;
  22. transform.position = posTwo.position;
  23. }
  24. if (choice==1 && Input.GetKeyDown(KeyCode.Space))
  25. {
  26. SceneManager.LoadScene(1); // 加载 Main 场景
  27. }
  28. }
  29. }

Player.cs

  • 目的:模拟玩家坦克Player
  • 思路:玩家坦克Player 的实现可以借鉴 敌方坦克Enemy
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Player : MonoBehaviour {
  5. //属性值
  6. public float moveSpeed = 3;
  7. private Vector3 bulletEulerAngles; // 子弹应该旋转的角度
  8. private float timeVal; // 攻击CD(计时器)
  9. private float defendTimeVal = 3; // 无敌CD(计时器)
  10. private bool isDefended = true; // 无敌状态
  11. //引用
  12. // 拿到渲染器
  13. // 拿到需要用到的图片,(上 右 下 左)方向;因为要改变精灵的值,所以设一个精灵数组(也就是拿一个和精灵值同类型的属性)
  14. public GameObject bulletPrefab;
  15. public GameObject explosionPrefab;
  16. public GameObject defendEffectPrefab;
  17. // 控制音效的播放
  18. // 拿到音效播放的资源
  19. private void Awake() // 一般的引用在awake中赋值比较好,因为所有属性的确定和引用都应该在游戏物体一开始的时候就拿到
  20. {
  21. // 拿到了精灵渲染的组件
  22. // 接下来去控制它的图片属性就可以了
  23. }
  24. void Update()
  25. {
  26. // 无敌CD
  27. if(isDefended)
  28. {
  29. defendEffectPrefab.SetActive(true); // 控制无敌状态时护盾打开
  30. defendTimeVal -= Time.deltaTime;
  31. if(defendTimeVal <= 0)
  32. {
  33. isDefended = false;
  34. // 控制无敌状态时护盾关闭
  35. }
  36. }
  37. }
  38. private void FixedUpdate() {// 生命周期函数,也叫固定物理帧,是在每一秒都匀称的情况下执行的
  39. if(PlayerManager.Instance.isDefeat)
  40. {
  41. return; // true则return:游戏失败,禁止玩家所有操作
  42. }
  43. Move();
  44. // 攻击CD
  45. if (timeVal >= 0.4f)
  46. {
  47. Attack();
  48. }
  49. else
  50. {
  51. timeVal += Time.fixedDeltaTime;
  52. }
  53. }
  54. //坦克的攻击方法
  55. public void Attack()
  56. {
  57. if() //若检测到玩家输入为空格键
  58. {
  59. //子弹产生的角度:当前坦克的角度+子弹应该旋转的角度(rotation中的欧拉角要转为四元数的形式表示角度)
  60. //子弹预制体 坦克位置 旋转角
  61. timeVal = 0;
  62. }
  63. }
  64. // 坦克的移动方法
  65. private void Move() // 把移动代码封装成 Move()函数
  66. {
  67. // 监听玩家垂直轴值的输入
  68. transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World);
  69. if (v < 0) {
  70. }
  71. else if (v > 0) {
  72. }
  73. // v和h不为0,播放...音效 (在判断v是否为0之前判断是考虑了它的优先级)
  74. if(Mathf.Abs(v)>0.05f)
  75. {
  76. // 若音效播放时又调用,声音会很杂,所以要判断...
  77. if(!moveAudio.isPlaying)
  78. {
  79. moveAudio.Play();
  80. }
  81. }
  82. if (v != 0) { // 确保按照上下移动的同时不能进行左右移动
  83. return;
  84. }
  85. //监听玩家水平轴值的输入h来控制在X轴上的位移的方向,按右方向为1,左为-1
  86. //控制玩家的移动,Vector3.right 表示X轴方向的移动,Y轴是up,Z轴是forward
  87. transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World); //Time.deltaTime:按每秒移动;逗号右边的第二个方向是以世界坐标轴的方向
  88. if (h < 0) {
  89. }
  90. else if (h > 0) {
  91. }
  92. if (Mathf.Abs(h) > 0.05f)
  93. {
  94. moveAudio.clip = tankAudio[1];
  95. if (!moveAudio.isPlaying)
  96. {
  97. moveAudio.Play();
  98. }
  99. }
  100. else
  101. {
  102. moveAudio.clip = tankAudio[0];
  103. if (!moveAudio.isPlaying)
  104. {
  105. moveAudio.Play();
  106. }
  107. }
  108. }
  109. // 坦克的死亡方法
  110. private void Die()
  111. {
  112. // 是否处于无敌状态
  113. if (isDefended)
  114. {
  115. return;
  116. }
  117. // 击中的话,就让玩家管理里面的属性变为true
  118. PlayerManager.Instance.isDead = true;
  119. // 产生爆炸特效
  120. Instantiate(explosionPrefab, transform.position, transform.rotation);
  121. //死亡
  122. Destroy(gameObject);
  123. }
  124. }

PlayerManager.cs

  • 目的:玩法管理和UI设计
  • 代码如下:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.AI;
  5. using UnityEngine.UI; // 为了更新 UI,拿一下它的引用,使用 UI,就要拿一下Unity的运营空间
  6. using UnityEngine.SceneManagement;
  7. public class PlayerManager : MonoBehaviour
  8. {
  9. //属性值
  10. public int lifeValue = 3;
  11. public int playerScore = 0;
  12. public bool isDead;
  13. public bool isDefeat;
  14. //引用
  15. public GameObject born;
  16. public Text playerScoreText;
  17. public Text playerLifeValueText;
  18. public GameObject isDefeatUI; // 拿一下游戏物体的引用
  19. //单例
  20. private static PlayerManager instance;
  21. public static PlayerManager Instance { get => instance; set => instance = value; }
  22. private void Awake()
  23. {
  24. Instance = this;
  25. }
  26. // Update is called once per frame
  27. void Update() // 在其中实时监听一些状态
  28. {
  29. if(isDefeat) // 失败的话
  30. {
  31. isDefeatUI.SetActive(true); // 让失败的图片显示
  32. return;
  33. }
  34. if(isDead) // 如果死亡
  35. {
  36. Recover();
  37. }
  38. // 实时更新消灭敌人的显示和生命值
  39. playerScoreText.text = playerScore.ToString(); // .ToString():把int转化为字符串
  40. playerLifeValueText.text = lifeValue.ToString();
  41. }
  42. private void Recover()
  43. {
  44. if (lifeValue<=0)
  45. {
  46. // 游戏失败,返回主界面
  47. isDefeat = true;
  48. Invoke("ReturnToTheMainMenu", 3); // 延时3s调用这个方法
  49. }
  50. else
  51. {
  52. lifeValue --;
  53. GameObject go = Instantiate(born, new Vector3(-2, -7.9f, 0), Quaternion.identity); // 接收born的脚本
  54. go.GetComponent<Born>().createPlayer = true;
  55. isDead = false;
  56. }
  57. }
  58. private void ReturnToTheMainMenu()
  59. {
  60. SceneManager.LoadScene(0); // 返回开始界面
  61. }
  62. }

完结,撒花❀~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/104033?site
推荐阅读
相关标签
  

闽ICP备14008679号