11.Unity2D 横版 简单AI 之背后受击转身+寻路跟随敌人+模块化+射线检测

 总目录icon-default.png?t=M666https://blog.csdn.net/qq_54263076/category_11900070.html?spm=1001.2014.3001.5482

1.敌人背后受击转身+背部攻击伤害翻倍

回顾上节课,我们已经完成了范围内检索敌人自动攻击,随机移动功能。简易的AI已经完成了,淡是敌人还是有些呆,例如从背后偷袭,敌人好像没有感觉似的并不会转过身来,说道背后偷袭,顺便可以完成背后伤害按倍增长功能。由于并不需要频繁检测,只需要在攻击的一瞬间完成,所以这个代码并不需要写在敌人行为脚本enemybehavior,只需要写在攻击碰撞盒脚本attacktrigger

10.Unity2D 横版 简单AI 之 敌人随机移动+自动巡逻+障碍物跳跃+悬崖处转身+射线检测_ζั͡ ั͡雾 ั͡狼 ั͡✾的博客-CSDN博客Unity2D 横版 简单AI 之 敌人随机移动+自动巡逻+障碍物跳跃+悬崖处转身+射线检测。我们能够发现问题,随机移动会跳下悬崖,也会前面有遮挡,确一直往前走,不能自动跳跃。解决这个问题我们需要进行射线检测 。白线三条是检测跳跃视线,蓝线是检测转向线。白线中下线检测到物体直接跳跃,上线检测,进行一次判定,是跳上去还是钻过去。......https://blog.csdn.net/qq_54263076/article/details/125735356attacktrigger脚本更新

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

public class attackTrigger : MonoBehaviour
{
   
    private Animator ani;//代码优化 获取自己动画器
    private AnimatorStateInfo state;//动画状态


    public float atkItemsBack = 1;
    public float atkItemsUp = 1;
    public float playerSpeedInfectBack = 1;
    public float backAtkTime = 2;//背后攻击伤害倍率
    // Start is called before the first frame update
    void Start()
    {

        ani = transform.parent.GetComponent<Animator>();


    }

    public void OnTriggerEnter2D(Collider2D collision)
    {

        //攻击到了物品或敌人产生击退 
        if (collision.tag != transform.parent.tag && collision.tag!="ground" && collision.tag!="notAtkCheckCollision")//由于ground和bottom有碰撞盒但不需要攻击检测,需要剔除,给bottom标签notAtkCheckCollision
        {
       
            //获取人物与物品位置向量
            Vector3 v = collision.transform.position-transform.parent.position ;
        
            //冻结z轴
            v.z = 0;
            //获取横轴,速度影响击退距离
            float h = Input.GetAxis("Horizontal");
            //角色标签额外有挑飞效果  //如果处于动画2,4时额外实施向上的力、速度
            if (transform.parent.tag == "Player")
            {    
                //挑飞
                state = ani.GetCurrentAnimatorStateInfo(0);
                if (state.IsName("attack2") || state.IsName("attack4"))
                {
                    v.y += (atkItemsBack *12* atkItemsUp);
                }
            }

                collision.GetComponent<Rigidbody2D>().velocity = v * atkItemsBack + Vector3.right * h * playerSpeedInfectBack * 5;

        }
        //攻击到了敌人 代码更新判断条件为碰撞体的标签跟自己的标签不一样,这样敌人也可以对主角产生效果。
        if (collision.tag == "Player" || collision.tag == "enemy")
        {
            if (collision.tag != transform.parent.tag)
            {

                //敌人受伤

                //找到被攻击到的敌人
                GameObject enemyGo = collision.gameObject;
                //背后攻击
                if (collision.gameObject.GetComponent<CharacterPanel>().lookdir * (collision.gameObject.transform.position.x - transform.parent.position.x) > 0)
                {
                    enemyGo.GetComponent<CharacterPanel>().hurt(transform.parent.GetComponent<CharacterPanel>().Atk * backAtkTime);
                    //如果被攻击的是敌人,转身
                    if (collision.tag == "enemy")
                    {
                        collision.GetComponent<CharacterPanel>().Turndir();
                        collision.GetComponent<EnemyBehavior>().moveTime += 1;//移动时间加1s
                    }
                }
                else {
                    enemyGo.GetComponent<CharacterPanel>().hurt(transform.parent.GetComponent<CharacterPanel>().Atk);
                }

                //伤害计算 调用角色面板脚本的hurt函数
               


            }
        }

    }

}

2.寻路跟随敌人

随机移动的坏处就是看到敌人(主角)会略过去,按照自己的行为行走,所以我们要再写一个行为脚本,用来看到敌人后的行为,当然如果没有看到敌人还是随机移动的函数

其中黄线为视线线,变红色为找到主角,并记录此时主角的位置

嗯,主要思路就是设置一个追寻时间变量,如果这段时间变量不为零的话,就射线检测,直接记录上一次看到敌人的位置然后尽量移动到这个点上,如果没有检测到,然后每过一秒追寻时间减少1;检测到话就重置追寻时间为常数。如果变量为零的话,就执行随机移动。

在enemybehavior脚本下,当然脚本的Debug函数可以不写,只是为了结果可视化我写上去划线的

using System.Collections;
using UnityEngine;

public class EnemyBehavior : MonoBehaviour
{
    private CharacterPanel characterPanel;
    public float moveTime = 3;//移动时间
    private float _moveTime;//定值
    public float waitTime = 3;//等待时间
    private float _waitTime;//定值

    private bool OneIsMustJump = true;//一次头顶检测,跳跃判断
    private int OneTurn = 1;//一次转向,防止抽搐

    private Vector2 playerPoi;
    public float findDistance = 1;//寻找距离
    public float findAngle = 1;//寻找角度
    public float followTime = 3;//跟随时间
    private float _followTime;//定值
    // Start is called before the first frame update
    void Start()
    {
        characterPanel = transform.GetComponent<CharacterPanel>();
        _moveTime = moveTime;
        _waitTime = waitTime;
        _followTime = followTime;
        followTime = 0;//默认起初不跟随
    }

    // Update is called once per frame
    void Update()
    {
        JumpObstacle();
        FindPlayer();
        //如果有跟随时间的话,就只请跟随函数,如果没有跟随时间就实行随机移动按数。
        if (followTime <= 0)
        { RandomMove(); }
        else
        {
            FollowPlayer();
            followTime -= Time.deltaTime;
        }

        CliffTurn();


    }
    //随机横向移动
    private void RandomMove()
    {
        if (moveTime > 0)//如果处于移动时间
        {
            characterPanel.move();
            moveTime -= Time.deltaTime;//移动时间减少一秒
            if (moveTime < 0)
            {
                waitTime = _waitTime;//初始化等待时间
            }
        }
        else
        {
            characterPanel.idle();
            if (waitTime > 0)//如果处于等待时间
            {
                waitTime -= Time.deltaTime;//等待时间减少一秒
            }
            else
            {
                moveTime = _moveTime;//初始化移动时间
                //等待结束,随机转向
                bool Isturn = (Random.value > 0.5f);
                if (Isturn)
                {
                    characterPanel.Turndir();//转向
                }


            }
        }
    }
    //跳跃障碍物
    private void JumpObstacle()
    {

        for (int i = -1; i <= 1; i++)//i取-1,0,1,使得y轴变正负,三条射线检测,形成一个小扇角
        {
            RaycastHit2D hit = Physics2D.Raycast(transform.position, characterPanel.lookdir * new Vector3(0.25f, -0.15f * i, 0),
                               2, LayerMask.GetMask("tilemap", "items"));//检测这俩种图层
            if (hit)//检测到了有碰赚点
            {
                if (Vector2.Distance(hit.point, transform.position) < Vector2.Distance(playerPoi, transform.position))//如果碰撞点的距离大于与敌人(主角)的距离就不进行跳跃,防治这张卡墙角。避免攻击。
                {
                    Debug.DrawLine(transform.position, hit.point);
                    if (hit.point.y <= transform.position.y)//脚底前方或前方检测到
                    {
                        characterPanel.jump();
                    }
                    else
                    { //头顶前方检测到 下面代码含义为,头顶检测到首先判断要不要跳,还是钻过去,
                      //如果钻过去下面,就要使得3秒内OneIsMustJump为flase,走过去,三秒后如果头顶前方有东西,再进行一次判定再
                        bool mustJump = false;
                        if (OneIsMustJump)
                        {
                            mustJump = (Random.value > 0.5f);//进行一次判断是否跳跃
                            StartCoroutine(reMustJump()); //开启协程恢复OneIsMustJump为true
                            OneIsMustJump = false;

                        }
                        if (mustJump)
                        {
                            if (moveTime < _moveTime)
                            {
                                moveTime += Time.deltaTime;//移动时间加一秒确定能让他跳过去;
                            }
                            characterPanel.jump();
                        }


                    }
                }
            }
            else
            {

                Debug.DrawRay(transform.position, characterPanel.lookdir * new Vector3(0.25f, -0.15f * i, 0));
            }

        }






    }
    IEnumerator reMustJump()
    {
        yield return new WaitForSeconds(3);
        moveTime += Time.deltaTime;
        OneIsMustJump = true;
    }
    //悬崖勒马,不让角色跳崖
    private void CliffTurn()
    {
        RaycastHit2D hit = Physics2D.Raycast(transform.position, new Vector2(0, -1),
                            Mathf.Infinity, LayerMask.GetMask("tilemap"));
        if (!hit)//检测没有有碰撞点
        {
            if (OneTurn >= 1)
            {
                StartCoroutine(turnDir());//协程转向,转向一次后延迟0.5秒才能下一次转向,防止过短时间内频繁转向,抽搐
                OneTurn--;
            }
            moveTime += 0.5f;
        }
        Debug.DrawRay(transform.position, new Vector2(0, -1), Color.blue);
    }
    IEnumerator turnDir()
    {
        characterPanel.Turndir();//转向
        yield return new WaitForSeconds(0.5f);
        OneTurn = 1;
    }
    //寻找敌人
    private void FindPlayer()
    {
        for (int i = -2; i <= 2; i++)//使得y轴变正负,五条条射线检测,形成一个小扇角
        {
            RaycastHit2D hit = Physics2D.Raycast(transform.position, characterPanel.lookdir * new Vector3(0.25f, -0.10f * i * findAngle, 0),
                               10 * findDistance, LayerMask.GetMask("tilemap", "items", "player"));//检测这三种图层
            if (hit)//检测到了有碰赚点
            {
                if (hit.collider.tag == "Player")
                {
                    followTime = _followTime;//找到主角,不执行随机移动
                    playerPoi = hit.point;//存储敌人找到的主角位置 更新找到的主角位置
                    Debug.DrawLine(transform.position, hit.point, Color.red);

                }
                else
                {
                    Debug.DrawLine(transform.position, hit.point, Color.yellow);
                }
            }
            else
            {

                Debug.DrawRay(transform.position, (characterPanel.lookdir * new Vector3(0.25f, -0.15f * i * findAngle, 0)).normalized * 10 * findDistance, Color.yellow);
            }
        }
    }
    //跟随移动函数
    private void FollowPlayer()
    {
        Vector2 v = new Vector2(playerPoi.x - transform.position.x, playerPoi.y - transform.position.y);//获取上一次看到的主角与自己的位置向量
        if (v.x * characterPanel.lookdir < 0)
        {//如果位置向量与朝向相反,转向
            if (OneTurn >= 1)
            {
                StartCoroutine(turnDir());//协程转向,转向一次后延迟0.5秒才能下一次转向,防止过短时间内频繁转向,抽搐
                OneTurn--;
            }
        }

        characterPanel.move();//向前移动

        if (v.y > 2 && v.x < 3)
        {
            characterPanel.jump();
        }

    }

}

3.模块化思想

像上面咱们刚写敌人转身和自动追求敌人功能。如果你不想做一个非常让人玩不下去的游戏(都会追踪太困难了,不适合大部分玩家(除了高玩))的话,这应该是特殊体所具有的功能,你或者可以起我记得高级的行为,并把它模块化,一个一个分开,然后分别设置布尔变量当布尔之为真的时候,就会执行一些特殊行为。我们生成敌人的时候,我可以通过控制这些布尔值来生成各种行为组合的敌人,使得敌人不单一化,枯燥华。这就是模块化思想。

完成了!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ζั͡ ั͡雾 ั͡狼 ั͡✾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值