学习Unity开发过程中在Update中调用ridigbody.MovePosition编写移动逻辑,发现实际移动速度异常。
private Rigidbody2D ballRid;
void Start()
{
ballRid = transform.GetComponent<Rigidbody2D>();
}
void Update()
{
Move();
}
/// <summary>
/// 通过设置Rigidbody2D.MovePosition来移动
/// </summary>
void Move()
{
Vector3 dir = new Vector3(Input.GetAxisRaw("Horizontal") * moveSpeed, 0, 0);
ballRid.MovePosition(transform.position + dir * Time.deltaTime);
}
虽然是在Update中调用,但是乘了Time.deltaTime后效果应该和在FixedUpdate中一致,但实际效果是游戏低帧率时移动正常,高帧率时移动缓慢。
刚体组件的MovePosition方法与修改transform.position不同,方法执行后不会立即造成位移的更改(可以通过在方法前后Debug物体的transform检测,具体可见https://www.jianshu.com/p/6dbe71cde6f2),无论是在Update中还是FixedUpdate中。
经测试,在Update中调用MovePosition,在大于30帧的时候的实际执行次数是小于每秒30次的,同时随着帧数的增长实际执行次数会非线性增加并逼近50次每秒(fixedupdate的执行频率)。
推测是调用MovePosition方法后会在下一次fixedupdate中更新位移,当帧率较低时,update的调用次数无法填满fixedupdate的执行次数,实际移动次数会少于50次(此时如果位移距离乘了Time.deltaTime则会达到和 transform.position += 位移距离 *Time.deltaTime 相近的效果;如果乘的是Time.fixedDeltaTime则会因为Time.fixedDeltaTime在低于50帧的时候数值小于Time.deltaTime导致位移速度小于transform.position += 位移距离 * Time.deltaTime);帧率高于50后,MovePosition的每秒位移次数接近50次(但由于帧率波动或其他原因不能像fixedupdate一样稳定每秒执行50次),此时如果还是用位移距离乘Time.deltaTime则会导致实际速度慢于transform.position += 位移距离 *Time.deltaTime的方式,如果乘的是Time.fixedDeltaTime会达到类似在fixedupdate中调用的效果,但因为执行次数不稳定,可能出现抖动或停顿。
因此导致了无论乘Time.deltaTime还是Time.fixedDeltaTime,在Update中的调用都会受到帧率影响,应该在FixedUpdate中调用以在不同帧率下保持同速度。
测试代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AboutMovePosition : MonoBehaviour
{
private Rigidbody2D ballRid;
public float moveSpeed = 2f;
private float timer = 10f; //计时器,移动几秒后停止
public int count = 0; //Update的执行次数
public int countf = 0; //FixedUpdate的执行次数
public float rate = 0; //MovePosition的位移次数和FixedUpdate的执行次数之比
// Start is called before the first frame update
void Start()
{
ballRid = transform.GetComponent<Rigidbody2D>();
//Application.targetFrameRate = 50; //限制帧率测试不同帧率下的执行次数
}
private void FixedUpdate()
{
if (timer > 0)
{
++countf;
}
rate = transform.position.x / countf;
}
// Update is called once per frame
void Update()
{
if (timer > 0)
{
Move();
++count;
}
timer -= Time.deltaTime;
}
void Move()
{
ballRid.MovePosition(transform.position + Vector3.right );
//每次向x轴正向移动1,x坐标即为执行次数(x初始设为0)
}
}
测试用Unity版本为2022.3.29fc1
个人学习记录使用,如有错漏欢迎指正。