342 lines
11 KiB
C#
342 lines
11 KiB
C#
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using TMPro;
|
|
|
|
public struct Inputs
|
|
{
|
|
public Vector2 PlayerMove;
|
|
}
|
|
|
|
public class InputContoller
|
|
{
|
|
public static void UpdateInput(ref Inputs input)
|
|
{
|
|
input.PlayerMove.x = Input.GetAxisRaw("Horizontal");
|
|
input.PlayerMove.y = Input.GetAxisRaw("Vertical");
|
|
}
|
|
}
|
|
|
|
public class Player : MonoBehaviour
|
|
{
|
|
// 컴포넌트 프로퍼티
|
|
public Rigidbody2D rb { get; private set; }
|
|
|
|
// SPUM_Prefabs를 가져옵니다.
|
|
private SPUM_Prefabs spumPrefab;
|
|
|
|
private float maxSpeed;
|
|
private Inputs _inputs;
|
|
public Inputs Inputse => _inputs;
|
|
|
|
// 이동 관련 변수
|
|
[SerializeField] private float speed = 5f;
|
|
[SerializeField] public int hp = 5;
|
|
[SerializeField] public int maxHp = 5;
|
|
|
|
private bool isFacingRight = false;
|
|
private int currentAnimationState = -1;
|
|
|
|
private Animator animator;
|
|
|
|
// 캐릭터 생성 정보 캐싱
|
|
private string characterName = "데이터 로딩 중...";
|
|
private int generation = 1;
|
|
|
|
public int Generation => generation;
|
|
public string CharacterName => characterName;
|
|
|
|
[Header("UI 연결")]
|
|
public TextMeshProUGUI nameText;
|
|
public Image hpImage;
|
|
|
|
private static readonly string[] RANDOM_NAMES = new string[]
|
|
{
|
|
"자르반", "가렌", "럭스", "다리우스", "야스오", "티모", "아리", "이즈리얼",
|
|
"조이", "제이스", "카이사", "제드", "탈론", "리븐", "애쉬", "트린다미어",
|
|
"소나", "룰루", "유미", "레오나", "다이애나", "세주아니", "우디르", "신 짜오"
|
|
};
|
|
|
|
void Awake()
|
|
{
|
|
// 1. Rigidbody2D 할당
|
|
rb = GetComponent<Rigidbody2D>();
|
|
|
|
// 2. 부모 오브젝트에 붙어 있는 SPUM_Prefabs를 가져옵니다.
|
|
spumPrefab = GetComponentInParent<SPUM_Prefabs>();
|
|
|
|
if (spumPrefab == null)
|
|
{
|
|
Debug.LogError($"{gameObject.name} 오브젝트에 SPUM_Prefabs 컴포넌트가 없습니다! 인스펙터를 확인하세요.");
|
|
}
|
|
|
|
// 캐릭터 방향 기본 오른쪽 설정
|
|
Vector3 initialScale = transform.localScale;
|
|
initialScale.x = Mathf.Abs(initialScale.x);
|
|
transform.localScale = initialScale;
|
|
|
|
animator = GetComponentInChildren<Animator>();
|
|
maxHp = hp;
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
// 시작 시 백엔드로부터 다음 세대 정보 조회
|
|
StartCoroutine(GetNextGenerationFromServer());
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
// 안전 조치: maxHp가 비정상(0 이하)이거나 현재 체력이 초과된 경우 자동 동기화
|
|
if (maxHp <= 0 || hp > maxHp)
|
|
{
|
|
maxHp = hp;
|
|
}
|
|
|
|
if (hpImage != null && maxHp > 0)
|
|
{
|
|
float fillRatio = Mathf.Clamp01((float)hp / maxHp);
|
|
hpImage.fillAmount = fillRatio;
|
|
|
|
// 이미지 컴포넌트의 타입(Simple, Filled 등)과 상관없이 무조건 가로 크기가 깎이도록
|
|
// RectTransform의 로컬 스케일 X축도 비율에 맞춰 실시간으로 동기화합니다.
|
|
hpImage.rectTransform.localScale = new Vector3(fillRatio, 1f, 1f);
|
|
}
|
|
|
|
if (hp <= 0)
|
|
{
|
|
if (animator != null)
|
|
{
|
|
animator.SetBool("1_Move", false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
InputContoller.UpdateInput(ref _inputs);
|
|
|
|
// 좌우 방향 전환 (Flip)
|
|
if (_inputs.PlayerMove.x < 0f && isFacingRight)
|
|
{
|
|
Flip();
|
|
}
|
|
else if (_inputs.PlayerMove.x > 0f && !isFacingRight)
|
|
{
|
|
Flip();
|
|
}
|
|
|
|
// 애니메이션 업데이트 호출
|
|
if (_inputs.PlayerMove.x != 0f || _inputs.PlayerMove.y != 0f)
|
|
{
|
|
animator.SetBool("1_Move", true);
|
|
}
|
|
else
|
|
{
|
|
animator.SetBool("1_Move", false);
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.Z))
|
|
{
|
|
animator.SetTrigger("2_Attack");
|
|
}
|
|
}
|
|
|
|
void FixedUpdate()
|
|
{
|
|
Move();
|
|
}
|
|
|
|
private void Move()
|
|
{
|
|
if (rb == null) return;
|
|
|
|
if (hp <= 0)
|
|
{
|
|
rb.linearVelocity = new Vector2(0f, rb.linearVelocity.y);
|
|
return;
|
|
}
|
|
|
|
rb.linearVelocity = new Vector2(_inputs.PlayerMove.x * speed, rb.linearVelocity.y);
|
|
}
|
|
|
|
private void Flip()
|
|
{
|
|
isFacingRight = !isFacingRight;
|
|
Vector3 scale = transform.localScale;
|
|
scale.x *= -1f;
|
|
transform.localScale = scale;
|
|
}
|
|
|
|
// 데미지를 입는 메서드
|
|
public void TakeDamage(int damage, string attackerName = "알 수 없는 위험")
|
|
{
|
|
if (hp <= 0) return; // 이미 사망한 경우 제외
|
|
|
|
hp -= damage;
|
|
if (hp < 0) hp = 0;
|
|
|
|
Debug.Log($"플레이어가 {attackerName}로부터 {damage} 데미지를 입었습니다. 현재 체력: {hp}");
|
|
|
|
if (animator != null)
|
|
{
|
|
if (hp <= 0)
|
|
{
|
|
animator.SetTrigger("4_Death");
|
|
Debug.Log($"플레이어가 {attackerName}에 의해 사망했습니다.");
|
|
StartCoroutine(SendDeathReport(attackerName));
|
|
}
|
|
else
|
|
{
|
|
animator.SetTrigger("3_Damaged");
|
|
}
|
|
}
|
|
}
|
|
|
|
// 인스펙터 우클릭 메뉴에서 데미지를 테스트할 수 있는 기능 추가
|
|
[ContextMenu("Debug/Take 5 Damage")]
|
|
public void DebugTake5Damage()
|
|
{
|
|
TakeDamage(5, "인스펙터 디버그 공격");
|
|
}
|
|
|
|
[ContextMenu("Debug/Restore HP")]
|
|
public void DebugRestoreHP()
|
|
{
|
|
hp = maxHp;
|
|
if (animator != null)
|
|
{
|
|
animator.Rebind();
|
|
animator.Update(0f);
|
|
}
|
|
Debug.Log($"플레이어 체력이 {hp}로 회복되었습니다.");
|
|
|
|
// 체력 복구 시 다음 캐릭터 이름 및 세대 정보 갱신
|
|
StartCoroutine(GetNextGenerationFromServer());
|
|
}
|
|
|
|
// 임시 디버그용 화면 버튼 (게임 실행 시 화면 좌측 상단에 뜸)
|
|
private void OnGUI()
|
|
{
|
|
GUI.backgroundColor = Color.red;
|
|
if (GUI.Button(new Rect(10, 10, 180, 50), "Debug: Take 5 Damage (Trap)"))
|
|
{
|
|
TakeDamage(5, "테스트 함정");
|
|
}
|
|
|
|
GUI.backgroundColor = Color.blue;
|
|
// 기존 겹쳐있던 위치(10, 10)를 피해 y 좌표를 130으로 내렸습니다.
|
|
if (GUI.Button(new Rect(10, 130, 180, 50), "Debug: Take 3 Damage (Monster)"))
|
|
{
|
|
TakeDamage(3, "테스트 몬스터");
|
|
}
|
|
|
|
GUI.backgroundColor = Color.green;
|
|
if (GUI.Button(new Rect(10, 70, 180, 50), "Debug: Restore HP"))
|
|
{
|
|
DebugRestoreHP();
|
|
}
|
|
|
|
// 캐릭터 정보 UI 노출
|
|
GUI.color = Color.white;
|
|
GUI.skin.label.fontSize = 16;
|
|
GUI.Label(new Rect(10, 200, 400, 30), $"이름: {characterName}");
|
|
GUI.Label(new Rect(10, 230, 400, 30), $"세대: {generation}대손");
|
|
}
|
|
|
|
// 백엔드로부터 다음 세대 번호를 받아와 랜덤 이름과 매핑하는 코루틴
|
|
private System.Collections.IEnumerator GetNextGenerationFromServer()
|
|
{
|
|
string url = "http://127.0.0.1:8000/api/game/generation/next";
|
|
|
|
using (UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.Get(url))
|
|
{
|
|
yield return request.SendWebRequest();
|
|
|
|
if (request.result == UnityEngine.Networking.UnityWebRequest.Result.ConnectionError ||
|
|
request.result == UnityEngine.Networking.UnityWebRequest.Result.ProtocolError)
|
|
{
|
|
Debug.LogError("다음 세대 번호 조회 실패: " + request.error);
|
|
// 실패 시 로컬 기본값 적용
|
|
generation = 1;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
string json = request.downloadHandler.text;
|
|
NextGenerationResponse response = JsonUtility.FromJson<NextGenerationResponse>(json);
|
|
generation = response.next_generation;
|
|
Debug.Log($"서버로부터 다음 세대 번호({generation}대) 조회 성공.");
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
Debug.LogError("세대 JSON 파싱 에러: " + ex.Message);
|
|
generation = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 이름 랜덤 선택 후 세대 접미사 추가 (예: 자르반 3세)
|
|
string baseName = RANDOM_NAMES[Random.Range(0, RANDOM_NAMES.Length)];
|
|
characterName = $"{baseName} {generation}세";
|
|
if (nameText != null)
|
|
{
|
|
nameText.text = characterName;
|
|
}
|
|
Debug.Log($"캐릭터 생성 완료! 이름: {characterName}, 세대: {generation}대");
|
|
}
|
|
|
|
// 백엔드로 완성된 사망 기록을 전송하는 코루틴
|
|
private System.Collections.IEnumerator SendDeathReport(string cause)
|
|
{
|
|
string url = "http://127.0.0.1:8000/api/game/death";
|
|
|
|
AncestorDeathCreate requestData = new AncestorDeathCreate
|
|
{
|
|
name = characterName,
|
|
generation = generation,
|
|
cause_of_death = cause,
|
|
floor = 1 // 장소는 임시로 1층 고정
|
|
};
|
|
|
|
string json = JsonUtility.ToJson(requestData);
|
|
|
|
using (UnityEngine.Networking.UnityWebRequest request = new UnityEngine.Networking.UnityWebRequest(url, "POST"))
|
|
{
|
|
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(json);
|
|
request.uploadHandler = new UnityEngine.Networking.UploadHandlerRaw(bodyRaw);
|
|
request.downloadHandler = new UnityEngine.Networking.DownloadHandlerBuffer();
|
|
request.SetRequestHeader("Content-Type", "application/json");
|
|
|
|
yield return request.SendWebRequest();
|
|
|
|
if (request.result == UnityEngine.Networking.UnityWebRequest.Result.ConnectionError ||
|
|
request.result == UnityEngine.Networking.UnityWebRequest.Result.ProtocolError)
|
|
{
|
|
Debug.LogError("사망 기록 전송 실패: " + request.error);
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("사망 기록 전송 성공: " + request.downloadHandler.text);
|
|
// 사망 전송 성공 즉시 다음 세대 정보 받아와 자동 갱신
|
|
StartCoroutine(GetNextGenerationFromServer());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// JSON 파싱용 세대 응답 스키마 객체
|
|
[System.Serializable]
|
|
public class NextGenerationResponse
|
|
{
|
|
public int next_generation;
|
|
}
|
|
|
|
// JSON 전송용 사망 기록 객체
|
|
[System.Serializable]
|
|
public class AncestorDeathCreate
|
|
{
|
|
public string name;
|
|
public int generation;
|
|
public string cause_of_death;
|
|
public int floor;
|
|
} |