c# - 我可以写我的运动脚本速记吗?
问题描述
我是 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;
}
}
}
解决方案
欢迎来到 StackOverflow。由于您至少是 C# 和 Unity 编码的新手,因此请首先接受以下建议:继续做您所做的事情 - 先让它工作,然后看看您是否可以改进它。:)
首先,我建议您不要使用KeyCode
基于输入,而是研究输入管理器的工作原理。目前,这是声明输入键绑定的标准方式,无需考虑代码中的实际键。在 2019.3 之后的 Unity 版本中,出现了另一种方法,但现在这可能有点太复杂了。
输入管理器允许您做的是定义一个特定的动作,比如Fire
,然后将键、鼠标移动、按钮和操纵杆输入绑定到它。在代码中,您可以使用GetAxis或GetButtonDown以及操作名称来引用该操作:
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);
}
}
玩得开心!:)
推荐阅读
- java - Tomcat中奇怪的JSON字符串失真
- html - CSS不能居中菜单和子菜单2nt行中的空白
- vue.js - 将 props 值传递给组件
- xml - 如何使用 xslt 1.0 在 xml 元素中划分字符串
- android - Android ChipGroup:芯片中的多行文本
- c# - 停止刷新 mvc 布局中的 _navigation 部分视图或仅加载 _navigation 1 次
- apache-flink - Flink:如何在数据库中保存行列表
- android - 在 NodeJS 服务器中解决 csrf 和身份验证问题的最佳方法
- html - 手风琴内的可点击链接(仅限 HTML CSS)
- rest - 检查输入数据是否匹配数据库记录的正确 RESTful 方法