注:该文为学习笔记,具体请看b站upMichael-Studio的unity教程2D入门:13敌人Enemy!
环境:unity2020.3
敌人效果:若玩家碰到敌人会掉血,若玩家踩到敌人的头上,则可以将敌人消灭,平时敌人会左右移动
准备工作
1.将素材导入
2.将素材尺寸调整好——Pixels Per Unit
即每个小格代表多大的像素
开始
3.创建2Dsprite
4.给该sprite添加图片,一般默认是最常用的一张图——Sprite属性
另外如果不显示,则需要调整sorting layer
添加重力效果和碰撞组件
5.添加Rigidbody2D 和 collider
如果是tile碰撞体记得锁定RigidBody的z轴
添加动画效果
6.添加animator
7.在asset中创建一个新的animator controller,并赋值给animator的Controller属性
具体添加动画的内容不再赘述
消灭敌人
该函数为内置的函数,意为:当碰到2D碰撞体时执行
为了实现“踩到敌人将敌人消灭”我的判断条件是当Player的动画状态是falling时...判断条件不唯一,根据需要来
如果不是falling的状态则做出另外的反应(代码省略)
如果想要游戏有更好的体验,可以在消灭敌人后有一些小反馈,比如反作用力向上起跳一小段
这里有个小bug,如果“下落”不是“弹起-->下落",而是自由落体,那么不仅Player的动画不会切换到falling,而且踩在enemy头上也不会将它消灭,反而会掉血,改正方法如下
//下落切换fall效果
if(rb.velocity.y<0.1f&&!Coll.IsTouchingLayers(Groud))
{
Anim.SetBool("falling",true);
}
受伤Hurt效果
首先hurt动画需要读者自己给Player添加好
Plyer: 被撞到后往后弹一段距离,切换Hurt动画并掉血
首先,这部分代码写在刚才else的括号中,如果想要有回弹的效果,本质上是改变了velocity,但是在update中不断的调用左右移动的函数,在该函数中的velocity改变值没来得及实现就被盖过去了,为了防止这种情况出现,我们需要这样做。
首先,else部分添加如下代码
注:ishurt为一个bool值,可以帮助我们在hurt期间先停止左右移动的刷新,只需在update中如下写
什么时候才可以让ishurt重归为false呢?
移动一段距离、经历一段时间、又或是velocity的速度减小为0
这里用的是最后一个方法,请注意,最后一个方法默认的是有摩擦力,也就是速度确实是在不断减小的,我们可以把Player-->Rigidbody-->linear Drag改大一些
然后写下面的代码:↓
至于这几行代码该写在哪里?写在update中或者update调用的任意一个函数中,这里我写在了统一改变Player状态的函数中
接下来,Player切换受伤效果动画
1.需要添加一个bool参数hurt
2.动画间的关系如下,idle、run、jump都可能切换到hurt状态
3.切换时的参数调整记得注意
最后hurt应该能回到状态idle 条件为 hurt==false&&idle==true
其他地方不用改,此处改几行代码即可
PlayerControler代码如下,需要自取
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
//控制角色的移动
//控制物品的收集
public class PlayerControler : MonoBehaviour
{
//获取刚体
private Rigidbody2D rb;//物理
private Animator Anim;//动画
private bool getCherry=false;
private bool ishurt=false;
public float speed;
public float Jumpforce;
public LayerMask Groud;//LayerMask是获得图层的
public Collider2D Coll;//player's colliderBox
public int Cherry=0;//吃到的cherry的个数
//UI
public Text Cherrynumber;
// Start is called before the first frame update
void Start()
{
rb=GetComponent<Rigidbody2D>();
Anim=GetComponent<Animator>();
}
// Update is called once per frame
void FixedUpdate()//
{
if(ishurt==false)
Movement();
SwitchAnim();
if(getCherry)
{
Cherry+=1;
getCherry=false;
Cherrynumber.text=Cherry.ToString();
}
}
void Movement()
{
float Horizontalmove=Input.GetAxis("Horizontal");//获得玩家横向的输入 -1:left 0:static 1:right 是有-1--1的小数的
float FaceDirection=Input.GetAxisRaw("Horizontal");//Row可以直接获得-1.0.1的整数
//角色移动
if(Horizontalmove!=0)
{
rb.velocity=new Vector2(Horizontalmove*speed*Time.deltaTime,rb.velocity.y);//Time.deltaTime单位时间
Anim.SetFloat("running",Mathf.Abs(Horizontalmove));//调用动画的代码 改动相应的 参数
}else
{
Anim.SetFloat("running",0);//如果不回复的话,running的值会一直在
}
//根据 向左向右移动反转身体
if(FaceDirection!=0)
{
transform.localScale=new Vector3(FaceDirection,1,1);//直接用的transform 就是挂脚本的组件的
}
//跳跃
if(Input.GetButtonDown("Jump"))
{
if(Coll.IsTouchingLayers(Groud))
{
rb.velocity=new Vector2(rb.velocity.x,Jumpforce*Time.deltaTime);
Anim .SetBool("jumping",true);
Anim .SetBool("idle",false);
}
}
}
void SwitchAnim()
{
if(Anim.GetBool("jumping"))
{
if(rb.velocity.y<0)
{
Anim .SetBool ("jumping",false);
Anim .SetBool ("falling",true);
}
}
else if(ishurt)
{
Anim.SetBool("hurt",true);
if(Mathf.Abs(rb.velocity.x)<0.1f)
{
Anim.SetBool("hurt",false);
Anim.SetBool("idle",true);
ishurt=false;
}
}
else if(Coll.IsTouchingLayers(Groud ))
{
Anim.SetBool("falling",false);
Anim.SetBool ("idle",true);
}
}
//捡起樱桃
private void OnTriggerEnter2D(Collider2D obj)
{
if(obj.tag=="Collection")
{
Destroy(obj.gameObject);
getCherry=true;
}
}
//消灭敌人
private void OnCollisionEnter2D(Collision2D other)
{
if(other.gameObject.tag=="enemy")
{
//只有掉落的时候才会消失
if(Anim.GetBool("falling"))
{
Destroy(other.gameObject);
rb.velocity=new Vector2(rb.velocity.x,Jumpforce*Time.deltaTime);
Anim.SetBool("jumping",true);
}
//enemy在左侧 向左后退
else if(transform.position.x<other.gameObject.transform.position.x)
{
rb.velocity=new Vector2(-5,rb.velocity.y);
ishurt=true;
}
//同理
else if(transform.position.x>other.gameObject.transform.position.x)
{
rb.velocity=new Vector2(5,rb.velocity.y);
ishurt=true;
}
}
}
}
移动AI
上面都是Player对Enemy反应的部分,下面写一下Enemy在平时自己来回走动的代码,当然了,也可能走着走着跳一下啥的,不过,我还不会~
思路:给enemy设一个左右的限制,判断enemy的位置,假如超过了左侧界限则向右转并向右走,反之亦然,这一部分只需要比较transform.position,并适时改变rigidbody.velocity和transform.localscale
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy_Frog : MonoBehaviour
{
private Rigidbody2D rb;
public float Speed;
private float leftPoint,rightPoint;//左右终止点
private bool faceleft=true;
void Start()
{
rb=GetComponent<Rigidbody2D>();
leftPoint=transform.position.x-2;
rightPoint=transform.position.x+2;
//transform.DetachChildren();//改变该节点的transform时,该节点的孩子不受影响
}
void Update()
{
Movement();
}
void Movement()
{
if(faceleft)
{
rb.velocity=new Vector2(-Speed,rb.velocity.y);
if(transform.position.x<leftPoint)
{
transform.localScale=new Vector3(-1,1,1);
faceleft=false;
}
}else
{
rb.velocity=new Vector2(Speed,rb.velocity.y);
if(transform.position.x>rightPoint)
{
transform.localScale=new Vector3(1,1,1);
faceleft=true;
}
}
}
}