Unity 2D游戏 模拟物理系统

Wesley13
• 阅读 1193

1.为什么不用自带的物理系统

用unity进行2D游戏开发的时候一般都不会使用unity自带的2D物理系统,有几个因素:不可控(位置 旋转 )

2.静态的碰撞体

只带有碰撞体且没有刚体的物体属于静态碰撞体 如果需要让静态碰撞体移动 最好使用刚体使用物理方法 或者给对象加一个动力学刚体 不然也很消耗性能

3.移动

为了模拟真实的移动,你需要分别模拟 加速 匀速 减速  为了模拟这三种状态 你需要模拟加速度 和 摩擦力

    //变速写法
        inputHori = Input.GetAxis("Horizontal");
        float s = 0.8f * speedChangeFactor;
        speedIncrement = 0;
        frictionPower = 0;

        //使用摇杆就清除所有除了遥感之外的横向反向速度
        if (inputHori > 0.1f)
        {
            speedIncrement = s;
            if (shootDir.x < 0)
            {
                shootx = 0;
                shootSpeedX = 0;
            }
            if (reBoundDir.x < 0)
            {
                reBoundX = 0;
                reboundSpeedX = 0;
            }

        }
        else if (inputHori < -0.1f)
        {
            speedIncrement = -s;
            if (shootDir.x > 0)
            {
                shootx = 0;
                shootSpeedX = 0;
            }
            if (reBoundDir.x > 0)
            {
                reBoundX = 0;
                reboundSpeedX = 0;
            }
        }

         //摩擦力       
        if (moveSpeed > 0) { frictionPower = -Friction; }
        else if (moveSpeed < 0) { frictionPower = Friction; }
        
        moveSpeed += (speedIncrement + frictionPower) * Time.fixedDeltaTime;

        //静止方法 (这是偷懒方法 正确写法是摩擦力不会让速度反转 暂时不知道怎么写)
        if (Mathf.Abs(moveSpeed) < frictionPower * Time.fixedDeltaTime)
        { moveSpeed = 0; }
     //速度限制
        if (Mathf.Abs(moveSpeed) > MoveSpeed)
        { moveSpeed = (moveSpeed / Mathf.Abs(moveSpeed)) * MoveSpeed; }
        

这里的核心思路就是 利用摇杆的的变换来累计增量 而不是直接改位置 我们只对加速度进行控制 这里的 moveSpeed是一帧要移动的位置 当对加速度计算完毕后,再在Update最后一帧把所有的速度增量一起赋值给位置,因为在上一次模拟角色控制的时候在每一个需要移动的方法里都会对当前的位置进行改变,这样写虽然方便,但是主角的移动会有掉帧和卡顿的感觉,原因就是在一个Update里面多次修改主角的位置,

4.射线检测

unity里面的射线检测在使用的时候,有几个需要注意的盲点:一、3D的射线无法从碰撞体里面检测的物体,但是2D的射线可以,二、当主角一帧位移的距离大于射线长度的时候射线就会大概率检测不到。

射线检测是模拟物体是否碰到障碍,是否碰到物体的常用方法。为了解决盲点的第二个问题 ,就需要对所有的会对主角产生位移的方法,先算他们的加速度,在每一帧进行射线检测的提前将这一帧玩家要位移的距离计算出来,然后动态修改射线的长度。

注意当射线检测到的时候要注意将玩家的位置放置到合适的位置

void HorDownRayCheck()
    {       
        //提前计算位移量 posX是算好的这一帧的X方向的位移增量
        lengthX = Mathf.Abs(posX);
        lengthY = Mathf.Abs(posY);
        if (lengthY < 0.4f)
        {
            lengthY = 0.4f;
        }

        if (lengthX < 0.4f)
        {
            lengthX = 0.4f;
        }

        if (posX > 0)
        {
            horiRaynub = 1;
        }
        else if (posX < 0)
        {
            horiRaynub = -1;
        }
        else
        {
            if (inputHori > 0)
            {
                horiRaynub = 1;
            }
            else if (inputHori < 0)
            {
                horiRaynub = -1;
            }
        }

        RaycastHit2D horiHitOne = Physics2D.Raycast(horiRayOne.position, Vector3.right * horiRaynub, lengthX + 0.4f, 1 << 8);
        if (horiHitOne)
        {
            if (horiHitOne.collider.CompareTag("VeritualGround"))
            {
                transform.position = new Vector2(horiHitOne.point.x - 0.34f * horiRaynub, transform.position.y);
                isHorWallHere = true;
            }
        }
        RaycastHit2D horiHitTwo = Physics2D.Raycast(horiRayTwo.position, Vector3.right * horiRaynub, lengthX + 0.4f, 1 << 8);
        if (horiHitTwo)
        {
            if (horiHitTwo.collider.CompareTag("VeritualGround"))
            {
                transform.position = new Vector2(horiHitTwo.point.x - 0.34f * horiRaynub, transform.position.y);
                isHorWallHere = true;
            }
        }

        if (!horiHitOne && !horiHitTwo)
        {
            isHorWallHere = false;
        }

        RaycastHit2D downHitOne = Physics2D.Raycast(downRayOne.position, Vector3.down, lengthY, 1 << 8);
        if (downHitOne)
        {
            if (downHitOne.collider.CompareTag("DownGround"))
            {               
                transform.position = new Vector2(transform.position.x, downHitOne.point.y + 0.34f);
                currentState = states.ground;               
                isDownWallHere = true;                
                jumpTimes = JumpTimes;
                return;
            }
        }
        RaycastHit2D downHitTwo = Physics2D.Raycast(downRayTwo.position, Vector3.down, lengthY, 1 << 8);
        if (downHitTwo)
        {
            if (downHitTwo.collider.CompareTag("DownGround"))
            {              
                transform.position = new Vector2(transform.position.x, downHitTwo.point.y + 0.34f);
                currentState = states.ground;         
                isDownWallHere = true;
                jumpTimes = JumpTimes;
                return;
            }
        }

        isDownWallHere = false;  
    }

    void UpRayCheck()
    {
        if (posY < 0) return;
        RaycastHit2D upHitOne = Physics2D.Raycast(upRayOne.position, Vector3.up, lengthX, 1 << 8);
        if (upHitOne)
        {                                
            if (upHitOne.collider.CompareTag("UpGround"))
            {                               
                transform.position = new Vector2(transform.position.x, upHitOne.point.y - 0.34f);
                gvec = -0.2f;                
                return;
            }
        }
        RaycastHit2D upHitTwo = Physics2D.Raycast(upRayTwo.position, Vector3.up, lengthY, 1 << 8);
        if (upHitTwo)
        {     
            if (upHitTwo.collider.CompareTag("UpGround"))
            {                
                transform.position = new Vector2(transform.position.x, upHitTwo.point.y - 0.34f);
    //修改重力 让主角下坠 防止一直被上一行代码影响 这里有多种解决方法 目前这一种是配合模拟重力比较好用的
                gvec = -0.2f;                
                return;
            }
        }             
    }

5.模拟重力

void Gravity()
    {        
        gvec += Physics2D.gravity.y * Time.fixedDeltaTime * Time.fixedDeltaTime * JumpSpeed;
    }

将 gvec作为Y轴的增量给到付给位置就可以了 如果需要人物跳跃 只需要给gvec附一正值就可以了 jumpSeed是控制主角跳跃的速度 也同时是重力倍数

自己模拟物理移动是有趣并且很帮助理解物理的过程,重点就是模拟 当我们想要一种运动方式的时候 首先要了解它在真实世界运动的本质,不要尝试对位置直接修改一部到位。

下面这张gif是在上面代码的基础上模拟效果出来的一个三维弹球的效果 使用了球形射线 置位置 计算了入射角和反射角等

 Unity  2D游戏 模拟物理系统

点赞
收藏
评论区
推荐文章
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Unity小王子私藏的开发2D游戏的常用插件合集
Unity以开发3D游戏见长,早期版本的Unity在开发2D游戏时不慎方便,因此AssetStore出现了很多2D游戏开发引擎。现在Unity对2D游戏的支持越来越好,而这些开发2D游戏的Unity插件也得到了更多开发者的喜爱。1:RexEngine:Classic2DPlatformerEngine(https://www.os
Wesley13 Wesley13
3年前
Unity 4.x 2D游戏开发基础教程大学霸第一更
Unity4.x2D游戏开发基础教程大学霸第1章  Unity及其组成的介绍本书主要讲解的是,如何使用Unity开发2D游戏。但在开始讲解之前,最好先熟悉一下Unity这个工具。本章会首先介绍Unity的下载和安装,然后会介绍Unity界面的各组成部分,这些知
Wesley13 Wesley13
3年前
Unity 2D游戏开发快速入门第1章创建一个简单的2D游戏
Unity2D游戏开发快速入门第1章创建一个简单的2D游戏即使是现在,很多初学游戏开发的同学,在谈到Unity的时候,依然会认为Unity只能用于制作3D游戏的。实际上,Unity在2013年发布4.3版本的时候,就开始提供对制作2D游戏的支持了。例如,提供了一些专用于开发2D游戏的Unit
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
Unity 利用射线实现弹孔效果
1、利用 Camera.main.ScreenPointToRay从屏幕到鼠标点击位置生成一条射线。2、利用 Physics.Raycast 发射射线,同时判断是否碰撞到目标物体。3、通过 RaycastHit获取射线所碰撞到的位置。4、在目标位置生成一个弹孔预设体。 具体代码:using UnityEngine;
Wesley13 Wesley13
3年前
Java PinBall 简单弹球小游戏【4】碰撞检测及边界检测
4.游戏中的碰撞检测(1)弹球游戏中的边界碰撞检测。这个原理大家应该很容易就想到了,用游戏元素的坐标值与整场景的大小来比较就可以判断出元素是否碰到边界了,为了丰富游戏框架,我制作了一个游戏边界碰撞检测器类。大家可以参考类:BorderCrossing,代码我就不贴到这里了。(2)游戏中的元素相互之间的碰撞检测。这里的碰撞包括但
Wesley13 Wesley13
3年前
UIDynamic——UIKit动力学
简介UIKit动力学最大的特点是将现实世界动力驱动的动画引入了UIKit,比如重力,铰链连接,碰撞,悬挂等效果,即将2D物理引擎引入了UIKit注意:UIKit动力学的引入,并不是为了替代CA或者UIView动画,在绝大多数情况下CA或者UIView动画仍然是最优方案,只有在需要引入逼真的交互设计的时候,才需要使用UIKit动力学它是作为现有交互设计
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究