Unity 2D游戲 模擬物理系統


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是在上面代碼的基礎上模擬效果出來的一個三維彈球的效果 使用了球形射線 置位置 計算了入射角和反射角等

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM