飞道的博客

【Unity3D】血条(HP)

465人阅读  评论(0)

1 需求实现

        人机交互Input 中实现了通过键盘控制坦克运动,通过鼠标控制坦克发射炮弹,本文将在此基础上,增加血条(HP)功能。炮弹命中后,HP 值会减少,因此需要应用到 刚体组件Rigidbody 和 碰撞体组件Collider;从不同角度攻击敌人时,敌人的血条始终朝向相机,因此需要用到 相机跟随;血条通过 Image 显示,因此需要用到 UGUI之Image;玩家的血条始终显示在屏幕左上角,因此需要使用到 锚点

        1)需求实现

  • 前后箭头键或 W, S 键控制玩家前进或后退;
  • 左右箭头键或 A, D 键控制玩家左右转向;
  • 鼠标左键或空格键控制玩家发射炮弹;
  • 玩家血条显示在屏幕左上角;
  • 相机在玩家后上方的位置,始终跟随玩家,朝玩家正前方看;
  • 玩家移动时,敌人转向玩家,当偏离玩家的角度小于5°时,发射炮弹;
  • 敌人血条显示在其上方,并且始终看向相机。

        2)涉及技术栈

2 游戏对象

        1)游戏界面

        2)游戏对象层级结构

        3 Transform组件参数

        1)玩家 Transform 组件参数

Name Type Position Rotation Scale Color/Texture
Player Empty (0, 0.25, -5) (0, 0, 0) (1, 1, 1) #228439FF
Botton Cube (0, 0, 0) (0, 0, 0) (2, 0.5, 2) #228439FF
Top Cube (0, 0.5, 0) (0, 0, 0) (1, 0.5, 1) #228439FF
Gun Cylinder (0, 0, 1.5) (90, 0, 0) (0.2, 1, 0.4) #228439FF
FirePoint Empty (0, 1.15, 0) (0, 0, 0) (1, 1, 1) ——

        补充:Player 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 1,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。 

        2)玩家 HP RectTransform 组件参数

Name Type Rect Width/Height Pos Color/Texture
PlayerHP Canvas —— —— —— ——
Panel Panel (0, 0, 0, 0) —— (-, -, 0) #FFFFFF00
HealthBG Image (0, 0.25, -5) (200, 20) (125, -30, 0) #FFFFFFFF
Health Image (0, 0.25, -5) (200, 20) (125, -30, 0) #FF2230FF

        补充: 玩家 PlayerHP 的 Canvas 渲染模式是 Screen Space - Overlay,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。

        3)敌人 Transform 组件参数

Name Type Position Rotation Scale Color/Texture
Enemy Empty (0, 0.25, 5) (0, 180, 0) (1, 1, 1) #15D3F9FF
Botton Cube (0, 0, 0) (0, 0, 0) (2, 0.5, 2) #15D3F9FF
Top Cube (0, 0.5, 0) (0, 0, 0) (1, 0.5, 1) #15D3F9FF
Gun Cylinder (0, 0, 1.5) (90, 0, 0) (0.2, 1, 0.4) #15D3F9FF
FirePoint Empty (0, 1.15, 0) (0, 0, 0) (1, 1, 1) ——

        补充:Enemy 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 0.5,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。  

        4)敌人 HP RectTransform 组件参数

Name Type Width/Height Pos Color/Texture
HP Canvas (2, 0.2) (0, 0.85, 0) ——
HealthBG Image (2, 0.2) (0, 0, 0) #FFFFFFFF
Health Image (2, 0.2) (0, 0, 0) #FF2230FF

        补充: 敌人 HP 的 Canvas 渲染模式是 World Space,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。

        5)地面和炮弹 Transform 组件参数

Name Type Position Rotation Scale Color/Texture
Plane Plane (0, 0, 0) (0, 0, 0) (10, 10, 10) GrassRockyAlbedo
Bullet Sphere (0, 0.5, -5) (0, 0, 0) (0.3, 0.3, 0.3) #228439FF

        补充:炮弹作为预设体拖拽到 Assets/Resources/Prefabs 目录下,并且添加了刚体组件。

3 脚本组件

        1)CameraController

        CameraController.cs


  
  1. using UnityEngine;
  2. public class CameraController : MonoBehaviour {
  3. private Transform player; // 玩家
  4. private Vector3 relaPlayerPos; // 相机在玩家坐标系中的位置
  5. private float targetDistance = 15f; // 相机看向玩家前方的位置
  6. private void Start() {
  7. relaPlayerPos = new Vector3( 0, 4, -8);
  8. player = GameObject.Find( "Player/Top").transform;
  9. }
  10. private void LateUpdate() {
  11. CompCameraPos();
  12. }
  13. private void CompCameraPos() { // 计算相机坐标
  14. Vector3 target = player.position + player.forward * targetDistance;
  15. transform.position = transformVecter(relaPlayerPos, player.position, player.right, player.up, player.forward);
  16. transform.rotation = Quaternion.LookRotation(target - transform.position);
  17. }
  18. // 求以origin为原点, locX, locY, locZ 为坐标轴的本地坐标系中的向量 vec 在世界坐标系中对应的向量
  19. private Vector3 transformVecter(Vector3 vec, Vector3 origin, Vector3 locX, Vector3 locY, Vector3 locZ) {
  20. return vec.x * locX + vec.y * locY + vec.z * locZ + origin;
  21. }
  22. }

        说明: CameraController 脚本组件挂在 MainCamera 游戏对象上。

        2)PlayerController

        PlayerController.cs


  
  1. using System;
  2. using UnityEngine;
  3. public class PlayerController : MonoBehaviour {
  4. private Transform firePoint; // 开火点
  5. private GameObject bulletPrefab; // 炮弹预设体
  6. private float tankMoveSpeed = 4f; // 坦克移动速度
  7. private float tankRotateSpeed = 2f; // 坦克转向速度
  8. private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间
  9. private float bulletCoolTime = 0.15f; // 炮弹冷却时间
  10. private void Start() {
  11. firePoint = transform.Find( "Top/Gun/FirePoint");
  12. bulletPrefab = (GameObject) Resources.Load( "Prefabs/Bullet");
  13. }
  14. private void Update() {
  15. fireWaitTime += Time.deltaTime;
  16. float hor = Input.GetAxis( "Horizontal");
  17. float ver = Input.GetAxis( "Vertical");
  18. Move(hor, ver);
  19. if (Input.GetMouseButtonDown( 0) || Input.GetKeyDown(KeyCode.Space)) {
  20. Fire();
  21. }
  22. }
  23. private void Move(float hor, float ver) { // 坦克移动
  24. if (Math.Abs(hor) > 0.1f || Math.Abs(ver) > 0.1f) {
  25. GetComponent<Rigidbody>().velocity = transform.forward * tankMoveSpeed * ver;
  26. GetComponent<Rigidbody>().angularVelocity = Vector3.up * tankRotateSpeed * hor;
  27. }
  28. }
  29. private void Fire() { // 开炮
  30. if (fireWaitTime > bulletCoolTime) {
  31. BulletInfo bulletInfo = new BulletInfo( "PlayerBullet", Color.red, transform.forward, 10f, 15f);
  32. GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity);
  33. bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);
  34. fireWaitTime = 0f;
  35. }
  36. }
  37. }

        说明: PlayerController 脚本组件挂在 Player 游戏对象上。 

        3)EnemyController

        EnemyController.cs


  
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. public class EnemyController : MonoBehaviour {
  4. private Transform target; // 目标
  5. private Transform top; // 炮头
  6. private Transform firePoint; // 开火点
  7. private Transform hp; // 血条
  8. private GameObject bulletPrefab; // 炮弹预设体
  9. private float rotateSpeed = 0.4f; // 坦克转向速度
  10. private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间
  11. private float bulletCoolTime = 1f; // 炮弹冷却时间
  12. private void Start () {
  13. target = GameObject.Find( "Player/Top").transform;
  14. top = transform.Find( "Top");
  15. firePoint = transform.Find( "Top/Gun/FirePoint");
  16. hp = transform.Find( "HP");
  17. bulletPrefab = (GameObject) Resources.Load( "Prefabs/Bullet");
  18. }
  19. private void Update () {
  20. fireWaitTime += Time.deltaTime;
  21. LookAtTarget();
  22. float angle = Vector3.Angle(target.position - top.position, top.forward);
  23. if (LookAtTarget()) {
  24. Fire();
  25. }
  26. HPLookAtCamera();
  27. }
  28. private bool LookAtTarget() {
  29. Vector3 dir = target.position - top.position;
  30. float angle = Vector3.Angle(dir, top.forward);
  31. if (angle > 5) {
  32. int axis = Vector3.Dot(Vector3.Cross(dir, top.forward), Vector3.up) > 0 ? -1 : 1;
  33. GetComponent<Rigidbody>().angularVelocity = axis * Vector3.up * rotateSpeed;
  34. return false;
  35. }
  36. GetComponent<Rigidbody>().angularVelocity = Vector3.zero;
  37. return true;
  38. }
  39. private void HPLookAtCamera() {
  40. Vector3 cameraPos = Camera.main.transform.position;
  41. Vector3 target = new Vector3(cameraPos.x, hp.position.y, cameraPos.z);
  42. hp.LookAt(target);
  43. }
  44. private void Fire() {
  45. if (fireWaitTime > bulletCoolTime) {
  46. BulletInfo bulletInfo = new BulletInfo( "EnemyBullet", Color.yellow, top.forward, 5f, 10f);
  47. GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity); // 通过预设体创建炮弹
  48. bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);
  49. fireWaitTime = 0;
  50. }
  51. }
  52. }

        说明: EnemyController 脚本组件挂在 Enemy 游戏对象上。  

        4)BulletController

        BulletController.cs


  
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. public class BulletController : MonoBehaviour {
  4. private BulletInfo bulletInfo; // 炮弹信息
  5. private volatile bool isDying = false;
  6. private void Start () {
  7. gameObject.name = bulletInfo.name;
  8. GetComponent<MeshRenderer>().material.color = bulletInfo.color;
  9. float lifeTime = bulletInfo.fireRange / bulletInfo.speed; // 存活时间
  10. Destroy(gameObject, lifeTime);
  11. }
  12. private void Update () {
  13. transform.GetComponent<Rigidbody>().velocity = bulletInfo.flyDir * bulletInfo.speed;
  14. }
  15. private void OnCollisionEnter(Collision other) {
  16. if (isDying) {
  17. return;
  18. }
  19. if (IsHitEnemy(gameObject.name, other.gameObject.name)) {
  20. other.transform.Find( "HP/Health").GetComponent<Image>().fillAmount -= 0.1f;
  21. isDying = true;
  22. Destroy(gameObject, 0.1f);
  23. } else if (IsHitPlayer(gameObject.name, other.gameObject.name)) {
  24. GameObject.Find( "PlayerHP/Panel/Health").GetComponent<Image>().fillAmount -= 0.1f;
  25. isDying = true;
  26. Destroy(gameObject, 0.1f);
  27. }
  28. }
  29. public void SetBulletInfo(BulletInfo bulletInfo) {
  30. this.bulletInfo = bulletInfo;
  31. }
  32. private bool IsHitEnemy(string name, string otherName) { // 射击到敌军
  33. return name.Equals( "PlayerBullet") && otherName.Equals( "Enemy");
  34. }
  35. private bool IsHitPlayer(string name, string otherName) { // 射击到玩家
  36. return name.Equals( "EnemyBullet") && otherName.Equals( "Player");
  37. }
  38. }

        说明: BulletController 脚本组件挂在 Bullet 游戏对象上(代码里动态添加)。   

        5)BulletInfo

        BulletInfo.cs


  
  1. using UnityEngine;
  2. public class BulletInfo {
  3. public string name; // 炮弹名
  4. public Color color; // 炮弹颜色
  5. public Vector3 flyDir; // 炮弹飞出方向
  6. public float speed; // 炮弹飞行速度
  7. public float fireRange; // 炮弹射程
  8. public BulletInfo(string name, Color color, Vector3 flyDir, float speed, float fireRange) {
  9. this.name = name;
  10. this.color = color;
  11. this.flyDir = flyDir;
  12. this.speed = speed;
  13. this.fireRange = fireRange;
  14. }
  15. }

4 运行效果


转载:https://blog.csdn.net/m0_37602827/article/details/125862315
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场