当前位置:   article > 正文

Unity射击游戏(完整版步骤)_unity的reactivetarget组件

unity的reactivetarget组件

b4e7c632d7bb416fb1556d2e3cb16cae.png

基本功能

  • 创建基本场景(Cube创建,后续添加的场景物体都需要有Collider组件,实现碰撞)

  • Player(胶囊体)的移动和视角(Player下需要包含一个Camera对象,以便于实现视角和射线检测)

KeyboardMove.cs代码(添加到Player下),通过键盘控制移动:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. [RequireComponent(typeof(CharacterController))]
  5. [AddComponentMenu("Control Script/FPS Input")]
  6. public class KeyboardMove : MonoBehaviour
  7. {
  8. public float speed = 6.0f;
  9. public float gravity = 0;
  10. private CharacterController _charController; //用于引用CharacterController的变量
  11. // Use this for initialization
  12. void Start()
  13. {
  14. _charController = GetComponent<CharacterController>(); //使用附加到相同对象上的其他组件
  15. }
  16. // Update is called once per frame
  17. void Update()
  18. {
  19. float deltaX = Input.GetAxis("Horizontal") * speed;
  20. float deltaZ = Input.GetAxis("Vertical") * speed;
  21. Vector3 movement = new Vector3(deltaX, 0, deltaZ);
  22. movement = Vector3.ClampMagnitude(movement, speed); //将对角移动的速度限制为和沿着轴移动的速度一样
  23. movement.y = gravity;
  24. movement *= Time.deltaTime;
  25. movement = transform.TransformDirection(movement); //把movement向量从本地变换为全局坐标
  26. _charController.Move(movement); //告知CharacterController通过movement向量移动
  27. }
  28. }

MouseLook.cs代码(添加到Camera下),通过鼠标控制视野:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class MouseLook : MonoBehaviour
  5. {
  6. public enum RotationAxes
  7. {
  8. MouseXAndY = 0,
  9. MouseX = 1,
  10. MouseY = 2
  11. }
  12. public RotationAxes axes = RotationAxes.MouseXAndY;
  13. public float sensitivityHor = 9.0f;
  14. public float sensitivityVert = 9.0f;
  15. public float minimumVert = -15.0f;
  16. public float maximumVert = 45.0f;
  17. private float _rotationX = 0;
  18. // Use this for initialization
  19. void Start()
  20. {
  21. Rigidbody body = GetComponent<Rigidbody>();
  22. if (body != null)
  23. {
  24. body.freezeRotation = true;
  25. }
  26. }
  27. // Update is called once per frame
  28. void Update()
  29. {
  30. if (axes == RotationAxes.MouseX)
  31. {
  32. //horizontal rotation here
  33. transform.Rotate(0, Input.GetAxis("Mouse X") * sensitivityHor, 0);
  34. }
  35. else if (axes == RotationAxes.MouseY)
  36. {
  37. //vertical rotation here
  38. _rotationX -= Input.GetAxis("Mouse Y") * sensitivityVert;
  39. _rotationX = Mathf.Clamp(_rotationX, minimumVert, maximumVert);
  40. float rotationY = transform.localEulerAngles.y;
  41. transform.localEulerAngles = new Vector3(_rotationX, rotationY, 0);
  42. }
  43. else
  44. {
  45. //both horizontal and vertical rotation here
  46. _rotationX -= Input.GetAxis("Mouse Y") * sensitivityVert;
  47. _rotationX = Mathf.Clamp(_rotationX, minimumVert, maximumVert);
  48. float delta = Input.GetAxis("Mouse X") * sensitivityHor;
  49. float rotationY = transform.localEulerAngles.y + delta;
  50. transform.localEulerAngles = new Vector3(_rotationX, rotationY, 0);
  51. }
  52. }
  53. }
  • 通过实例化子弹实现角色的射击功能(该功能代码需要添加到Camera上)

RayShooter.cs代码(添加到Camera下),通过鼠标左键进行射击:

这里的代码同时也实现了换弹和子弹数量的UI显示

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. public class RayShooter : MonoBehaviour
  6. {
  7. private Camera _camera;
  8. //加入子弹计数和换弹功能并在UI上显示
  9. //子弹弹夹
  10. public int bulletsMag = 31;//一个弹夹子弹数量
  11. public int bulletLeft = 300;//备弹
  12. public int currentBullects;//当前子弹数量
  13. //关于子弹数量的UI设置,需要导入命名空间UnityEngine.UI
  14. [Header("UI设置")]
  15. public Image CrossHairUI;//瞄点UI
  16. public Text AmmoTextUI;//子弹数量UI
  17. [Header("键位设置")]
  18. [SerializeField][Tooltip("填装子弹按键")]private KeyCode reloadInputName;
  19. // Use this for initialization
  20. void Start()
  21. {
  22. _camera = GetComponent<Camera>(); //访问相同对象上附加的其他组件
  23. Cursor.lockState = CursorLockMode.Locked;
  24. Cursor.visible = false; //隐藏屏幕中心的光标
  25. //关于子弹数量
  26. currentBullects = bulletsMag;
  27. UpdateAmmoUI();//UI
  28. reloadInputName = KeyCode.R;//R键填装子弹
  29. }
  30. void OnGUI()
  31. {
  32. int size = 12;
  33. float posX = _camera.pixelWidth / 2 - size / 4;
  34. float posY = _camera.pixelHeight / 2 - size / 2;
  35. GUI.Label(new Rect(posX, posY, size, size), "*"); //GUI.Label()在屏幕上显示文本,射击瞄点
  36. }
  37. // Update is called once per frame
  38. void Update()
  39. {
  40. if (Input.GetMouseButtonDown(0) && currentBullects!=0) //鼠标左键按下且目前子弹数量不为0才射击
  41. {
  42. Vector3 point = new Vector3(_camera.pixelWidth / 2, _camera.pixelHeight / 2, 0); //屏幕中心是宽高的一半
  43. Ray ray = _camera.ScreenPointToRay(point); //使用ScreenPointToRay()在摄像机所在位置创建射线
  44. RaycastHit hit;
  45. if (Physics.Raycast(ray, out hit)) //检测是否击中物体
  46. {
  47. GameObject hitObject = hit.transform.gameObject;//获取被击中的游戏对象
  48. ReactiveTarget target = hitObject.GetComponent<ReactiveTarget>();
  49. if (target != null)//如果有ReactiveTarget组件
  50. {
  51. target.ReactToHit();
  52. }
  53. else
  54. {
  55. StartCoroutine(SphereIndicator(hit.point)); //运行协程来响应击中
  56. }
  57. //射击即触发,关于子弹的数量
  58. currentBullects--;
  59. UpdateAmmoUI();
  60. }
  61. }
  62. if (Input.GetKeyDown(reloadInputName) && currentBullects < bulletsMag && bulletLeft > 0) //执行换子弹
  63. {
  64. Reload();
  65. }
  66. }
  67. private IEnumerator SphereIndicator(Vector3 pos) //协程使用IEnumerator方法
  68. {
  69. GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
  70. sphere.transform.position = pos;
  71. yield return new WaitForSeconds(1); //yield关键字告诉协程在何处暂停
  72. Destroy(sphere); //移除GameObject并清除它占用的内存
  73. }
  74. //更新UI
  75. public void UpdateAmmoUI()
  76. {
  77. AmmoTextUI.text = "子弹数量:" + currentBullects + "/" + bulletLeft;
  78. }
  79. //换子弹逻辑
  80. public void Reload()
  81. {
  82. if (bulletLeft <= 0) return;
  83. //需要填装子弹数
  84. int bullectToLoad = bulletsMag - currentBullects;
  85. //备弹需扣除子弹数
  86. int bullectToReduce = (bulletLeft >= bullectToLoad) ? bullectToLoad : bulletLeft;
  87. bulletLeft -= bullectToReduce;//减少备弹
  88. currentBullects += bullectToReduce;//当前子弹数增加
  89. UpdateAmmoUI();
  90. }
  91. }
  • 通过滚轮控制瞄具

miaoju.cs代码(添加到Camera下),实现缩放:

  1. using UnityEngine;
  2. public class RifleScopeZoom : MonoBehaviour
  3. {
  4. [SerializeField] private Camera playerCamera;
  5. [SerializeField] private float zoomSpeed = 10f;
  6. [SerializeField] private float minFOV = 20f;
  7. [SerializeField] private float maxFOV = 60f;
  8. private void Update()
  9. {
  10. // 获取滚轮滑动的值
  11. float scrollValue = Input.GetAxis("Mouse ScrollWheel");
  12. // 根据滚轮滑动的值调整 FOV
  13. playerCamera.fieldOfView -= scrollValue * zoomSpeed;
  14. // 限制 FOV 的范围在最小值和最大值之间
  15. playerCamera.fieldOfView = Mathf.Clamp(playerCamera.fieldOfView, minFOV, maxFOV);
  16. }
  17. }
  • 创建敌人预设(敌人需要实现的功能:随机出现,主动寻找玩家,攻击Player,响应玩家的攻击)

创建预设:通过在层及创建需要物体,然后拖到项目文件夹中,就可以形成预制体了。

创建了敌人和Fireball预制体后,需要在层级面板中删除

敌人组件ReactiveTarget.cs,实现敌人的响应:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class ReactiveTarget : MonoBehaviour
  5. {
  6. public void ReactToHit()
  7. {
  8. WanderingAI behavior = GetComponent<WanderingAI>();
  9. if (behavior != null) //检查角色是否有WanderingAI脚本
  10. {
  11. behavior.SetAlive(false);
  12. }
  13. StartCoroutine(Die()); //通过射击脚本调用的方法
  14. }
  15. private IEnumerator Die()
  16. {
  17. this.transform.Rotate(-75, 0, 0); //推到命中物体,等待1.5秒后摧毁命中物体
  18. yield return new WaitForSeconds(0.5f);
  19. Destroy(this.gameObject); //对象能销毁自己,就像一个分开独立的对象
  20. }
  21. // Use this for initialization
  22. void Start()
  23. {
  24. }
  25. // Update is called once per frame
  26. void Update()
  27. {
  28. }
  29. private void OnTriggerEnter(Collider other)
  30. {
  31. // Debug.Log("进去");
  32. //下面是判断与障碍碰撞的是不是主角,如果是则调用PlayerController中减少血量的函数
  33. PlayerCharacter pc = other.GetComponent<PlayerCharacter>();
  34. if (pc!=null)
  35. {
  36. // Debug.Log("碰到我啦");
  37. pc.Hurt(5);
  38. if (pc.currentHealth == 0)//判断血量是否为零,为零则死亡
  39. {
  40. pc.death();
  41. }
  42. //障碍物被碰撞后会销毁
  43. Destroy(gameObject);
  44. }
  45. }
  46. }

敌人组件WanderingAI.cs,实现敌人自动寻找玩家,进行攻击:

  1. using System.Collections; // 引入使用集合的命名空间
  2. using System.Collections.Generic; // 引入使用泛型集合的命名空间
  3. using UnityEditor; // 引入Unity编辑器命名空间
  4. using UnityEngine; // 引入Unity引擎命名空间
  5. using UnityEngine.AI;
  6. public class WanderingAI : MonoBehaviour // 定义名为WanderingAI的类,继承自MonoBehaviour
  7. {
  8. //AI寻路
  9. private Transform target; //设置追踪目标的位置
  10. private NavMeshAgent navMeshAgent; //设置寻路组件
  11. [SerializeField] private GameObject fireballPrefab; // 序列化字段,用于存储火球预制体
  12. private GameObject fireball; // 火球对象
  13. public float speed = 3.0f; // 移动速度
  14. public float obstacleRange = 2f; // 障碍物检测范围
  15. private bool _alive; // 存储AI是否存活的布尔值
  16. private void Awake()
  17. {
  18. Messenger<float>.AddListener(GameEvent.SPEED_CHANGED, OnSpeedChanged); // 在Awake方法中添加速度改变事件监听器
  19. }
  20. private void OnDestroy()
  21. {
  22. Messenger<float>.RemoveListener(GameEvent.SPEED_CHANGED, OnSpeedChanged); // 在对象销毁时移除速度改变事件监听器
  23. }
  24. private void Start()
  25. {
  26. target = GameObject.FindWithTag("Player").transform; //获取游戏中主角的位置,在我的工程里面主角的标签是Player
  27. navMeshAgent = GetComponent<NavMeshAgent>();
  28. navMeshAgent.speed = speed; //设置寻路器的行走速度
  29. if (navMeshAgent == null)
  30. {
  31. navMeshAgent = gameObject.AddComponent<NavMeshAgent>();
  32. }
  33. _alive = true; // 设置AI为存活状态
  34. }
  35. // Update is called once per frame
  36. void Update()
  37. {
  38. if (_alive) // 如果AI存活
  39. {
  40. navMeshAgent.SetDestination(target.transform.position); //设置寻路目标
  41. transform.Translate(0, 0, speed * Time.deltaTime); // 沿着Z轴移动
  42. Ray ray = new Ray(transform.position, transform.forward); // 创建射线
  43. RaycastHit hitInfo; // 存储射线碰撞信息
  44. if (Physics.SphereCast(ray, 100f, out hitInfo)) // 发射球形射线检测碰撞
  45. {
  46. GameObject hitObject = hitInfo.transform.gameObject; // 获取碰撞的游戏对象
  47. if (hitObject.GetComponent<PlayerCharacter>()) // 如果碰撞到玩家角色
  48. {
  49. if (fireball == null) // 如果火球对象为空
  50. {
  51. fireball = Instantiate(fireballPrefab) as GameObject; // 实例化火球对象
  52. fireball.transform.position = transform.TransformPoint(Vector3.forward * 1.5f); // 设置火球位置
  53. fireball.transform.rotation = transform.rotation; // 设置火球旋转
  54. }
  55. }
  56. if (hitInfo.distance < obstacleRange) // 如果碰撞距离小于障碍物检测范围
  57. {
  58. float angle = Random.Range(-110, 110); // 随机旋转角度
  59. transform.Rotate(0, angle, 0); // 绕Y轴旋转
  60. }
  61. }
  62. }
  63. }
  64. public void SetAlive(bool alive)
  65. {
  66. _alive = alive; // 设置AI存活状态
  67. }
  68. private void OnSpeedChanged(float value)
  69. {
  70. speed = speed * value; // 根据事件传递的值调整速度
  71. }
  72. }

在添加完脚本组建后,要实现敌人的自动寻路,需要将场景物体设置为静态,还需要添加 Nav Mesh Agent组件, 本文代码仅实现静态寻路。设置敌人自动寻路参考文章:

首先点击窗口,查看是否有AI插件,若没有请参考:unity的AI自动寻路Navigation,及其组件详解_ai navigation-CSDN博客进行下载。

下载完成后参考进行设置:

【Unity自动寻路】使用Navigation系统实现物体自动寻路绕开障碍物_unity navigation动态寻路-CSDN博客

  • 创建敌人射击时的Fireball预设

构建 Fireball.cs脚本,实例化敌人向玩家设计的子弹:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Fireball : MonoBehaviour
  5. {
  6. public float speed = 10.0f;
  7. public int damage = 1;
  8. // Use this for initialization
  9. void Start()
  10. {
  11. }
  12. // Update is called once per frame
  13. void Update()
  14. {
  15. transform.Translate(0, 0, speed * Time.deltaTime);
  16. }
  17. void OnTriggerEnter(Collider other)
  18. {
  19. PlayerCharacter player = other.GetComponent<PlayerCharacter>();
  20. if (player != null)
  21. {
  22. player.Hurt(damage);
  23. }
  24. Destroy(this.gameObject);
  25. }
  26. }
  • 实现Player响应敌人的攻击(射击和撞击)

注意将Player的标签设置为Player

PlayerCharacter.cs脚本:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.SceneManagement;
  5. public class PlayerCharacter : MonoBehaviour
  6. {
  7. private Rigidbody rbody; // 用于存储 Rigidbody 组件的私有变量
  8. private CapsuleCollider cCollider; // 用于存储 CapsuleCollider 组件的私有变量
  9. private int maxhealth = 5;
  10. public int MaxHealth
  11. {
  12. get { return maxhealth; }
  13. }
  14. public float currentHealth;
  15. public float CurrentHealth
  16. {
  17. get { return currentHealth; }
  18. }
  19. // Use this for initialization
  20. void Start()
  21. {
  22. currentHealth = maxhealth;
  23. rbody = GetComponent<Rigidbody>(); // 获取附加到 GameObject 上的 Rigidbody 组件
  24. HealthBar.Instance.changeHealth();//UI
  25. }
  26. public void Hurt(int damage)
  27. {
  28. currentHealth = Mathf.Clamp(currentHealth - damage, 0, maxhealth);
  29. // Mathf.Clamp(expression, min, max) - 该函数将结果限制在指定范围内
  30. //currentHealth -= damage;
  31. HealthBar.Instance.changeHealth();
  32. Debug.Log("Health: " + currentHealth);
  33. }
  34. //角色死亡
  35. public void death()
  36. {
  37. if (currentHealth == 0)
  38. {
  39. Destroy(gameObject);
  40. }
  41. }
  42. // Update is called once per frame
  43. void Update()
  44. {
  45. }
  46. }
  • 敌人随机出现

实现当消灭敌人后,再次出现敌人,保持游戏中一直有一个敌人。

首先创建一个空对象SceneController,然后编写SceneController.cs脚本:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class SceneController : MonoBehaviour
  5. {
  6. [SerializeField] private GameObject enemyPrefab; //序列化变量,用于链接预设对象
  7. private GameObject _enemy; //一个私有变量,跟踪场景中敌人的实例
  8. // Use this for initialization
  9. void Start()
  10. {
  11. }
  12. // Update is called once per frame
  13. void Update()
  14. {
  15. if (_enemy == null) //只用当场景中没有敌人时才产生一个新敌人
  16. {
  17. _enemy = Instantiate(enemyPrefab) as GameObject; //这个方法复制了预设对象
  18. _enemy.transform.position = new Vector3(0, 1, 1);
  19. float angle = Random.Range(0, 360);
  20. _enemy.transform.Rotate(0, angle, 0);
  21. }
  22. }
  23. }

基础UI

Player血量

参考文章:U3D游戏角色血条制作并显示血量变化_unity 3d血条显示-CSDN博客

仅需要参考在场景中添加UI,代码部分如下HealthBar.cs:

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.UI;
  6. public class HealthBar : MonoBehaviour
  7. {
  8. Slider healthBar;
  9. //在unity中关联游戏角色
  10. public PlayerCharacter _play = new PlayerCharacter();
  11. //创建一个单例
  12. public static HealthBar Instance;
  13. //挂载创建的CurrentHealth的文本UI
  14. public Text healthNumber;
  15. void Awake()
  16. {
  17. Instance = this;
  18. }
  19. public void changeHealth()
  20. {
  21. //在playcontrol脚本中调用该函数,所以先判断是否获取到组件,
  22. //若是放在该脚本的Start中可能会获取不到
  23. if (healthBar == null)
  24. {
  25. healthBar = GetComponent<Slider>();
  26. }
  27. //使用该段代码前,在Slider检视器中勾选整数,设置最大最小值
  28. healthBar.value = _play.CurrentHealth;
  29. healthNumber.text = healthBar.value + "/" + _play.MaxHealth;
  30. }
  31. }

子弹数

在RayShooter.cs实现了UI的动态变化,现在需要在场景中添加UI。

首先在Canvas添加文本,UI——旧版——文本,文本命名为AmmoTextUI

然后编辑文本:

0c02ca9f834245cc9f903922a68b9d00.png

小地图

创建一个小地图是为了方便查看敌人的位置,因为敌人的速度较快,撞击到Player游戏会结束,所以创建一个小地图。但是由于只用一个长方体当做敌人,小地图上显示不明显,所以在长方体头上加了一个物体。

846949d29a6847399b365a2b98cf7578.png

创建小地图:Unity小地图制作_unity3d中两种绘制小地图的方法-CSDN博客​​​​​​

建议使用第一种方法比较简单

 

 

 


 

 

 

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/975694
推荐阅读
相关标签
  

闽ICP备14008679号