Unity横版2D游戏学习实例(07)- 简单AI敌人&受伤状态

前言:这节内容主要为添加敌人并为敌人加上移动逻辑,以及角色受和敌人的伤状态和动画

 

一、添加敌人

1. 打开SunnyLand -> artwork -> Sprites -> Enemies -> frog -> idle ,随便选择一个拖拽到sence窗口中(记得设置Pixels Per Unit)


2. 在Hierarchy中建立一个空对象命名为Enemies,把刚添加的frog放在Enemies下方便管理


3. 为frog添加一个2D圆形碰撞体和2D刚体,碰撞体并调整到合适大小。

 

二、添加动画(创建动画和动画切换详细步骤参考04章节,此处只描述步骤省去具体操作过程)

1. 在Project -> Asset -> Animation 添加收集物分类文件夹Enemies


2. 在Animation窗口创建frog的动画,并添加上动画(frog_idle,frog_jump,frog_fall,frog_death)


(death动画在SunnyLand -> artwork -> Sprites -> Fx -> enemy-death)

3.设置动画切换

 

 

 

三、受伤状态

1. Any State

这里需要补充一个状态Any State,Any State表示Animator里所有状态。
使用场景比如:受伤状态、死亡状态。 无论角色还是敌人在站、在跑、在跳、在爬、在飞都能切换到这类状态,如果一个一个状态来回连线,状态转换会变得非常复杂。
这时使用Any State转到这类状态就可只使用一次连线

注意:Any State包含所有状态意味着也包含这里的death状态, 所有需要把下图can transition to self的√去掉,不然就会出现 death -> death 的死循环。
这里给frog添加了任意状态在isDamaged转为true时,触发受伤后death的效果。


2. 给角色添加受伤状态

同样角色也通过Any State给角色设置一个受伤状态。角色受伤后可以恢复原来的状态,所以给一个bool值来标识可以从hurt状态切出来。
death动画在SunnyLand -> artwork -> Sprites -> player -> hurt


3. 限制敌人移动范围

敌人在场景中进行移动时,需要给他们设置一个范围防止他们跑丢了。这里通过两个位置标记来规定frog左右移动的范围。
在frog下创建两个空对象,移动到frog左右。然后就可以在代码运行时获取这两点位置,设置frog移动的边界。
(leftPoint不需要任何其他设置,只作为位置标记)


 

(小技巧:可以设置这两个空对象的Icon,来在编辑界面显示方便查看)


4. 上代码

敌人公共方法

using UnityEngine;

public class Enemy : MonoBehaviour
{
    public bool isDamaged;

    protected Collider2D enemy_Coll;
    protected Rigidbody2D enemy_Rbody;

    protected virtual void Start()
    {
        enemy_Rbody = GetComponent<Rigidbody2D>();
        enemy_Coll = GetComponent<Collider2D>();
    }

    public void Damaged()
    {
        isDamaged = true;
        enemy_Coll.enabled = false;//敌人受伤death时,禁用掉碰撞体,反正在消失时还能碰到
        enemy_Rbody.constraints = RigidbodyConstraints2D.FreezeAll;//敌人受伤death时,冻结刚体,防止因碰撞体失效而掉出地面
    }

    public void Death()
    {
        Destroy(gameObject);
    }
}


frog的代码

using UnityEngine;

public class Frog : Enemy
{
    public LayerMask groundLayer;
    public bool isOnGround;

    public Transform leftPoint, rightPoint;//敌人左右活动范围
    private float leftX, rightX;//范围的x值

    public float moveSpeed = 6f;//水平速度
    public float jumpForce = 6f;//纵向速度

    protected override void Start()
    {
        base.Start();

        //获取AI左右预置的点,取得水平x值
        leftX = leftPoint.transform.position.x;
        rightX = rightPoint.transform.position.x;
        Destroy(leftPoint.gameObject);
        Destroy(rightPoint.gameObject);
    }

    void FixedUpdate()
    {
        isOnGround = enemy_Coll.IsTouchingLayers(groundLayer);
    }

    private void Movement()
    {
        //如果超过活动范围就转向
        float posX = transform.position.x;
        if (posX < leftX)
            transform.localScale = new Vector3(-1, 1, 1);
        else if (posX > rightX)
            transform.localScale = new Vector3(1, 1, 1);

        //给AI敌人水平速度(前进)和纵向速度(跳跃)
        enemy_Rbody.velocity = new Vector2(moveSpeed * -transform.localScale.x, jumpForce);
    }
}


敌人动画

using UnityEngine;

public class EnemyAnimation : MonoBehaviour
{
    private Animator enemy_anim;
    private int isOnGroundID;
    private int yVelocityID;
    private readonly string State_isDamaged = "isDamaged";

    private Frog frog;

    private void Start()
    {
        frog = GetComponent<Frog>();
        enemy_anim = GetComponent<Animator>();

        //注意:StringToHash中的参数要和Animator中设置Parameters一致
        isOnGroundID = Animator.StringToHash("isOnGround");
        yVelocityID = Animator.StringToHash("yVelocity");
    }

    private void Update()
    {
        enemy_anim.SetFloat(yVelocityID, GetComponent<Rigidbody2D>().velocity.y);
        enemy_anim.SetBool(isOnGroundID, frog.isOnGround);
        if (frog.isDamaged)//受伤时触发death动画
            enemy_anim.SetTrigger(State_isDamaged);
    }

}


角色需要添加的代码

public class PlayerControl : MonoBehaviour
{
	public bool isHurt;
    
    void FixedUpdate()
    {
    	//放在原本代码的上面,标识如果处于受伤状态则不能操作角色
        if (isHurt)
            return;
    }
    
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Enemy")
        {
            //碰到敌人时如果角色位置高于敌人,且速度向下,则视为踩到敌人
            GameObject enemyObj = collision.gameObject;
            if (transform.position.y > enemyObj.transform.position.y
                && player_Rbody.velocity.y < 0)
            {
                player_Rbody.velocity = new Vector2(player_Rbody.velocity.x, 5f);//给予角色一个小跳
                enemyObj.GetComponent<Frog>().Damaged();//调用敌人的受伤方法
            }
            //否则给与角色伤害,并且使角色小幅后退
            else
            {
                bool isOnLeft = transform.position.x < enemyObj.transform.position.x;
                player_Rbody.velocity = new Vector2(isOnLeft ? -3f : 3f, player_Rbody.velocity.y);
                isHurt = true;
            }
        }
    }

    private void HurtEnd()
    {
        isHurt = false;
    }
}


挂到frog上

 

5.  补充动画事件

看过上面的代码,不难发现Frog的Movement(),Enemy的Death(),PlayerControl的HurtEnd(),都没有被使用。
这是因为Frog的Movement()需要放在站立的动画idle后面,Enemy的Death()需要放在消失动画结束后,PlayerControl的HurtEnd()则要放在角色受伤动画结束。
这么一想不难发现,这些都是以动画事件形式调用的。


6. 补充标签

代码中判断碰撞到敌人使用了tag,这个需要记得给frog设置一下。没有这个tag可以在下拉框最下面(Add Tag)添加一个

 

至此,frog的简单行动,角色的受伤,以及消灭敌人的方式都完成了

posted @ 2022-01-11 22:00  rkmao  阅读(1169)  评论(0编辑  收藏  举报