详解Unity地面检测方案

 更新时间:2021年05月31日 14:35:55   作者:zhxmdefj  
本文主要介绍了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地面检测方案的资料请关注脚本之家其它相关文章!

相关文章

  • C#/VB.NET 实现彩色PDF转为灰度PDF

    C#/VB.NET 实现彩色PDF转为灰度PDF

    本文以C#代码为例介绍如何实现将彩色PDF文件转为灰度(黑白)的PDF文件,即将PDF文档里面的彩色图片或者文字等通过调用方法转为文档页面为灰色调、无彩色效果的文档。快来跟随小编一起学习吧
    2021-11-11
  • C#面向对象实现图书管理系统

    C#面向对象实现图书管理系统

    这篇文章主要为大家详细介绍了C#面向对象实现图书管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • C#利用KPM算法解决字符串匹配问题详解

    C#利用KPM算法解决字符串匹配问题详解

    Knuth-Morris-Pratt 字符串查找算法,简称为 “KMP算法”,常用于在一个文本串S内查找一个模式串P 的出现位置。本文将利用该算法解决字符串匹配问题,感兴趣的可以学习一下
    2022-11-11
  • C#实现屏幕抓图并保存的示例代码

    C#实现屏幕抓图并保存的示例代码

    这篇文章主要为大家详细介绍了如何利用C#实现屏幕抓图并保存的功能,文中的示例代码讲解详细,对我们学习C#有一定的帮助,感兴趣的小伙伴可以了解一下
    2022-12-12
  • 基于C#实现获取Windows所有窗口句柄

    基于C#实现获取Windows所有窗口句柄

    在做录屏或截屏操作时,需要获取当前正在运行中的桌面程序句柄,所以这篇文章主要为大家详细介绍了如何使用C#实现获取Windows所有窗口句柄,需要的可以参考下
    2023-12-12
  • Linq利用Distinct去除重复项问题(可自己指定)

    Linq利用Distinct去除重复项问题(可自己指定)

    这篇文章主要介绍了Linq利用Distinct去除重复项问题(可自己指定),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • C#操作进程的方法介绍

    C#操作进程的方法介绍

    这篇文章介绍了C#操作进程的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-02-02
  • wpf将表中数据显示到datagrid示例

    wpf将表中数据显示到datagrid示例

    这篇文章主要介绍了wpf将表中数据显示到datagrid示例,需要的朋友可以参考下
    2014-02-02
  • C# 位图BitArray的使用

    C# 位图BitArray的使用

    如果我们着重处理一个以位为单位的数据时,就可以考虑使用位数组。本文就介绍了C# 位图BitArray的使用,感兴趣的可以了解一下
    2021-06-06
  • C#中split用法实例总结

    C#中split用法实例总结

    这篇文章主要介绍了C#中split用法,结合实例形式总结分析了C#常见的split操作字符串相关技巧,需要的朋友可以参考下
    2016-06-06

最新评论