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的简单行动,角色的受伤,以及消灭敌人的方式都完成了
本文来自博客园,作者:rkmao,转载请注明原文链接:https://www.cnblogs.com/rkmao/p/15756601.html