애니메이션 추가
This commit is contained in:
@@ -6,7 +6,7 @@ public enum ClimbState
|
||||
Falling, // 낙하 중
|
||||
Standing, // 발판 위에 서 있음
|
||||
Walking, // 발판 위를 걷는 중 (목표 X 로 이동)
|
||||
Climbing, // 다른 발판으로 올라가는 중
|
||||
Jumping, // 다른 발판으로 뛰어오르는 중
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,11 +28,16 @@ public class WindowClimber : MonoBehaviour
|
||||
[SerializeField] Camera viewCamera;
|
||||
|
||||
[Header("물리 (화면 픽셀 기준)")]
|
||||
[Tooltip("초당 낙하 가속도")]
|
||||
[SerializeField] float gravity = 2600f;
|
||||
// 2600 정도가 실제 중력에 해당한다(캐릭터 키를 화면 400px 로 보면 1m≈250px,
|
||||
// 9.8m/s² ≈ 2450px/s²). 그런데 실제 중력으로 떨어지면 400px 낙하가 0.56초라
|
||||
// 낙하 애니메이션이 눈에 들어오지 않는다. 데스크톱 펫은 일부러 둥실하게 둔다.
|
||||
[Tooltip("초당 낙하 가속도. 낮출수록 천천히 떨어져 낙하 동작이 잘 보인다. " +
|
||||
"2600 은 실제 중력에 가까워 너무 빠르다")]
|
||||
[SerializeField] float gravity = 1200f;
|
||||
|
||||
[Tooltip("최대 낙하 속도")]
|
||||
[SerializeField] float maxFallSpeed = 1800f;
|
||||
[Tooltip("최대 낙하 속도. 긴 낙하의 체감 속도를 좌우한다. " +
|
||||
"이 값이 크면 화면을 가로지르는 낙하가 순식간에 끝난다")]
|
||||
[SerializeField] float maxFallSpeed = 900f;
|
||||
|
||||
[Tooltip("걷는 속도 (픽셀/초)")]
|
||||
[SerializeField] float walkSpeed = 160f;
|
||||
@@ -63,6 +68,15 @@ public class WindowClimber : MonoBehaviour
|
||||
[Tooltip("올라가는 속도 (픽셀/초). 거리에 비례해 시간이 정해진다")]
|
||||
[SerializeField] float climbSpeed = 420f;
|
||||
|
||||
[Tooltip("점프할 때 목표보다 얼마나 더 높이 떠오르는지(픽셀). 0 이면 직선으로 간다")]
|
||||
[SerializeField] float jumpArcHeight = 70f;
|
||||
|
||||
[Tooltip("가장 짧은 점프에 걸리는 시간(초). 너무 작으면 순간이동처럼 보인다")]
|
||||
[SerializeField] float minJumpDuration = 0.35f;
|
||||
|
||||
[Tooltip("가장 먼 점프에 걸리는 시간(초). 너무 크면 화면을 오래 가린다")]
|
||||
[SerializeField] float maxJumpDuration = 0.9f;
|
||||
|
||||
[Header("발판 여백")]
|
||||
[Tooltip("발판 좌우 끝에서 최소한 이만큼 안쪽에 선다. " +
|
||||
"캐릭터 반폭보다 작으면 반폭이 우선 적용된다")]
|
||||
@@ -75,7 +89,27 @@ public class WindowClimber : MonoBehaviour
|
||||
[Tooltip("착지 판정 여유. 발판을 살짝 지나쳐도 잡아준다")]
|
||||
[SerializeField] float landTolerance = 24f;
|
||||
|
||||
public ClimbState State { get; private set; } = ClimbState.Falling;
|
||||
ClimbState state = ClimbState.Falling;
|
||||
|
||||
/// <summary>
|
||||
/// 지금 이동 상태. 값이 실제로 바뀔 때만 <see cref="StateChanged"/> 가 불린다.
|
||||
///
|
||||
/// 대입하는 자리가 여러 군데라 각각에서 이벤트를 쏘면 빠뜨리기 쉽다.
|
||||
/// 프로퍼티 한 곳에 모아두면 새 전환을 추가해도 자동으로 알려진다.
|
||||
/// </summary>
|
||||
public ClimbState State
|
||||
{
|
||||
get => state;
|
||||
private set
|
||||
{
|
||||
if (state == value) return;
|
||||
state = value;
|
||||
StateChanged?.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>이동 상태가 바뀔 때. 애니메이션 전환이 이걸 듣는다.</summary>
|
||||
public event System.Action<ClimbState> StateChanged;
|
||||
|
||||
Transform character;
|
||||
float depth; // 카메라로부터의 깊이. 드래그와 같은 평면을 유지한다.
|
||||
@@ -159,7 +193,7 @@ void LateUpdate()
|
||||
case ClimbState.Falling: TickFalling(dt); break;
|
||||
case ClimbState.Standing: TickStanding(dt); break;
|
||||
case ClimbState.Walking: TickWalking(dt); break;
|
||||
case ClimbState.Climbing: TickClimbing(dt); break;
|
||||
case ClimbState.Jumping: TickJumping(dt); break;
|
||||
}
|
||||
|
||||
Apply();
|
||||
@@ -249,21 +283,36 @@ void BeginClimbArc()
|
||||
climbProgress = 0f;
|
||||
|
||||
float distance = Vector2.Distance(climbStart, new Vector2(ClampToPlatform(climbTarget), climbTarget.Y));
|
||||
climbDuration = Mathf.Max(0.25f, distance / Mathf.Max(1f, climbSpeed));
|
||||
|
||||
State = ClimbState.Climbing;
|
||||
// 점프는 거리에 정비례해 느려지지 않는다. 아주 짧은 도약이 순간이동처럼 보이거나
|
||||
// 먼 도약이 하염없이 길어지지 않도록 위아래를 막는다.
|
||||
climbDuration = Mathf.Clamp(distance / Mathf.Max(1f, climbSpeed),
|
||||
minJumpDuration, maxJumpDuration);
|
||||
|
||||
State = ClimbState.Jumping;
|
||||
}
|
||||
|
||||
void TickClimbing(float dt)
|
||||
/// <summary>
|
||||
/// 발판 사이를 뛰어오른다.
|
||||
///
|
||||
/// 원래는 대각선으로 미끄러지듯 올라갔는데, 그러면 화면을 오래 가리고
|
||||
/// "뒤로 도는지 옆으로 도는지" 애매해서 어떤 클라이밍 클립과도 맞지 않았다.
|
||||
/// 포물선 도약은 짧고, 방향이 분명하고, 점프 클립 하나로 어떤 거리든 커버된다.
|
||||
/// (Desktop Mate 도 Shimeji 도 "올라가는 과정"을 길게 보여주지 않는다)
|
||||
/// </summary>
|
||||
void TickJumping(float dt)
|
||||
{
|
||||
climbProgress += dt / climbDuration;
|
||||
|
||||
float t = Mathf.Clamp01(climbProgress);
|
||||
float targetX = ClampToPlatform(climbTarget);
|
||||
|
||||
// 수평은 일정하게, 수직은 뒤로 갈수록 느려지게 해서 "올라타는" 느낌을 준다.
|
||||
screenPos.x = Mathf.Lerp(climbStart.x, targetX, t);
|
||||
screenPos.y = Mathf.Lerp(climbStart.y, climbTarget.Y, Mathf.SmoothStep(0f, 1f, t));
|
||||
|
||||
// 4t(1-t) 는 t=0.5 에서 1, 양 끝에서 0 이다. 그래서 시작점과 도착점을
|
||||
// 정확히 지나면서 가운데만 부풀어 오른다 — 목표보다 높이 떠올랐다 내려앉는다.
|
||||
float straight = Mathf.Lerp(climbStart.y, climbTarget.Y, t);
|
||||
screenPos.y = straight + jumpArcHeight * 4f * t * (1f - t);
|
||||
|
||||
if (t >= 1f) Land(climbTarget);
|
||||
}
|
||||
@@ -398,8 +447,8 @@ void ConsiderClimb()
|
||||
walkTargetX = ClampToPlatform(best);
|
||||
nextClimbAllowedTime = Time.unscaledTime + climbCooldown;
|
||||
|
||||
State = Mathf.Abs(walkTargetX - screenPos.x) > 2f ? ClimbState.Walking : ClimbState.Climbing;
|
||||
if (State == ClimbState.Climbing) BeginClimbArc();
|
||||
State = Mathf.Abs(walkTargetX - screenPos.x) > 2f ? ClimbState.Walking : ClimbState.Jumping;
|
||||
if (State == ClimbState.Jumping) BeginClimbArc();
|
||||
}
|
||||
|
||||
// ---------------- 반영 ----------------
|
||||
|
||||
Reference in New Issue
Block a user