我的游戏的 2D 物理有问题。当玩家跳跃并与物体碰撞时,该物体会被稍微推入地面。如何防止碰撞体相互穿过?
我首先尝试将刚体设置更改为连续和插值,但它不起作用。然后我更改了 Unity 物理 2D 设置,例如接触偏移,但也不起作用。
这些是我的设置和玩家移动代码:
using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
public class MovePlayer : MonoBehaviour
{
public Rigidbody2D rb;
Vector2 moveInput;
public Animator anim;
[SerializeField]
float moveSpeed = 10f, acceleration = 1f, decceleration = -1f, velPower = 1f, jumpForce = 3f, coyoteTime = 0.1f, jumpInputBuffer = 0.1f, jumpCut = 1f;
[SerializeField]
float gravityScale = 6f, fallGravityMultiplier = 1.1f, jumpHangGravityThreshold = 0.1f, jumpHangGravityMultiplier = 0.8f, frictionAmount = 0.2f;
[SerializeField]
Vector2 groundChecksize = new Vector2(0.49f, 0.03f);
public LayerMask ground;
public GameObject groundCheckObject;
PlayerAttack attackScript;
float LastOnGroundTime = 0f, LastJumpPressed = 0f, LastMovePressed = 0f;
float tmp = 1f;
bool isJumping;
bool isFacingRight = true;
// Start is called before the first frame update
void Start()
{
anim = this.GetComponent<Animator>();
attackScript = this.GetComponent<PlayerAttack>();
}
// Update is called once per frame
void Update()
{
LastOnGroundTime -= Time.deltaTime;
LastJumpPressed -= Time.deltaTime;
LastMovePressed -= Time.deltaTime;
moveInput.x = Input.GetAxisRaw("Horizontal");
moveInput.y = Input.GetAxisRaw("Vertical");
if (moveInput.x != 0)
{
LastMovePressed = tmp;
}
if ((moveInput.x > 0f && isFacingRight == false) || (moveInput.x < 0f && isFacingRight == true))
{
FlipPlayer();
}
if(attackScript.isAttacking>=0)
{
moveInput.x = 0f;
}
groundCheck();
if (isJumping && rb.velocity.y < 0) isJumping = false;
if (Input.GetKeyDown(KeyCode.Space))
{
LastJumpPressed = jumpInputBuffer;
}
if (Input.GetKeyUp(KeyCode.Space) && isJumping && rb.velocity.y > 1f)
{
rb.AddForce(jumpCut * Vector2.down * (rb.velocity.y < 2f ? rb.velocity.y - 1 : 1), ForceMode2D.Impulse);
}
if (LastOnGroundTime > 0 && LastJumpPressed > 0)
{
Jump();
}
if (rb.velocity.y < 0)
{
rb.gravityScale = gravityScale * fallGravityMultiplier;
}
else if (Mathf.Abs(rb.velocity.y) < jumpHangGravityThreshold)
{
rb.gravityScale = gravityScale * jumpHangGravityMultiplier;
}
else
{
rb.gravityScale = gravityScale;
}
if (Input.GetAxisRaw("Horizontal") == 0)
{
anim.SetBool("walk", false);
}
else
{
anim.SetBool("walk", true);
}
if (LastOnGroundTime > 0 && Input.GetAxisRaw("Horizontal") == 0)
{
float friction = Mathf.Min(Mathf.Abs(rb.velocity.x), Mathf.Abs(frictionAmount));
friction *= Mathf.Sign(rb.velocity.x);
rb.AddForce(Vector2.right * -friction, ForceMode2D.Impulse);
}
}
private void FixedUpdate()
{
float targetSpeed = moveInput.x * moveSpeed;
float speedDif = targetSpeed - rb.velocity.x;
float accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? acceleration : decceleration;
float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);
rb.AddForce(movement * Vector2.right);
}
void Jump()
{
LastOnGroundTime = 0;
LastJumpPressed = 0;
isJumping = true;
float force = jumpForce;
if (rb.velocity.y < 0)
{
force -= rb.velocity.y;
}
rb.AddForce(force * Vector2.up, ForceMode2D.Impulse);
}
void groundCheck()
{
if (Physics2D.OverlapBox(groundCheckObject.transform.position, groundChecksize, 0, ground))
{
LastOnGroundTime = coyoteTime;
}
}
void FlipPlayer() //이동 방향에 맞게 플레이어를 반전
{
isFacingRight = !isFacingRight;
Vector3 PlayerScale = transform.localScale;
PlayerScale.x = PlayerScale.x * -1;
transform.localScale = PlayerScale;
}
public void InitPlayer()
{
LastOnGroundTime = 0f; LastJumpPressed = 0f; LastMovePressed = 0f;
isJumping = false;
if(!isFacingRight)
{
FlipPlayer();
}
}
private void OnDrawGizmosSelected()
{
Gizmos.DrawWireCube(groundCheckObject.transform.position, groundChecksize);
}
}
经过一些实验,我可以得出结论,原因很可能是由于处理
FixedUpdate()
内部的运动:
private void FixedUpdate()
{
float targetSpeed = moveInput.x * moveSpeed;
float speedDif = targetSpeed - rb.velocity.x;
float accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? acceleration : decceleration;
float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);
rb.AddForce(movement * Vector2.right);
}
我尝试将我的运动功能移动到固定更新中,它也会在对象之间出现故障并口吃,我只需将你的运动代码放在一个函数中:
private void Move() {
float targetSpeed = moveInput.x * moveSpeed;
float speedDif = targetSpeed - rb.velocity.x;
float accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? acceleration : decceleration;
float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);
rb.AddForce(movement * Vector2.right);
}
然后在更新函数内部调用它:
void Update() {
Move()
//...
}
我注意到的另一件事是你像我一样使用 rb.AddForce 而不是 rb.velocity 。我明白你为什么会使用它,但这也可能是另一个原因。