第二章.物理

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MovingSphere : MonoBehaviour
{
    //Vector3 playerInput;
    Vector3 desiredVelocity,velocity;
    //刚体
    Rigidbody rb;


    //环境判断变量
    bool desiredJump;
    int groundContactCount;
    bool OnGround => groundContactCount > 0;

    [Header("physic data")]
    [SerializeField]
    float maxSpeed;
    [SerializeField,Range(0f,100f)]
    float maxAcceleration,maxAirAcceleration;

    //air参数
    [Header("midAir")]
    [SerializeField]
    float jumpHeight;
    [SerializeField,Range(0,3)]
    int maxAirJumps;
    int jumpPhase = 0;
    //斜坡测试有关
    [SerializeField,Range(0,90)]
    float maxGroundAngle;
    float minGroundDotProduct;
    Vector3 contactNormal;

    
    void Awake()
    {
        rb = GetComponent<Rigidbody>();
        OnValidate();
    }

    // Update is called once per frame
    void Update()
    {   Vector3 playerInput;
        playerInput.x = Input.GetAxis("Horizontal");
        playerInput.z = Input.GetAxis("Vertical");
        desiredVelocity = new Vector3(playerInput.x,0f,playerInput.z) * maxSpeed;
        //Debug.Log($"{desiredVelocity}");
        desiredJump |= Input.GetButtonDown("Jump");
    }
    void FixedUpdate() 
    {
        UpdateState();

        AdjustVelocity();
        
        if(desiredJump)
        {
            desiredJump = false;
            Jump();
        }
        rb.velocity = velocity;
        //onGround = false;
        ClearState();
    }
    void Jump()
    {
        if(OnGround|| jumpPhase < maxAirJumps)
        {
            jumpPhase += 1;
            //V = sqr(-2gh)
            float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight);
            
            //速度在接触法线方向的分量
            float alignedSpeed = Vector3.Dot(velocity,contactNormal);
            if(alignedSpeed > 0 )
            {
                //确保不为负值
                jumpSpeed = Mathf.Max(jumpSpeed - alignedSpeed,0f) ;
            }
            //接触面的法线方向增加跳跃速度
            velocity += contactNormal*jumpSpeed;
            
        }
    }
    void OnCollisionEnter(Collision other) {
        EvaluateCollision(other);
    }
    void OnCollisionStay(Collision other) {
        EvaluateCollision(other);
    }
    
    void EvaluateCollision(Collision other)
    {
        for (int i = 0; i < other.contactCount; i++)
        {
            //获取碰撞点的法线
            Vector3 normal = other.GetContact(i).normal;

            // //法线的y轴分向量大于一个值,才表示在地面上
            // onGround |= normal.y >= minGroundDotProduct;
            if(normal.y >= minGroundDotProduct)
            {
                //onGround = true;
                groundContactCount += 1;
                //如果有多个接触点,累加法线向量
                contactNormal += normal;
            }
        }
    }

    //更新状态模块
    void UpdateState()
    {
        velocity = rb.velocity;
        //
        if(OnGround)
        {
            jumpPhase = 0;
            // 归一化接触法线
            if(groundContactCount > 1)
            contactNormal.Normalize();
        }
        else 
        {
            contactNormal = Vector3.up;
        }
    }
    //Clear State mode
    void ClearState()
    {
        //onGround = false;
        groundContactCount = 0;
        contactNormal = Vector3.zero;
    }
    //计算角度的cos值,得出在y分量上的大小
    void OnValidate() {
        minGroundDotProduct = Mathf.Cos(maxGroundAngle * Mathf.Deg2Rad);
    }
    //将x轴变成物体的x轴方向
    Vector3 ProjectOnContactPlane(Vector3 vector)
    {
        //减去在法线方向的向量,
        return vector - contactNormal * Vector3.Dot(vector,contactNormal); 
    }
    
    //速度调整函数
    void AdjustVelocity()
    {
        //自定义x,z轴
        Vector3 xAxis = ProjectOnContactPlane(Vector3.right).normalized;
        Vector3 zAxis = ProjectOnContactPlane(Vector3.forward).normalized; 

        //当前的速度在自定义x轴上的大小
        float currentX = Vector3.Dot(velocity,xAxis);
        float currentZ = Vector3.Dot(velocity,zAxis);

        //选择加速度
        float acceleration  = OnGround ? maxAcceleration : maxAirAcceleration;

        //单帧速度的改变量
        float maxSpeedChange = acceleration * Time.deltaTime;

        //将速度改成所需的速度
        float newX = Mathf.MoveTowards(velocity.x,desiredVelocity.x,maxSpeedChange);
        float newZ = Mathf.MoveTowards(velocity.z,desiredVelocity.z,maxSpeedChange);

        //xAxis控制方向,(newX 所需的速度- currentX在x轴的分量)控制大小
        velocity += xAxis *(newX - currentX) + zAxis * (newZ - currentZ);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值