目录
一、敌人的实现
今天试图将敌人添加进来让游戏变得更有趣
首先按照之前的步骤,使用素材制作三种敌人的动画
这里面需要添加的组件(敌人首先需要是个刚体,然后要加入动画,碰撞体)
这里的一些操作和之前的博客没有什么不同,就不过多赘述了,这里我们要尝试加入人物消灭怪物的效果,这里的思路和收集品的思路是一样的,但是注意,我们这里不能把碰撞体设为触发器,这样会导致怪物直接掉出地图,所以我们这里使用另外一个函数
和之前收集品的思路一样为了让我们的脚本可以识别当前正在碰撞的物品是什么,我们需要添加一个Tag
这样我们就可以在脚本中通过这个Tag实现对物体种类的分辨,但是我们这里发现我们写完代码后会出现一个bug就是我们只要碰到了就会消灭怪物,这不符合我们的想法,我们的思路是踩一下才会消灭,所以我们使用是否处于掉落状态来作为消灭的条件
然后为了实现踩怪跳的效果,可以在销毁的时候加上跳跃的函数
private void OnCollisionEnter2D(Collision2D collision) //消灭怪物
{
if(anim.GetBool("falling") && collision.gameObject.tag == "enemy")
{
Destroy(collision.gameObject); //消灭怪物
rb.velocity = new Vector2(rb.velocity.x, jumpforce); //跳跃
anim.SetBool("jumping", true);
}
}
二、受伤效果的实现
那么我们有了敌人自然要做出受伤的效果,首先我们的想法是当玩家碰到敌人后,会受伤并反弹,我们首先在代码中实现这样一个功能。
if(collision.gameObject.tag == "enemy")
{
if(anim.GetBool("falling"))
{
Destroy(collision.gameObject); //消灭怪物
rb.velocity = new Vector2(rb.velocity.x, jumpforce); //跳跃
anim.SetBool("jumping", true);
}
else if (transform.position.x < collision.gameObject.transform.position.x) //如果玩家在敌人的左侧
{
rb.velocity = new Vector2(-10, rb.velocity.y);
}
else if (transform.position.x > collision.gameObject.transform.position.x) //如果玩家在敌人的左侧
{
rb.velocity = new Vector2(10, rb.velocity.y);
}
}
但是执行后我们发现并没有出现反弹的效果,经过分析,是因为我们的Move函数会不断的执行,覆盖了我们的改变,所以我们定义一个bool值来判断是否受伤,在受伤的时候不执行Move
private bool isHurt;
if(!isHurt)
{
Move();
}
我们会在玩家受伤的地方来将这个变量改成true,但是又出现了新的bug就是玩家会一直后退不能停下,我们需要在动画切换函数里面把玩家的状态切换回来,所以这里我们先把动画加进来,然后把这个动画加入动画器同时加入一个hurting的变量作为判断条件
然后我们重写动画转换函数的部分
if(Mathf.Abs(rb.velocity.x) < 0.1f) //人物可以返回站立状态
{
anim.SetBool("hurting", false);
isHurt = false;
anim.SetBool("idleing", true);
}
这样下来就实现了我们的效果,还是比较简单的。
三、代码优化及二段跳实现
几天下来我的代码已经十分凌乱,还有不少可以优化的地方我决定对代码进行优化,然后实现一个二段跳功能
public Transform groundCheck; //地面检测点
首先为了实现二段跳,我定义了一个地面检测点用于判断玩家是否回到地面,然后写了一个对应的判断的变量
public bool isGround;
注意,这里不初始化的时候,默认是false。
bool jumpPressed;
int jumpCount;
然后定义了跳跃按键按下和计数的变量
if(Input.GetButtonDown("Jump") && jumpCount > 0)
{
jumpPressed = true;
}
然后在Update中把原本的跳跃函数改成是否允许跳跃的代码,我们会另外实现一个跳跃函数来实现跳跃
我们首先给角色创建一个用于地面检测的空对象
我们把这个变量放在脚底
和之前的下蹲思路一样我们利用这个对象来检测是否站在地面上
isGround = Physics2D.OverlapCircle(groundCheck.position, 0.1f, ground);
跳跃的实现部分
void Jump() //控制玩家跳跃函数
{
if(isGround) //落到地面恢复跳跃次数
{
jumpCount = 2;
}
if(jumpPressed && jumpCount > 0) //跳跃
{
rb.velocity = new Vector2(rb.velocity.x, jumpforce);
jumpCount--;
jumpPressed = false; //确保跳跃执行完毕
}
}
此代码在fixedupdate中执行,至此我们就实现了流畅的二段跳,然后我们来修改角色动画
但是这样导致了一个问题就是在角色爬坡的时候会显示跳跃动画,我们通过对检测圆半径的调整来修复这个bug
然后我们删除没有使用的脚步碰撞体,然后我发现了一个新的bug就是在空中二连跳的时候,如果是下落状态二连跳不会切换回跳跃状态,所以修改动画器,添加一条边,成功解决此问题
总的代码如下(优化后)
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public Rigidbody2D rb; //刚体
public Collider2D bodyColl; //身体碰撞体
public Animator anim; //动画控制器
public float speed = 10; //速度
public float jumpforce; //跳跃力
public LayerMask ground; //碰撞体过滤
public int CherryCount = 0; //收集品樱桃计数器
public int GemCount = 0; //收集品宝石计数器
public Text Cherrynumber; //控制樱桃数目显示的UI
public Text Gemnumber; //控制樱桃数目显示的UI
public Transform groundCheck; //地面检测点
bool isGround, isHurt; //监测玩家状态
bool jumpPressed;
int jumpCount;
void Start()
{
}
void Update()
{
if(Input.GetButtonDown("Jump") && jumpCount > 0)
{
jumpPressed = true;
}
if (!Physics2D.OverlapCircle(rb.position, 0.1f, ground)) //判断人物在下蹲的时候不可以起身,
{
if (Input.GetButton("Crouch")) //实现角色下蹲
{
anim.SetBool("crouching", true);
bodyColl.enabled = false;
speed = 5; //下蹲速度减慢的效果
}
else //恢复姿态
{
anim.SetBool("crouching", false);
bodyColl.enabled = true;
speed = 10;
}
}
}
private void FixedUpdate()
{
isGround = Physics2D.OverlapCircle(groundCheck.position, 0.2f, ground); //检测玩家是否在地面
SwitchAnim();
Jump();
if(!isHurt)
{
Move();
}
}
void Move() //控制玩家移动函数
{
float Horizontaldirection = Input.GetAxisRaw("Horizontal"); //玩家移动方向
rb.velocity = new Vector2(Horizontaldirection * speed, rb.velocity.y);
if (Horizontaldirection != 0)
{
transform.localScale = new Vector3(Horizontaldirection, 1, 1); //控制角色翻转
}
}
void Jump() //控制玩家跳跃函数
{
if(isGround) //落到地面恢复跳跃次数
{
jumpCount = 2;
}
if(jumpPressed && jumpCount > 0) //跳跃
{
rb.velocity = new Vector2(rb.velocity.x, jumpforce);
jumpCount--;
jumpPressed = false; //确保跳跃执行完毕
}
}
void SwitchAnim() //玩家动画切换函数
{
anim.SetFloat("running", Mathf.Abs(rb.velocity.x)); //切换玩家奔跑的动画
if(isGround)
{
anim.SetBool("falling", false); //从下落回到站立
}
else if(rb.velocity.y > 0) //向上移动
{
anim.SetBool("jumping", true);
anim.SetBool("falling", false);
}
else if(rb.velocity.y < 0) //掉落
{
anim.SetBool("jumping", false);
anim.SetBool("falling", true);
}
if(isHurt) //从受伤状态恢复
{
anim.SetBool("hurting", true);
if(Mathf.Abs(rb.velocity.x) < 0.1f) //人物可以返回站立状态
{
anim.SetBool("hurting", false);
isHurt = false;
anim.SetBool("idleing", true);
}
}
}
private void OnTriggerEnter2D(Collider2D collision) //物品收集函数
{
if(bodyColl.IsTouching(collision) && collision.tag == "cherry") //如果身体碰到了樱桃
{
Destroy(collision.gameObject); //销毁游戏体
CherryCount++; //收集品数量加一
Cherrynumber.text = CherryCount.ToString();
}
if (bodyColl.IsTouching(collision) && collision.tag == "gem") //如果身体碰到了宝石
{
Destroy(collision.gameObject); //销毁游戏体
GemCount++; //收集品数量加一
Gemnumber.text = GemCount.ToString();
}
}
private void OnCollisionEnter2D(Collision2D collision) //消灭怪物
{
if(collision.gameObject.tag == "enemy")
{
if(anim.GetBool("falling"))
{
Destroy(collision.gameObject); //消灭怪物
rb.velocity = new Vector2(rb.velocity.x, jumpforce); //跳跃
jumpCount = 1; //踩到怪物后恢复跳跃次数,但是反弹用掉一次
anim.SetBool("jumping", true);
}
else if (transform.position.x < collision.gameObject.transform.position.x) //如果玩家在敌人的左侧
{
rb.velocity = new Vector2(-10, rb.velocity.y);
isHurt = true;
}
else if (transform.position.x > collision.gameObject.transform.position.x) //如果玩家在敌人的左侧
{
rb.velocity = new Vector2(10, rb.velocity.y);
isHurt = true;
}
}
}
}