Unity2D角色跳上天花板的方法
写在前面:
自学Unity2D中,本文所用方法是我作为初学者自己设想出来的方法,在实际生产中可能存在更好,更优的解决办法,欢迎大佬们指出。 本内容基于B站Unity官方讲师M_Studio的视频:Unity教程 Your First Game|入门Tutorial:07 跳跃动画 LayerMask和其之前内容所做,本文直接从其已提及知识点开始讲,相关知识点可以去看该UP主的视频进行学习。其后继视频可能有提及相关解决办法,此处仅为我个人在当时的设想和实际解决测试。
1.具体问题
视频中UP主采用对地图地形添加Tilemap Collider 2D组件,和向玩家对象的脚部区域添加Circle Collider 2D组件并将原本的Box Collider 2D移至头部作为将来的头部碰撞检测来实现玩家脚部对地图地形的碰撞检测,但是在实际游玩中,容易发生玩家脚碰到墙壁地形会导致角色卡住在墙上的问题,同时也玩家角色也不能像冒险岛一样实现从下面的地形跳上上面的地形的问题(会因为碰撞到地形的底部,即“天花板”)。
2.需求分析
1.当前游戏地形主要分类两类:地面地形和墙壁地形。
2.玩家可以通过直接跳跃的方式从低处的地面地形中跳上高处的地面地形(穿过“天花板”)
3.玩家碰撞到墙壁的时候X轴方向的运动会停下来,但是Y轴的运动不变。
3.解决思路
在Collider中存在一个属性:is trigger,该属性在未被勾选时,如果对象的Collider与其他的Collider之间发生碰撞时,会对该对象进行碰撞的计算。该项目中玩家与地形的碰撞如同显示中人与地形的碰撞,玩家(人)因为与墙壁发送碰撞,导致在x轴方向上的速度被减为0。
但如果将is trigger属性勾选上,虽然项目中Collider.IsTouchingLayers(ground)的判断结果依然为true,但是对象却不会因此而对当前的运动状态进行重新的计算,即实现穿墙的效果。
4.实际解决
4.1修改地图的tilemap collider
打开地图资源的spride editor,进入"Custom Physics Shape",选择要修改碰撞器的资源,点击"Generate",此时界面会重置并展示当前资源的碰撞器范围,根据自己需求修改碰撞器范围。修改后记得重新添加如Tile Palette中。
4.2绘制地形和墙壁的Tilemap
绘制两个瓦片地图,此时我们可以看到我们的地形地图中,地形的tilemap collider只剩下了我们在spride editor中修改的那顶部的部分了,另外记得添加Layer Tag
4.3实现向上的跳跃
按照思路分析,我们可以让角色在跳跃的时候将is trigger属性设置为true,此时角色不会因为在向上跳跃的时候因为撞天花板而停下。
if (Input.GetKey(KeyCode.Space) && onFloor == true)
{
rigidBody.velocity = new Vector2(rigidBody.velocity.x, jumpForce*Time.deltaTime);
animator.SetBool("jumping", true);
onFloor = false;
footCollider.isTrigger = true;
}
同时,当角色因为下落而触碰到地面是,角色才会因此停下来,此时我们的is trigger就可以修改回来了。
if (footCollider.IsTouchingLayers(ground) && animator.GetBool("falling"))//当向下掉落到地面上时
{
footCollider.isTrigger = false;
animator.SetBool("falling", false);
onFloor = true;
rigidBody.velocity = new Vector2(rigidBody.velocity.x, 0f);
}
4.4实现撞墙的效果
当角色撞墙的时候,X轴方向运动停止,但是Y轴方向运动继续,当角色在地面上的时候,is trigger为真,此时系统会自己让角色的X轴方向运动停止,因此我们只需要在角色处于空中的时候进行修改即可。
void HitWall()
{
if(footCollider.isTrigger == true && footCollider.IsTouchingLayers(wall))
{
rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y);
}
}
//也可以写成
void HitWall()
{
if((animator.GetBool("falling") || animator.GetBool("jumping")) == true && footCollider.IsTouchingLayers(wall))
{
rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y);
}
}
5.代码总和
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Animator animator;//玩家的动画
private Rigidbody2D rigidBody;//玩家的刚体
public Collider2D footCollider;//脚的碰撞体
public LayerMask ground;//地面
public LayerMask wall;//墙壁
public float speed;//移动速度
public float jumpForce;//跳跃力度
public Boolean onFloor;
void Start()
{
rigidBody = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
onFloor = false;
}
void FixedUpdate()
{
Move();
SwitchAnimation();
HitWall();
}
void Move()
{
if (onFloor == true)
{
if (Input.GetKey(KeyCode.A))
{
rigidBody.velocity = new Vector2(-speed * Time.deltaTime, rigidBody.velocity.y);
transform.localScale = new Vector3(-1, 1, 1);
animator.SetBool("running", true);
}
else if (Input.GetKey(KeyCode.D))
{
rigidBody.velocity = new Vector2(speed * Time.deltaTime, rigidBody.velocity.y);
transform.localScale = new Vector3(1, 1, 1);
animator.SetBool("running", true);
}
else
{
animator.SetBool("running", false);
}
}
if (Input.GetKey(KeyCode.Space) && onFloor == true)
{
rigidBody.velocity = new Vector2(rigidBody.velocity.x, jumpForce*Time.deltaTime);
animator.SetBool("jumping", true);
onFloor = false;
footCollider.isTrigger = true;
}
}
void SwitchAnimation()
{
if (rigidBody.velocity.y < 0 && !animator.GetBool("falling"))//当角色向下掉落时
{
animator.SetBool("jumping", false);
animator.SetBool("falling",true);
footCollider.isTrigger = true;
}
if (footCollider.IsTouchingLayers(ground) && animator.GetBool("falling"))//当向下掉落到地面上时
{
footCollider.isTrigger = false;
animator.SetBool("falling", false);
onFloor = true;
rigidBody.velocity = new Vector2(rigidBody.velocity.x, 0f);
}
}
void HitWall()
{
if((animator.GetBool("falling") || animator.GetBool("jumping")) && footCollider.IsTouchingLayers(wall))
{
rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y);
}
}
}
6.运行结果