using UnityEngine;
using UnityEngine.InputSystem;
public enum MoveState
{
Idle,
Run,
Jump,
Fall,
WallSlide,
}
public class PlayerMovement : MonoBehaviour
{
public MoveState state;
Vector2 input;
Rigidbody2D rb;
public float speed;
public float fallMulti;
public float jumpPower;
public CollisionChecker groundChecker;
public CollisionChecker rightWall;
public CollisionChecker leftWall;
public float coyoteTime = 0.1f;
float coyoteNowTime = 0f;
public float jumpBufferTime = 0.1f;
float jumpBufferNow = 0f;
public float wallSlideSpeed;
public float minJumpTime = 0.05f;
public float maxJumpTime = 0.15f;
bool jumpPressed = false;
public float jumpCutMultiplier = 0.33f; // ������ ���� �� �ӵ� ���� ����
float jumpTimer = 0f;
private void Start()
{
ChangeState(MoveState.Idle);
InputManager.moveEvent += OnMove;
InputManager.jumpEvent += OnJump;
rb= GetComponent<Rigidbody2D>();
}
private void Update()
{
if (!groundChecker.isTouched)
{
coyoteNowTime -= Time.deltaTime;
}
else
{
coyoteNowTime = coyoteTime;
}
if (jumpBufferNow > 0f)
{
jumpBufferNow -= Time.deltaTime;
}
if (state == MoveState.Jump)
{
jumpTimer += Time.deltaTime;
}
else
{
jumpTimer = 0;
}
}
public void ChangeState(MoveState newState)
{
switch (state) // EXIT
{
case MoveState.Jump:
StopJump();
break;
}
state = newState;
switch (newState) // ENTER
{
case MoveState.Jump:
StartJump();
break;
}
}
public void HoldState()
{
switch (state)
{
case MoveState.Jump:
HoldState_Jump();
break;
case MoveState.Idle:
HoldState_Idle();
break;
case MoveState.Fall:
HoldState_Fall();
break;
case MoveState.Run:
HoldState_Run();
break;
}
}
private void FixedUpdate()
{
rb.linearVelocity = new Vector2(input.x * speed, rb.linearVelocityY);
HoldState();
setGravity();
}
public void HandleJump()
{
if (jumpBufferNow > 0f && (groundChecker.isTouched || coyoteNowTime > 0))
{
ChangeState(MoveState.Jump);
jumpBufferNow = 0f;
coyoteNowTime = 0;
}
}
void HoldState_Run()
{
HandleJump();
if (!groundChecker.isTouched && rb.linearVelocity.y < 0)
{
ChangeState(MoveState.Fall);
}
else if (input.x == 0)
{
ChangeState(MoveState.Idle);
}
}
void HoldState_Jump()
{
HandleJump();
if (jumpPressed && jumpTimer < maxJumpTime)
{
// ����, AND �����ð� < �������ð�
}
else if (jumpTimer < minJumpTime)
{
// !����, �����ð� < �������ð� �� ��
}
else
{
ChangeState(MoveState.Fall);
}
}
void HoldState_Fall()
{
if (groundChecker.isTouched)
{
ChangeState(MoveState.Idle);
}
}
void HoldState_Idle()
{
HandleJump();
if (groundChecker.isTouched)
{
if (input.x != 0 && state != MoveState.Run)
{
ChangeState(MoveState.Run);
}
}
else if (!groundChecker.isTouched)
{
if (rb.linearVelocityY < 0f)
{
ChangeState(MoveState.Fall);
}
}
}
public void setGravity()
{
if (state == MoveState.Fall)
{
rb.linearVelocityY += Physics2D.gravity.y * fallMulti * Time.fixedDeltaTime;
return;
}
}
public void OnJump(InputAction.CallbackContext context)
{
if (context.started)
{
jumpPressed = true;
jumpBufferNow = jumpBufferTime;
}
if (context.canceled)
{
jumpPressed = false;
}
}
public void StartJump()
{
if (groundChecker.isTouched || coyoteNowTime > 0)
{
rb.linearVelocity = new Vector2(rb.linearVelocityX, jumpPower);
}
}
public void HoldJump()
{
// rb.linearVelocity = new Vector2(rb.linearVelocityX, jumpPower);
}
public void StopJump()
{
rb.linearVelocity = new Vector2(rb.linearVelocity.x, rb.linearVelocity.y * jumpCutMultiplier);
}
public void WallSlide()
{
rb.linearVelocityY = Mathf.Max(rb.linearVelocityY, -wallSlideSpeed);
}
public void OnMove(Vector2 vec)
{
input = vec;
}
}
코요테타임이랑 점프버퍼까지있는거보면 조작감 고려를 많이하신거같네요. 속도를 더 세분화해서 일반 속도 최대치, 대쉬 속도 최대치 등으로 나누고 가속역시 일반가속도, 대쉬가속도, 감속등을 추가해도 괜찮을것 같습니다. 여기에 점프중엔 가속도가 다르게 적용되도록 만들어도 되고요.
벽방향 콜리전체커는 안쓰는거같아서 왜있는지 모르겠는데, 단순 진행방향 체크때문이면 현재플레이어 진행방향을 받아와서 앞쪽만 체크하면되지 않을까 싶습니다. 그리고 그라비티는 왜 따로계산하는지 모르겠네요. 리기드바디기능을 안쓰시는 이유가 있나요
벽방향은 원래 쓰던 코드에 벽타기도 넣어져있었는데 갈아엎다가 사라져서 나중에 다시 넣을듯... 중력스케일 변동하는건 별로라고 생각하는데 떨어지는게 올라가는거보다 빠른게 조작감이 좋다고 해서 추가했는데 중력스케일 바꾸는게 더 좋을까용??
아뇨 의도가 있으면 상관없죠.
그렇게 세심하게 의도를 넣을만한 실력은 아니라서... 중력스케일을 쓰는게 보편적이면 그거 쓰는게 나을거같은데 이것저것 찾아보니까 다 방식이 다르더라구요 그냥 중력스케일 쓸까요?
ㄴㄴ 저정도만 의도가있는게 맞고, 의도한대로 작동했으면 굳이 안바꿔도 됩니다
아 그리고 유니티 6부터는 리기드바디에 가속도 넣을때 매번 new vector2 안넣으셔도됩니다. 그냥 linearVelocityY = jumpPower 하시면 됨
@Indie1(119.199) 리기드가 아니라 리지드임
지금 PlayerMovement.cs에 여러 기능이 포함되어 있는 것 같으니까 분할 해보는 것도 좋을 듯? 그리고, FSM 써서 상태 별로 로직 정의 하는 것도 좋아 보임.
글 수정했는데 글처럼 상태별 로직 정하라는거?
@ㅇㅇ(58.233) 내가 이야기한 거랑 틀림. FSM(유한상태머신) 하라는 건데 너 코드는 State 패턴임. 일단, 너 코드 봤을 땐 PlayerMovement는 필요 없고 키 입력 받는 클래스 분리하고 FSM 클래스 분리하고 Player 클래스 객체에 Player 관련 데이터들과 분리한 클래스들 모아두라는 말인데 이게 말로 설명하기 어렵네.
@Indie2(61.78) 어 뭐야 글 수정이 안됐었음 ㅈㅅ; 다시 수정함
@ㅇㅇ(58.233) 윗댓과 동일.