EerieVillage/Assets/Scripts/Mechanics/KinematicObject.cs

178 lines
5.8 KiB
C#
Raw Normal View History

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Platformer.Mechanics
{
/// <summary>
/// Implements game physics for some in game entity.
/// </summary>
public class KinematicObject : MonoBehaviour
{
/// <summary>
/// The minimum normal (dot product) considered suitable for the entity sit on.
/// </summary>
public float minGroundNormalY = .65f;
/// <summary>
/// A custom gravity coefficient applied to this entity.
/// </summary>
public float gravityModifier = 1f;
/// <summary>
/// The current velocity of the entity.
/// </summary>
public Vector2 velocity;
/// <summary>
/// Is the entity currently sitting on a surface?
/// </summary>
/// <value></value>
public bool IsGrounded { get; private set; }
protected Vector2 targetVelocity;
protected Vector2 groundNormal;
protected Rigidbody2D body;
protected ContactFilter2D contactFilter;
protected RaycastHit2D[] hitBuffer = new RaycastHit2D[16];
protected const float minMoveDistance = 0.001f;
protected const float shellRadius = 0.01f;
/// <summary>
/// Bounce the object's vertical velocity.
/// </summary>
/// <param name="value"></param>
public void Bounce(float value)
{
velocity.y = value;
}
/// <summary>
/// Bounce the objects velocity in a direction.
/// </summary>
/// <param name="dir"></param>
public void Bounce(Vector2 dir)
{
velocity.y = dir.y;
velocity.x = dir.x;
}
/// <summary>
/// Teleport to some position.
/// </summary>
/// <param name="position"></param>
public void Teleport(Vector3 position)
{
body.position = position;
velocity *= 0;
body.linearVelocity *= 0;
}
protected virtual void OnEnable()
{
body = GetComponent<Rigidbody2D>();
body.bodyType = RigidbodyType2D.Kinematic;
}
protected virtual void OnDisable()
{
body.bodyType = RigidbodyType2D.Dynamic;
}
protected virtual void Start()
{
contactFilter.useTriggers = false;
contactFilter.SetLayerMask(Physics2D.GetLayerCollisionMask(gameObject.layer));
contactFilter.useLayerMask = true;
}
protected virtual void Update()
{
targetVelocity = Vector2.zero;
ComputeVelocity();
}
protected virtual void ComputeVelocity()
{
}
protected virtual void FixedUpdate()
{
//if already falling, fall faster than the jump speed, otherwise use normal gravity.
if (velocity.y < 0)
velocity += gravityModifier * Physics2D.gravity * Time.deltaTime;
else
velocity += Physics2D.gravity * Time.deltaTime;
velocity.x = targetVelocity.x;
IsGrounded = false;
var deltaPosition = velocity * Time.deltaTime;
var moveAlongGround = new Vector2(groundNormal.y, -groundNormal.x);
var move = moveAlongGround * deltaPosition.x;
PerformMovement(move, false);
move = Vector2.up * deltaPosition.y;
PerformMovement(move, true);
}
void PerformMovement(Vector2 move, bool yMovement)
{
var distance = move.magnitude;
if (distance > minMoveDistance)
{
//check if we hit anything in current direction of travel
var count = body.Cast(move, contactFilter, hitBuffer, distance + shellRadius);
for (var i = 0; i < count; i++)
{
var currentNormal = hitBuffer[i].normal;
//is this surface flat enough to land on?
if (currentNormal.y > minGroundNormalY)
{
IsGrounded = true;
// if moving up, change the groundNormal to new surface normal.
if (yMovement)
{
groundNormal = currentNormal;
currentNormal.x = 0;
}
}
if (IsGrounded)
{
//how much of our velocity aligns with surface normal?
var projection = Vector2.Dot(velocity, currentNormal);
if (projection < 0)
{
//slower velocity if moving against the normal (up a hill).
velocity = velocity - projection * currentNormal;
}
}
else
{
BT5-Dev #68: 점프 X·Y 분리 + TileGround* 벽 자동 분류 제외 (PD 2종 명시 채택) PD 명시 (2026-05-08): 1. "대각선으로 점프로 벽 부분을 통과하려는 상황 → Player 점프 상태에서 밀려나는 현상" (애초에 통과 X 의무) 2. "전진하며 점프할 경우 앞이 막혀있으면 위로만 점프되어야 하는데 점프 자체가 막힘" (앞 막혀도 정상 점프 의무) 본 PM 직접 read 진단: 버그 1 (벽 통과) 원인: - 자동 분류 영역(BT47/BT48)이 TileGround·TileGroundDark·TileGroundTop도 AutoForeground로 이동 - AutoForeground = Layer 16 + Drop-Through 패턴 (ascending mask OFF) - = 대각선 점프 시 벽 Tile 통과 가능 버그 2 (점프 막힘) 원인: - KinematicObject.PerformMovement line 164~165: velocity.x *= 0; velocity.y = Mathf.Min(velocity.y, 0); - = X 이동 시점 hit (앞 벽)에서도 velocity.y 영점 강제 - = 점프 시작 시 앞 벽 hit → 점프 속도 0 → 점프 자체 차단 정정: 1. KinematicObject.cs (버그 2): - 공중 hit 처리 X·Y 분리: - X 이동 시점 hit (앞 벽) → velocity.x = 0, velocity.y 보존 (점프 속도 유지) - Y 이동 시점 hit (천장) → velocity.x·y 모두 cap (기존) 2. GameOptimizer.cs (버그 1): - TileGround* (3종 — 지면·벽 의도) 자동 분류 제외 추가 - tileAsset.name.StartsWith("TileGround") → continue - TileGround·TileGroundDark·TileGroundTop = Level 잔존·Layer 0·영구 충돌·통과 X 효과: - 버그 1: 벽 통과 X (TileGround* = Layer 0 영구 충돌) - 버그 2: 앞 막혀도 위로 점프 정합 (Y 속도 보존) - 발판 동작 그대로 (TileFloating·MidgroundFiller·Building·cloud·hillside·midground·mountains 자동 분류) - 나무·plant·fence·house 자체 통과 그대로 후속 의무: - PD Refresh+Play 시각 검증 (대각선 점프 벽 통과 X + 앞 막힌 점프 작동) - 본 PM Editor.log [BT48-MoveTiles] direct read
2026-05-07 15:20:53 +00:00
// BT68 — X·Y 분리 처리 (PD 보고 2026-05-08: "전진 점프 시 앞 막혀도 위로 점프 가능 의무"):
// X 이동 시점 hit (앞 벽) → velocity.x만 0, velocity.y 보존 (점프 속도 유지)
// Y 이동 시점 hit (천장) → velocity.x·y 모두 cap (기존 동작)
velocity.x = 0;
if (yMovement) velocity.y = Mathf.Min(velocity.y, 0);
}
//remove shellDistance from actual move distance.
var modifiedDistance = hitBuffer[i].distance - shellRadius;
distance = modifiedDistance < distance ? modifiedDistance : distance;
}
}
body.position = body.position + move.normalized * distance;
}
}
}