Unity学习笔记:fps游戏敌人模块(1)

  功能:生成敌人按指定路线移动,受伤减血死亡,到达终点攻击玩家,播放各种行为动画。

   需求分析:

  敌人马达 EnemyMotor,移动 旋转 寻路功能。

  敌人状态EnemyStatusInfo,血量 受伤 死亡。

  敌人动画EnemyAnimation,整合动画"名称" 播放功能。

  敌人AI EnemyAI 用来判断状态 执行敌人行为。

  创建根线路,添加多条配有路点的路线坐标。

  路线WayLine,包含路点坐标Vector3[] 是否可用bool。

  敌人生成器EnemySpawn,放在跟线路中 生成敌人。

  生成器启动 获取所有路线的路点。

  生成敌人时 随机选择可使用路线。

  SpawnSystem,管理生成器 提供激活下一个生成器。

  SpawnTigger,检测接触玩家。

敌人马达EnemyMotor↓

/// <summary>
/// 敌人马达,负责敌人运动功能
/// </summary>
public class EnemyMotor : MonoBehaviour
{
    //生成敌人时传递路线引用
    public WayLine wayline;

    /// <summary>
    /// 移动速度
    /// </summary>
    public float moveSpeed = 5;

    /// <summary>
    /// 向前移动
    /// </summary>
    public void MovementForward()
    {
        transform.Translate(0, 0, moveSpeed * Time.deltaTime);
    }

    /// <summary>
    /// 朝向目标点的旋转
    /// </summary>
    /// <param name="targetPos">目标位置</param> 
    public void LookRotation(Vector3 targetPos)
    {
        //暂时……  一帧旋转至目标方位
        transform.LookAt(targetPos);
    }

    private int currentIndex;
    /// <summary>
    /// 寻路
    /// </summary>
    public bool Pathfinding()
    { 
        //如果索引超过最大值  则 返回false ,表示寻路结束
        if (currentIndex >= wayline.Points.Length) return false;

        LookRotation(wayline.Points[currentIndex]);
        MovementForward();

        if (Vector3.Distance(transform.position, wayline.Points[currentIndex]) <= 0.1)
            currentIndex++;

        return true;//返回true 表示 可以继续寻路
    }
}

 敌人状态EnemyStatusInfo↓

/// <summary>
/// 敌人状态信息类
/// </summary>
public class EnemyStatusInfo : MonoBehaviour
{
    /// <summary>
    /// 当前血量
    /// </summary>
    public float currentHP;
    /// <summary>
    /// 最大血量
    /// </summary>
    public float maxHP;

    public void Damage(float amount)
    {
        //如果敌人已经死亡 则退出(防止虐尸)
        if (currentHP <= 0) return;

        currentHP -= amount;

        if (currentHP <= 0)
            Death();
    }

    /// <summary>
    /// 死亡延迟时间
    /// </summary>
    public float deathDelay =10;

    //敌人生成器引用  敌人创建时由生成器传递
    public EnemySpawn spawn;
    /// <summary>
    /// 死亡
    /// </summary>
    public void Death()
    {
        //销毁当前游戏物体
        Destroy(gameObject, deathDelay);

        //播放动画
        var anim = GetComponent<EnemyAnimation>();
        anim.Play(anim.deathName);

        //修改状态
        GetComponent<EnemyAI>().state = EnemyAI.State.Death;

        //修改路线状态
        GetComponent<EnemyMotor>().wayline.IsUsable = true;

        //需要再生成一个敌人
        spawn.GenerateEnemy(); 
    }
}

敌人动画EnemyAnimation↓

/// <summary>
/// 敌人动画类
/// </summary>
public class EnemyAnimation : MonoBehaviour
{
    /// <summary>
    /// 跑步动画
    /// </summary>
    public string runName = "run";

    /// <summary>
    /// 攻击动画
    /// </summary>
    public string atkName = "shooting";

    /// <summary>
    /// 死亡动画
    /// </summary>
    public string deathName = "death";

    /// <summary>
    /// 闲置动画
    /// </summary>
    public string idleName = "idleWgun";

    private Animation anim;
//public AnimationAction action;
    private void Awake()
    {
        //查找动画组件
        anim = GetComponentInChildren<Animation>();
     //action=new AnimationAction();
    }

    /// <summary>
    /// 播放指定名称的人物动画
    /// </summary>
    /// <param name="name">动画名称</param>
    public void Play(string name)
    {
        anim.CrossFade(name);
    }

    /// <summary>
    /// 指定动画片段是否正在播放
    /// </summary>
    /// <param name="name">动画片段名称</param>
    /// <returns></returns>
    public bool IsPlaying(string name)
    {
        return anim.IsPlaying(name);
    }
}

播放行为类AnimationAction↓

public class AnimationAction
{
//附加在敌人模型上的动画组件引用
   private Animation anim;
//创建动画行为类
   public AnimationAction(Animation anim)
   {
   this.anim=anim;
   }
//播放动画
   public void Play(string animName)
   {
     anim.CrossFade(animName);
   }
}

敌人AI EnemyAI↓

/// <summary>
/// 人工智能
/// </summary>
[RequireComponent(typeof(EnemyAnimation),typeof(EnemyMotor),typeof(EnemyStatusInfo))]
public class EnemyAI : MonoBehaviour
{
    /// <summary>
    /// 敌人状态
    /// </summary>
    public enum State
    { 
        /// <summary>
        /// 攻击状态
        /// </summary>
        Attack,
        /// <summary>
        /// 死亡状态
        /// </summary>
        Death,
        /// <summary>
        /// 寻路状态
        /// </summary>
        Pathfinding
    }

    private EnemyAnimation animAction;
    private EnemyMotor motor;
    private Gun gun;
    private GunAnimation gunAnim;
    private void Start()
    {
        animAction = GetComponent<EnemyAnimation>();
        motor = GetComponent<EnemyMotor>();
        gun = GetComponent<Gun>();
        gunAnim = GetComponent<GunAnimation>();
    }

    /// <summary>
    /// 敌人状态
    /// </summary>
    public State state = State.Pathfinding;

    private void Update()
    {
        switch (state)
        { 
            case State.Pathfinding:
                Pathfinding(); 
                break;
            case State.Attack:
                Attack();  
                break;
        }
    }

    private float atkTime;
    /// <summary>
    /// 攻击间隔
    /// </summary>
    public float atkInterval = 3;

    /// <summary>
    /// 攻击延迟时间
    /// </summary>
    public float delay = 0.3f;

    private void Attack()
    {
        motor.LookRotation(PlayerStatusInfo.Instance.transform.position);

        //限制攻击频率
        //播放攻动画
        if (atkTime <= Time.time &&  !gunAnim.IsPlaying(gunAnim.updateAnimName))
        { 
            animAction.Play(animAction.atkName);
            //希望动画播放到某一时刻再执行攻击行为
            //建议使用动画事件
            Invoke("Shoot", delay); 

            atkTime = Time.time + atkInterval;
        }

        if (!animAction.IsPlaying(animAction.atkName) && !gunAnim.IsPlaying(gunAnim.updateAnimName))
        {
            //如果攻击动画没有播放  再  播放闲置动画
            animAction.Play(animAction.idleName);
        }
    }

    private void Pathfinding()
    {
        //播放跑步动画
        animAction.Play(animAction.runName);
        //调用马达寻路功能  如果到达终点,修改状态为 state 攻击
        if (!motor.Pathfinding()) state = State.Attack; 
    }

    private void Shoot()
    {
        if (!gun.Firing(PlayerStatusInfo.Instance.headTF.position - gun.firePoint.position))
        {
            //如果发射子弹失败
            gun.UpdateAmmo();
        }
    }
}

线路WayLine↓

/// <summary>
/// 路线类
/// </summary>
public class WayLine 
{
    /// <summary>
    /// 所有路点坐标
    /// </summary>
    public Vector3[]  Points { get; set; }

    /// <summary>
    /// 是否可用
    /// </summary>
    public bool IsUsable { get; set; }

    //默认 IsUsable 为 false  
    //       Points  为 null
    public WayLine(int pointCount)
    {
        this.Points = new Vector3[pointCount];
        this.IsUsable = true;
    }
}
敌人生成器EnemySpawn↓
/// <summary>
/// 敌人生成器
/// </summary>
public class EnemySpawn : MonoBehaviour
{
    /// <summary>
    /// 开始时需要创建的敌人数量
    /// </summary>
    public int startCount = 2;

    private void Start()
    {
        CalculateWayLines();

        for (int i = 0; i < startCount; i++)
        {
            GenerateEnemy();
        }
    }

    //计算路线
    private WayLine[] lines;
    private void CalculateWayLines()
    { 
        //WayLine 路线  与 子物体   对应
        //lines[0].Points 该路线所有路点坐标   与  子物体的子物体.Position对应
        lines = new WayLine[transform.childCount];
        //创建路线
        for (int i = 0; i < lines.Length; i++)//0    1     2
        { 
            Transform waylineTF =  transform.GetChild(i);
            lines[i] = new WayLine(waylineTF.childCount);
            for (int pointIndex = 0; pointIndex < waylineTF.childCount; pointIndex++)
            {
                //获取每个路点坐标
                lines[i].Points[pointIndex] = waylineTF.GetChild(pointIndex).position;
            } 
        }
    }

    //当前产生的敌人数量
    private int spawnedCount;
    /// <summary>
    /// 可以产生敌人的最大值
    /// </summary>
    public int maxCount;

    /// <summary>
    /// 产生敌人的最大延迟时间
    /// </summary>
    public int maxDelay = 10;

    /// <summary>
    /// 敌人类型
    /// </summary>
    public GameObject[] enemyTypes;

    /// <summary>
    /// 生成一个敌人(一开始根据startCount生成敌人,敌人死亡时生成敌人)
    /// </summary>
    public void GenerateEnemy()
    {
        if (spawnedCount < maxCount)
        { 
            spawnedCount++;
            // 随机延迟时间 
            float delay = Random.Range(0, maxDelay);
            Invoke("CreateEnemy", delay); 
        }
        else
        {
            print("over");
           //生成任务结束……
            //如果所有敌人死亡,再启用下一个生成器
            if (IsEnemyAllDeath())
            {
                print("death");
                GetComponentInParent<SpawnSystem>().ActivateNextSpawn();
            }
        } 
    }

    private bool IsEnemyAllDeath()
    {
        //便利所有路线
        foreach (var item in lines)
        {
            if (!item.IsUsable)
                return false;
        }
        return true;
    }

    private void CreateEnemy()
    {
        //查找所有可用路线 
        var usableWaylines = SelectUsableWayLines();
        //随机选择一条
        WayLine wayLine = usableWaylines[Random.Range(0, usableWaylines.Length)];

        //创建一个敌人
        //GameObject enemyGO = Instantiate(敌人预制件, 第一个路点, Quaternion.identity) as GameObject;
        int enemyTypeIndex = Random.Range(0, enemyTypes.Length);
        GameObject enemyGO = Instantiate(enemyTypes[enemyTypeIndex], wayLine.Points[0], Quaternion.identity) as GameObject;
        //传递信息
        enemyGO.GetComponent<EnemyMotor>().wayline = wayLine;
        wayLine.IsUsable = false;//该路线不可用
        //传递当前生成器对象引用,便于敌人死亡时调用当前对象的生成敌人方法
        //[建议使用委托代替]
        enemyGO.GetComponent<EnemyStatusInfo>().spawn = this; 
    }

    private WayLine[] SelectUsableWayLines()
    {
        List<WayLine> result = new List<WayLine>(lines.Length);
        foreach (var item in lines)
        {
            if (item.IsUsable) result.Add(item);
        }
        return result.ToArray();
    }
}
生成器系统SpawnSystem↓
/// <summary>
/// 生成器系统
/// </summary>
public class SpawnSystem : MonoBehaviour
{
    private GameObject[] spawnList;
    private void Start()
    {
        spawnList = new GameObject[transform.childCount];
        for (int i = 0; i < spawnList.Length; i++)
        {
            spawnList[i] = transform.GetChild(i).gameObject;
        }
        ActivateNextSpawn();
    }

    public int currentIndex=-1;
    public void ActivateNextSpawn()
    {
        if (currentIndex != -1)
            spawnList[currentIndex].SetActive(false);

        if (currentIndex < spawnList.Length - 1)
        {
            spawnList[++currentIndex].SetActive(true);
        }
        else
        {
            //游戏结束
            Debug.Log("游戏结束喽");
        }
    }
}
SpawnTrigger
/// <summary>
/// 
/// </summary>
public class SpawnTrigger : MonoBehaviour
{
    public GameObject targetSpawn;

    private void OnTriggerEnter(Collider other)
    {
        //if (other.tag == "Player")
        if(other.CompareTag("Player"))
        {
            targetSpawn.SetActive(true);
            gameObject.SetActive(false);
        }
    }
 
}

 

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值