返回首页DA系统C#IDE文件同步服务屏保 今天是: 2026-05-05    "立夏"  夏季的第一个节气,表示盛夏时节的正式开始

搜索
热搜: linux 技术
Hi~登录注册
查看: 3130|回复: 0

[原创] 【原创】英雄无敌项目更新敌人动画播放模块,AI模块

[复制链接]
发表于 2021-5-6 00:32:11 | 显示全部楼层 |阅读模式

少侠不来段修仙之旅吗~

您需要 登录 才可以下载或查看,没有帐号?注册成为修仙之旅的少年~

x
本帖最后由 da11 于 2021-5-6 00:34 编辑

//敌人动画名称类
//EnemyDH.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 敌人动画类,定义需要播放的动画名称
/// </summary>
public class EnemyDH : MonoBehaviour
{
    /// <summary>
    /// 定义敌人跳的动画名称
    /// </summary>
    public string TiaoDH = "tiao2";

    /// <summary>
    /// 定义敌人攻击动画名称
    /// </summary>
    public string AttackDH = "EnemyAttack";

    /// <summary>
    /// 定义敌人的死亡动画名称
    /// </summary>
    public string DeathDH = "EnemyDeath";

    public DHPlay DHplayVar;

    //放Awake周期的解释是,因为游戏开始后有很多都是Start周期执行的,Awake比Start早,所以查找动画组件这类推荐放在Awake周期执行查找
    private void Awake()
    {
        //查找下级的第一个子物体,并获得动画组件形参,并初始化动画播放类
        //DHplayVar = new DHPlay(this.transform.GetChild(0).GetComponent<Animation>());
        DHplayVar = new DHPlay(GameObject.Find("boboNpc1").GetComponent<Animation>());
    }



}


 楼主| 发表于 2021-5-6 00:32:45 | 显示全部楼层
本帖最后由 da11 于 2021-5-6 00:34 编辑

//动画播放工具类
//DHPlay.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 动画播放类,提供有关动画的行为
/// </summary>
public class DHPlay
{
    /// <summary>
    /// 声明 附加在模型上的动画组件引用 字段
    /// </summary>
    public Animation AnimationComponent;

    /// <summary>
    /// 构造函数,创建对象的时候需要提供动画组件引用实参
    /// </summary>
    /// <param name="AnimationComponent">动画组件引用形参</param>
    public DHPlay(Animation AnimationComponent)
    {
        this.AnimationComponent = AnimationComponent;
    }


    /// <summary>
    /// 播放动画
    /// </summary>
    /// <param name="DHname">需要播放动画片段的名称</param>
    public void DHPlayFF(string DHname)
    {
        AnimationComponent.CrossFade(DHname);
    }

    public bool DHIsPlayering()
    {
        return AnimationComponent.isPlaying;
    }



}


回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-5-6 00:34:00 | 显示全部楼层
//敌人AI类--重点模块
//EnemyControl.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 敌人AI类
/// </summary>
public class EnemyControl : MonoBehaviour
{
    //声明EnemyDH脚本组件字段
    private EnemyDH EnemyDHVar;

    //声明EnemyMove脚本组件字段
    private EnemyMove EnemyMoveVar;

    //声明死亡计数字段,测试
    private int DeathCount;

    //声明攻击计数字段,测试
    private int AttackCount;

    //声明一个状态枚举
    private enum Status
    {
        /// <summary>
        /// 攻击状态
        /// </summary>
        Attack,
        /// <summary>
        /// 寻路状态
        /// </summary>
        WayFinding,
        /// <summary>
        /// 死亡状态
        /// </summary>
        Death
    }

    //声明状态枚举的引用,并默认一开始是寻路状态
    private Status status = Status.WayFinding;

    private void Start()
    {
        //找到本物体的EnemyDH脚本组件引用,并初始化
        EnemyDHVar = this.GetComponent<EnemyDH>();

        //找到本物体的EnemyMove脚本组件引用,并初始化
        EnemyMoveVar = this.GetComponent<EnemyMove>();

        //初始化攻击计数默认值为0,测试
        AttackCount = 0;

        //初始化死亡计数默认值为0,测试
        DeathCount = 0;
    }

    private void Update()
    {
        //根据枚举状态来使用switch判断各种状态需要执行什么功能
        switch (status)
        {
            case Status.Attack:                                          //攻击状态
                EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.AttackDH);      //播放攻击动画
                AttackCount++;                                           //攻击次数++
                if (AttackCount>=200)                                    //攻击次数大于200的时候更改状态为死亡状态,测试语句
                {
                    status = Status.Death;
                }
                break;
            case Status.WayFinding:                                      //寻路状态
                if (EnemyMoveVar.WayFinding()==true)                     //判断寻路方法的值是否达到所有路线节点的最大值(true),是则更改状态为攻击状态
                {
                    //寻路结束后更改状态为攻击状态
                    status = Status.Attack;
                }
                else
                {
                    EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.TiaoDH);    //false为继续播放跳的动画
                }
                break;
            case Status.Death:
                //死亡动画只执行一次
                for (; DeathCount < 2; DeathCount++)
                {
                    EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.DeathDH);
                }
                break;
        }
    }


}


回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-5-6 00:38:55 | 显示全部楼层
以下是整合敌人移动、寻路目标点、敌人信息类,一共7个类的整合演示示例(单个敌人从寻路到攻击到死亡过程)站内导航:https://www.swztdza.cn/dalt/forum.php?mod=viewthread&tid=292&extra=page%3D1


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册成为修仙之旅的少年~

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-5-18 17:07:08 | 显示全部楼层
//2021.05.18更新,根据敌人生成器模块代码补充,增加敌人AI类的死亡状态和闲置状态及其方法
//EnemyControl.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 敌人AI类
/// </summary>

//自动将脚本随此脚本挂载而挂载
[RequireComponent(typeof(EnemyMove))]
[RequireComponent(typeof(EnemyInfo))]
[RequireComponent(typeof(EnemyDH))]


public class EnemyControl : MonoBehaviour
{
    //声明EnemyDH脚本组件字段
    public EnemyDH EnemyDHVar;

    //声明EnemyMove脚本组件字段
    private EnemyMove EnemyMoveVar;

    //声明死亡计数字段,测试
    private int DeathCount;

    //声明攻击计数字段,测试
    private int AttackCount;

    //声明一个攻击间隔时间
    private float AttackTime;

    //声明一个状态枚举
    private enum Status
    {
        /// <summary>
        /// 攻击状态
        /// </summary>
        Attack,
        /// <summary>
        /// 寻路状态
        /// </summary>
        WayFinding,
        /// <summary>
        /// 死亡状态
        /// </summary>
        Death,
        /// <summary>
        /// 闲置状态
        /// </summary>
        WaitTime
    }

    //声明状态枚举的引用,并默认一开始是寻路状态
    private Status status = Status.WayFinding;

    private void Start()
    {
        //找到本物体的EnemyDH脚本组件引用,并初始化
        EnemyDHVar = this.GetComponent<EnemyDH>();

        //找到本物体的EnemyMove脚本组件引用,并初始化
        EnemyMoveVar = this.GetComponent<EnemyMove>();

        //初始化攻击计数默认值为0,测试
        AttackCount = 0;

        //初始化攻击间隔时间默认值为0,测试
        AttackTime = 0;

        //初始化死亡计数默认值为0,测试
        DeathCount = 0;
    }

    private void Update()
    {
        //根据枚举状态来使用switch判断各种状态需要执行什么功能
        switch (status)
        {
            case Status.Attack:                                          //攻击状态
                EnemyAttack();
                break;
            case Status.WayFinding:                                      //寻路状态
                WayFinding();
                break;
            case Status.Death:                                           //死亡状态
                EnemyDeath();
                break;
            case Status.WaitTime:                                        //闲置状态
                WaitTimeFF();
                break;
        }
    }

    //敌人死亡方法
    private void EnemyDeath()
    {
        GetComponent<EnemyInfo>().IsDead();                              //调用EnemyInfo类中,敌人死亡的方法


        status = Status.WaitTime;                                        //更改状态为闲置状态,测试语句,为了只执行一次,加上注释可以触发无限生成(利用update特性调用多次)

        /*
        //死亡动画只执行一次
        for (; DeathCount < 2; DeathCount++)
        {
            EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.DeathDH);
        }
        */
    }



    //敌人寻路方法
    private void WayFinding()
    {
        if (EnemyMoveVar.WayFinding() == true)                     //判断寻路方法的值是否达到所有路线节点的最大值(true),是则更改状态为攻击状态
        {
            //寻路结束后更改状态为攻击状态
            status = Status.Attack;
        }
        else
        {
            EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.TiaoDH);    //false为继续播放跳的动画
        }
    }

    //敌人攻击方法
    private void EnemyAttack()
    {
        //加判断,如果攻击动画没有播放,则播放跳跃动画
        if (!EnemyDHVar.DHplayVar.DHIsPlayering(EnemyDHVar.AttackDH))
            //取反操作,等同于if (EnemyDHVar.DHplayVar.DHIsPlayering(EnemyDHVar.AttackDH)==false)
            EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.TiaoDH);

        if (AttackTime < Time.time)                                    //攻击时间间隔判断,一开始肯定是true
        {
            EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.AttackDH);      //播放攻击动画
            AttackCount++;                                           //攻击次数++
            AttackTime = Time.time + 3;                              //攻击间隔时间,这里设定的是3

            if (AttackCount >= 3)                                    //攻击次数大于等于3的时候更改状态为死亡状态,测试语句
            {
                status = Status.Death;
            }
        }
    }

    //闲置状态方法,测试方法,为了让死亡状态只执行一次
    private void WaitTimeFF()
    {
        return;
    }

}



回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-4-25 16:38:58 | 显示全部楼层
本帖最后由 da11 于 2022-4-25 16:46 编辑

//动画播放类更新
//有些时候,动画片段的帧数特别短,用Animation.CrossFade就会出现播了一段第二段没播的异常现象,故使用Animation.PlayQueued解决!!

    /// <summary>
    /// 播放队列,用于动画片段帧数较少的时候
    /// 官方解释:在前一个动画播放完成之后直接播放下一个动画。
    /// 比如你可能会一个接一个播放一个特殊序列的动画。
    /// 动画状态在播放之前会复制自己,因此你可以在相同的动画之间进行淡入淡出。
    /// 这个可以用于覆盖两个相同的动画。比如你可能有一个剑挥舞的动画,玩家连续快速削两次。你可以倒播这个动画然后从头再播放但是你会看到动画之间有跳越现象。
    /// </summary>
    /// <param name="DHname">需要播放动画片段的名称</param>
    public void DHPlayQueuedFF(string DHname)
    {
        AnimationComponent.PlayQueued(DHname);
    }

官方文档详解:
下面几个模式可用:
如果queue是QueueMode.CompleteOthers,当所有其他动画停止播放,这个动画才会开始。
如果queue是QueueMode.PlayNow,这个动画将在一个重复的动画状态下立即开始播放。
After the animation has finished playing it will automatically clean itself up. Using the duplicated animation state after it has finished will result in an exception.
当动画完成播放之后,它会自动清理。在一个动画状态结束之后使用它,将会导致一个异常。
  1. using UnityEngine;
  2. using System.Collections;

  3. public class example : MonoBehaviour {
  4.         void Update() {
  5.                 if (Input.GetButtonDown("Fire1"))
  6.                         animation.PlayQueued("shoot", QueueMode.PlayNow);

  7.         }
  8. }
复制代码






回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-4-30 23:41:04 | 显示全部楼层
//敌人AI类更新至完整版--2022.4.30
//EnemyControl.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 敌人AI类
/// </summary>

//自动将脚本随此脚本挂载而挂载
[RequireComponent(typeof(EnemyMove))]
[RequireComponent(typeof(EnemyInfo))]
[RequireComponent(typeof(EnemyDH))]


public class EnemyControl : MonoBehaviour
{
    //声明EnemyDH脚本组件字段
    public EnemyDH EnemyDHVar;

    //声明EnemyMove脚本组件字段
    private EnemyMove EnemyMoveVar;

    //声明死亡计数字段,测试
    private int DeathCount;

    //声明攻击计数字段,测试
    private int AttackCount;

    //声明一个攻击间隔时间
    private float AttackTime;

    //声明一个游戏主角GameObject类型
    public GameObject PlayerObj;

    //声明枪类的变量--英雄无敌项目武器模块--20220425
    private Gun GunVar;

    //private PlayerStauts playerstatusInfo;

    //声明一个状态枚举
    private enum Status
    {
        /// <summary>
        /// 攻击状态
        /// </summary>
        Attack,
        /// <summary>
        /// 寻路状态
        /// </summary>
        WayFinding,
        /// <summary>
        /// 死亡状态
        /// </summary>
        Death,
        /// <summary>
        /// 闲置状态
        /// </summary>
        WaitTime
    }

    //声明状态枚举的引用,并默认一开始是寻路状态
    private Status status = Status.WayFinding;

    private void Start()
    {
        //找到本物体的EnemyDH脚本组件引用,并初始化
        EnemyDHVar = this.GetComponent<EnemyDH>();

        //找到本物体的EnemyMove脚本组件引用,并初始化
        EnemyMoveVar = this.GetComponent<EnemyMove>();

        //初始化攻击计数默认值为0,测试
        AttackCount = 0;

        //初始化攻击间隔时间默认值为0,测试
        AttackTime = 0;

        //初始化死亡计数默认值为0,测试
        DeathCount = 0;

        //获取标签为Player(主角)的GameObject类型引用
        PlayerObj = GameObject.FindGameObjectWithTag("Player");

        //获取枪的组件,实际开发项目中,枪类一般都在子物体上,此处子物体查找是包含本物体查找,本物体查找后即可返回组件信息给GunVar
        GunVar = GetComponentInChildren<Gun>();
    }

    private void Update()
    {
        //根据枚举状态来使用switch判断各种状态需要执行什么功能
        switch (status)
        {
            case Status.Attack:                                          //攻击状态
                EnemyAttack();
                break;
            case Status.WayFinding:                                      //寻路状态
                WayFinding();
                break;
            case Status.Death:                                           //死亡状态
                EnemyDeath();
                break;
            case Status.WaitTime:                                        //闲置状态
                WaitTimeFF();
                break;
        }


        //增加功能,在敌人的视线10米外,正120度为敌人攻击范围,玩家经过此范围,将会触发敌人攻击--20210627
        //因角度与英雄无敌最新的武器模块冲突,故直接使用距离判断是否攻击,此if 暂时注释
        /*
        if (Vector3.Distance(this.transform.position, PlayerObj.transform.position) < 10) //游戏角色与敌人之间小于10米
        {
            print("触发敌人视线范围");
            //自身transform.position加上this.transform.forward(代表前方1米)获得一个向量EnemyForward
            Vector3 EnemyForward = this.transform.position + this.transform.forward;

            //Vector3.Dot点乘参数:敌人正前方向量和玩家位置向量,获得反余弦值
            float ACOSNum = Vector3.Dot(EnemyForward.normalized, PlayerObj.transform.position.normalized);

            //计算夹角,反余弦值乘以转换为角度即可求出两根向量的夹角角度
            float DotNum = Mathf.Acos(ACOSNum) * Mathf.Rad2Deg;

            if (DotNum < 60) //小于60度,进入敌人攻击范围
            {
                print("触发敌人攻击状态");
                status = Status.Attack;  //敌人进入攻击状态
            }
            else
            {
                print("敌人退出攻击状态");
                status = Status.WaitTime;  //敌人退出攻击状态
            }
            //print("DosNum: "+DotNum+" ACOSNum: "+ACOSNum);
        }

        */

        if (Vector3.Distance(this.transform.position, PlayerObj.transform.position) < 10) //游戏角色与敌人之间小于10米
        {
            //print("触发敌人攻击状态");
            status = Status.Attack;  //敌人进入攻击状态
        }
        else
        {
            status = Status.WayFinding;  //敌人退出攻击状态
        }



    }

    //敌人死亡方法
    private void EnemyDeath()
    {
        print("进入死亡状态");
        GetComponent<EnemyInfo>().IsDead();                              //调用EnemyInfo类中,敌人死亡的方法


        status = Status.WaitTime;                                        //更改状态为闲置状态,测试语句,为了只执行一次,加上注释可以触发无限生成(利用update特性调用多次)

        /*
        //死亡动画只执行一次
        for (; DeathCount < 2; DeathCount++)
        {
            EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.DeathDH);
        }
        */
    }



    //敌人寻路方法
    private void WayFinding()
    {
        //print("进入寻路状态");
        if (EnemyMoveVar.WayFinding() == true)                     //判断寻路方法的值是否达到所有路线节点的最大值(true),是则更改状态为攻击状态
        {
            //寻路结束后更改状态为攻击状态
            //status = Status.Attack;
            //print("敌人找不到玩家呢");
        }
        else
        {
            EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.TiaoDH);    //false为继续播放跳的动画
        }
    }

    //敌人攻击方法,暂时注释,旧攻击方法无开火攻击,故注释禁用,注释于:20220425
    /*
    private void EnemyAttack()
    {
        print("进入攻击状态");
        //加判断,如果攻击动画没有播放,则播放跳跃动画
        if (!EnemyDHVar.DHplayVar.DHIsPlayering(EnemyDHVar.AttackDH))
            //取反操作,等同于if (EnemyDHVar.DHplayVar.DHIsPlayering(EnemyDHVar.AttackDH)==false)
            EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.TiaoDH);

        if (AttackTime < Time.time)                                    //攻击时间间隔判断,一开始肯定是true
        {
            EnemyDHVar.DHplayVar.DHPlayFF(EnemyDHVar.AttackDH);      //播放攻击动画
            AttackCount++;                                           //攻击次数++
            AttackTime = Time.time + 3;                              //攻击间隔时间,这里设定的是3

            if (AttackCount >= 3)                                    //攻击次数大于等于3的时候更改状态为死亡状态,测试语句
            {
                status = Status.Death;
            }
        }
    }

    */

    //敌人攻击方法,新方法为开火方法
    private void EnemyAttack()
    {
        //敌人攻击时朝向玩家--20220426
        EnemyMoveVar.EnemyRotateFF(PlayerStauts.PubPlayerStatus.PlayerTF.position);

        if (AttackTime < Time.time)                                  //攻击时间间隔判断,一开始肯定是true
        {
            //若敌人当前弹匣全部清空,则予以更换弹匣
            if (GunVar.NowAmmoBullet==0)
            {
                GunVar.UpdateAmmo();
            }

            //if (GunVar.NowAmmoBullet==0)
            //{
            //GunVar.UpdateAmmo();
            //}

            //GunVar.Fireing(this.transform.position - PlayerObj.transform.position);

            //延迟调用开火,因为正常项目会有动画需要播放从而抬起枪口,所以需要等待动画播放到枪口抬到平行线才能开枪,这里设置为0.5s后才能开枪
            //以后使用动画事件替代
            Invoke("EnemyFireDerly", 0.5f);

            AttackTime = Time.time + 0.5f;                              //攻击间隔时间,这里设定的是0.5s         
        }

        //print("敌人攻击方法执行测试");

    }

    //闲置状态方法,测试方法,为了让死亡状态只执行一次
    private void WaitTimeFF()
    {
        return;
    }

    //敌人开枪方法,因为需要使用延迟调用,所以另起一个方法方便调用!
    private void EnemyFireDerly()
    {
        //发起攻击(从枪口位置指向玩家受攻击点位置)
        //在重申,注意,位置朝向公式是  朝向哪 - 从哪里开始朝向
        GunVar.Fireing(PlayerStauts.PubPlayerStatus.PlayerTF.position - GunVar.GunFirePoint.position);
    }

}


//联动枪类模块,访问帖子:https://www.swztdza.cn/dalt/foru ... 1&extra=#pid659


回复 支持 反对

使用道具 举报

游客
回复
*滑块验证:

DA论坛飞机票来了~
快速回复 返回顶部 返回列表