詳解Unity地面檢測方案
1.普通射線
在角色坐標(一般是腳底),發射一根向下的射線(長度約0.2)
但是簡單射線隻適用於簡單地形,實際使用中常常遇到以下問題
1.用collider去碰撞地面,某些時候會有一定的穿插,於是角色的最低點就可能穿透地面,你發射射線的點可能就到瞭地面以下,射線一直檢測不到真正的地面,於是角色就一直懸空
2.角色走斜坡時,角色中點可能會離開地面一小段距離,這一小段距離往往就足夠讓判斷機制誤以為角色已經離地,如果你增加射線的長度,那麼一定程度上能緩解斜坡問題,但是會降低跳躍判斷的精度,精度過低就有可能出現:角色跳起,會有那麼一小段距離,其實已經離地瞭,但是仍然返回瞭isGround = true;
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RaycastTest : MonoBehaviour { private bool isGround = false; private Rigidbody2D myRigidbody2D; void Awake () { myAnimator = GetComponent<Animator>(); myRigidbody2D = GetComponent<Rigidbody2D>(); } void FixedUpdate () { Debug.DrawRay(transform.position, Vector2.down * 0.11f, Color.red); RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down, 0.15f, 1 << 8); if (hit.collider != null) isGround = true; else isGround = false; }
2.Unity官方的Character Controller
直接給角色加入Character Controller組件,在腳本中Get到Character Controller,調用.isGrounded即可,但是實際效果讓人失望
因為.isGrounded是當角色移動的時候才會檢測是否著地的,也就是說它隻能在調用simplemove(和move等移動函數)時,判斷isGrounded(是否著地)
這時播放一些動畫會導致判斷在true和false狀態來回切換,並且Skinwidth也會導致這種問題,再加上一些角色控制器的限制,邏輯上不是那麼自由,例如需要自己實現物理模擬,比如重力,個人覺得非常麻煩,不推薦
using System.Collections; using System.Collections.Generic; using UnityEngine; public class OnGroundSensor : MonoBehaviour { public CapsuleCollider capcol; public float offset = 0.1f; private Vector3 point1; private Vector3 point2; private float radius; void Awake() { radius = capcol.radius - 0.05f; } void FixedUpdate() { point1 = transform.position + transform.up * (radius - offset); point2 = transform.position + transform.up * (capcol.height - offset) - transform.up * radius; Collider[] outputCols = Physics.OverlapCapsule(point1, point2, radius, LayerMask.GetMask("Ground")); if (outputCols.Length != 0) { //foreach (var col in outputCols) // print("collision:" + col.name); SendMessageUpwards("IsGround"); } else SendMessageUpwards("IsNotGround"); } }
3.三射線
這個方法是社團內的Aery同志傳授的,他應用到瞭一個2d卷軸遊戲上https://github.com/KillerAery/DarkAdventrue
寫法和簡單射線沒有什麼不同,區別在於給角色加上三條射線:左腳,右腳,襠
三條射線有一條返回true則isGround為true
在我們的這個2d遊戲上表現不錯
我們看看他的代碼
float dt = Time.deltaTime; //累計時間 vida.chargeTimer += dt; vida.jumpTimer += dt; //TODO 待優化代碼 //是否到地面 用三個groundCheck來檢測 //public Transform[] groundChecks = new Transform[3]; for (int i = 0; i < 3; ++i) { checkResult = Physics2D.Linecast(transform.position, groundChecks[i].position, 1 << LayerMask.NameToLayer("Ground")); vida.grounded = checkResult; if (vida.grounded) break; } if (vida.grounded) { //火箭蛋(硬件蛋特殊技能) if (vida.playerType == Tags.YingjianDan) { vida.jumpable = 1; } } //跳躍 //按下K時 if (Input.GetKeyDown(KeyCode.K) && vida.jumpTimer >= 0.03f) { if (vida.grounded) { //允許跳躍 vida.jumpTimer = 0.0f; vida.jump = true; } else if (vida.jumpable > 0) { vida.jumpable--; //允許跳躍 vida.jumpTimer = 0.0f; vida.jump = true; } }
4.OverlapCapsule 投射膠囊碰撞體
這個方法是看傅老師的黑魂復刻系列視頻學的
point0,point1,radius 分別為膠囊體起點球心,膠囊體終點球心,膠囊體半徑
我們這裡隻要用到這一重載方法 Physics.OverlapCapsule(pointBottom, pointTop, radius, LayerMask)
private CapsuleCollider capsuleCollider; private Vector3 pointBottom, pointTop; private float radius; void Awake () { capsuleCollider = GetComponent<CapsuleCollider>(); radius = capsuleCollider.radius; }
5.LayerMask設置方法
最後提下這個LayerMask
假設ground層為10,指定碰撞第10層Layer,寫法為:Layermask mask=1<<10
但是,投射的膠囊體也會檢測自己本身,如果你希望遊戲中基本上任何能碰撞物體都能夠用來站腳,那麼應設置為:碰撞除瞭角色所在的Layer以外的所有層(假設Player層為8
寫法為:~(1<<8)
bool OnGround() { pointBottom = transform.position + transform.up * radius-transform.up*overLapCapsuleOffset; pointTop = transform.position + transform.up * capsuleCollider.height - transform.up * radius; LayerMask ignoreMask = ~(1 << 8); colliders = Physics.OverlapCapsule(pointBottom, pointTop, radius, ignoreMask); Debug.DrawLine(pointBottom, pointTop,Color.green); if (colliders.Length!=0) { isOnGround = true; return true; } else { isOnGround = false; return false; } }
以上就是詳解Unity地面檢測方案的詳細內容,更多關於Unity地面檢測方案的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Unity 從UI中拖拽對象放置並拖動效果 附demo
- 詳解Unity入門之GameObject
- Unity3D實現經典小遊戲Pacman
- Unity實現物體運動時畫出軌跡
- Python實現C#代碼生成器應用服務於Unity示例解析