From 83bc9e3200adc44c3f365279cde482e343e90c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=86=E1=85=B5=E1=86=AB?= =?UTF-8?q?=E1=84=80=E1=85=AE?= Date: Fri, 3 Jul 2026 18:20:32 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9C=A0=EB=8B=88=ED=8B=B0=20=EC=83=81?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=EA=B0=80=20=EC=82=AC=EB=A7=9D=20=EC=8B=9C=20fastapi=EB=A1=9C?= =?UTF-8?q?=20=EC=A0=84=EB=8B=AC=ED=95=98=EC=97=AC=20db=EC=97=90=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai-server/backend/routers/game_router.py | 5 +- ai-server/backend/schemas/game_schema.py | 3 ++ ai-server/backend/services/game_service.py | 31 +++++++++--- unity-client/Assets/Scripts/PlayerMove.cs | 55 +++++++++++++++++++--- 4 files changed, 80 insertions(+), 14 deletions(-) diff --git a/ai-server/backend/routers/game_router.py b/ai-server/backend/routers/game_router.py index 44f235c..854b3a0 100644 --- a/ai-server/backend/routers/game_router.py +++ b/ai-server/backend/routers/game_router.py @@ -3,6 +3,7 @@ from sqlalchemy.orm import Session from backend.repository.db_init import get_db from backend.schemas.game_schema import ( AncestorDeathCreate, + DeathRecordRequest, AncestorDeathResponse, DialogueResponse, SkillCreateRequest, @@ -13,8 +14,8 @@ from backend.services import game_service router = APIRouter(prefix="/api/game", tags=["Game"]) @router.post("/death", response_model=AncestorDeathResponse) -def record_death(req: AncestorDeathCreate, db: Session = Depends(get_db)): - return game_service.create_death_history(db=db, death_data=req) +def record_death(req: DeathRecordRequest, db: Session = Depends(get_db)): + return game_service.create_death_history(db=db, cause_of_death=req.cause_of_death) @router.get("/npc-dialogue", response_model=DialogueResponse) def get_npc_dialogue(current_generation: int = Query(..., description="현재 플레이어 세대"), db: Session = Depends(get_db)): diff --git a/ai-server/backend/schemas/game_schema.py b/ai-server/backend/schemas/game_schema.py index cde1fed..9ea549f 100644 --- a/ai-server/backend/schemas/game_schema.py +++ b/ai-server/backend/schemas/game_schema.py @@ -8,6 +8,9 @@ class AncestorDeathCreate(BaseModel): cause_of_death: str = Field(..., description="사망 원인 (예: 가시 함정, 화염 슬라임 등)") floor: int = Field(..., description="사망한 던전 층수") +class DeathRecordRequest(BaseModel): + cause_of_death: str = Field(..., description="사망 원인 (누구에게 죽었는지)") + class AncestorDeathResponse(BaseModel): id: int name: str diff --git a/ai-server/backend/services/game_service.py b/ai-server/backend/services/game_service.py index 3efa5fe..9506c56 100644 --- a/ai-server/backend/services/game_service.py +++ b/ai-server/backend/services/game_service.py @@ -1,20 +1,39 @@ import json import re +import random from sqlalchemy.orm import Session -from sqlalchemy import desc +from sqlalchemy import desc, func from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from backend.ai.llm import hugging_llm from backend.repository.models import AncestorDeath from backend.schemas.game_schema import AncestorDeathCreate, SkillCreateResponse +# 랜덤 이름 리스트 (자르반, 가렌 등 판타지 풍 이름) +RANDOM_NAMES = [ + "자르반", "가렌", "럭스", "다리우스", "야스오", "티모", "아리", "이즈리얼", + "조이", "제이스", "카이사", "제드", "탈론", "리븐", "애쉬", "트린다미어", + "소나", "룰루", "유미", "레오나", "다이애나", "세주아니", "우디르", "신 짜오" +] + # 1. 선조 사망 기록 등록 -def create_death_history(db: Session, death_data: AncestorDeathCreate): +def create_death_history(db: Session, cause_of_death: str): + # 세대 계산: DB에서 현재 등록된 최대 세대를 조회하여 다음 세대 번호를 계산합니다. (기록이 없다면 1세대) + max_gen = db.query(func.max(AncestorDeath.generation)).scalar() + generation = (max_gen + 1) if max_gen is not None else 1 + + # 랜덤 이름 선택 후 세대 접미사 추가 (예: 자르반 3세) + base_name = random.choice(RANDOM_NAMES) + name = f"{base_name} {generation}세" + + # 장소는 임시로 1층 설정 + floor = 1 + db_death = AncestorDeath( - name=death_data.name, - generation=death_data.generation, - cause_of_death=death_data.cause_of_death, - floor=death_data.floor + name=name, + generation=generation, + cause_of_death=cause_of_death, + floor=floor ) db.add(db_death) db.commit() diff --git a/unity-client/Assets/Scripts/PlayerMove.cs b/unity-client/Assets/Scripts/PlayerMove.cs index 78a9f4c..4b85c36 100644 --- a/unity-client/Assets/Scripts/PlayerMove.cs +++ b/unity-client/Assets/Scripts/PlayerMove.cs @@ -125,21 +125,22 @@ public class PlayerMove : MonoBehaviour } // 데미지를 입는 메서드 - public void TakeDamage(int damage) + public void TakeDamage(int damage, string attackerName = "알 수 없는 위험") { if (hp <= 0) return; // 이미 사망한 경우 제외 hp -= damage; if (hp < 0) hp = 0; - Debug.Log($"플레이어가 {damage} 데미지를 입었습니다. 현재 체력: {hp}"); + Debug.Log($"플레이어가 {attackerName}로부터 {damage} 데미지를 입었습니다. 현재 체력: {hp}"); if (animator != null) { if (hp <= 0) { animator.SetTrigger("4_Death"); - Debug.Log("플레이어가 사망했습니다."); + Debug.Log($"플레이어가 {attackerName}에 의해 사망했습니다."); + StartCoroutine(SendDeathReport(attackerName)); } else { @@ -152,7 +153,7 @@ public class PlayerMove : MonoBehaviour [ContextMenu("Debug/Take 5 Damage")] public void DebugTake5Damage() { - TakeDamage(5); + TakeDamage(5, "인스펙터 디버그 공격"); } [ContextMenu("Debug/Restore HP")] @@ -171,9 +172,16 @@ public class PlayerMove : MonoBehaviour private void OnGUI() { GUI.backgroundColor = Color.red; - if (GUI.Button(new Rect(10, 10, 180, 50), "Debug: Take 5 Damage")) + if (GUI.Button(new Rect(10, 10, 180, 50), "Debug: Take 5 Damage (Trap)")) { - TakeDamage(5); + 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; @@ -182,4 +190,39 @@ public class PlayerMove : MonoBehaviour DebugRestoreHP(); } } + + // 백엔드로 사망 원인을 전송하는 코루틴 + private System.Collections.IEnumerator SendDeathReport(string cause) + { + string url = "http://127.0.0.1:8000/api/game/death"; + DeathRecordRequest requestData = new DeathRecordRequest { cause_of_death = cause }; + 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); + } + } + } +} + +// JSON 파싱용 요청 스키마 객체 +[System.Serializable] +public class DeathRecordRequest +{ + public string cause_of_death; } \ No newline at end of file