加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Unity游戏开发--AIMonster(怪物巡逻)

(2019-07-08 09:51:09)
标签:

it

分类: Unity工具
Unity游戏开发--AIMonster(怪物巡逻)

Unity游戏开发--AIMonster(怪物巡逻)

AI效果如下:

1. 将怪物分为如下几个状态:
待机状态(该状态内有3种行为:原地呼吸、原地观察、和游走,可通过权重配置待机状态下三种行为发生的比例);
警戒状态(怪物向玩家发出警告,并持续盯着玩家);
追击状态(怪物跑向玩家)
返回状态(跑回出生位置,该状态下不再理会玩家)

2. 可以为怪物配置各项状态的数值
游走半径,待机状态下,怪物游走时不会超出这个范围,根据出生点计算
警戒半径,当玩家与怪物之间距离小于警戒半径时进入警戒状态,根据怪物实时位置计算
自卫半径,当玩家与怪物之间距离小于自卫半径时进入追击状态
追击半径,怪物追击玩家时,自身不会超出这个范围,超出后跑回出生点,可以理解为最大活动范围
攻击距离,当玩家与怪物之间的距离小于攻击距离时触发战斗(我这边是进入之前做好的回合制战斗场景)

3. 各项数值设定的关系如下
比较合理的关系是:追击半径>警戒半径>自卫半径>攻击距离,游走半径只需要小于追击半径即可
1)自卫半径不建议大于警戒半径,否则就无法触发警戒状态,直接开始追击了
2)攻击距离不建议大于自卫半径,否则就无法触发追击状态,直接开始战斗了
3)游走半径不建议大于追击半径,否则怪物可能刚刚开始追击就返回出生点

Demo测试使用参数:
Unity游戏开发--AIMonster(怪物巡逻)

Demo中,需要控制的玩家(Tag设置为Player),然后是怪物,代码中通过状态机切换实现怪物的不同状态(Animator),然后把脚本附加在怪物上就可以。

Demo代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
 
public class MonsterWander : MonoBehaviour {
 
    private GameObject playerUnit;          //获取玩家单位
    private Animator thisAnimator;          //自身动画组件
    private Vector3 initialPosition;            //初始位置
 
    public float wanderRadius;          //游走半径,移动状态下,如果超出游走半径会返回出生位置
    public float alertRadius;         //警戒半径,玩家进入后怪物会发出警告,并一直面朝玩家
    public float defendRadius;          //自卫半径,玩家进入后怪物会追击玩家,当距离<攻击距离则会发动攻击(或者触发战斗)
    public float chaseRadius;            //追击半径,当怪物超出追击半径后会放弃追击,返回追击起始位置
 
    public float attackRange;            //攻击距离
    public float walkSpeed;          //移动速度
    public float runSpeed;          //跑动速度
    public float turnSpeed;         //转身速度,建议0.1
 
    private enum MonsterState
    {
        STAND,      //原地呼吸
        CHECK,       //原地观察
        WALK,       //移动
        WARN,       //盯着玩家
        CHASE,      //追击玩家
        RETURN      //超出追击范围后返回
    }
    private MonsterState currentState = MonsterState.STAND;          //默认状态为原地呼吸
 
    public float[] actionWeight = { 3000, 3000, 4000 };         //设置待机时各种动作的权重,顺序依次为呼吸、观察、移动
    public float actRestTme;            //更换待机指令的间隔时间
    private float lastActTime;          //最近一次指令时间
 
    private float diatanceToPlayer;         //怪物与玩家的距离
    private float diatanceToInitial;         //怪物与初始位置的距离
    private Quaternion targetRotation;         //怪物的目标朝向
 
    private bool is_Warned = false;
    private bool is_Running = false;
 
    void Start () {
        playerUnit = GameObject.FindGameObjectWithTag("Player");
        thisAnimator = GetComponent();
 
        //保存初始位置信息
        initialPosition = gameObject.GetComponent().position;
 
        //检查并修正怪物设置
        //1. 自卫半径不大于警戒半径,否则就无法触发警戒状态,直接开始追击了
        defendRadius = Mathf.Min(alertRadius, defendRadius);
        //2. 攻击距离不大于自卫半径,否则就无法触发追击状态,直接开始战斗了
        attackRange = Mathf.Min(defendRadius, attackRange);
        //3. 游走半径不大于追击半径,否则怪物可能刚刚开始追击就返回出生点
        wanderRadius = Mathf.Min(chaseRadius, wanderRadius);
 
        //随机一个待机动作
        RandomAction();
    }
 
    ///
    /// 根据权重随机待机指令
    ///
    void RandomAction()
    {
        //更新行动时间
        lastActTime = Time.time;
        //根据权重随机
        float number = Random.Range(0, actionWeight[0] + actionWeight[1] + actionWeight[2]);
        if (number <= actionWeight[0])
        {
            currentState = MonsterState.STAND;
            thisAnimator.SetTrigger("Stand");
        }
        else if (actionWeight[0] < number && number <= actionWeight[0] + actionWeight[1])
        {
            currentState = MonsterState.CHECK;
            thisAnimator.SetTrigger("Check");
        }
        if (actionWeight[0] + actionWeight[1] < number && number <= actionWeight[0] + actionWeight[1] + actionWeight[2])
        {
            currentState = MonsterState.WALK;
            //随机一个朝向
            targetRotation = Quaternion.Euler(0, Random.Range(1, 5) * 90, 0);
            thisAnimator.SetTrigger("Walk");
        }
    }
 
    void Update () {
        switch (currentState)
        {
            //待机状态,等待actRestTme后重新随机指令
            case MonsterState.STAND:
                if (Time.time - lastActTime > actRestTme)
               
                    RandomAction();         //随机切换指令
                }
                //该状态下的检测指令
                EnemyDistanceCheck();
                break;
 
            //待机状态,由于观察动画时间较长,并希望动画完整播放,故等待时间是根据一个完整动画的播放长度,而不是指令间隔时间
            case MonsterState.CHECK:
                if (Time.time - lastActTime > thisAnimator.GetCurrentAnimatorStateInfo(0).length)
                {
                    RandomAction();         //随机切换指令
                }
                //该状态下的检测指令
                EnemyDistanceCheck();
                break;
 
            //游走,根据状态随机时生成的目标位置修改朝向,并向前移动
            case MonsterState.WALK:
                transform.Translate(Vector3.forward * Time.deltaTime * walkSpeed);
                transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, turnSpeed);
                
                if (Time.time - lastActTime > actRestTme)
                {
                    RandomAction();         //随机切换指令
                }
                //该状态下的检测指令
                WanderRadiusCheck();
                break;
 
            //警戒状态,播放一次警告动画和声音,并持续朝向玩家位置
            case MonsterState.WARN:
                if (!is_Warned)
                {
                    thisAnimator.SetTrigger("Warn");
                    gameObject.GetComponent().Play();
                    is_Warned = true;
                }
                //持续朝向玩家位置
                targetRotation = Quaternion.LookRotation(playerUnit.transform.position - transform.position, Vector3.up);
                transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, turnSpeed);
                //该状态下的检测指令
                WarningCheck();
                break;
 
            //追击状态,朝着玩家跑去
            case MonsterState.CHASE:
                if (!is_Running)
                {
                    thisAnimator.SetTrigger("Run");
                    is_Running = true;
                }
                transform.Translate(Vector3.forward * Time.deltaTime * runSpeed);
                //朝向玩家位置
                targetRotation = Quaternion.LookRotation(playerUnit.transform.position - transform.position, Vector3.up);
                transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, turnSpeed);
                //该状态下的检测指令
                ChaseRadiusCheck();
                break;
 
            //返回状态,超出追击范围后返回出生位置
            case MonsterState.RETURN:
                //朝向初始位置移动
                targetRotation = Quaternion.LookRotation(initialPosition - transform.position, Vector3.up);
                transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, turnSpeed);
                transform.Translate(Vector3.forward * Time.deltaTime * runSpeed);
                //该状态下的检测指令
                ReturnCheck();
                break;
        }
}
 
    ///
    /// 原地呼吸、观察状态的检测
    ///
    void EnemyDistanceCheck()
    {
        diatanceToPlayer = Vector3.Distance(playerUnit.transform.position, transform.position);
        if (diatanceToPlayer < attackRange)
        {
            SceneManager.LoadScene("Battle");
        }
        else if (diatanceToPlayer < defendRadius)
        {
            currentState = MonsterState.CHASE;
        }
        else if (diatanceToPlayer < alertRadius)
        {
            currentState = MonsterState.WARN;
        }
    }
 
    ///
    /// 警告状态下的检测,用于启动追击及取消警戒状态
    ///
    void WarningCheck()
    {
        diatanceToPlayer = Vector3.Distance(playerUnit.transform.position, transform.position);
        if (diatanceToPlayer < defendRadius)
        {
            is_Warned = false;
            currentState = MonsterState.CHASE;
        }
 
        if (diatanceToPlayer > alertRadius)
        {
            is_Warned = false;
            RandomAction();
        }
    }
 
    ///
    /// 游走状态检测,检测敌人距离及游走是否越界
    ///
    void WanderRadiusCheck()
    {
        diatanceToPlayer = Vector3.Distance(playerUnit.transform.position, transform.position);
        diatanceToInitial = Vector3.Distance(transform.position, initialPosition);
 
        if (diatanceToPlayer < attackRange)
        {
            SceneManager.LoadScene("Battle");
        }
        else if (diatanceToPlayer < defendRadius)
        {
            currentState = MonsterState.CHASE;
        }
        else if (diatanceToPlayer < alertRadius)
        {
            currentState = MonsterState.WARN;
        }
 
        if (diatanceToInitial > wanderRadius)
        {
            //朝向调整为初始方向
            targetRotation = Quaternion.LookRotation(initialPosition - transform.position, Vector3.up);
        }
    }
 
    ///
    /// 追击状态检测,检测敌人是否进入攻击范围以及是否离开警戒范围
    ///
    void ChaseRadiusCheck()
    {
        diatanceToPlayer = Vector3.Distance(playerUnit.transform.position, transform.position);
        diatanceToInitial = Vector3.Distance(transform.position, initialPosition);
 
        if (diatanceToPlayer < attackRange)
        {
            SceneManager.LoadScene("Battle");
        }
        //如果超出追击范围或者敌人的距离超出警戒距离就返回
        if (diatanceToInitial > chaseRadius || diatanceToPlayer > alertRadius)
        {
            currentState = MonsterState.RETURN;
        }
    }
 
    ///
    /// 超出追击半径,返回状态的检测,不再检测敌人距离
    ///
    void ReturnCheck()
    {
        diatanceToInitial = Vector3.Distance(transform.position, initialPosition);
        //如果已经接近初始位置,则随机一个待机状态
        if (diatanceToInitial < 0.5f)
        {
            is_Running = false;
            RandomAction();
        }
    }
 
}


参考:https://blog.csdn.net/c252270036/article/details/77414141

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有