首页 > 解决方案 > 我可以写我的运动脚本速记吗?

问题描述

我是 Unity 和 C# 编码的新手,我想看看我为运动编写的代码是否可以写得更简写,甚至只是改进运动的流动方式。我认为当我在其中添加更多脚本时,看到我的问题的缩短解决方案也会对我有所帮助。谢谢!

//Variables
Rigidbody rb;
public GameObject player;
public float speed = 4f;
private float minLimit = 0.51f; //MinLimit is the height you need to be below so you can jump
private float airTime = 0.875f; //The amount of time spent in the air if you press a movement key
private float maxLimit = 3.66f; //Maxlimit is the maximum jump height

下面是运动脚本。想象一下这个复制并粘贴到所有四个方向。由于(我认为)相机位置,玩家会以与正常相反的方向移动。

    if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A))
    {
        if (transform.position.y > minLimit && !Input.GetKey(KeyCode.Space) && transform.position.y < maxLimit)
        {
            if (Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.LeftArrow))
            {
                rb.velocity = new Vector3(1, -airTime, 0) * speed;
            }
        }

        else
        {
            if (Input.GetKeyDown(KeyCode.Space) && player.transform.position.y < minLimit)
            {
                rb.velocity = new Vector3(1, 2, 0) * speed;
            }
            if (!Input.GetKey(KeyCode.S) && !Input.GetKey(KeyCode.W) && !Input.GetKey(KeyCode.UpArrow) && !Input.GetKey(KeyCode.DownArrow) && !Input.GetKey(KeyCode.Space))
            {
                rb.velocity = new Vector3(1, 0, 0) * speed;
            }
            if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
            {
                rb.velocity = new Vector3 (1, 0, 1) * speed;
            }
            if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
            {
                rb.velocity = new Vector3(1, 0, -1) * speed;
            }
        }
    }

标签: c#unity3d

解决方案


欢迎来到 StackOverflow。由于您至少是 C# 和 Unity 编码的新手,因此请首先接受以下建议:继续做您所做的事情 - 先让它工作,然后看看您是否可以改进它。:)

首先,我建议您不要使用KeyCode基于输入,而是研究输入管理器的工作原理。目前,这是声明输入键绑定的标准方式,无需考虑代码中的实际键。在 2019.3 之后的 Unity 版本中,出现了另一种方法,但现在这可能有点太复杂了。

输入管理器允许您做的是定义一个特定的动作,比如Fire,然后将键、鼠标移动、按钮和操纵杆输入绑定到它。在代码中,您可以使用GetAxisGetButtonDown以及操作名称来引用该操作:

Input.GetButtonDown("Fire")

如果您决定稍后更改键绑定,则无需修改您的代码。

对于您的代码,我建议将所有内容都放入具有“说话”名称的各个变量中:

var moveLeft = Input.GetButton("Left"); // Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A);
var moveUp = Input.GetButton("Up");     // Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow);
var moveDown = Input.GetButton("Down"); // Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow);
var jump = Input.GetButtonDown("Jump"); // Input.GetKeyDown(KeyCode.Space);

if (moveLeft)
{
    if (!jump && transform.position.y > minLimit && transform.position.y < maxLimit)
    {
        // No need to check for "Left Arrow || A" again, because you already did.
        rb.velocity = new Vector3(1, -airTime, 0) * speed;
    }
    else
    {
        if (jump && player.transform.position.y < minLimit)
        {
            rb.velocity = new Vector3(1, 2, 0) * speed;
        }
        if (!moveUp && !moveDown)
        {
            rb.velocity = new Vector3(1, 0, 0) * speed;
        }
        if (moveDown)
        {
            rb.velocity = new Vector3 (1, 0, 1) * speed;
        }
        if (moveUp)
        {
            rb.velocity = new Vector3(1, 0, -1) * speed;
        }
    }
}

接下来,尝试通过反转条件来移除嵌套,或者尽可能早地离开该方法:

private void Update()
{
    var moveLeft = Input.GetButton("Left");
    if (moveLeft) MoveLeft();
}

private void MoveLeft()
{
    var moveUp = Input.GetButton("Up");
    var moveDown = Input.GetButton("Down");
    var jump = Input.GetButtonDown("Jump");

    if (!jump && transform.position.y > minLimit && transform.position.y < maxLimit)
    {
        rb.velocity = new Vector3(1, -airTime, 0) * speed;
        return;
    }

    // Note the use of "else if" here. You are overwriting "rb.velocity"
    // in every block, so the last condition wins. In this change,
    // the first condition to be true is applied, but it makes the priorities
    // explicit.
    if (jump && player.transform.position.y < minLimit)
    {
        rb.velocity = new Vector3(1, 2, 0) * speed;
    }
    else if (!moveUp && !moveDown)
    {
        rb.velocity = new Vector3(1, 0, 0) * speed;
    }
    else if (moveDown)
    {
        rb.velocity = new Vector3 (1, 0, 1) * speed;
    }
    else if (moveUp)
    {
        rb.velocity = new Vector3(1, 0, -1) * speed;
    }
}

更进一步,尽量避免查找属性,transform.position并尽可能避免创建实例。它对您当前的代码没有太大影响,但让我们尝试一下:

// Note that directions should always be normalized as they would otherwise
// introduce different "strength" factors.
private readonly Vector3 LeftJumpDirection = new Vector3(1, 2, 0).normalized;
private readonly Vector3 LeftDirection = new Vector3(1, 0, 0).normalized;
private readonly Vector3 DownDirection = new Vector3 (1, 0, 1).normalized;
private readonly Vector3 UpDirection = new Vector3(1, 0, -1).normalized;

private void float jumpSpeed = 1f;

private void Update()
{
    var moveLeft = Input.GetButton("Left");
    if (moveLeft) MoveLeft();
}

private void MoveLeft()
{
    var moveUp = Input.GetButton("Up");
    var moveDown = Input.GetButton("Down");
    var jump = Input.GetButtonDown("Jump");

    // This property is cached here since we only read from it.
    var position = transform.position;

   // Also, let's add clear names ...
    var inAir = position.y > minLimit && position.y < maxLimit;
    var canJump = position.y < maxLimit;

    if (!jump && inAir)
    {
        rb.velocity = new Vector3(1, -airTime, 0) * jumpSpeed;
        return;
    }

    // Note the use of "else if" here. You are overwriting "rb.velocity"
    // in every block, so the last condition wins. In this change,
    // the first condition to be true is applied, but it makes the priorities
    // explicit.
    if (jump && canJump)
    {
        rb.velocity = JumpDirection * jumpSpeed;
    }
    else if (!moveUp && !moveDown)
    {
        rb.velocity = LeftDirection * speed;
    }
    else if (moveDown)
    {
        rb.velocity = DownDirection * speed;
    }
    else if (moveUp)
    {
        rb.velocity = UpDirection * speed;
    }
}

到目前为止,您的代码只改变了它的可读性(但请记住,代码是由人类读取的,而不是由机器读取的)。

对于接下来的步骤,这里有一些提示:

  • 您的对象的transform(属性)具有forward指向对象正向的属性。
  • 始终标准化您的方向向量,然后缩放它们(例如按speed)。
  • 如果您需要同时考虑不同的方向,请添加您的方向向量,然后再次进行归一化。
  • 要跳跃,也许可以尝试通过调用来为刚体添加冲力rb.AddForce(Direction * Intensity, ForceMode.Impulse)

使用这些信息,尝试将关注点分开:

private void Update()
{
    // GetAxis already gives you a notion of positive or negative, e.g.
    // left or right. Try using axisValue * transform.forward.
    var moveIntensity = Input.GetAxis("Horizontal");
    if (moveIntensity != 0) TryMove(moveIntensity);

    var jump = Input.GetButtonDown("Jump");
    if (jump) TryJump();

}

我个人喜欢将这些动作分成DoSomething()and TryDoSomething(),我只DoSomething()在应该确定完成某件事时才调用,并且TryDoSomething()该调用可能由于进一步的逻辑而失败。在这里,TryJump()将表示用户表达了跳跃的愿望,但它可能不会导致任何状态变化,例如因为用户在空中。

也许这已经给了你一些想法。

其他地方的示例代码

由于您询问如何针对多个方向概括您的代码,而我只是回答您有不同的代码,但没有给您不同的解决方案 - 这是一个额外的解决方案。这是我前段时间用于播放器控制器的一些代码,涉及步行和跳跃:

[SerializeField]
private float moveSpeed;

[SerializeField]
private float jumpForce;

[SerializeField]
private float groundDistance = 0.7f;

private Rigidbody _rig;

private void Awake()
{
    _rig = GetComponent<Rigidbody>();
}

private void Update()
{
    Move();

    // In Unity, select "Edit > Project Settings > Input" for the input configuration.
    if (Input.GetButtonDown("Jump"))
    {
        TryJump();
    }
}

void Move()
{
    var xInput = Input.GetAxis("Horizontal");
    var zInput = Input.GetAxis("Vertical");

    // Set a new speed velocity vector.
    var dir = new Vector3(xInput, 0, zInput) * moveSpeed;

    // Patch in the y velocity without affecting it by the move speed..
    dir.y = _rig.velocity.y;

    // Assign the updated velocity to our rigid body.
    _rig.velocity = dir;

    // Update the forward direction according to the movement vector.
    var facingDir = new Vector3(xInput, 0, zInput);
    if (facingDir.sqrMagnitude > 0)
    {
        transform.forward = facingDir;
    }
}

void TryJump()
{
    // This implements ray-cast jumping:
    // We're shooting a ray downwards to see whether we're on the ground. With a player height of
    // 1 unit and the reference point at the center of the object, a maximum distance
    // of 0.7 would tell us whether there was an object up to 0.2 units below the player object.
    // We treat this as an indicator that there was solid ground, so the player can jump.
    var pos = transform.position;
    var ray = new Ray(pos + Vector3.zero, Vector3.down);

    var hit = Physics.Raycast(ray, groundDistance);
    if (hit)
    {
        // We want the force to be applied instantaneously, not accelerating the player constantly.
        // Because of this, we use "impulse" force mode.
        _rig.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
    }
}

玩得开心!:)


推荐阅读