1

一段时间以来,我一直在尝试在 Unity 中真实地在盒子的墙壁上反射 3d 球体。出于某种原因,反射通常是正确的,但是当球在某些方向撞到墙壁时,反射是不正确的。为了说明球在撞墙时会发生什么:T = 顶墙,R = 右墙,L = 左墙,B = 底墙。令 r = 球向右移动/向右移动,l = 向左移动,s = 球停止/显着减速。下面的说明采用这种格式:Xyz,其中 X = 球即将撞击的墙壁,y = 球的初始方向,z = 反射。游戏有一个自上而下的视角,说明是基于墙的视角。我也是 C# 的新手,所以代码可能会令人眼花缭乱。

说明:Tll、Trl;比尔, 比尔; Rls 或撞到另一面墙后 Rlr、Rrl;Lls 或撞到另一面墙后 Llr, Lrl

通常,当球停止时,它会在空中跳跃。我想知道这是否是因为角度沿错误的轴反射,但为什么这种情况有时会发生?此外,当只按住一个键时,球会来回弹跳,直到它离开竞技场。我知道离散和连续击球检测,并且设置是离散的,但是墙壁通常可以很好地容纳球,这种情况是例外。

我尝试了什么:

  1. 弄清楚如何使用 Vector3.Reflect。我不明白这个函数应该包含哪些变量以及如何将它应用到我的代码中。我确实查看了 Unity 文档页面以寻求帮助,但它没有回答我的问题。
  2. 更改负号,因为角度必须是 y 轴上的反射,这确实会改变反射的工作方式,但不能解决问题。目前订购底片的方式是我发现的最理想的方式。
  3. 为球提供弹性的物理材料。
  4. 在反正切方程的分母上添加一个小数,以帮助防止除以零。这根本没有帮助。
  5. 为正负加速度的不同组合创建不同的方程(基本上改变负数)。由于每次按下按钮都会产生一定的正加速度或负加速度(请参阅运动脚本),并且所述符号似乎存在问题,我想知道将每个加速度与其自己的一组方程相关联是否可以解决问题。那没起效。
  6. 检查墙壁是否处于不同的角度。
  7. 删除变量 xA 和 yA,或将它们放在不同的位置。
  8. 尝试找到对象的速度,但不知道如何实现它。

名为 Controller 的玩家的运动脚本代码:

public class Movement : MonoBehaviour
{

    public static float xAcceleration = 0.0f;
    public static float yAcceleration = 0.0f;

    // Update is called once per frame
    void Update()
    {

        if (Input.GetKey(KeyCode.W))                            //If the key W is pressed:
        {                                                       
            Vector3 position = this.transform.position;         //Variable position is set to transform the players placement in the game.
            if (yAcceleration >= -5 && yAcceleration <= 5)      //If the y vector of the acceleration is >= -5 and <= 5:
            {
                yAcceleration = yAcceleration + 0.01f;          //The y vector of the acceleration increases by 0.01 as long as the key W is pressed.
            }
            position.z = position.z + (0.1f * yAcceleration);   //The position of the object on the z-axis (pretend it is the y-axis in the game world) is transformed by its original position plus its speed times its yAcceleration.
            this.transform.position = position;                 //The gameObject is now transformed to a position equal to the variable position by the z-axis.
        }

        else                                                    //If the key W is let go of:
        {
            Vector3 position = this.transform.position;         
            position.z = position.z + (0.1f * yAcceleration);
            this.transform.position = position;                 //The position of the gameObject continues to update, but its acceleration does not change. Basically, it continues to move forward.
        }

//The rest of the code is very similar to the above, but I included it just in case there was something wrong.

        if (Input.GetKey(KeyCode.S))
        {
            Vector3 position = this.transform.position;
            if (yAcceleration >= -5 && yAcceleration <= 5)
            {
                yAcceleration = (yAcceleration) - 0.01f;
            }
            position.z = position.z + (0.1f * yAcceleration);
            this.transform.position = position;
        }

        else
        {
            Vector3 position = this.transform.position;
            position.z = position.z + (0.1f * yAcceleration);
            this.transform.position = position;
        }

        if (Input.GetKey(KeyCode.A))
        { 
            Vector3 position = this.transform.position;
            if (xAcceleration >= -5 && xAcceleration <= 5)
            {
                xAcceleration = (xAcceleration) - 0.01f;
            }
            position.x = position.x + (0.1f * xAcceleration);
            this.transform.position = position;
        }

        else
        {
            Vector3 position = this.transform.position;
            position.x = position.x + (0.1f * xAcceleration);
            this.transform.position = position;
        }

        if (Input.GetKey(KeyCode.D))
        {
            Vector3 position = this.transform.position;
            if (xAcceleration >= -5 && xAcceleration <= 5)
            {
                xAcceleration = (xAcceleration) + 0.01f;
            }
            position.x = position.x + (0.1f * xAcceleration);
            this.transform.position = position;
        }

        else
        {
            Vector3 position = this.transform.position;
            position.x = position.x + (0.1f * xAcceleration);
            this.transform.position = position;
        }


    }   
}

这是碰撞器和反射的代码:

public class Collider : MonoBehaviour
{

    public float xA;
    public float yA;

    void OnCollisionEnter(Collision collision)          //If a gameObject enters the collision of another object, this immediately happens once.
    {
        if (gameObject.tag == "Boundary")               //If the gameObject has a tag named Boundary:
        {
            yA = -Movement.yAcceleration;                                                                       //yA stores the value of yAcceleration after being called from script Movement as a negative. Its a reflection.
            Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration));       //xAcceleration is changed based on this equation: A * artan(A_y / A_x). The 0.000001 was here, adding to A_x to help prevent a 0 as the denominator.
            xA = Movement.xAcceleration;                                                                        //This is declared now...
            Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA));        //This uses xA because Movement.xAcceleration is changed, and the yAcceleration calculation is based on the xAcceleration prior the collision.
            
        }
    }

    void OnCollisionStay(Collision collision)
    {
        if (gameObject.tag == "Boundary")
        {         
            
            yA = Movement.yAcceleration;                                                                        //The same thing happens as before.
            Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration));
            xA = Movement.xAcceleration;
            Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA));

            Movement.xAcceleration = -Movement.xAcceleration / 2;                                               //On collision, the ball is reflected across the x-axis at half its speed.
            Movement.yAcceleration = Movement.yAcceleration / 2;                                                //yAcceleration is half its original value.
            
        }   
    }
}

下图是游戏设置。我很抱歉这是一个链接;我没有足够的声誉来值得在此页面上加载图像。另外,如果有什么不清楚的,请给我留言。

https://i.stack.imgur.com/VREV4.png

我非常感谢您的帮助。谢谢!

4

1 回答 1

0

这里有一个非常重要的注意事项:一旦有任何Rigidbody涉及,您就不想通过设置任何值.transform- 这会破坏物理和碰撞检测!

你的运动应该Rigidbody通过简单地改变它来改变 eg的行为Rigibody.velocity

然后,我还将碰撞检查直接放入球的组件中,并检查您是否撞到墙壁(“边界”)

然后另一个注意事项:您的代码当前取决于帧速率。这意味着如果您的目标设备以每秒 30 帧的速度运行,您将0.3每秒增加加速度。但是,如果您在更强大的设备上运行,该设备设法以每秒 200 帧的速度运行,那么您2每秒添加。

您应该定义每秒的减少/增加并将其乘以Time.deltaTime

在一起可能是这样的

public class Movement : MonoBehaviour
{
    // Adjust these settings via the Inspector
    [SerializeField] private float _maxMoveSpeed = 5f;
    [SerializeField] private float _speedIncreasePerSecond = 1f;

    // Already reference this via the Inspector
    [SerializeField] private Rigidbody _rigidbody;

    private void Awake()
    {
        if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
    }

    // Get User Input in Update
    private void Update()
    {
        var velocity = _rigidbody.velocity;
        velocity.y = 0;

        if (Input.GetKey(KeyCode.W) && velocity.z < _maxMoveSpeed)                            
        {                                                       
            velocity.z += _speedIncreasePerSecond * Time.deltaTime;          
        }

        if (Input.GetKey(KeyCode.S) && velocity.z > -_maxMoveSpeed)
        {
            velocity.z -= _speedIncreasePerSecond * Time.deltaTime;
        }

        if (Input.GetKey(KeyCode.A) && velocity.x > -_maxMoveSpeed)
        { 
            velocity.x -= _speedIncreasePerSecond * Time.deltaTime;
        }

        if (Input.GetKey(KeyCode.D) && velocity.x < _maxMoveSpeed)
        {
            velocity.x += _speedIncreasePerSecond * Time.deltaTime;
        }

        // clamp to the max speed in case you move diagonal
        if(velocity.magnitude > _maxMoveSpeed)
        {
            velocity = velocity.normalized * _maxMoveSpeed;
        }

        _rigidbody.velocity = velocity;
    }
}

然后最后简单地添加一个PhysicsMaterial具有所需设置的墙壁和球。

我用Friction = 0fandBounciness = 0.7f来做球和墙。对于慢速运动,您可能还需要/必须Bounce Threshold在项目的物理设置2中进行调整,否则如果速度小于默认值,则不会出现弹跳。

这在一定程度上取决于您对“现实”的定义。我禁用了重力,所以球也没有旋转和角摩擦:

在此处输入图像描述

于 2020-08-14T08:10:09.420 回答