Unity 對(duì)象移動(dòng)(物理)

一蚯舱、準(zhǔn)備工作

1.創(chuàng)建項(xiàng)目

2.創(chuàng)建示例場(chǎng)景

a.創(chuàng)建3d ——Plane(地面)(0,0,0)和Sphere(游戲?qū)ο?(0,0.5,0)陆赋,Sphere添加拖尾組件TrailRenderer
b.創(chuàng)建腳本 MovingSphere ——該腳本掛載到Sphere上并添加Rigidboy組件


二拍柒、需求如下

  1. 控制剛體球體的速度灯节。
    2.支持通過(guò)跳躍進(jìn)行垂直移動(dòng)汁掠。
    3.檢測(cè)地面及其角度史隆。
    4.沿斜坡移動(dòng)谣妻。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MovingSphere : MonoBehaviour
{
    // 最大速度,通過(guò)Inspector面板調(diào)整勾拉,范圍0到100
    [SerializeField, Range(0f, 100f), Tooltip("最大速度")]
    private float maxSpeed = 10f;
    // 最大加速度鞠苟,通過(guò)Inspector面板調(diào)整,范圍0到100
    [SerializeField, Range(0f, 100f), Tooltip("最大加速度")]
    private float maxAcceleration = 10f;
    // 最大空中加速度熔酷,通過(guò)Inspector面板調(diào)整孤紧,范圍0到100
    [SerializeField, Range(0f, 100f), Tooltip("最大空氣加速度")]
    private float maxAirAcceleration = 1f;

    // 跳躍高度,通過(guò)Inspector面板調(diào)整拒秘,范圍0到10
    [SerializeField, Range(0f, 10f), Tooltip("跳躍高度")]
    private float jumpHeight = 2f;
    // 最大空中跳躍次數(shù)号显,通過(guò)Inspector面板調(diào)整臭猜,范圍0到5
    [SerializeField, Range(0, 5), Tooltip("最大空中跳躍次數(shù)")]
    private int maxAirJumps = 0;
    // 最大地面角度,通過(guò)Inspector面板調(diào)整押蚤,范圍0到90度
    [SerializeField, Range(0, 90), Tooltip("最大地面角度")]
    private float maxGroundAngle = 25;

    // 當(dāng)前速度
    Vector3 velocity;
    // 期望速度
    Vector3 desiredVelocity;

    // Rigidbody組件蔑歌,用于物理模擬
    Rigidbody body;
    // 是否需要跳起
    bool desiredJump;

    // 地面接觸點(diǎn)計(jì)數(shù)
    int groundContactCount;
    // 是否在地面上
    bool OnGround
    {
        get { return groundContactCount > 0; }
    }

    // 跳躍階段計(jì)數(shù)器
    int jumpPhase;

    // 地面法線與球體移動(dòng)方向的最小點(diǎn)積,用于判斷接觸角度
    float minGroundDotProduct;
    // 接觸點(diǎn)法線
    Vector3 contactNormal;

    // 當(dāng)Inspector面板中的值改變時(shí)調(diào)用
    private void OnValidate()
    {
        // 計(jì)算最大地面角度的余弦值
        minGroundDotProduct = Mathf.Cos(maxGroundAngle * Mathf.Deg2Rad);
    }

    // Awake在對(duì)象實(shí)例化時(shí)調(diào)用一次
    private void Awake()
    {
        // 獲取Rigidbody組件
        body = GetComponent<Rigidbody>();
        // 調(diào)用OnValidate以確保minGroundDotProduct被正確初始化
        OnValidate();
    }

    // 每幀調(diào)用
    void Update()
    {
        // 獲取玩家輸入
        Vector2 playerInput;
        playerInput.x = Input.GetAxis("Horizontal");
        playerInput.y = Input.GetAxis("Vertical");
        // 限制輸入向量的長(zhǎng)度為1
        playerInput = Vector2.ClampMagnitude(playerInput, 1f);

        // 根據(jù)輸入和最大速度計(jì)算期望速度
        desiredVelocity = new Vector3(playerInput.x, 0f, playerInput.y) * maxSpeed;

        // 如果按下跳躍鍵揽碘,則設(shè)置desiredJump為true
        desiredJump |= Input.GetButtonDown("Jump");

        // 根據(jù)地面接觸點(diǎn)數(shù)調(diào)整球體顏色(僅作為示例)
        GetComponent<Renderer>().material.SetColor("_Color", Color.white * (groundContactCount * 0.25f));
    }

    // FixedUpdate用于物理更新次屠,每固定時(shí)間間隔調(diào)用一次
    private void FixedUpdate()
    {
        // 更新?tīng)顟B(tài)信息
        UpdateState();
        // 根據(jù)當(dāng)前狀態(tài)調(diào)整速度
        AdjustVelocity();

        // 如果需要跳躍,則執(zhí)行跳躍動(dòng)作
        if (desiredJump)
        {
            desiredJump = false;
            Jump();
        }
        // 應(yīng)用新速度
        body.velocity = velocity;
        // 清除狀態(tài)信息雳刺,為下一幀準(zhǔn)備
        ClearState();
    }

    // 碰撞開(kāi)始和持續(xù)時(shí)調(diào)用
    private void OnCollisionEnter(Collision collision)
    {
        EvaluateCollision(collision);
    }
    private void OnCollisionStay(Collision collision)
    {
        EvaluateCollision(collision);
    }

    // 清除狀態(tài)信息
    void ClearState()
    {
        groundContactCount = 0;
        contactNormal = Vector3.zero;
    }

    // 根據(jù)當(dāng)前狀態(tài)和期望狀態(tài)調(diào)整速度
    void AdjustVelocity()
    {
        // 計(jì)算接觸平面的X軸和Z軸方向
        Vector3 xAxis = ProjectOnContactPlane(Vector3.right).normalized;
        Vector3 zAxis = ProjectOnContactPlane(Vector3.forward).normalized;

        // 計(jì)算當(dāng)前速度在X軸和Z軸上的分量
        float currentX = Vector3.Dot(velocity, xAxis);
        float currentZ = Vector3.Dot(velocity, zAxis);

        // 根據(jù)是否在地面上選擇加速度
        float acceleration = OnGround ? maxAcceleration : maxAirAcceleration;
        // 計(jì)算每幀最大速度變化量
        float maxSpeedChange = acceleration * Time.deltaTime;

        // 計(jì)算新的X軸和Z軸速度分量
        float newX = Mathf.MoveTowards(currentX, desiredVelocity.x, maxSpeedChange);
        float newZ = Mathf.MoveTowards(currentZ, desiredVelocity.z, maxSpeedChange);

        // 更新速度
        velocity += xAxis * (newX - currentX) + zAxis * (newZ - currentZ);
    }

    // 將向量投影到接觸平面上
    Vector3 ProjectOnContactPlane(Vector3 vector)
    {
        return vector - contactNormal * Vector3.Dot(vector, contactNormal);
    }

    // 更新?tīng)顟B(tài)信息劫灶,包括速度和接觸信息
    void UpdateState()
    {
        // 獲取當(dāng)前速度
        velocity = body.velocity;
        // 如果在地面上,重置跳躍階段和更新接觸法線
        if (OnGround)
        {
            jumpPhase = 0;
            if (groundContactCount > 0)
            {
                contactNormal.Normalize();
            }
        }
        else
        {
            // 如果不在地面上掖桦,將接觸法線設(shè)置為向上方向
            contactNormal = Vector3.up;
        }
    }

    // 根據(jù)碰撞信息更新地面接觸點(diǎn)和法線
    void EvaluateCollision(Collision collision)
    {
        for (int i = 0; i < collision.contactCount; i++)
        {
            Vector3 normal = collision.GetContact(i).normal;
            // 如果法線與Y軸的夾角小于最大地面角度本昏,則認(rèn)為是地面接觸
            if (normal.y >= minGroundDotProduct)
            {
                groundContactCount += 1;
                contactNormal += normal;
            }
        }
    }

    // 執(zhí)行跳躍動(dòng)作
    void Jump()
    {
        // 如果在地面上或者跳躍次數(shù)未超過(guò)限制,則執(zhí)行跳躍
        if (OnGround || jumpPhase < maxAirJumps)
        {
            jumpPhase += 1;
            // 計(jì)算跳躍速度
            float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight);
            float alignedSpeed = Vector3.Dot(velocity, contactNormal);
            // 如果球體已經(jīng)向上移動(dòng)滞详,則減少跳躍速度
            if (alignedSpeed > 0f)
            {
                jumpSpeed = Mathf.Max(jumpSpeed - velocity.y, 0f);
            }
            // 添加跳躍速度到當(dāng)前速度上
            velocity += contactNormal * jumpSpeed;
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凛俱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子料饥,更是在濱河造成了極大的恐慌蒲犬,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岸啡,死亡現(xiàn)場(chǎng)離奇詭異原叮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)巡蘸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門奋隶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人悦荒,你說(shuō)我怎么就攤上這事唯欣。” “怎么了搬味?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵境氢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我碰纬,道長(zhǎng)萍聊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任悦析,我火速辦了婚禮寿桨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘强戴。我一直安慰自己亭螟,他們只是感情好挡鞍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著媒佣,像睡著了一般匕累。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上默伍,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音衰琐,去河邊找鬼也糊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛羡宙,可吹牛的內(nèi)容都是我干的狸剃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼狗热,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼钞馁!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起匿刮,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤僧凰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后熟丸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體训措,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年光羞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绩鸣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纱兑,死狀恐怖呀闻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情潜慎,我是刑警寧澤捡多,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站勘纯,受9級(jí)特大地震影響局服,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驳遵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一淫奔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堤结,春花似錦唆迁、人聲如沸鸭丛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鳞溉。三九已至,卻和暖如春鼠哥,著一層夾襖步出監(jiān)牢的瞬間熟菲,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工朴恳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抄罕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓于颖,卻偏偏與公主長(zhǎng)得像呆贿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子森渐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容