游戏中的敌人有三种,我们先拿青蛙和老鹰来做例子,我们要实现的效果是:青蛙要能共在一定的范围内往返跳动,老鹰则是在一定的范围内上下运动。
要实现敌人在一定范围内移动,拿青蛙为例,我们需要确定他活动的左右边界点,我们在frog下创建两个空物体,让他们分别代表左端点和右端点,在右侧Inspector窗口中,可以设置这个物体的颜色。
这样我就就可以依靠拖动两点的位置来界定敌人移动的边界了。
下一步我们在代码中实现敌人的移动,在这里我用文字阐述一下我的设计思想,首先为青蛙创建一个脚本,由于青蛙做往返运动,当青蛙面向左边时向左走,面向右边是向右走,当向左走碰到左端点是改变面部朝向,更改为向右走,通过上面描述,我们知道需要创建一个float类型的speed来控制青蛙的移动速度,在创建一个bool型的变量控制青蛙的面部朝向,假设用facing_left=true/false来控制面部朝向(初始值我们设为true)。接下来,代码运行时,我们首先要获取到做右端点的x的值,因此我们能需要两个变量来获取,获取了之后,这两个物体对于我们就是去了意义,留着只会浪费性能,所以我们要销毁这两个物体。接下来再考虑,青蛙是一跳一跳的,所以他还需要一个向上跳跃的力jumpforce,我们还需要为跳跃添加限制条件,每接触一次地面才能条一次,那不然就一直跳了。因此青蛙在运动中就会出现四种情况:1.面向左(在空中/不在空中)2.面向右(在空中/不在空中),下面直接上代码:
public class Enemy_frog : Enemy
{
private Rigidbody2D rb;
public Collider2D coll;
public float speed,jumpforce;
public LayerMask ground;
public Transform leftpoint, rightpoint;
private float leftx, rightx;
private bool facing_left=true;
//private Animator anim;
protected override void Start()
{
base.Start();
rb=GetComponent<Rigidbody2D>();
//anim = GetComponent<Animator>();
coll = GetComponent<Collider2D>();
transform.DetachChildren();
leftx = leftpoint.position.x;
rightx = rightpoint.position.x;
Destroy(leftpoint.gameObject);
Destroy(rightpoint.gameObject);
}
// Update is called once per frame
void Update()
{
SwitchAnim();
}
void Movement()
{
if (facing_left)
{
if (coll.IsTouchingLayers(ground))
{
anim.SetBool("jumping", true);
rb.velocity = new Vector2(-speed, jumpforce);
}
if (transform.position.x<leftx)
{
rb.velocity = new Vector2(0, 0);
transform.localScale = new Vector3(-1, 1, 1);
facing_left = false;
}
}
else
{
if (coll.IsTouchingLayers(ground))
{
anim.SetBool("jumping", true);
rb.velocity = new Vector2(speed, jumpforce);
}
if (transform.position.x > rightx)
{
rb.velocity = new Vector2(0, 0);
transform.localScale = new Vector3(1, 1, 1);
facing_left = true;
}
}
}
void SwitchAnim()
{
if (anim.GetBool("jumping"))
{
if (rb.velocity.y < 0.1)
{
anim.SetBool("jumping",false);
anim.SetBool("falling",true);
}
}
if (coll.IsTouchingLayers(ground)&&anim.GetBool("falling"))
{
anim.SetBool("falling", false);
}
}
}
以上代码中有一些要注意的是这里用Collider变量去检测青蛙是不是和属于Grid瓦片地图发生了碰撞,这里瓦片地图要新建一个Layer取名为ground,另外动画的切换还存在一些问题,那就是这个青蛙根本不存在静止的状态,跳完一次之后紧接着开始第二次跳跃,我们希望他跳完一次后静止一段时间,才开始第二次跳跃,那么我们就需要用到在上节笔记中用到的动画event了,我们为青蛙创建了静止动画后,在静止动画结束的时候,为其添加两个事件,一个是Movement函数另一个是SwitchAnim函数,这样就是先了跳跃-静止-跳跃的过程。
老鹰的运动实现和青蛙的十分相似,而且更为简单,在此不做赘述。
下面来说一下消灭敌人的效果实现,因为敌人不只有一种,如果在每个敌人的脚本单独写消灭的效果也可以,但是会显得比较繁琐,所以我们可以为老鹰和青蛙添加一个共同的父类,在父类中写消灭敌人的效果,这样子类调用父类,就可以了。而且由于消灭敌人的效果我们是写在了Player Controller中,我们还要在其他的类中实现敌人类的实例化,代码:
public class Enemy : MonoBehaviour
{
protected Animator anim;
protected AudioSource deathAudio;
// Start is called before the first frame update
protected virtual void Start()
{
anim = GetComponent<Animator>();
deathAudio = GetComponent<AudioSource>();
}
public void Death()
{
GetComponent<Collider2D>().enabled = false;
Destroy(gameObject);
}
public void JumpOn()
{
deathAudio.Play();
anim.SetTrigger("death");
}
}
下面是在PlayerController中写道的消灭敌人的代码,两者需要结合着看,后面我来解释一下。
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Enemy")
{
//eagle_test eagle = collision.gameObject.GetComponent<eagle_test>();
//Enemy_eagle eagle = collision.gameObject.GetComponent<Enemy_eagle>();
Enemy enemy = collision.gameObject.GetComponent<Enemy>();
if (anim.GetBool("falling"))
{
enemy.JumpOn();
//eagle.JumpOn();
rb.velocity = new Vector2(rb.velocity.x, jumpforce * Time.fixedDeltaTime*50);
anim.SetBool("jumping", true);
anim.SetBool("falling", false);
}else if (transform.position.x < collision.gameObject.transform.position.x)
{
rb.velocity = new Vector2(-10, rb.velocity.y);
isHurt = true;
hurtAudio.Play();
}else if(transform.position.x > collision.gameObject.transform.position.x)
{
rb.velocity = new Vector2(10, rb.velocity.y);
isHurt = true;
hurtAudio.Play();
}
}
}
首先在Player代码中,如果碰撞体检测碰撞到tag标记为Enemy的物体,在继续判断,只有当角色处在下落的状态,也就是首先要碰到敌人,而且是在敌人的头顶,才能把敌人踩死,那么就去调用JumpOn函数,他的作用是出发名为death的动画,我们在来到Animator窗口中,我们可以看到一个看色的方块anystate,也就是说,在任何情况下都有可能出发的动画。那么这个动画触发了之后,我们在这个动画后添加event,首先先让他的active状态变为false,不然他只是切换了动画,他的碰撞体还在,会持续与游戏场景中的其他物体碰撞,然后再销毁它。重点是,在Player的代码中,通过 Enemy enemy = collision.gameObject.GetComponent<Enemy>();这一行代码实现了类的调用和实例化。
没有提及到的代码的功能是,如果消灭了敌人,玩家可以获得一次额外的跳跃,否则,如果不是踩在敌人头上,那么就是受伤,会被敌人击退,所以另外两个else是向两个方向击退的效果。
以上是以青蛙作为例子,老鹰的实现过程也是类似的,在此不做赘述。