这节实现敌人AI的效果
为了保证代码的复用性,所以我们这里创建一个敌人父类,之后所有的敌人类都继承之这个敌人父类
先创建一个敌人父类脚本,命名为Enemy
然后简单写一下Enemy代码
这里运用了虚函数,函数前面加上visual表示这个函数是虚函数。这里简单说一下什么是虚函数
虚函数是面向对象语言中多态性的重要实现. 当一个函数方法在声明时, 前面带了virtual关键字, 这个函数就是一个虚函数. 它与非虚函数的主要区别在于它的实现可以在派生类中进行重写(override)(非强制要求). 重写后的函数一样也是虚函数, 当一个类或其基类中存在虚函数后就不允许出现同名, 返回值, 参数类型个数相同的非虚函数。
想仔细了解的话可以找一些文章看,这里就不赘述了。
然后我们创建玩敌人父类之后我们就要创建敌人了
我们先在Hierarchy栏创建一个空组件命名为Enemies,表示敌人集合,然后创建一个空的子组件命名为Enemy_Frog,剩下的操作这里就不赘述了,和角色的创建是一样的,我们这里选择角色素材包那里的青蛙
不过记得改变它的图层,改成之前创建的Enemy,不然会显示不出来
同样添加collider和Rigidbody
然后就是让敌人动起来
我们给敌人添加一下动画,和人物一样,这里不再赘述,为了方便管理,我们在Animation文件夹里新建一个文件夹Enemy来保存我们敌人的动画资源
这里我们只做了idle和run的动画即可,然后我们在Animator设置动画间的转换
用一个bool变量控制即可,run为true时切换成run动画,run为false切换成idle动画
然后我们设置一下敌人移动的边界,这个和角色检测是否碰到地面给角色添加一个checkground是一样的操作,我们给Enemy_Frog添加两个空组件,分别命名为left和right,分别表示青蛙可以移动的左右边界,然后我们调整一下位置
然后我们给Enemy_Frog写一下脚本来控制它的行动,新建一个C#文件之后直接拖给Enemy_Frog即可
我们打开脚本,照例先获得collider,rigidbody等引用,注意Enemy_Frog要继承Enemy类,而且重载了虚函数Start,所以Start函数前面要加上override
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy_Frog : Enemy
{
private Collider2D coll;
private Rigidbody2D rb;
public int speed;
//---------上面照旧----------
public Transform left, right;//左边边界的引用
private float leftMargin, rightMargin;//Frog可以移动的左右边界
private bool faceRight=true;//开始青蛙是面向右的
protected override void Start()
{
base.Start();//调用父类的Start函数
coll = GetComponent();
rb = GetComponent();
//分别获得左右边界位置的x轴的大小
leftMargin = left.position.x;
rightMargin = right.position.x;
//防止左边边界跟随敌人移动,获得后直接销毁
Destroy(left.gameObject);
Destroy(right.gameObject);
}
void Update()
{
Movement();
}
//基础移动
void Movement()
{
//如果面向右
if(faceRight)
{
anim.SetBool("run", true);
rb.velocity = new Vector2(speed, rb.velocity.y);
//当敌人超过右边界时
if (transform.position.x > rightMargin)
{
rb.velocity = new Vector2(0, 0);
transform.localScale = new Vector3(-1, 1, 1);
faceRight = false;
}
}
else
{
anim.SetBool("run", true);
rb.velocity = new Vector2(-speed, rb.velocity.y);
//当敌人越过左边界时
if(transform.position.x
代码里面重点已经注释过了,需要注意的是左右边界在敌人移动时也会跟着移动,所以我们让获得左右边界之后让他们销毁。
然后这样我们点击运行之后敌人就会左右移动了,但是当敌人碰到人物的时候敌人会飞起来,这是因为敌人和人物一样都是用的圆形碰撞器,所以会滑出去,但是又不能用盒形碰撞器,所以这里我们把敌人的重力调高一点就行,这里我们调成5即可
然后当人物碰到敌人时,人物应该有个受击动画,所以我们实现一下人物撞到敌人时的效果
我们给人物添加一个受击动画,接着在Animator那里新建一个bool条件hurt来判断人物是否受击,稍微调整一下
其中run,idle和jump都可以切换到hurt状态,条件都是hurt为true,hurt可以退回到idle状态,条件是hurt为false
然后我们就需要判断人物是否碰到敌人,我们先创建一个布尔变量来判断人物是否受击,当人物没有碰到敌人时在执行Movement函数,这里稍微修改一下FixedUpdate函数里的内容,当人物未受击时执行movement函数
这里我们不用触发器,因为把敌人当做触发器的话,敌人就会落下去,因为添加了Rigidbody,所以我们这里用tag判断,我们创建一个Enemytag,把Enemy_Frog的tag改成Enemy
然后我们判断角色碰到的物体的tag是否是Enemy,然后执行后面的操作
切换动画还没有设置,所以在SwitchAnim函数修改一下,这里要注意这个受击代码要放在地面代码的前面,不然会优先执行人物在地面时的操作,不会执行人物受击的操作
我们点击运行之后发现人物会停不下来,这是因为我们给人物的圆形碰撞器添加了光滑的物理材质,所以人物会停不下来,为了让人物可以停下来,我们给人物添加一个盒型碰撞器,把圆形碰撞器的物理材质取消,添加到盒型碰撞器那里,然后调整一下位置,让盒型碰撞器在上,圆形碰撞器在下
这样就ok了,我们再简单设置一下人物跳到敌人头上将敌人消灭并且跳一下的操作
因为所有的敌人都有这个操作,所以我们把这个功能放到敌人总类那里实现
然后在人物那里执行这个功能
这样就实现了人物跳到敌人头上消灭敌人的功能,不过这样还不是很完美,后面我们再做修改。
最后贴一下代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
protected Animator anim;
protected virtual void Start()
{
anim = GetComponent();
}
public void Death()
{
Destroy(gameObject);
}
}
Enemy_Frog
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy_Frog : Enemy
{
private Collider2D coll;
private Rigidbody2D rb;
public int speed;
//---------上面照旧----------
public Transform left, right;//左边边界的引用
private float leftMargin, rightMargin;//Frog可以移动的左右边界
private bool faceRight=true;//开始青蛙是面向右的
protected override void Start()
{
base.Start();//调用父类的Start函数
coll = GetComponent();
rb = GetComponent();
//分别获得左右边界位置的x轴的大小
leftMargin = left.position.x;
rightMargin = right.position.x;
//防止左边边界跟随敌人移动,获得后直接销毁
Destroy(left.gameObject);
Destroy(right.gameObject);
}
void Update()
{
Movement();
}
//基础移动
void Movement()
{
//如果面向右
if(faceRight)
{
anim.SetBool("run", true);
rb.velocity = new Vector2(speed, rb.velocity.y);
//当敌人超过右边界时
if (transform.position.x > rightMargin)
{
rb.velocity = new Vector2(0, 0);
transform.localScale = new Vector3(-1, 1, 1);
faceRight = false;
}
}
else
{
anim.SetBool("run", true);
rb.velocity = new Vector2(-speed, rb.velocity.y);
//当敌人越过左边界时
if(transform.position.x
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class playercontroller : MonoBehaviour
{
private Rigidbody2D rb;//获得Rigidbody2D组件
private Collider2D coll;//获得Collider2D组件
private Animator anim;//获得动画组件
public float speed, jumpForce;//公开,设置速度和跳跃力
public LayerMask ground;//获得地面图层
public Transform groundCheck;//检测角色是否碰到地面
private bool isJump, isGround;//判断是否按下空格键,判断是否在地面
private int jumpCount;//用来设置角色是几段跳
private int cherries;//计数变量
public Text cherryText;//樱桃计数的UI组件
private bool isHurt;//受击判断
//初始化
void Start()
{
rb = GetComponent();
coll = GetComponent();
anim = GetComponent();
}
void Update()
{
//如果按下空格键并且在地面上
if (Input.GetKeyDown(KeyCode.Space) && jumpCount > 0)
{
isJump = true;
}
}
private void FixedUpdate()
{
isGround = Physics2D.OverlapCircle(groundCheck.position, 0.1f, ground);
if(!isHurt)
{
Movement();
}
Jump();
SwitchAnim();
}
//基础移动
void Movement()
{
float horizontal = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(horizontal * speed, rb.velocity.y);//设置x轴的移动
//设置角色的转向问题
if (horizontal != 0)
{
transform.localScale = new Vector3(horizontal, 1, 1);
anim.SetBool("run", true);
}
else
anim.SetBool("run", false);
}
//跳跃
void Jump()
{
//如果在地面设置二段跳
if (isGround)
{
jumpCount = 2;
}
//按下跳跃键且在地面上
if (isJump && isGround)
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
jumpCount--;
isJump = false;
}
//按下跳跃键且不在地面上且jumpCount大于0
else if (isJump && !isGround && jumpCount > 0)
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
jumpCount--;
isJump = false;
}
}
//切换动画
void SwitchAnim()
{
//如果在下落状态
if (rb.velocity.y < 0 && !isGround)
{
anim.SetBool("fall", true);
anim.SetBool("jump", false);
}
//如果在跳跃状态
if (!isGround && rb.velocity.y > 0)
{
anim.SetBool("jump", true);
}
//如果人物受击
else if (isHurt)
{
anim.SetBool("hurt", true);
if (Mathf.Abs(rb.velocity.x) < 0.1f)
{
anim.SetBool("hurt", false);
isHurt = false;
}
}
//如果在地面上
else if (coll.IsTouchingLayers(ground))
{
anim.SetBool("fall", false);
}
}
//判断是否碰到物品
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag=="Collection")
{
Destroy(collision.gameObject);
cherries++;
cherryText.text = "樱桃:" + cherries;
}
}
//消灭敌人
private void OnCollisionEnter2D(Collision2D collision)
{
//判断是否碰到敌人
if(collision.gameObject.tag=="Enemy")
{
//消灭敌人
Enemy enemy = collision.gameObject.GetComponent ();//获得敌人父类引用
if(anim.GetBool("fall"))//判断是否下落跳到敌人头上消灭敌人
{
enemy.Death();//调用父类函数
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
anim.SetBool("jump", true);
}
//如果人物在敌人的左边
else if(transform.position.xcollision.gameObject.transform.position.x)
{
rb.velocity = new Vector2(8, rb.velocity.y);
isHurt = true;
}
}
}
}
如有错漏之处,欢迎指正!