JoshWindsor 发表于 2023-2-24 15:04

【Unity3D】血条(HP)

1 需求实现


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

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

2)涉及技术栈
Transform组件人机交互刚体组件Rigidbody碰撞体组件Collider相机跟随UGUI之ImageCanvas渲染模式与锚点

2 游戏对象

1)游戏界面
http://upload-images.jianshu.io/upload_images/20142973-26692f814403d913.png
2)游戏对象层级结构
http://upload-images.jianshu.io/upload_images/20142973-4c76413ae70a1960.png
3)Transform组件参数
玩家 Transform 组件参数
NameTypePositionRotationScaleColor/TexturePlayerEmpty(0, 0.25, -5)(0, 0, 0)(1, 1, 1)#228439FFBottonCube(0, 0, 0)(0, 0, 0)(2, 0.5, 2)#228439FFTopCube(0, 0.5, 0)(0, 0, 0)(1, 0.5, 1)#228439FFGunCylinder(0, 0, 1.5)(90, 0, 0)(0.2, 1, 0.4)#228439FFFirePointEmpty(0, 1.15, 0)(0, 0, 0)(1, 1, 1)——
补充:Player 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 1,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。
玩家 HP RectTransform 组件参数
NameTypeRectWidth/HeightPosColor/TexturePlayerHPCanvas————————PanelPanel(0, 0, 0, 0)——(-, -, 0)#FFFFFF00HealthBGImage(0, 0.25, -5)(200, 20)(125, -30, 0)#FFFFFFFFHealthImage(0, 0.25, -5)(200, 20)(125, -30, 0)#FF2230FF
补充: 玩家 PlayerHP 的 Canvas 渲染模式是 Screen Space - Overlay,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。
敌人 Transform 组件参数
NameTypePositionRotationScaleColor/TextureEnemyEmpty(0, 0.25, 5)(0, 180, 0)(1, 1, 1)#15D3F9FFBottonCube(0, 0, 0)(0, 0, 0)(2, 0.5, 2)#15D3F9FFTopCube(0, 0.5, 0)(0, 0, 0)(1, 0.5, 1)#15D3F9FFGunCylinder(0, 0, 1.5)(90, 0, 0)(0.2, 1, 0.4)#15D3F9FFFirePointEmpty(0, 1.15, 0)(0, 0, 0)(1, 1, 1)——
补充:Enemy 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 0.5,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。
敌人 HP RectTransform 组件参数
NameTypeWidth/HeightPosColor/TextureHPCanvas(2, 0.2)(0, 0.85, 0)——HealthBGImage(2, 0.2)(0, 0, 0)#FFFFFFFFHealthImage(2, 0.2)(0, 0, 0)#FF2230FF
补充: 敌人 HP 的 Canvas 渲染模式是 World Space,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。
地面和炮弹 Transform 组件参数
NameTypePositionRotationScaleColor/TexturePlanePlane(0, 0, 0)(0, 0, 0)(10, 10, 10)GrassRockyAlbedoBulletSphere(0, 0.5, -5)(0, 0, 0)(0.3, 0.3, 0.3)#228439FF
补充:炮弹作为预设体拖拽到 Assets/Resources/Prefabs 目录下,并且添加了刚体组件。
3 脚本组件


1)CameraController

CameraController.cs
using UnityEngine; public class CameraController : MonoBehaviour {    private Transform player; // 玩家    private Vector3 relaPlayerPos; // 相机在玩家坐标系中的位置    private float targetDistance = 15f; // 相机看向玩家前方的位置   private void Start() {      relaPlayerPos = new Vector3(0, 4, -8);      player = GameObject.Find("Player/Top").transform;    }   private void LateUpdate() {      CompCameraPos();    }    private void CompCameraPos() { // 计算相机坐标      Vector3 target = player.position + player.forward * targetDistance;      transform.position = transformVecter(relaPlayerPos, player.position, player.right, player.up, player.forward);      transform.rotation = Quaternion.LookRotation(target - transform.position);    }    // 求以origin为原点, locX, locY, locZ 为坐标轴的本地坐标系中的向量 vec 在世界坐标系中对应的向量    private Vector3 transformVecter(Vector3 vec, Vector3 origin, Vector3 locX,Vector3 locY,Vector3 locZ) {      return vec.x * locX + vec.y * locY + vec.z * locZ + origin;    }}
说明: CameraController 脚本组件挂在 MainCamera 游戏对象上。

2)PlayerController

PlayerController.cs
using System;using UnityEngine;public class PlayerController : MonoBehaviour {    private Transform firePoint; // 开火点    private GameObject bulletPrefab; // 炮弹预设体    private float tankMoveSpeed = 4f; // 坦克移动速度    private float tankRotateSpeed = 2f; // 坦克转向速度    private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间    private float bulletCoolTime = 0.15f; // 炮弹冷却时间    private void Start() {      firePoint = transform.Find("Top/Gun/FirePoint");      bulletPrefab = (GameObject) Resources.Load("Prefabs/Bullet");    }    private void Update() {      fireWaitTime += Time.deltaTime;      float hor = Input.GetAxis("Horizontal");      float ver = Input.GetAxis("Vertical");      Move(hor, ver);      if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)) {            Fire();      }    }    private void Move(float hor, float ver) { // 坦克移动      if (Math.Abs(hor) > 0.1f || Math.Abs(ver) > 0.1f) {            GetComponent<Rigidbody>().velocity = transform.forward * tankMoveSpeed * ver;            GetComponent<Rigidbody>().angularVelocity = Vector3.up * tankRotateSpeed * hor;      }    }    private void Fire() { // 开炮      if (fireWaitTime > bulletCoolTime) {            BulletInfo bulletInfo = new BulletInfo("PlayerBullet", Color.red, transform.forward, 10f, 15f);            GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity);            bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);            fireWaitTime = 0f;      }    }}
说明: PlayerController 脚本组件挂在 Player 游戏对象上。

3)EnemyController

EnemyController.cs
using UnityEngine;using UnityEngine.UI;public class EnemyController : MonoBehaviour {    private Transform target; // 目标    private Transform top; // 炮头    private Transform firePoint; // 开火点    private Transform hp; // 血条    private GameObject bulletPrefab; // 炮弹预设体    private float rotateSpeed = 0.4f; // 坦克转向速度    private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间    private float bulletCoolTime = 1f; // 炮弹冷却时间    private void Start () {      target = GameObject.Find("Player/Top").transform;      top = transform.Find("Top");      firePoint = transform.Find("Top/Gun/FirePoint");      hp = transform.Find("HP");      bulletPrefab = (GameObject) Resources.Load("Prefabs/Bullet");    }    private void Update () {      fireWaitTime += Time.deltaTime;      LookAtTarget();      float angle = Vector3.Angle(target.position - top.position, top.forward);      if (LookAtTarget()) {            Fire();      }      HPLookAtCamera();    }    private bool LookAtTarget() {      Vector3 dir = target.position - top.position;      float angle = Vector3.Angle(dir, top.forward);      if (angle > 5) {            int axis = Vector3.Dot(Vector3.Cross(dir, top.forward), Vector3.up) > 0 ? -1 : 1;            GetComponent<Rigidbody>().angularVelocity = axis * Vector3.up * rotateSpeed;            return false;      }      GetComponent<Rigidbody>().angularVelocity = Vector3.zero;      return true;    }    private void HPLookAtCamera() {      Vector3 cameraPos = Camera.main.transform.position;      Vector3 target = new Vector3(cameraPos.x, hp.position.y, cameraPos.z);      hp.LookAt(target);    }    private void Fire() {      if (fireWaitTime > bulletCoolTime) {            BulletInfo bulletInfo = new BulletInfo("EnemyBullet", Color.yellow, top.forward, 5f, 10f);            GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity); // 通过预设体创建炮弹            bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);            fireWaitTime = 0;      }    }}
说明: EnemyController 脚本组件挂在 Enemy 游戏对象上。

4)BulletController

BulletController.cs
using UnityEngine;using UnityEngine.UI;public class BulletController : MonoBehaviour {    private BulletInfo bulletInfo; // 炮弹信息    private volatile bool isDying = false;    private void Start () {      gameObject.name = bulletInfo.name;      GetComponent<MeshRenderer>().material.color = bulletInfo.color;      float lifeTime = bulletInfo.fireRange / bulletInfo.speed; // 存活时间      Destroy(gameObject, lifeTime);    }    private void Update () {      transform.GetComponent<Rigidbody>().velocity = bulletInfo.flyDir * bulletInfo.speed;    }    private void OnCollisionEnter(Collision other) {      if (isDying) {            return;      }      if (IsHitEnemy(gameObject.name, other.gameObject.name)) {            other.transform.Find("HP/Health").GetComponent<Image>().fillAmount -= 0.1f;            isDying = true;            Destroy(gameObject, 0.1f);      } else if (IsHitPlayer(gameObject.name, other.gameObject.name)) {            GameObject.Find("PlayerHP/Panel/Health").GetComponent<Image>().fillAmount -= 0.1f;            isDying = true;            Destroy(gameObject, 0.1f);      }    }    public void SetBulletInfo(BulletInfo bulletInfo) {      this.bulletInfo = bulletInfo;    }    private bool IsHitEnemy(string name, string otherName) { // 射击到敌军      return name.Equals("PlayerBullet") && otherName.Equals("Enemy");    }    private bool IsHitPlayer(string name, string otherName) { // 射击到玩家      return name.Equals("EnemyBullet") && otherName.Equals("Player");    }}
说明: BulletController 脚本组件挂在 Bullet 游戏对象上(代码里动态添加)。

5)BulletInfo

BulletInfo.cs
using UnityEngine;public class BulletInfo {    public string name; // 炮弹名    public Color color; // 炮弹颜色    public Vector3 flyDir; // 炮弹飞出方向    public float speed; // 炮弹飞行速度    public float fireRange; // 炮弹射程    public BulletInfo(string name, Color color, Vector3 flyDir, float speed, float fireRange) {      this.name = name;      this.color = color;      this.flyDir = flyDir;      this.speed = speed;      this.fireRange = fireRange;    }}4 运行效果

http://upload-images.jianshu.io/upload_images/20142973-618e88182cdf891d.png
声明:本文转自【Unity3D】血条(HP)
页: [1]
查看完整版本: 【Unity3D】血条(HP)