今天,又是充满希望的一天!

UnitySir - 00008-1_解决生成怪物出现的严重BUG

Posted on By Zj

需要实现功能

修复上一章的问题(怪物不会移动)

问题出现在 使用的 SimpleMsgMechanism 上,该问题主要在于只能使用一次


知识点


完整代码

弃用之前所写的消息机制代码

SimplePlayerAni
using UnityEngine;

public class SimplePlayerAni : MonoBehaviour
{
    private Animator _animator;

    void Awake()
    {
        _animator = GameObject.Find("MainPlayer/Player").GetComponent<Animator>();
    }

    void Update()
    {
        SetMoveAni();
    }

    /// <summary>
    /// 设置移动动画
    /// </summary>
    void SetMoveAni()
    {
        SimpleMsgMechanism.ReceiveMsg("PlayerMove", objects =>
        {
            float move = (float) objects[0];
            bool isRun = (bool) objects[1];
            _animator.SetFloat("ForwardMove",
                move * (isRun
                    ? (Mathf.Lerp(_animator.GetFloat("ForwardMove"), 2f, 0.1f))
                    : Mathf.Lerp(_animator.GetFloat("ForwardMove"), 1f, 0.1f)));
        });
    }
}
SimplePlayerCtrl
using UnityEngine;

public class SimplePlayerCtrl : MonoBehaviour
{
    private Rigidbody _rigidbody;

    [Header("角色方向控制变量")] [SerializeField] private Vector2 moveDir = Vector2.zero; //移动方向
    [SerializeField] private Vector2 moveDirNormalized = Vector2.zero;
    [SerializeField] private float targetDir = 0f; //角色目标方向
    [SerializeField] private float moveSpeed = 0f;
    [SerializeField] private float walkSpeed = 2f; //行走速度
    [SerializeField] private float runSpeed = 6f;
    [SerializeField] private bool isRunning = false;
    private float _smoothTime = 0.1f; //过渡时间
    private float _currentVelocity = 0f;
    private float _vMove = 0f; //垂直输入
    private float _hMove = 0f; //水平输入

    private float dirMag = 0f; //方向大小
    private Vector2 toRound = Vector2.zero; //方形转圆形

    [Header("键盘输入控制")] [SerializeField] private KeyCode keyA = KeyCode.LeftShift;
    [SerializeField] private KeyCode keyB = KeyCode.None;
    [SerializeField] private KeyCode keyC = KeyCode.None;
    [SerializeField] private KeyCode keyD = KeyCode.None;


    private void Awake()
    {
        _rigidbody = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        _hMove = Input.GetAxis("Horizontal");
        _vMove = Input.GetAxis("Vertical");
        SetMoveDir();
        SetRun();
        toRound = SimpleTools.Vec2Rect2Round(new Vector2(_hMove, _vMove));
        dirMag = Mathf.Sqrt(toRound.x * toRound.x + toRound.y * toRound.y);
        SimpleMsgMechanism.SendMsg("PlayerMove", dirMag, isRunning);
    }

    private void FixedUpdate()
    {
        PlayerMove();
    }

    /// <summary>
    /// 设置朝向
    /// </summary>
    void SetMoveDir()
    {
        moveDir = new Vector2(_hMove, _vMove); //获取到输入的值
        moveDirNormalized = moveDir.normalized; //将值归一化(获取方向)
        targetDir = Mathf.Atan2(moveDirNormalized.x, moveDirNormalized.y) * Mathf.Rad2Deg; //计算朝向目标的角度
        if (moveDirNormalized != Vector2.zero) //当输入的值不为0时,转向目标角度
        {
            _rigidbody.transform.eulerAngles =
                Vector3.up * Mathf.SmoothDampAngle(_rigidbody.transform.eulerAngles.y, targetDir,
                    ref _currentVelocity, _smoothTime);
        }
    }

    /// <summary>
    /// 角色移动
    /// </summary>
    void PlayerMove()
    {
        moveSpeed = isRunning ? runSpeed : walkSpeed;
        Vector3 output = SimpleTools.Vec3Rect2Round(new Vector3(_hMove, 0, _vMove));
        _rigidbody.velocity = new Vector3(moveSpeed * output.x, _rigidbody.velocity.y, moveSpeed * output.z);
    }

    /// <summary>
    /// 设置角色奔跑
    /// </summary>
    void SetRun()
    {
        isRunning = Input.GetKey(keyA);
    }
}

MonsterAni

using System;
using UnityEngine;

public class MonsterAni : MonoBehaviour
{
    [SerializeField] private Animator _animator;

    [SerializeField] private MonsterCtrl _monsterCtrl;

    [Header("是否移动")] [SerializeField] private bool isMove;

    private void Start()
    {
        _animator = transform.GetChild(0).GetComponent<Animator>();
        _monsterCtrl = GetComponent<MonsterCtrl>();

    }

    private void Update()
    {
        isMove = _monsterCtrl.isMove;
        _animator.SetInteger("move", isMove ? 1 : 0);
    }
}

MonsterCtrl

using UnityEngine;

public class MonsterCtrl : MonoBehaviour
{
    [SerializeField] private CharacterController monsterController;

    [Header("是否进入怪物检测区")] [SerializeField] private bool isInCheck;

    [Header("主角与怪物的距离")] [SerializeField] private float distance;

    [Header("怪物的出生点位置")] [SerializeField] private Vector3 monsterBronPointPos = Vector3.zero;

    [Header("怪物的位置")] [SerializeField] private Vector3 monsterPos = Vector3.zero;

    [Header("怪物是否移动")] [SerializeField] public bool isMove;
    [Header("怪物是否开始攻击")] [SerializeField] private bool isAttack;
    [Header("怪物是否死亡")] [SerializeField] private bool isDeath;

    [Header("主角对象")] [SerializeField] private GameObject playerObj;
    [Header("主角当前的位置")] [SerializeField] private Vector3 playerPos = Vector3.zero;

    //怪物控制必须要有以下几个条件 :
    //怪物需要实时计算与主角的距离:
    //1.主角进入了怪物的检测区 (范围 = (25))
    //此时怪物会向主角移动,播放移动动画
    //2.怪物的攻击区 (范围 = (1.8))
    //当角色进入怪物的攻击区,怪物才会攻击,此时播放攻击动画
    //3.怪兽死亡
    //当怪物血量低于0时,播放死亡动画
    //4.主角退出了怪物的检测区 (范围 = (25))
    //此时怪物会回到生成区
    private void Awake()
    {
        monsterController = GetComponent<CharacterController>();
        monsterPos = transform.position;
    }

    private void Start()
    {
        monsterBronPointPos = transform.position; //获取出生点
        playerObj = GameObject.FindWithTag("Player");
    }

    private void Update()
    {
        playerPos = playerObj.transform.position; //主角实时位置
        monsterPos = transform.position; //怪物的实时位置

        distance = Vector3.Distance(playerPos, monsterPos);
        isInCheck = distance < 25f;

        Debug.DrawLine(playerPos, monsterPos);

        if (isInCheck)
        {
            //如果角色进入检测区,怪物实时看着主角并向主角移动
            //实时看着主角
            transform.LookAt(playerPos);
            //获取主角与怪物之间的距离
            var distance = Vector3.Distance(playerPos, monsterPos);
            //获取主角和怪物之间的偏移
            var offset = (playerPos - monsterPos);
            //如果怪物没有接触地面
            if (!monsterController.isGrounded)
            {
                //应用重力
                offset.y -= 20f * Time.deltaTime;
            }

            if (distance > 1.8f)
            {
                //当主角和怪物之间的的距离大于 0.1 时 ,怪物就向主角移动
                //获取到目标方向
                offset = offset.normalized * 1.2f;
                //移动到目标点
                monsterController.Move(offset * Time.deltaTime);
                isMove = true; //移动
            }
            else
            {
                //当检车到主角时,如果和主角的距离小于等于2,则开始攻击
                isMove = false;
            }
        }
        else
        {
            //如果主角退出检测区,怪物就回到出生点
            isAttack = false;
            //怪物要实时看着出生点
            transform.LookAt(monsterBronPointPos);
            //获取出生点与怪物之间的距离
            var distance = Vector3.Distance(monsterBronPointPos, monsterPos);
            //获取出生点和怪物之间的偏移
            var offset = (monsterBronPointPos - monsterPos);

            if (!monsterController.isGrounded)
            {
                //应用重力
                offset.y -= 20f * Time.deltaTime;
            }

            if (distance > 1f)
            {
                //获取到目标方向
                offset = offset.normalized * 1.5f;
                //移动到目标点
                monsterController.Move(offset * Time.deltaTime);
                isMove = true; //移动
            }
            else
            {
                isMove = false;
            }
        }
    }
}

参考链接


UnitySir (bilibili)

B站ID:UnitySir

如果内容对你有所帮助: