当前位置:   article > 正文

使用团结引擎开发Unity 3D射击游戏_unity 团结引擎

unity 团结引擎

一、前言

       本案例是初级案例,意在引导想使用unity的初级开发者能较快的入门,体验unity开发的方便性和简易性能。

      本次我们将使用团结引擎进行开发,帮助想体验团结引擎的入门开发者进行较快的环境熟悉。

     本游戏是一个俯视角度的射击游戏。主角始终位于屏幕中心位置,玩家使用键盘控制主角移动,并可开枪射击场景中的敌人。玩家具有多种武器,如手枪、霰弹枪和自动步枪,玩家可以切换武器。敌人也会向玩家方向移动并射击玩家。当玩家角色或敌人的生命值减为零时则死亡。当玩家死亡后,游戏结束。

二、开发环境

        操作系统:Windows

        Unity 版本:团结引擎1.0.0

        团结引擎的安装参考《团结引擎的安装》

三、场景搭建

     3.1 创建项目

        使用团结引擎新建项目和使用Unity Hub一样。打开Tuanjie Hub,点击左上角【create project】按钮,选择【3D】模版,输入项目名称和项目保存路径,点击右下【Create project】按钮完成创建。

         3.2 搭建场景

        1. 我们创建一个plane作为地面 。在Hierarchy面板上依次【右键】-> 【3D Object】->【plane】,将【Plane】创命名为【Ground】,并将x、z缩放大小变为4。我们将地面颜色改成灰色。新建文件夹,命名为【Materals】用来存放材质。在材质文件夹右键,依次选择【Create】->【Material】, 并命名为【Ground】。修改材质颜色为灰色(颜色值:4B4747),并将材质拖拽到地面上。

        2. 同样, 我们创建一个胶囊体(Capsule)来作为主角,并命名为Player。创建一个立方体(Cube)放置在Player上,并调整到合适的位置。Cube将作为Player的正面,以便分辨方向。创建一个材质,命名为Face,改变材质样色为蓝色(也可以设置成自己喜欢的颜色),然后把这个材质赋值给Cube。

        3.  调整摄像机角度

        由于我们是俯视角度的射击游戏,因此需要调整摄像机的位置。选中摄像机,将摄像机调整到Player后方合适的位置。

        4. 创建敌人 

        敌人角色与玩家角色造型基本一致,只是将白色换成红色。我们复制场景中的Player(选中Player, 按 ctrl + D),命名为Enermy。然后创建一个材质Enermy, 然后将材质的颜色改为红色。把材质赋给场景中的Enermy物体。新建标签(Tag),命名为Enermy,将玩家Player的Tag设置为“Player”, Enermy 的Tag设置为“Enermy”,方便区分玩家和敌人。之后我们新建文件夹,命名为 Prefabs,将Enermy制作成预知体(直接把Hierarchy中的Enermy物体拖到Prefabs文件夹中)。

 四、编写脚本,实现功能

        上面基本场景已经搭建完毕,接下来我们就开始编写脚本,实现我们的游戏功能。

        4.1 让玩家动起来

        首先,我们来编写玩家脚本,让玩家动起来。新建文件夹,命名为【Scripts】,用来保存我自己编写的脚本文件。在【Scripts】右键->【Create】->【C# Script】,命名为Player。然后将Player脚本挂载到Player物体上。

         和《3D小球跑酷》类似,我们使用键盘来控制Player的移动。双击打开Player脚本,输入以下内容:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Player : MonoBehaviour
  5. {
  6. public float speed = 10f; // 玩家的移动速度
  7. public int maxHp = 10; // 玩家的最大血量
  8. private int hp; // 玩家的当前血量
  9. private bool idDead = false;
  10. void Start()
  11. {
  12. hp = maxHp; // 初始血量为最大值
  13. }
  14. void Update()
  15. {
  16. if (!idDead)
  17. {
  18. Move();
  19. }
  20. }
  21. void Move()
  22. {
  23. var input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); // 接受键盘输入
  24. input = input.normalized;
  25. Vector3 currentPos = transform.position;
  26. currentPos += input * speed * Time.deltaTime;
  27. if (input.magnitude > 0.1f)
  28. {
  29. transform.forward = input; // 使玩家面向移动方向
  30. }
  31. // 限制玩家的移动范围,范围为方圆20以内
  32. const float distance = 20f;
  33. if (currentPos.x > distance)
  34. currentPos.x = distance;
  35. if (currentPos.x < -distance)
  36. currentPos.x = -distance;
  37. if(currentPos.z > distance)
  38. currentPos.z = distance;
  39. if(currentPos.z < -distance)
  40. currentPos.z = -distance;
  41. transform.position = currentPos;
  42. }
  43. }

        保存脚本,运行游戏,按键盘上的方向键或者A、D、S、W键,可以看到Player朝着指定的方向移动起来了。

注意:

        以上移动方式没有考虑阻挡,为放置玩家或者敌人移动到地面的边缘掉下去,我们使用了简单的方式来限定他们的可移动范围。

      4.2 相机跟随

        运行游戏,发现Player在移动时,相机是固定的。下面我们把相机改为随着Player移动而移动。在《3D小球跑酷》我们采用的是把相机直接作为Player的子物体的方式,这里我们采用另一中方式(当然,你也可以使用作为子物体的方式),就是编写相机跟随脚本。

        在Scripts文件夹下新建C#脚本,命名为FlowCamera。将脚本挂在到Camera上,双击打开脚本,输入以下代码:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class FlowCamera : MonoBehaviour
  5. {
  6. public Transform target;
  7. Vector3 offset;
  8. void Start()
  9. {
  10. offset = transform.position - target.position;
  11. }
  12. void Update()
  13. {
  14. transform.position = target.position + offset ;
  15. }
  16. }

        将Player物体拖拽到脚本参数target字段。保存再次运行游戏,发现相机已经可以跟随Player移动了。

      4.3 制作子弹

        创建一个球体,大小缩放为0.2倍。命名为Bullet, 这将作为我们发射的子弹。新建脚本Bullet,将脚本挂在到Bullet物体上。双击打开脚本,输入以下内容:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Bullet : MonoBehaviour
  5. {
  6. public float lifeTime = 2f; // 子弹的生命周期
  7. public float speed = 10f; // 子弹的飞行速度
  8. float startTime; // 子弹的生成时间
  9. // Start is called before the first frame update
  10. void Start()
  11. {
  12. startTime = Time.time;
  13. }
  14. // Update is called once per frame
  15. void Update()
  16. {
  17. transform.position += speed * transform.forward * Time.deltaTime;
  18. // 超过一定时间后
  19. if (startTime + lifeTime < Time.time)
  20. {
  21. Destroy(gameObject); // 销毁子弹
  22. }
  23. }
  24. }

        将球体拖到Prefabs文件夹,做成预制体。

      4.4 实现武器系统

        下面我们实现一下Player切换武器的功能,这是我们游戏的一个重要功能。我们按Q键可以切换武器。武器有三种,分别是手枪(Pistol),自动步枪(Rifle)和散弹枪(shotgun)。

        新建脚本,命名为Weapon。双击打开脚本,输入以下内容:

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. public class Weapon : MonoBehaviour
  6. {
  7. public GameObject bulletPrefab; // 子弹预制体
  8. public float pistolFireCD = 0.2f; // 手枪发射子弹的时间间隔
  9. public float rifleFireCD = 0.1f; // 自动步枪发射子弹的时间间隔
  10. public float shotgunFireCD = 0.5f; // 散弹枪发射子弹的时间间隔
  11. float lastFireTime; // 上次开火时间
  12. public int curGun; //当前使用的武器 0:手枪 1:自动步枪 2:散弹枪
  13. // 开火方法,由角色脚本调用, keyDown 表示按一下就发射一颗子弹, keyPressed 表示长按
  14. public void Fire(bool keyDown, bool keyPressed)
  15. {
  16. switch (curGun)
  17. {
  18. case 0:
  19. if (keyDown)
  20. {
  21. PistolFire();
  22. }
  23. break;
  24. case 1:
  25. if (keyDown)
  26. {
  27. ShotgunFire();
  28. }
  29. break;
  30. case 2:
  31. if (keyPressed)
  32. {
  33. RifleFire();
  34. }
  35. break;
  36. default:
  37. break;
  38. }
  39. }
  40. // 更换武器
  41. public int ChangWeapon()
  42. {
  43. curGun += 1;
  44. if (curGun >= 3)
  45. curGun = 0;
  46. return curGun;
  47. }
  48. // 手枪射击
  49. private void PistolFire()
  50. {
  51. // 子弹发射时间间隔判定
  52. if (lastFireTime + pistolFireCD > Time.time)
  53. {
  54. return;
  55. }
  56. lastFireTime = Time.time;
  57. GameObject bullet = GameObject.Instantiate(bulletPrefab);
  58. bullet.transform.position = transform.position + transform.forward * 1.0f; // 子弹的位置,位于角色前方1米
  59. bullet.transform.forward = transform.forward; // 子弹的方向
  60. }
  61. // 自动步枪射击
  62. private void RifleFire()
  63. {
  64. // 子弹发射时间间隔判定
  65. if (lastFireTime + rifleFireCD > Time.time)
  66. {
  67. return;
  68. }
  69. lastFireTime = Time.time;
  70. GameObject bullet = GameObject.Instantiate(bulletPrefab);
  71. bullet.transform.position = transform.position + transform.forward * 1.0f; // 子弹的位置,位于角色前方1米
  72. bullet.transform.forward = transform.forward; // 子弹的方向
  73. }
  74. // 散弹枪射击,一次发射5颗子弹,子弹间隔10
  75. private void ShotgunFire()
  76. {
  77. // 子弹发射时间间隔判定
  78. if (lastFireTime + shotgunFireCD > Time.time)
  79. {
  80. return;
  81. }
  82. lastFireTime = Time.time;
  83. for (int i = -2; i <= 2; i++)
  84. {
  85. GameObject bullet = GameObject.Instantiate(bulletPrefab);
  86. var dir = Quaternion.Euler(0, i * 10, 0) * transform.forward;
  87. bullet.transform.position = transform.position + dir * 1.0f; // 子弹的位置,位于角色前方1米
  88. bullet.transform.forward = dir; // 子弹的方向
  89. }
  90. }
  91. }

        修改Player脚本调用武器系统,使角色发射子弹。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Player : MonoBehaviour
  5. {
  6. ......
  7. // 声明武器对象
  8. private Weapon weapon;
  9. void Start()
  10. {
  11. hp = maxHp; // 初始血量为最大值
  12. weapon = GetComponent<Weapon>(); // 获取武器对象
  13. }
  14. void Update()
  15. {
  16. if (!idDead)
  17. {
  18. Move();
  19. Fire(); // 开火,发射子弹
  20. ChangeWeapon(); // 切换武器
  21. }
  22. }
  23. void Move()
  24. {
  25. ......
  26. }
  27. void Fire()
  28. {
  29. bool keyDown = Input.GetKeyDown(KeyCode.J);
  30. bool keyPressed = Input.GetKey(KeyCode.J);
  31. weapon.Fire(keyDown, keyPressed);
  32. }
  33. void ChangeWeapon()
  34. {
  35. bool changWeapon = Input.GetKeyDown(KeyCode.Q);
  36. if (changWeapon)
  37. weapon.ChangWeapon();
  38. }
  39. }

        将武器脚本(Weapon)挂载到Player物体上,同时把子弹预制体拖到武器脚本的bulletPrefab字段上。保存场景,运行游戏,按Q键可以切换枪支,按J键可以发射子弹。读者可以自行调整玩家的移动速度和子弹的发射速度,以获得更好的游戏体验。

      4.5 实现敌人的脚本

        敌人脚本和Player脚本类似,单敌人会始终朝着玩家前进,并朝着玩家发射子弹。在Scripts文件夹下新建脚本文件,命名为Enermy,并将其挂载到Enermy预制体上。双击打开脚本,输入以下内容:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Enermy : MonoBehaviour
  5. {
  6. private Transform playerTransform; // player 的 transform 组件
  7. public float speed = 2f; // 敌人的移动速度
  8. public int maxHp = 1; // 敌人的最大血量
  9. private int hp; // 敌人的当前血量
  10. private bool isDead = false;
  11. private Weapon weapon;
  12. void Start()
  13. {
  14. hp = maxHp; // 初始血量为最大值
  15. weapon = GetComponent<Weapon>();
  16. playerTransform = GameObject.Find("Player").transform;
  17. }
  18. void Update()
  19. {
  20. if (!isDead)
  21. {
  22. Move();
  23. Fire();
  24. }
  25. }
  26. void Move()
  27. {
  28. Vector3 dir = playerTransform.position - transform.position;
  29. var currentPos = transform.position + dir.normalized * speed * Time.deltaTime;
  30. // 限制敌人的移动范围,范围为方圆20以内
  31. const float distance = 20f;
  32. if (currentPos.x > distance)
  33. currentPos.x = distance;
  34. if (currentPos.x < -distance)
  35. currentPos.x = -distance;
  36. if (currentPos.z > distance)
  37. currentPos.z = distance;
  38. if (currentPos.z < -distance)
  39. currentPos.z = -distance;
  40. transform.position = currentPos;
  41. transform.forward = dir.normalized;
  42. }
  43. void Fire()
  44. {
  45. weapon.Fire(true, true);
  46. }
  47. }

        然后,把武器脚本挂载到Enermy预制体上,保存。再场景中放置几个敌人(直接把Enermy预制体拖到场景中即可),运行游戏,发现敌人开始朝着玩家移动,并不断发射子弹。

      4.6 子弹的碰撞逻辑

        虽然现在我们可以控制Player移动,并且能发射子弹,敌人也能够发射子弹,但是子弹碰到敌人或者Player都没有反应,接下来我们就开始添加子弹碰撞的逻辑。

        子弹和物体的碰撞分为以下几种情况:

        1)  Player的子弹击中敌人

        2)敌人的子弹击中Player

        3)玩家的子弹和Player的子弹碰撞

        4)Player的子弹不会击中Player,敌人的子弹也不会击中敌人。

        为了区分敌人子弹和Player的子弹,我们采用标签(Tag)来进行区分。我们新建两个标签,分别为PlayerBullet和EnermyBullet。选中之前的子弹预制体,重命名为PlayerBullet, 并将Tag设置为PlayerBullet。复制PlayerBullet,重命名为EnermyBullet,并将Tag设置为EnermyBullet。

       4.6.1 修改Enermy预制体

       双击Enermy预制体,将脚本Weapon上的BulletPrefab字段设置为EnermyBullet。保存退出。

      4.6.2 修改Bullet脚本

       双击Bullet脚本,添加碰撞逻辑。添加以下代码:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Bullet : MonoBehaviour
  5. {
  6. .....
  7. private void OnTriggerEnter(Collider other)
  8. {
  9. if (CompareTag(other.tag)) // 如果子弹的tag和碰撞到的物体的Tag相同
  10. return;
  11. Destroy(gameObject);
  12. }
  13. }

        4.6.3 修改Player脚本

        当敌人的子弹碰到玩家的时候,玩家血量减1,当血量为0时,玩家死亡,游戏结束。双击打来Player脚本,添加以下代码:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Player : MonoBehaviour
  5. {
  6. ......
  7. private void OnTriggerEnter(Collider other)
  8. {
  9. if (other.CompareTag("EnermyBullet"))
  10. {
  11. if (hp <= 0) return;
  12. hp--;
  13. if (hp <= 0)
  14. {
  15. idDead = true;
  16. Debug.Log("Game Over!");
  17. }
  18. }
  19. }
  20. }

        4.6.4 修改Enermy脚本

        当玩家的子弹碰到敌人的时候,敌人血量减1。当敌人的血量为0时,敌人死亡。双击打开Enermy脚本,添加以下代码:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Enermy : MonoBehaviour
  5. {
  6. ......
  7. private void OnTriggerEnter(Collider other)
  8. {
  9. if (other.CompareTag("PlayerBullet"))
  10. {
  11. if (hp <= 0) return;
  12. hp--;
  13. if (hp <= 0)
  14. {
  15. Destroy(gameObject);
  16. }
  17. }
  18. }
  19. }

           保存场景,运行游戏,发现子弹碰撞后并没有什么效果,这是因为我们没有添加刚体,并设置触发器。

        4.6.5 设置触发器

        选中敌人的子弹和Player的子弹的预制体,添加刚体组件(RigidBody),并勾选 Collider 上的Is Trigger 复选框。选择Enermy预制体,并勾选 Collider 上的Is Trigger 复选框。选择Player,并勾选 Collider 上的Is Trigger 复选框。保存场景再次运行游戏,发现Player 被击中之后,血量减少,当血量减少到0是,控制台输出“Game Over”。

        4.7 生成大量敌人

        现在我们的游戏已经初具模型了,单要手动把敌人预制体拖到场景中,如果要有许多敌人就显得太麻烦了,而且拖到场景中的敌人有限,杀完了就没有了。 我们要有一个可以源源不断生成敌人的机制,每隔一段时间就生成一个敌人,这样就可以一直玩下去了。

        在场景中新建一个空物体,命名为EnemySpawn,同时新建脚本文件,也命名为EnemySpawn,并挂载到EnemySpawn物体上。脚本内容如下:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class EnermySpawn : MonoBehaviour
  5. {
  6. public GameObject enermyPrefab;
  7. public float spawTimer = 2.0f;
  8. List<GameObject> enermies = new List<GameObject> ();
  9. void Update()
  10. {
  11. if (enermies.Count >= 10) return; // 场景中最多10个敌人
  12. spawTimer -= Time.deltaTime;
  13. if (spawTimer <= 0)
  14. {
  15. spawTimer = 2.0f;
  16. GameObject enermy = Instantiate(enermyPrefab);
  17. enermy.transform.position = new Vector3(-20, 1, -20);
  18. }
  19. }
  20. }

        保存脚本和场景,运行游戏,发现没隔2秒就会有一个敌人生成,并且场景中最多会有10个敌人,当场景中的敌人数量少于10个时,又会有新的敌人生成出来,这样就可以一直玩下去了。

五、结束语

        好了,射击游戏到这里就结束了,虽然还有很多不足,但作为初学者的练习案例,也用到了许多Unity的基础知识,希望您能够获得一些启发,对您的学习有所帮助。您也可以发挥自己的想想力,完善本案例,比如增加积分、音效等等,使游戏更加丰富。

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

闽ICP备14008679号