DDD-43 이동 기본 구현

This commit is contained in:
Jeonghyeon Ha 2025-07-15 13:02:53 +09:00
parent 0d2a8a5e2c
commit 70702f3235
3 changed files with 238 additions and 43 deletions

View File

@ -105,6 +105,18 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 2607481b15fd548b18ca4897db56ab3f, type: 3}
m_Name:
m_EditorClassIdentifier:
maxSpeed: 10
rotationSpeed: 120
accelerationRate: 2
minSpeedThreshold: 0.1
dragFactor: 0.98
turnSpeedPenalty: 0.5
maxTurnAngle: 180
showDebugLines: 1
debugLineLength: 4
debugLineHeightStep: 0.1
maxTiltAngle: 15
tiltSpeed: 5
--- !u!1 &6407855916708530114
GameObject:
m_ObjectHideFlags: 0
@ -118,7 +130,7 @@ GameObject:
- component: {fileID: 9176830315433650152}
- component: {fileID: 8182957266188503550}
m_Layer: 0
m_Name: Sphere
m_Name: Ship_Mesh
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@ -134,8 +146,8 @@ Transform:
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_LocalScale: {x: 2, y: 2, z: 2}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 7177504742663284911}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -3,66 +3,249 @@ using UnityEngine.InputSystem;
public class VoyagePlayerShipMovement : MonoBehaviour
{
private void Start()
{
[Header("Movement Settings")]
[SerializeField] private float maxSpeed = 10f;
[SerializeField] private float rotationSpeed = 120f;
[SerializeField] private float accelerationRate = 2f;
[SerializeField] private float minSpeedThreshold = 0.1f;
[SerializeField] private float dragFactor = 0.98f;
[Header("Turn Settings")]
[SerializeField] private float turnSpeedPenalty = 0.5f; // 선회 시 감속 정도 (0: 감속 없음, 1: 완전 정지)
[SerializeField] private float maxTurnAngle = 180f; // 최대 감속이 적용되는 각도
#if UNITY_EDITOR
// 현재 방향을 표시할 LineRenderer 설정
forwardDirectionLine = CreateLineRenderer("CurrentDirectionLine", Color.green);
upDirectionLine = CreateLineRenderer("upDirectionLine", Color.yellow);
// 입력 방향을 표시할 LineRenderer 설정
inputDirectionLine = CreateLineRenderer("InputDirectionLine", Color.red);
#endif
}
public void OnMove(InputAction.CallbackContext context)
{
Vector2 inputVector = context.ReadValue<Vector2>();
Vector3 currentForward = transform.forward;
Vector3 currentUp = transform.up;
Vector3 inputDirection = new Vector3(inputVector.x, 0, inputVector.y).normalized;
#if UNITY_EDITOR
DrawDebugLine(forwardDirectionLine, currentForward);
DrawDebugLine(upDirectionLine, currentUp);
DrawDebugLine(inputDirectionLine, inputDirection);
#endif
}
// Debug draw below...
#if UNITY_EDITOR
private LineRenderer inputDirectionLine;
[Header("Debug Settings")]
[SerializeField] private bool showDebugLines = true;
[SerializeField] private float debugLineLength = 4f;
[SerializeField] private float debugLineHeightStep = 0.1f;
private LineRenderer velocityLine;
private LineRenderer forwardDirectionLine;
private LineRenderer upDirectionLine;
private LineRenderer inputDirectionLine;
private bool lineRendererCreated = false;
#endif
private Vector3 currentVelocity;
private Vector2 currentInput;
private float targetSpeed;
private float currentSpeed;
[Header("Tilt Settings")]
[SerializeField] private float maxTiltAngle = 15f;
[SerializeField] private float tiltSpeed = 5f;
[SerializeField] private string meshObjectName = "Ship_Mesh"; // 메시 오브젝트의 이름
private Transform meshTransform; // 메시 트랜스폼
private float currentTilt = 0f;
private Quaternion originalMeshRotation; // 메시의 초기 회전값 저장
private void Start()
{
// 하위 오브젝트에서 메시 찾기
meshTransform = transform.Find(meshObjectName);
if (meshTransform == null)
{
Debug.LogError($"메시 오브젝트를 찾을 수 없습니다: {meshObjectName}");
return;
}
// 메시의 초기 회전값 저장
originalMeshRotation = meshTransform.localRotation;
}
private void FixedUpdate()
{
if (currentInput.magnitude > minSpeedThreshold)
{
HandleMovement();
HandleRotation();
}
else
{
// 입력이 없을 때는 서서히 감속
currentSpeed = Mathf.Lerp(currentSpeed, 0f, accelerationRate * Time.fixedDeltaTime);
}
ApplyDrag();
ApplyMovement();
#if UNITY_EDITOR
if (showDebugLines)
{
UpdateAllDebugLines();
}
#endif
}
private void HandleMovement()
{
// 기본 목표 속도 계산 (입력 크기에 비례)
float baseTargetSpeed = Mathf.Clamp01(currentInput.magnitude) * maxSpeed;
// 현재 방향과 목표 방향 사이의 각도 계산
Vector3 inputDirection = new Vector3(currentInput.x, 0, currentInput.y).normalized;
float angleDifference = Vector3.Angle(transform.forward, inputDirection);
// 각도에 따른 속도 페널티 계산 (각도가 클수록 더 큰 감속)
float turnPenaltyFactor = 1f - (angleDifference / maxTurnAngle * turnSpeedPenalty);
turnPenaltyFactor = Mathf.Clamp01(turnPenaltyFactor);
// 최종 목표 속도 계산 (기본 속도에 선회 페널티 적용)
targetSpeed = baseTargetSpeed * turnPenaltyFactor;
// 현재 속도를 목표 속도로 부드럽게 보간
currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, accelerationRate * Time.fixedDeltaTime);
// 최소 임계값 이하면 완전히 정지
if (currentSpeed < minSpeedThreshold && targetSpeed < minSpeedThreshold)
{
currentSpeed = 0f;
}
// 현재 바라보는 방향으로 속도 벡터 업데이트
currentVelocity = transform.forward * currentSpeed;
}
private void HandleRotation()
{
if (currentInput.magnitude > minSpeedThreshold)
{
Vector3 inputDirection = new Vector3(currentInput.x, 0, currentInput.y).normalized;
Quaternion targetRotation = Quaternion.LookRotation(inputDirection, Vector3.up);
// 회전 속도를 현재 속도에 비례하도록 설정
float currentRotationSpeed = rotationSpeed * (currentSpeed / maxSpeed);
currentRotationSpeed = Mathf.Max(currentRotationSpeed, rotationSpeed * 0.3f);
// 회전 방향에 따른 목표 기울기 계산
float turnDirection = Vector3.SignedAngle(transform.forward, inputDirection, Vector3.up);
float targetTilt = -Mathf.Sign(turnDirection) * maxTiltAngle * (currentSpeed / maxSpeed);
// 현재 기울기를 목표 기울기로 부드럽게 보간
currentTilt = Mathf.Lerp(currentTilt, targetTilt, tiltSpeed * Time.fixedDeltaTime);
// 기본 회전 적용 (오브젝트 전체)
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
targetRotation,
currentRotationSpeed * Time.fixedDeltaTime
);
// 메시에만 z축 기울기 적용
if (meshTransform != null)
{
meshTransform.localRotation = originalMeshRotation * Quaternion.Euler(0, 0, currentTilt);
}
}
else
{
// 입력이 없을 때는 기울기를 서서히 0으로
currentTilt = Mathf.Lerp(currentTilt, 0f, tiltSpeed * Time.fixedDeltaTime);
if (meshTransform != null)
{
meshTransform.localRotation = originalMeshRotation * Quaternion.Euler(0, 0, currentTilt);
}
}
}
private void ApplyDrag()
{
currentSpeed *= dragFactor;
// 최소 속도 이하면 완전히 정지
if (currentSpeed < minSpeedThreshold)
{
currentSpeed = 0f;
}
// 현재 방향으로 감속된 속도 적용
currentVelocity = transform.forward * currentSpeed;
}
private void ApplyMovement()
{
transform.position += currentVelocity * Time.fixedDeltaTime;
}
public void OnMove(InputAction.CallbackContext context)
{
currentInput = context.ReadValue<Vector2>();
// Debug Log this
Debug.Log(currentInput);
}
#if UNITY_EDITOR
private void UpdateAllDebugLines()
{
if (lineRendererCreated == false)
{
lineRendererCreated = true;
forwardDirectionLine = CreateLineRenderer("CurrentDirectionLine", Color.green);
upDirectionLine = CreateLineRenderer("UpDirectionLine", Color.yellow);
inputDirectionLine = CreateLineRenderer("InputDirectionLine", Color.red);
velocityLine = CreateLineRenderer("VelocityLine", Color.blue);
}
// 전방 방향 표시 (기본 높이)
DrawDebugLine(forwardDirectionLine, transform.forward, debugLineLength, 0);
// 메시의 위쪽 방향 표시 (틸팅 반영)
if (meshTransform is not null)
{
DrawDebugLine(upDirectionLine, meshTransform.up, debugLineLength, debugLineHeightStep);
}
// 입력 방향 표시 (두 단계 위)
Vector3 inputDirection = new Vector3(currentInput.x, 0, currentInput.y).normalized;
DrawDebugLine(inputDirectionLine, inputDirection, debugLineLength * currentInput.magnitude, debugLineHeightStep * 2);
// 속도 벡터 표시 (세 단계 위)
DrawDebugLine(velocityLine, currentVelocity.normalized, currentVelocity.magnitude, debugLineHeightStep * 3);
}
private LineRenderer CreateLineRenderer(string name, Color color)
{
GameObject lineObj = new GameObject(name);
lineObj.transform.SetParent(transform);
LineRenderer line = lineObj.AddComponent<LineRenderer>();
// LineRenderer 기본 설정
line.startWidth = 0.1f;
line.endWidth = 0.1f;
line.material = new Material(Shader.Find("Universal Render Pipeline/Unlit"));
line.startColor = color;
line.endColor = color;
line.positionCount = 2;
// 라인 렌더러가 다른 오브젝트를 가리지 않도록 설정
line.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
line.receiveShadows = false;
line.material.color = color;
return line;
}
private void DrawDebugLine(LineRenderer renderer, Vector3 direction)
private void DrawDebugLine(LineRenderer renderer, Vector3 direction, float length, float heightOffset)
{
const float lineLength = 4;
Vector3 position = transform.position;
// 현재 방향 라인 업데이트 (파란색)
if (!renderer) return;
Vector3 position = transform.position + Vector3.up * heightOffset;
renderer.SetPosition(0, position);
renderer.SetPosition(1, position + direction * lineLength);
renderer.SetPosition(1, position + direction * length);
}
#endif
private void OnValidate()
{
// 에디터에서 메시 오브젝트 이름이 변경될 때 자동으로 찾기
if (Application.isEditor && !Application.isPlaying)
{
meshTransform = transform.Find(meshObjectName);
}
}
}

View File

@ -33,7 +33,7 @@ Material:
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BaseMap:
m_Texture: {fileID: 2800000, guid: 9fab180d66628484a9bf08ce8bbd51c8, type: 3}
m_Texture: {fileID: 2800000, guid: b28a01dbcbf8f0c4c8b70e10b99d5a28, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BlendTex: