zoukankan      html  css  js  c++  java
  • 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;
        }
    

    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;
            }
    }
    
  • 相关阅读:
    linux socket c : send data when socket close—SIGPIPE, Broken pipe
    (OK) server-client-pthread-c language
    (OK) Linux epoll模型—socket epoll server client chat—pthread
    (OK) pthread—epoll-loops-on-disconnection-of-a-client—server
    (OK) Linux epoll模型—socket epoll server client chat
    (OK) 刘姐实验中的大数据分析—awk—paste—system
    (OK) cBPM-CentOS—wrapped by fastcgi—files—executing commands
    (Not OK) 在CentOS7—编译nginx—for—Android—Makefile
    (Not OK) 在CentOS7—编译nginx—for—Android
    (OK) 在CentOS7—编译OpenSSL 静态库—for—Android
  • 原文地址:https://www.cnblogs.com/zhxmdefj/p/10740095.html
Copyright © 2011-2022 走看看