美文网首页Unity3D
【Unity3D】血条(HP)

【Unity3D】血条(HP)

作者: LittleFatSheep | 来源:发表于2023-02-23 09:19 被阅读0次

    1 需求实现

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

    1)需求实现

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

    2)涉及技术栈

    本文代码资源见 → Unity3D 血条效果

    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。

    1. 玩家 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。

    1. 敌人 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。

    1. 敌人 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。

    1. 地面和炮弹 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

    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 运行效果

    声明:本文转自【Unity3D】血条(HP)

    相关文章

      网友评论

        本文标题:【Unity3D】血条(HP)

        本文链接:https://www.haomeiwen.com/subject/vvpohdtx.html