Unity - AddForce() 同时使用 MovePosition() 进行移动
Unity - AddForce() while using MovePosition() for movement
我正在开发自上而下的游戏,但卡住了。
我试图让玩家在敌人击中他时被击退(他们的刚体碰撞)。
现在的问题是我正在使用 MovePosition 进行玩家移动,然后当我使用任何会改变玩家 RB 速度的东西时(设置 velocity/AddForce()),应用 force/velocity只是一瞬间。我认为这是因为我使用了以某种方式忽略或重置速度的 MovePosition。
有什么方法可以解决这个问题,而不必让玩家控制基于以最大速度添加力或计算击退持续多长时间?
很快,我希望击退顺利,因为现在我必须添加一个非常大的力才能有非常快(基本上是瞬间)的移动。
尝试重写它,让物理引擎为您处理一切。
您可以尝试使用 AddForce
移动对象,而在 "knockback" 状态下,您可以使用 AddForce
和 Force.Impulse
作为参数。它应该按预期工作,并且会解决您肩膀的运动问题。
以防万一其他人偶然发现:从技术上讲,您可以通过将 MovePosition(pos)
替换为 transform.position = pos
.
来完成 TE 最初想要的操作
这将保持 inertia\velocity 等,并且可能是您在非常特殊的情况下所需要的(在我的例子中,创建了一种自定义类型的非弹性铰链接头,其中强制位置变化非常小)。
此解决方案的主要缺点是,当位置转变为固体碰撞时,物理引擎可能会做出意想不到的事情。引擎仍将尝试解决这种情况并将对象推出碰撞,但它不知道它来自哪个方向(与 MovePosition 不同),可能会向错误的方向移动,从而让对象穿过地形。这就是为什么通常不建议在应用刚体时触摸 'transform'。
唯一对我有用的是设置一个 knockedOut
临时标志来决定何时停止调用 RigidBody.MovePosition()
暂时给 RigidBody.AddForce()
一些时间来采取行动。
然后使用 forceValue
、RigidBody.mass
和 RigidBody.linearDrag
来获得想要的效果。
这是我的 PlayerController 的实现:
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
[SerializeField] float speed = 1f;
[SerializeField] float impactEffect = 5f;
[SerializeField] float knockOutTime = 0.05f;
Vector2 direction = Vector2.zero;
Rigidbody2D rbody;
bool knockedOut;
IEnumerator knockOutCoroutine;
void Awake()
{
rbody = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
if(!knockedOut) // Don't call Move() if I am knockedOut
Move();
}
// Triggered by the InputSystem
void OnMove(InputValue value)
{
// Set the direction value
direction = value.Get<Vector2>();
}
// Controls the normal movement of the Player based on the actual `direction` value
void Move()
{
Vector2 adjustedMovement = direction * speed * Time.fixedDeltaTime;
Vector2 newPos = rbody.position + adjustedMovement;
rbody.MovePosition(newPos);
}
// Invoke this method when Collision
public void Impact(Vector2 impactPosition)
{
Vector2 impactDirection = ((Vector2)transform.position - impactPosition).normalized;
rbody.velocity = Vector2.zero;
rbody.AddForce(impactDirection * impactEffect, ForceMode2D.Impulse);
KnockOut(knockOutTime);
}
void KnockOut(float seconds)
{
if(knockOutCoroutine != null)
StopCoroutine(knockOutCoroutine);
knockOutCoroutine = KnockOutCoroutine(seconds);
StartCoroutine(knockOutCoroutine);
}
// KnockedOut stop movement effect
IEnumerator KnockOutCoroutine(float seconds)
{
direction = Vector2.zero;
knockedOut = true;
yield return new WaitForSeconds(seconds);
knockedOut = false;
}
}
我正在开发自上而下的游戏,但卡住了。 我试图让玩家在敌人击中他时被击退(他们的刚体碰撞)。
现在的问题是我正在使用 MovePosition 进行玩家移动,然后当我使用任何会改变玩家 RB 速度的东西时(设置 velocity/AddForce()),应用 force/velocity只是一瞬间。我认为这是因为我使用了以某种方式忽略或重置速度的 MovePosition。
有什么方法可以解决这个问题,而不必让玩家控制基于以最大速度添加力或计算击退持续多长时间?
很快,我希望击退顺利,因为现在我必须添加一个非常大的力才能有非常快(基本上是瞬间)的移动。
尝试重写它,让物理引擎为您处理一切。
您可以尝试使用 AddForce
移动对象,而在 "knockback" 状态下,您可以使用 AddForce
和 Force.Impulse
作为参数。它应该按预期工作,并且会解决您肩膀的运动问题。
以防万一其他人偶然发现:从技术上讲,您可以通过将 MovePosition(pos)
替换为 transform.position = pos
.
这将保持 inertia\velocity 等,并且可能是您在非常特殊的情况下所需要的(在我的例子中,创建了一种自定义类型的非弹性铰链接头,其中强制位置变化非常小)。
此解决方案的主要缺点是,当位置转变为固体碰撞时,物理引擎可能会做出意想不到的事情。引擎仍将尝试解决这种情况并将对象推出碰撞,但它不知道它来自哪个方向(与 MovePosition 不同),可能会向错误的方向移动,从而让对象穿过地形。这就是为什么通常不建议在应用刚体时触摸 'transform'。
唯一对我有用的是设置一个 knockedOut
临时标志来决定何时停止调用 RigidBody.MovePosition()
暂时给 RigidBody.AddForce()
一些时间来采取行动。
然后使用 forceValue
、RigidBody.mass
和 RigidBody.linearDrag
来获得想要的效果。
这是我的 PlayerController 的实现:
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
[SerializeField] float speed = 1f;
[SerializeField] float impactEffect = 5f;
[SerializeField] float knockOutTime = 0.05f;
Vector2 direction = Vector2.zero;
Rigidbody2D rbody;
bool knockedOut;
IEnumerator knockOutCoroutine;
void Awake()
{
rbody = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
if(!knockedOut) // Don't call Move() if I am knockedOut
Move();
}
// Triggered by the InputSystem
void OnMove(InputValue value)
{
// Set the direction value
direction = value.Get<Vector2>();
}
// Controls the normal movement of the Player based on the actual `direction` value
void Move()
{
Vector2 adjustedMovement = direction * speed * Time.fixedDeltaTime;
Vector2 newPos = rbody.position + adjustedMovement;
rbody.MovePosition(newPos);
}
// Invoke this method when Collision
public void Impact(Vector2 impactPosition)
{
Vector2 impactDirection = ((Vector2)transform.position - impactPosition).normalized;
rbody.velocity = Vector2.zero;
rbody.AddForce(impactDirection * impactEffect, ForceMode2D.Impulse);
KnockOut(knockOutTime);
}
void KnockOut(float seconds)
{
if(knockOutCoroutine != null)
StopCoroutine(knockOutCoroutine);
knockOutCoroutine = KnockOutCoroutine(seconds);
StartCoroutine(knockOutCoroutine);
}
// KnockedOut stop movement effect
IEnumerator KnockOutCoroutine(float seconds)
{
direction = Vector2.zero;
knockedOut = true;
yield return new WaitForSeconds(seconds);
knockedOut = false;
}
}