【Unity3D】实现横版2D游戏——单向平台(简易版)
目录
问题
项目Demo直接使用免费资源:Hero Knight - Pixel Art (Asset Store搜索)
打开Demo场景,进行如下修改,注意Tag是自定义标签SingleDirCollider
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SingleDirCollider : MonoBehaviour
{
public float minY { get; private set; }
public float maxY { get; private set; }
private void Awake()
{
var bounds = GetComponent<BoxCollider2D>().bounds;
minY = bounds.min.y;
maxY = bounds.max.y;
}
}
对HeroKnight增加如下脚本:
核心思路:利用Physics2D.IgnoreCollision(碰撞器1,碰撞器2,[true])忽略或恢复传入的2个碰撞器之间的碰撞。
利用OnTriggerEnter2D去做提前检测角色和平台是否接触,而不是OnCollisionEnter2D,如果使用OnCollisionEnter2D,你会看到碰撞物理效果会先发生,导致忽略碰撞就没有意义了,角色会直接碰到单向平台往下掉。为了保证触发比碰撞先执行,需要将触发器往下偏移一点。当触发时检测到角色在平台之下则直接忽略碰撞,直到OnTriggerExit2D触发时,检测角色在平台之上或平台之下才进行恢复碰撞。
角色身上会挂有5个小圆形触发器,并且必然在碰撞之前触发的,平台的碰撞器不需要改为触发器,小球触发器也能检测到碰撞器执行触发的。
注意:传递到协程的参数不要是Collision2D,而是Collider2D,假设传的是Collision2D,可能在等待若干秒后Collision2D的collider(Collider2D)已经置空了,因为碰撞消息已经结束了。
利用OnCollisionStay2D 当玩家处于平台上时,按下↓键就会忽略碰撞,从而直接让玩家坠落,直到OnTriggerExit2D触发时,检测角色在平台之上或平台之下才进行恢复碰撞。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HeroColliderController : MonoBehaviour
{
BoxCollider2D heroCollider;
bool isOn;
private void Awake()
{
heroCollider = GetComponent<BoxCollider2D>();
}
IEnumerator StartIgnoreCollision(Collider2D collider2D)
{
Debug.Log("忽略碰撞");
Physics2D.IgnoreCollision(heroCollider, collider2D); //这2个碰撞体互相忽略碰撞
//等待人物完整地达到平台上 或 平台下时 退出死循环
while (isOn)
{
yield return new WaitForEndOfFrame();
}
Debug.Log("恢复");
Physics2D.IgnoreCollision(heroCollider, collider2D, false); //恢复
isOn = false;
}
private void OnTriggerEnter2D(Collider2D collision)
{
//实现从下往上跳跃穿过单向平台效果
if (collision != null && collision.gameObject.tag == "SingleDirCollider" && !isOn)
{
//this.transform.position.y + 0.062f是人物minY
if (collision.GetComponent<SingleDirCollider>().maxY > (this.transform.position.y + 0.062f))
{
IgnoreCollision(collision);
}
}
}
private void OnCollisionStay2D(Collision2D collision)
{
//实现按下↓键位 让角色从单方向平台往下掉效果
if (Input.GetKeyDown(KeyCode.DownArrow) && collision.gameObject.tag == "SingleDirCollider")
{
IgnoreCollision(collision.collider);
}
}
public void IgnoreCollision(Collider2D collision)
{
isOn = true;
StartCoroutine(StartIgnoreCollision(collision));
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision != null && collision.gameObject.tag == "SingleDirCollider" && isOn)
{
var singleDirCollider = collision.GetComponent<SingleDirCollider>();
//this.transform.position.y + 0.062f是人物minY, this.transform.position.y + 1.31f是人物maxY
//当人物完整地位于平台之上 或 之下时
if (singleDirCollider.maxY < (this.transform.position.y + 0.062f) || singleDirCollider.minY > (this.transform.position.y + 1.31f))
{
//协程死循环退出
isOn = false;
}
}
}
}
该案例无法适应复杂平台,例如斜面平台、凹凸不平的,只能应用在平面且带有厚度。
因为是利用触发器来检测什么时候开始忽略碰撞、什么时候恢复碰撞的,最好是利用2D射线检测来做。推荐参考做,它内部实现了一套不依赖Unity物理的移动,即自己实现了类似让角色能站立在一个碰撞盒的效果:GitHub - prime31/CharacterController2D
大概看了下就是会一直有一个对自身受力方向做射线,检测到底板的话就会让Y轴速度恒定在0,来保持角色在平台上的效果,而穿越平台则是忽略单向平台所在Layer层的射线检测,等穿越完成后再恢复,而按↓键让角色从平台往下掉也是同理。
问题
OnCollisionStay2D 只会在角色移动时会触发,并非一直触发,检查2D刚体确实是持续触发的状态的,所以最好还是射线吧
解决修改为: