2026-06-19 UI, UI로직
This commit is contained in:
514
Assets/My project/TruthFountain/Scripts/README_KR.md
Normal file
514
Assets/My project/TruthFountain/Scripts/README_KR.md
Normal file
@@ -0,0 +1,514 @@
|
||||
# 진실의 샘 UI 스크립트 사용 설명서
|
||||
|
||||
이 폴더는 `진실의 샘 UI`를 만들기 위한 Unity C# 스크립트 세트입니다.
|
||||
|
||||
진실의 샘은 질문에 답하면 선택지의 `Lie Amount`에 따라 코 게이지가 올라가고, 최종적으로 성공/실패를 판정하는 구조입니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 포함된 스크립트
|
||||
|
||||
| 파일 | 역할 | 오브젝트에 붙이는가 |
|
||||
|---|---|---|
|
||||
| `TruthChoiceData.cs` | 선택지 하나의 데이터 | 붙이지 않음 |
|
||||
| `TruthQuestionData.cs` | 질문 하나의 데이터 | 붙이지 않음 |
|
||||
| `TruthFountainData.cs` | 전체 질문 묶음 ScriptableObject | 붙이지 않음, asset 생성용 |
|
||||
| `TruthChoiceButtonUI.cs` | 선택지 버튼 하나 표시/클릭 처리 | 선택지 버튼 3개 각각 |
|
||||
| `TruthFountainUI.cs` | 질문, 선택지, 코 게이지, 결과창 표시 | `TruthFountainUI` 오브젝트 |
|
||||
| `TruthFountainGameManager.cs` | 게임 진행, 점수 계산, 성공/실패 판정 | `TruthFountainSystem` |
|
||||
| `TruthFountainReward.cs` | 성공 보상 지급 | `TruthFountainSystem` |
|
||||
| `TruthFountainOpenClose.cs` | UI 열기/닫기, VR 카메라 앞 배치 | `TruthFountainUI` 또는 별도 시스템 오브젝트 |
|
||||
|
||||
---
|
||||
|
||||
## 2. Unity 폴더 위치
|
||||
|
||||
추천 폴더 구조입니다.
|
||||
|
||||
```text
|
||||
Assets
|
||||
└── My project
|
||||
└── TruthFountain
|
||||
├── Data
|
||||
├── Prefabs
|
||||
├── Scripts
|
||||
└── Sprites
|
||||
```
|
||||
|
||||
스크립트는 여기에 넣으세요.
|
||||
|
||||
```text
|
||||
Assets/My project/TruthFountain/Scripts
|
||||
```
|
||||
|
||||
데이터 asset은 여기에 만드는 것을 추천합니다.
|
||||
|
||||
```text
|
||||
Assets/My project/TruthFountain/Data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 권장 Hierarchy
|
||||
|
||||
```text
|
||||
TruthFountainCanvas
|
||||
└── TruthFountainUI
|
||||
├── TruthFountainUI.cs
|
||||
├── TruthFountainOpenClose.cs
|
||||
├── Background
|
||||
├── TitleText
|
||||
├── QuestionPanel
|
||||
│ ├── QuestionBG
|
||||
│ ├── QuestionText
|
||||
│ └── QuestionHintText
|
||||
├── ChoiceRoot
|
||||
│ ├── ChoiceButton_01
|
||||
│ │ └── TruthChoiceButtonUI.cs
|
||||
│ ├── ChoiceButton_02
|
||||
│ │ └── TruthChoiceButtonUI.cs
|
||||
│ └── ChoiceButton_03
|
||||
│ └── TruthChoiceButtonUI.cs
|
||||
├── NoseGaugePanel
|
||||
│ ├── NoseGaugeBG
|
||||
│ ├── NoseGaugeFill
|
||||
│ ├── NoseIcon
|
||||
│ └── NoseValueText
|
||||
├── ProgressText
|
||||
├── ResultText
|
||||
├── FinalResultPanel
|
||||
│ ├── FinalResultBG
|
||||
│ ├── FinalTitleText
|
||||
│ ├── FinalDescriptionText
|
||||
│ └── ConfirmButton
|
||||
│ └── ConfirmButtonText
|
||||
└── CloseButton
|
||||
└── CloseText
|
||||
|
||||
TruthFountainSystem
|
||||
├── TruthFountainGameManager.cs
|
||||
└── TruthFountainReward.cs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. TruthFountainData asset 만들기
|
||||
|
||||
Project 창에서 우클릭합니다.
|
||||
|
||||
```text
|
||||
Create
|
||||
→ Adventure
|
||||
→ Truth Fountain
|
||||
→ Truth Fountain Data
|
||||
```
|
||||
|
||||
파일 이름 예시:
|
||||
|
||||
```text
|
||||
TruthFountainData_Default
|
||||
```
|
||||
|
||||
설정 예시:
|
||||
|
||||
```text
|
||||
Title: 진실의 샘
|
||||
Max Lie Score: 3
|
||||
Fail Immediately At Max Lie Score: 체크
|
||||
Result Delay: 1
|
||||
Success Title: 진실의 샘이 빛납니다
|
||||
Success Description: 당신의 진심이 샘에 닿았습니다. 기억의 조각을 획득했습니다.
|
||||
Fail Title: 코가 너무 길어졌습니다
|
||||
Fail Description: 샘이 당신의 대답을 믿지 못했습니다. 다시 도전해보세요.
|
||||
```
|
||||
|
||||
질문은 `Questions` 배열에 추가합니다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 질문 데이터 입력 예시
|
||||
|
||||
### 질문 1
|
||||
|
||||
```text
|
||||
Question Text:
|
||||
친구를 돕기 위해 거짓말을 해도 될까?
|
||||
|
||||
Hint Text:
|
||||
당신의 선택에 따라 코가 반응합니다.
|
||||
|
||||
Choice 1:
|
||||
Choice Text: 솔직하게 말한다
|
||||
Lie Amount: 0
|
||||
Result Message: 샘의 물결이 맑게 빛납니다.
|
||||
|
||||
Choice 2:
|
||||
Choice Text: 좋은 의도라면 거짓말한다
|
||||
Lie Amount: 1
|
||||
Result Message: 피노키오의 코가 조금 길어졌습니다.
|
||||
```
|
||||
|
||||
### 질문 2
|
||||
|
||||
```text
|
||||
Question Text:
|
||||
위험을 피하려고 약속을 어겨도 될까?
|
||||
|
||||
Choice 1:
|
||||
Choice Text: 약속을 지킨다
|
||||
Lie Amount: 0
|
||||
Result Message: 샘이 조용히 빛납니다.
|
||||
|
||||
Choice 2:
|
||||
Choice Text: 아무 말 없이 도망친다
|
||||
Lie Amount: 1
|
||||
Result Message: 코끝이 불편하게 간질거립니다.
|
||||
```
|
||||
|
||||
### 질문 3
|
||||
|
||||
```text
|
||||
Question Text:
|
||||
보상을 얻기 위해 모르는 척해도 될까?
|
||||
|
||||
Choice 1:
|
||||
Choice Text: 사실을 말한다
|
||||
Lie Amount: 0
|
||||
Result Message: 맑은 물방울이 떠오릅니다.
|
||||
|
||||
Choice 2:
|
||||
Choice Text: 모르는 척한다
|
||||
Lie Amount: 1
|
||||
Result Message: 샘이 잠시 어두워집니다.
|
||||
```
|
||||
|
||||
### 질문 4
|
||||
|
||||
```text
|
||||
Question Text:
|
||||
실수를 숨기면 모두가 편해질까?
|
||||
|
||||
Choice 1:
|
||||
Choice Text: 실수를 인정한다
|
||||
Lie Amount: 0
|
||||
Result Message: 물결이 부드럽게 퍼집니다.
|
||||
|
||||
Choice 2:
|
||||
Choice Text: 아무도 모르게 숨긴다
|
||||
Lie Amount: 1
|
||||
Result Message: 코가 길어지는 느낌이 듭니다.
|
||||
```
|
||||
|
||||
### 질문 5
|
||||
|
||||
```text
|
||||
Question Text:
|
||||
진실이 아파도 말해야 할까?
|
||||
|
||||
Choice 1:
|
||||
Choice Text: 조심스럽게 진실을 말한다
|
||||
Lie Amount: 0
|
||||
Result Message: 샘이 밝게 빛납니다.
|
||||
|
||||
Choice 2:
|
||||
Choice Text: 상처 주지 않기 위해 숨긴다
|
||||
Lie Amount: 1
|
||||
Result Message: 샘의 빛이 흔들립니다.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. `TruthChoiceButtonUI.cs` 붙이기
|
||||
|
||||
선택지 버튼 3개에 각각 붙입니다.
|
||||
|
||||
```text
|
||||
ChoiceButton_01 → TruthChoiceButtonUI
|
||||
ChoiceButton_02 → TruthChoiceButtonUI
|
||||
ChoiceButton_03 → TruthChoiceButtonUI
|
||||
```
|
||||
|
||||
각 버튼에서 연결할 것:
|
||||
|
||||
```text
|
||||
Button → 자기 자신의 Button 컴포넌트
|
||||
Background Image → 자기 자신의 Image 컴포넌트
|
||||
Choice Text → ChoiceText
|
||||
```
|
||||
|
||||
Sprite가 아직 없다면 아래는 비워도 됩니다.
|
||||
|
||||
```text
|
||||
Normal Sprite
|
||||
Selected Sprite
|
||||
Truth Sprite
|
||||
Lie Sprite
|
||||
```
|
||||
|
||||
Sprite가 없으면 색상으로 버튼 상태가 표시됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 7. `TruthFountainUI.cs` 붙이기
|
||||
|
||||
붙일 위치:
|
||||
|
||||
```text
|
||||
TruthFountainCanvas
|
||||
└── TruthFountainUI
|
||||
└── TruthFountainUI.cs
|
||||
```
|
||||
|
||||
Inspector 연결:
|
||||
|
||||
```text
|
||||
UI Root → TruthFountainUI
|
||||
Title Text → TitleText
|
||||
Question Text → QuestionPanel / QuestionText
|
||||
Question Hint Text → QuestionPanel / QuestionHintText
|
||||
Progress Text → ProgressText
|
||||
Result Text → ResultText
|
||||
|
||||
Choice Buttons Size: 3
|
||||
Element 0 → ChoiceButton_01의 TruthChoiceButtonUI
|
||||
Element 1 → ChoiceButton_02의 TruthChoiceButtonUI
|
||||
Element 2 → ChoiceButton_03의 TruthChoiceButtonUI
|
||||
|
||||
Nose Gauge Fill → NoseGaugePanel / NoseGaugeFill
|
||||
Nose Value Text → NoseGaugePanel / NoseValueText
|
||||
|
||||
Final Result Panel → FinalResultPanel
|
||||
Final Title Text → FinalResultPanel / FinalTitleText
|
||||
Final Description Text → FinalResultPanel / FinalDescriptionText
|
||||
Confirm Button → FinalResultPanel / ConfirmButton
|
||||
```
|
||||
|
||||
`NoseGaugeFill`의 Image 설정:
|
||||
|
||||
```text
|
||||
Image Type: Filled
|
||||
Fill Method: Vertical
|
||||
Fill Origin: Bottom
|
||||
Fill Amount: 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. `TruthFountainGameManager.cs` 붙이기
|
||||
|
||||
빈 오브젝트를 만듭니다.
|
||||
|
||||
```text
|
||||
TruthFountainSystem
|
||||
```
|
||||
|
||||
여기에 붙입니다.
|
||||
|
||||
```text
|
||||
TruthFountainSystem
|
||||
└── TruthFountainGameManager.cs
|
||||
```
|
||||
|
||||
Inspector 연결:
|
||||
|
||||
```text
|
||||
Fountain Data → TruthFountainData_Default
|
||||
UI → TruthFountainUI 오브젝트의 TruthFountainUI 컴포넌트
|
||||
Reward → TruthFountainSystem의 TruthFountainReward 컴포넌트
|
||||
Start On Awake → 테스트할 때만 체크
|
||||
Show Debug Log → 체크
|
||||
```
|
||||
|
||||
테스트할 때는 `Start On Awake`를 켜면 Play 직후 바로 질문이 표시됩니다.
|
||||
실제 게임에서는 `TruthFountainOpenClose.Open()`에서 시작하게 하는 것을 추천합니다.
|
||||
|
||||
---
|
||||
|
||||
## 9. `TruthFountainReward.cs` 붙이기
|
||||
|
||||
붙일 위치:
|
||||
|
||||
```text
|
||||
TruthFountainSystem
|
||||
└── TruthFountainReward.cs
|
||||
```
|
||||
|
||||
기본 설정:
|
||||
|
||||
```text
|
||||
Give Only Once → 체크
|
||||
Show Debug Log → 체크
|
||||
```
|
||||
|
||||
### 기억의 조각 보상과 연결하는 방법 A
|
||||
|
||||
이미 만든 `MemoryPieceReward.cs`를 `TruthFountainSystem`에 같이 붙입니다.
|
||||
|
||||
```text
|
||||
TruthFountainSystem
|
||||
├── TruthFountainGameManager
|
||||
├── TruthFountainReward
|
||||
└── MemoryPieceReward
|
||||
```
|
||||
|
||||
`TruthFountainReward`에서:
|
||||
|
||||
```text
|
||||
Reward Receiver → MemoryPieceReward 컴포넌트
|
||||
Reward Method Name → GiveReward
|
||||
```
|
||||
|
||||
이렇게 하면 진실의 샘 성공 시 `MemoryPieceReward.GiveReward()`가 자동 호출됩니다.
|
||||
|
||||
### 기억의 조각 보상과 연결하는 방법 B
|
||||
|
||||
`TruthFountainReward`의 `On Reward Given` 이벤트에 직접 연결합니다.
|
||||
|
||||
```text
|
||||
On Reward Given
|
||||
→ MemoryPieceReward
|
||||
→ GiveReward()
|
||||
```
|
||||
|
||||
방 클리어 상태도 같이 바꾸고 싶으면 `On Reward Given` 이벤트에 `RoomProgressManager.MarkRoomCleared(RoomData)` 또는 `RoomProgressManager.MarkMemoryPieceCollected(RoomData)`를 추가로 연결할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 10. `TruthFountainOpenClose.cs` 붙이기
|
||||
|
||||
붙일 위치 추천:
|
||||
|
||||
```text
|
||||
TruthFountainCanvas
|
||||
└── TruthFountainUI
|
||||
└── TruthFountainOpenClose.cs
|
||||
```
|
||||
|
||||
Inspector 연결:
|
||||
|
||||
```text
|
||||
Truth Fountain Root → TruthFountainUI
|
||||
Game Manager → TruthFountainSystem의 TruthFountainGameManager
|
||||
Target Camera → Main Camera 또는 XR Camera
|
||||
Open On Start → 테스트할 때만 체크
|
||||
Start Game On Open → 체크
|
||||
Place In Front Of Camera On Open → 체크
|
||||
Face Camera On Open → 체크
|
||||
Distance From Camera → 2.2
|
||||
Vertical Offset → -0.1
|
||||
```
|
||||
|
||||
닫기 버튼 연결:
|
||||
|
||||
```text
|
||||
CloseButton OnClick
|
||||
→ TruthFountainUI
|
||||
→ TruthFountainOpenClose.Close()
|
||||
```
|
||||
|
||||
방 선택 UI에서 진실의 샘 방 입장 시 연결:
|
||||
|
||||
```text
|
||||
RoomEnterHandler 또는 진실의 샘 입장 이벤트
|
||||
→ TruthFountainOpenClose.Open()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 최종 작동 흐름
|
||||
|
||||
```text
|
||||
TruthFountainOpenClose.Open()
|
||||
→ TruthFountainUI 켜짐
|
||||
→ TruthFountainGameManager.StartGame()
|
||||
→ 첫 질문 표시
|
||||
→ 플레이어 선택지 클릭
|
||||
→ lieScore 증가 또는 유지
|
||||
→ NoseGaugeFill 갱신
|
||||
→ 다음 질문 표시
|
||||
→ 모든 질문 종료
|
||||
→ 성공/실패 판정
|
||||
→ 성공 시 TruthFountainReward.GiveReward()
|
||||
→ 최종 결과 패널 표시
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 테스트 순서
|
||||
|
||||
```text
|
||||
1. TruthFountainData_Default asset 만들기
|
||||
2. 질문 5개 입력하기
|
||||
3. ChoiceButton 3개에 TruthChoiceButtonUI 붙이기
|
||||
4. TruthFountainUI 오브젝트에 TruthFountainUI.cs 붙이기
|
||||
5. TruthFountainSystem 만들기
|
||||
6. TruthFountainGameManager 붙이기
|
||||
7. TruthFountainReward 붙이기
|
||||
8. GameManager에 Data, UI, Reward 연결하기
|
||||
9. Start On Awake 체크
|
||||
10. Play 누르기
|
||||
11. 질문이 표시되는지 확인
|
||||
12. 선택지를 누르면 ResultText와 NoseGaugeFill이 바뀌는지 확인
|
||||
13. 최종 성공/실패 패널이 뜨는지 확인
|
||||
14. 성공 시 보상 이벤트가 호출되는지 확인
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. 자주 나는 오류
|
||||
|
||||
### 질문이 안 뜸
|
||||
|
||||
확인할 것:
|
||||
|
||||
```text
|
||||
TruthFountainGameManager의 Fountain Data가 비어 있지 않은가?
|
||||
TruthFountainData의 Questions 배열에 질문이 들어 있는가?
|
||||
TruthFountainGameManager의 UI가 연결되어 있는가?
|
||||
```
|
||||
|
||||
### 선택지 버튼이 안 보임
|
||||
|
||||
확인할 것:
|
||||
|
||||
```text
|
||||
TruthQuestionData의 Choices 배열에 선택지가 들어 있는가?
|
||||
TruthFountainUI의 Choice Buttons 배열이 연결되어 있는가?
|
||||
각 ChoiceButton에 TruthChoiceButtonUI가 붙어 있는가?
|
||||
```
|
||||
|
||||
### 코 게이지가 안 움직임
|
||||
|
||||
확인할 것:
|
||||
|
||||
```text
|
||||
NoseGaugeFill이 TruthFountainUI에 연결되어 있는가?
|
||||
NoseGaugeFill의 Image Type이 Filled인가?
|
||||
Fill Method가 Vertical인가?
|
||||
선택지의 Lie Amount가 1 이상인가?
|
||||
```
|
||||
|
||||
### 버튼 클릭이 안 됨
|
||||
|
||||
확인할 것:
|
||||
|
||||
```text
|
||||
Canvas에 Graphic Raycaster가 있는가?
|
||||
씬에 EventSystem이 있는가?
|
||||
Button Image의 Raycast Target이 켜져 있는가?
|
||||
VR이라면 XR UI Input Module과 XR Ray Interactor UI Interaction이 켜져 있는가?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 현재 단계에서 가장 먼저 할 것
|
||||
|
||||
처음에는 보상 연결보다 아래 3가지만 먼저 확인하세요.
|
||||
|
||||
```text
|
||||
1. 질문 표시
|
||||
2. 선택지 클릭
|
||||
3. 코 게이지 증가
|
||||
```
|
||||
|
||||
이 3개가 정상 작동하면 진실의 샘 UI의 핵심은 완성입니다.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cac5f4ffa0618d242b30be932e29daf6
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
133
Assets/My project/TruthFountain/Scripts/TruthChoiceButtonUI.cs
Normal file
133
Assets/My project/TruthFountain/Scripts/TruthChoiceButtonUI.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
[RequireComponent(typeof(Button))]
|
||||
public class TruthChoiceButtonUI : MonoBehaviour
|
||||
{
|
||||
[Header("UI References")]
|
||||
[SerializeField] private Button button;
|
||||
[SerializeField] private Image backgroundImage;
|
||||
[SerializeField] private TMP_Text choiceText;
|
||||
|
||||
[Header("Optional Sprites")]
|
||||
[SerializeField] private Sprite normalSprite;
|
||||
[SerializeField] private Sprite selectedSprite;
|
||||
[SerializeField] private Sprite truthSprite;
|
||||
[SerializeField] private Sprite lieSprite;
|
||||
|
||||
[Header("Fallback Colors")]
|
||||
[SerializeField] private Color normalColor = new Color(0.08f, 0.35f, 0.42f, 0.9f);
|
||||
[SerializeField] private Color selectedColor = new Color(0.2f, 0.75f, 0.85f, 1f);
|
||||
[SerializeField] private Color truthColor = new Color(0.25f, 0.85f, 0.65f, 1f);
|
||||
[SerializeField] private Color lieColor = new Color(0.85f, 0.35f, 0.35f, 1f);
|
||||
|
||||
private TruthChoiceData currentChoice;
|
||||
private TruthFountainGameManager gameManager;
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
button = GetComponent<Button>();
|
||||
backgroundImage = GetComponent<Image>();
|
||||
choiceText = GetComponentInChildren<TMP_Text>(true);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (button == null)
|
||||
{
|
||||
button = GetComponent<Button>();
|
||||
}
|
||||
|
||||
if (backgroundImage == null)
|
||||
{
|
||||
backgroundImage = GetComponent<Image>();
|
||||
}
|
||||
|
||||
if (choiceText == null)
|
||||
{
|
||||
choiceText = GetComponentInChildren<TMP_Text>(true);
|
||||
}
|
||||
|
||||
button.onClick.RemoveListener(HandleClick);
|
||||
button.onClick.AddListener(HandleClick);
|
||||
}
|
||||
|
||||
public void Setup(TruthChoiceData choiceData, TruthFountainGameManager manager)
|
||||
{
|
||||
currentChoice = choiceData;
|
||||
gameManager = manager;
|
||||
|
||||
bool hasChoice = currentChoice != null && currentChoice.IsValid();
|
||||
gameObject.SetActive(hasChoice);
|
||||
|
||||
if (!hasChoice)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (choiceText != null)
|
||||
{
|
||||
choiceText.text = currentChoice.ChoiceText;
|
||||
}
|
||||
|
||||
SetInteractable(true);
|
||||
SetVisual(normalSprite, normalColor);
|
||||
}
|
||||
|
||||
public void SetInteractable(bool value)
|
||||
{
|
||||
if (button != null)
|
||||
{
|
||||
button.interactable = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowSelectedFeedback()
|
||||
{
|
||||
SetVisual(selectedSprite, selectedColor);
|
||||
}
|
||||
|
||||
public void ShowTruthOrLieFeedback()
|
||||
{
|
||||
if (currentChoice != null && currentChoice.MarkAsTruthChoice)
|
||||
{
|
||||
SetVisual(truthSprite, truthColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVisual(lieSprite, lieColor);
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetVisual()
|
||||
{
|
||||
SetVisual(normalSprite, normalColor);
|
||||
}
|
||||
|
||||
private void HandleClick()
|
||||
{
|
||||
if (currentChoice == null || gameManager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ShowSelectedFeedback();
|
||||
gameManager.SelectChoice(currentChoice, this);
|
||||
}
|
||||
|
||||
private void SetVisual(Sprite sprite, Color color)
|
||||
{
|
||||
if (backgroundImage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sprite != null)
|
||||
{
|
||||
backgroundImage.sprite = sprite;
|
||||
}
|
||||
|
||||
backgroundImage.color = color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff4308a02dc0dbd4d90489f2f89dc837
|
||||
30
Assets/My project/TruthFountain/Scripts/TruthChoiceData.cs
Normal file
30
Assets/My project/TruthFountain/Scripts/TruthChoiceData.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
[Serializable]
|
||||
public class TruthChoiceData
|
||||
{
|
||||
[Header("Choice")]
|
||||
[TextArea(1, 3)]
|
||||
[SerializeField] private string choiceText;
|
||||
|
||||
[Header("Result")]
|
||||
[Min(0)]
|
||||
[SerializeField] private int lieAmount = 0;
|
||||
|
||||
[TextArea(1, 4)]
|
||||
[SerializeField] private string resultMessage;
|
||||
|
||||
[Tooltip("선택 후 버튼을 정답/진실처럼 강조할지 여부입니다. lieAmount가 0이면 자동으로 진실 선택처럼 볼 수 있습니다.")]
|
||||
[SerializeField] private bool markAsTruthChoice;
|
||||
|
||||
public string ChoiceText => choiceText;
|
||||
public int LieAmount => lieAmount;
|
||||
public string ResultMessage => resultMessage;
|
||||
public bool MarkAsTruthChoice => markAsTruthChoice || lieAmount <= 0;
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(choiceText);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02d98b539fd31b347be44b5595f703b6
|
||||
55
Assets/My project/TruthFountain/Scripts/TruthFountainData.cs
Normal file
55
Assets/My project/TruthFountain/Scripts/TruthFountainData.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
|
||||
[CreateAssetMenu(fileName = "TruthFountainData_Default", menuName = "Adventure/Truth Fountain/Truth Fountain Data")]
|
||||
public class TruthFountainData : ScriptableObject
|
||||
{
|
||||
[Header("Basic")]
|
||||
[SerializeField] private string title = "진실의 샘";
|
||||
|
||||
[Header("Questions")]
|
||||
[SerializeField] private TruthQuestionData[] questions;
|
||||
|
||||
[Header("Rules")]
|
||||
[Min(1)]
|
||||
[SerializeField] private int maxLieScore = 3;
|
||||
|
||||
[Tooltip("켜두면 거짓 점수가 최대치에 도달하는 순간 바로 실패합니다. 끄면 모든 질문이 끝난 뒤 판정합니다.")]
|
||||
[SerializeField] private bool failImmediatelyAtMaxLieScore = true;
|
||||
|
||||
[Min(0f)]
|
||||
[SerializeField] private float resultDelay = 1.0f;
|
||||
|
||||
[Header("Success Result")]
|
||||
[SerializeField] private string successTitle = "진실의 샘이 빛납니다";
|
||||
|
||||
[TextArea(2, 5)]
|
||||
[SerializeField] private string successDescription = "당신의 진심이 샘에 닿았습니다.\n기억의 조각을 획득했습니다.";
|
||||
|
||||
[Header("Fail Result")]
|
||||
[SerializeField] private string failTitle = "코가 너무 길어졌습니다";
|
||||
|
||||
[TextArea(2, 5)]
|
||||
[SerializeField] private string failDescription = "샘이 당신의 대답을 믿지 못했습니다.\n다시 도전해보세요.";
|
||||
|
||||
public string Title => title;
|
||||
public TruthQuestionData[] Questions => questions;
|
||||
public int MaxLieScore => Mathf.Max(1, maxLieScore);
|
||||
public bool FailImmediatelyAtMaxLieScore => failImmediatelyAtMaxLieScore;
|
||||
public float ResultDelay => Mathf.Max(0f, resultDelay);
|
||||
public string SuccessTitle => successTitle;
|
||||
public string SuccessDescription => successDescription;
|
||||
public string FailTitle => failTitle;
|
||||
public string FailDescription => failDescription;
|
||||
|
||||
public int QuestionCount => questions == null ? 0 : questions.Length;
|
||||
|
||||
public TruthQuestionData GetQuestion(int index)
|
||||
{
|
||||
if (questions == null || index < 0 || index >= questions.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return questions[index];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f6934cb9bb93ca44815ef21af5126f8
|
||||
@@ -0,0 +1,230 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class TruthFountainGameManager : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private TruthFountainData fountainData;
|
||||
[SerializeField] private TruthFountainUI ui;
|
||||
[SerializeField] private TruthFountainReward reward;
|
||||
|
||||
[Header("Start Option")]
|
||||
[SerializeField] private bool startOnAwake = false;
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent onGameStarted;
|
||||
public UnityEvent onQuestionAnswered;
|
||||
public UnityEvent onGameSuccess;
|
||||
public UnityEvent onGameFailed;
|
||||
public UnityEvent onFinalConfirmed;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool showDebugLog = true;
|
||||
|
||||
private int currentQuestionIndex;
|
||||
private int lieScore;
|
||||
private bool isRunning;
|
||||
private bool isWaitingForNextQuestion;
|
||||
private bool finalSuccess;
|
||||
private Coroutine choiceRoutine;
|
||||
|
||||
public int CurrentQuestionIndex => currentQuestionIndex;
|
||||
public int LieScore => lieScore;
|
||||
public bool IsRunning => isRunning;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (ui == null)
|
||||
{
|
||||
ui = FindFirstObjectByType<TruthFountainUI>();
|
||||
}
|
||||
|
||||
if (reward == null)
|
||||
{
|
||||
reward = GetComponent<TruthFountainReward>();
|
||||
}
|
||||
|
||||
if (ui != null)
|
||||
{
|
||||
ui.Bind(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (startOnAwake)
|
||||
{
|
||||
StartGame();
|
||||
}
|
||||
}
|
||||
|
||||
public void StartGame()
|
||||
{
|
||||
if (fountainData == null)
|
||||
{
|
||||
Debug.LogWarning($"[{nameof(TruthFountainGameManager)}] TruthFountainData가 연결되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fountainData.QuestionCount <= 0)
|
||||
{
|
||||
Debug.LogWarning($"[{nameof(TruthFountainGameManager)}] 질문 데이터가 비어 있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (choiceRoutine != null)
|
||||
{
|
||||
StopCoroutine(choiceRoutine);
|
||||
choiceRoutine = null;
|
||||
}
|
||||
|
||||
currentQuestionIndex = 0;
|
||||
lieScore = 0;
|
||||
isRunning = true;
|
||||
isWaitingForNextQuestion = false;
|
||||
finalSuccess = false;
|
||||
|
||||
if (ui != null)
|
||||
{
|
||||
ui.Initialize(fountainData);
|
||||
ui.ShowQuestion(fountainData.GetQuestion(currentQuestionIndex), currentQuestionIndex, fountainData.QuestionCount, this);
|
||||
ui.UpdateNoseGauge(lieScore, fountainData.MaxLieScore);
|
||||
}
|
||||
|
||||
onGameStarted?.Invoke();
|
||||
|
||||
if (showDebugLog)
|
||||
{
|
||||
Debug.Log($"[{nameof(TruthFountainGameManager)}] 진실의 샘 시작");
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectChoice(TruthChoiceData choice, TruthChoiceButtonUI selectedButton = null)
|
||||
{
|
||||
if (!isRunning || isWaitingForNextQuestion || choice == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (choiceRoutine != null)
|
||||
{
|
||||
StopCoroutine(choiceRoutine);
|
||||
}
|
||||
|
||||
choiceRoutine = StartCoroutine(HandleChoiceRoutine(choice, selectedButton));
|
||||
}
|
||||
|
||||
private IEnumerator HandleChoiceRoutine(TruthChoiceData choice, TruthChoiceButtonUI selectedButton)
|
||||
{
|
||||
isWaitingForNextQuestion = true;
|
||||
|
||||
lieScore = Mathf.Max(0, lieScore + choice.LieAmount);
|
||||
|
||||
if (ui != null)
|
||||
{
|
||||
ui.SetChoiceButtonsInteractable(false);
|
||||
ui.ShowChoiceFeedback(selectedButton);
|
||||
ui.SetResultMessage(choice.ResultMessage, true);
|
||||
ui.UpdateNoseGauge(lieScore, fountainData.MaxLieScore);
|
||||
}
|
||||
|
||||
onQuestionAnswered?.Invoke();
|
||||
|
||||
if (showDebugLog)
|
||||
{
|
||||
Debug.Log($"[{nameof(TruthFountainGameManager)}] 선택: {choice.ChoiceText}, lie +{choice.LieAmount}, 현재 lieScore={lieScore}");
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(fountainData.ResultDelay);
|
||||
|
||||
bool reachedMaxLieScore = lieScore >= fountainData.MaxLieScore;
|
||||
if (reachedMaxLieScore && fountainData.FailImmediatelyAtMaxLieScore)
|
||||
{
|
||||
FinishGame(false);
|
||||
yield break;
|
||||
}
|
||||
|
||||
currentQuestionIndex++;
|
||||
|
||||
if (currentQuestionIndex >= fountainData.QuestionCount)
|
||||
{
|
||||
bool success = lieScore < fountainData.MaxLieScore;
|
||||
FinishGame(success);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (ui != null)
|
||||
{
|
||||
ui.ShowQuestion(fountainData.GetQuestion(currentQuestionIndex), currentQuestionIndex, fountainData.QuestionCount, this);
|
||||
}
|
||||
|
||||
isWaitingForNextQuestion = false;
|
||||
choiceRoutine = null;
|
||||
}
|
||||
|
||||
private void FinishGame(bool success)
|
||||
{
|
||||
isRunning = false;
|
||||
isWaitingForNextQuestion = false;
|
||||
finalSuccess = success;
|
||||
choiceRoutine = null;
|
||||
|
||||
if (ui != null)
|
||||
{
|
||||
string title = success ? fountainData.SuccessTitle : fountainData.FailTitle;
|
||||
string description = success ? fountainData.SuccessDescription : fountainData.FailDescription;
|
||||
ui.SetResultMessage(string.Empty, false);
|
||||
ui.ShowFinalResult(true, title, description);
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
reward?.GiveReward();
|
||||
onGameSuccess?.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
onGameFailed?.Invoke();
|
||||
}
|
||||
|
||||
if (showDebugLog)
|
||||
{
|
||||
Debug.Log($"[{nameof(TruthFountainGameManager)}] 게임 종료: {(success ? "성공" : "실패")}, lieScore={lieScore}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ConfirmFinalResult()
|
||||
{
|
||||
if (ui != null)
|
||||
{
|
||||
ui.ShowFinalResult(false, string.Empty, string.Empty);
|
||||
}
|
||||
|
||||
onFinalConfirmed?.Invoke();
|
||||
|
||||
if (showDebugLog)
|
||||
{
|
||||
Debug.Log($"[{nameof(TruthFountainGameManager)}] 최종 결과 확인: {(finalSuccess ? "성공" : "실패")}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetGameOnly()
|
||||
{
|
||||
if (choiceRoutine != null)
|
||||
{
|
||||
StopCoroutine(choiceRoutine);
|
||||
choiceRoutine = null;
|
||||
}
|
||||
|
||||
currentQuestionIndex = 0;
|
||||
lieScore = 0;
|
||||
isRunning = false;
|
||||
isWaitingForNextQuestion = false;
|
||||
|
||||
if (ui != null)
|
||||
{
|
||||
ui.Initialize(fountainData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70c77a1d5b979604cbafc1108512420b
|
||||
@@ -0,0 +1,140 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class TruthFountainOpenClose : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private GameObject truthFountainRoot;
|
||||
[SerializeField] private TruthFountainGameManager gameManager;
|
||||
[SerializeField] private Transform targetCamera;
|
||||
|
||||
[Header("Open Option")]
|
||||
[SerializeField] private bool openOnStart = false;
|
||||
[SerializeField] private bool startGameOnOpen = true;
|
||||
|
||||
[Header("VR Placement")]
|
||||
[SerializeField] private bool placeInFrontOfCameraOnOpen = true;
|
||||
[SerializeField] private bool faceCameraOnOpen = true;
|
||||
[Min(0.1f)]
|
||||
[SerializeField] private float distanceFromCamera = 2.2f;
|
||||
[SerializeField] private float verticalOffset = -0.1f;
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
truthFountainRoot = gameObject;
|
||||
gameManager = GetComponentInParent<TruthFountainGameManager>();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (truthFountainRoot == null)
|
||||
{
|
||||
truthFountainRoot = gameObject;
|
||||
}
|
||||
|
||||
if (gameManager == null)
|
||||
{
|
||||
gameManager = FindFirstObjectByType<TruthFountainGameManager>();
|
||||
}
|
||||
|
||||
if (targetCamera == null && Camera.main != null)
|
||||
{
|
||||
targetCamera = Camera.main.transform;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (openOnStart)
|
||||
{
|
||||
Open();
|
||||
}
|
||||
else
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
if (placeInFrontOfCameraOnOpen)
|
||||
{
|
||||
PlaceInFrontOfCamera();
|
||||
}
|
||||
|
||||
if (truthFountainRoot != null)
|
||||
{
|
||||
truthFountainRoot.SetActive(true);
|
||||
}
|
||||
|
||||
if (startGameOnOpen && gameManager != null)
|
||||
{
|
||||
gameManager.StartGame();
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (truthFountainRoot != null)
|
||||
{
|
||||
truthFountainRoot.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
if (truthFountainRoot == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (truthFountainRoot.activeSelf)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
Open();
|
||||
}
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
if (truthFountainRoot != null && !truthFountainRoot.activeSelf)
|
||||
{
|
||||
truthFountainRoot.SetActive(true);
|
||||
}
|
||||
|
||||
gameManager?.StartGame();
|
||||
}
|
||||
|
||||
private void PlaceInFrontOfCamera()
|
||||
{
|
||||
if (targetCamera == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Transform rootTransform = truthFountainRoot != null ? truthFountainRoot.transform : transform;
|
||||
Vector3 forward = targetCamera.forward;
|
||||
forward.y = 0f;
|
||||
|
||||
if (forward.sqrMagnitude < 0.001f)
|
||||
{
|
||||
forward = targetCamera.forward;
|
||||
}
|
||||
|
||||
forward.Normalize();
|
||||
rootTransform.position = targetCamera.position + forward * distanceFromCamera + Vector3.up * verticalOffset;
|
||||
|
||||
if (faceCameraOnOpen)
|
||||
{
|
||||
Vector3 directionToCamera = rootTransform.position - targetCamera.position;
|
||||
directionToCamera.y = 0f;
|
||||
|
||||
if (directionToCamera.sqrMagnitude > 0.001f)
|
||||
{
|
||||
rootTransform.rotation = Quaternion.LookRotation(directionToCamera.normalized, Vector3.up);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b02efc909d86c8a469c91b2981052e17
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class TruthFountainReward : MonoBehaviour
|
||||
{
|
||||
[Header("Reward Control")]
|
||||
[SerializeField] private bool giveOnlyOnce = true;
|
||||
|
||||
[Header("Optional Auto Call")]
|
||||
[Tooltip("MemoryPieceReward 같은 컴포넌트를 넣고 methodName을 GiveReward로 두면 성공 시 자동 호출됩니다. 비워두고 Events에 직접 연결해도 됩니다.")]
|
||||
[SerializeField] private MonoBehaviour rewardReceiver;
|
||||
|
||||
[SerializeField] private string rewardMethodName = "GiveReward";
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent onRewardGiven;
|
||||
public UnityEvent onRewardAlreadyGiven;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool showDebugLog = true;
|
||||
|
||||
private bool alreadyGiven;
|
||||
|
||||
public bool AlreadyGiven => alreadyGiven;
|
||||
|
||||
public void GiveReward()
|
||||
{
|
||||
if (giveOnlyOnce && alreadyGiven)
|
||||
{
|
||||
onRewardAlreadyGiven?.Invoke();
|
||||
|
||||
if (showDebugLog)
|
||||
{
|
||||
Debug.Log($"[{nameof(TruthFountainReward)}] 이미 지급된 진실의 샘 보상입니다.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
TryCallRewardReceiver();
|
||||
alreadyGiven = true;
|
||||
onRewardGiven?.Invoke();
|
||||
|
||||
if (showDebugLog)
|
||||
{
|
||||
Debug.Log($"[{nameof(TruthFountainReward)}] 진실의 샘 보상을 지급했습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetReward()
|
||||
{
|
||||
alreadyGiven = false;
|
||||
}
|
||||
|
||||
private void TryCallRewardReceiver()
|
||||
{
|
||||
if (rewardReceiver == null || string.IsNullOrWhiteSpace(rewardMethodName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MethodInfo method = rewardReceiver.GetType().GetMethod(
|
||||
rewardMethodName,
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
||||
null,
|
||||
Type.EmptyTypes,
|
||||
null
|
||||
);
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
Debug.LogWarning($"[{nameof(TruthFountainReward)}] {rewardReceiver.name}에서 {rewardMethodName}() 메서드를 찾지 못했습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
method.Invoke(rewardReceiver, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4e260c3dc9c058478480d3d19f8eb96
|
||||
191
Assets/My project/TruthFountain/Scripts/TruthFountainUI.cs
Normal file
191
Assets/My project/TruthFountain/Scripts/TruthFountainUI.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class TruthFountainUI : MonoBehaviour
|
||||
{
|
||||
[Header("Root")]
|
||||
[SerializeField] private GameObject uiRoot;
|
||||
|
||||
[Header("Texts")]
|
||||
[SerializeField] private TMP_Text titleText;
|
||||
[SerializeField] private TMP_Text questionText;
|
||||
[SerializeField] private TMP_Text questionHintText;
|
||||
[SerializeField] private TMP_Text progressText;
|
||||
[SerializeField] private TMP_Text resultText;
|
||||
|
||||
[Header("Choices")]
|
||||
[SerializeField] private TruthChoiceButtonUI[] choiceButtons;
|
||||
|
||||
[Header("Nose Gauge")]
|
||||
[SerializeField] private Image noseGaugeFill;
|
||||
[SerializeField] private TMP_Text noseValueText;
|
||||
|
||||
[Header("Final Result")]
|
||||
[SerializeField] private GameObject finalResultPanel;
|
||||
[SerializeField] private TMP_Text finalTitleText;
|
||||
[SerializeField] private TMP_Text finalDescriptionText;
|
||||
[SerializeField] private Button confirmButton;
|
||||
|
||||
private TruthFountainGameManager gameManager;
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
uiRoot = gameObject;
|
||||
}
|
||||
|
||||
public void Bind(TruthFountainGameManager manager)
|
||||
{
|
||||
gameManager = manager;
|
||||
|
||||
if (confirmButton != null)
|
||||
{
|
||||
confirmButton.onClick.RemoveAllListeners();
|
||||
confirmButton.onClick.AddListener(HandleConfirmClicked);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetVisible(bool visible)
|
||||
{
|
||||
GameObject targetRoot = uiRoot != null ? uiRoot : gameObject;
|
||||
targetRoot.SetActive(visible);
|
||||
}
|
||||
|
||||
public void Initialize(TruthFountainData data)
|
||||
{
|
||||
if (titleText != null)
|
||||
{
|
||||
titleText.text = data != null ? data.Title : "진실의 샘";
|
||||
}
|
||||
|
||||
SetResultMessage(string.Empty, false);
|
||||
ShowFinalResult(false, string.Empty, string.Empty);
|
||||
UpdateNoseGauge(0, data != null ? data.MaxLieScore : 1);
|
||||
}
|
||||
|
||||
public void ShowQuestion(TruthQuestionData question, int questionIndex, int totalQuestions, TruthFountainGameManager manager)
|
||||
{
|
||||
if (questionText != null)
|
||||
{
|
||||
questionText.text = question != null ? question.QuestionText : "질문 데이터가 없습니다.";
|
||||
}
|
||||
|
||||
if (questionHintText != null)
|
||||
{
|
||||
questionHintText.text = question != null ? question.HintText : string.Empty;
|
||||
}
|
||||
|
||||
if (progressText != null)
|
||||
{
|
||||
progressText.text = $"질문 {questionIndex + 1} / {Mathf.Max(1, totalQuestions)}";
|
||||
}
|
||||
|
||||
SetResultMessage(string.Empty, false);
|
||||
SetupChoiceButtons(question, manager);
|
||||
}
|
||||
|
||||
public void SetupChoiceButtons(TruthQuestionData question, TruthFountainGameManager manager)
|
||||
{
|
||||
TruthChoiceData[] choices = question != null ? question.Choices : null;
|
||||
|
||||
for (int i = 0; i < choiceButtons.Length; i++)
|
||||
{
|
||||
TruthChoiceData choice = choices != null && i < choices.Length ? choices[i] : null;
|
||||
|
||||
if (choiceButtons[i] != null)
|
||||
{
|
||||
choiceButtons[i].Setup(choice, manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetChoiceButtonsInteractable(bool interactable)
|
||||
{
|
||||
foreach (TruthChoiceButtonUI choiceButton in choiceButtons)
|
||||
{
|
||||
if (choiceButton != null && choiceButton.gameObject.activeSelf)
|
||||
{
|
||||
choiceButton.SetInteractable(interactable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowChoiceFeedback(TruthChoiceButtonUI selectedButton)
|
||||
{
|
||||
foreach (TruthChoiceButtonUI choiceButton in choiceButtons)
|
||||
{
|
||||
if (choiceButton == null || !choiceButton.gameObject.activeSelf)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (choiceButton == selectedButton)
|
||||
{
|
||||
choiceButton.ShowTruthOrLieFeedback();
|
||||
}
|
||||
else
|
||||
{
|
||||
choiceButton.ResetVisual();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetResultMessage(string message, bool visible = true)
|
||||
{
|
||||
if (resultText == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
resultText.text = message;
|
||||
resultText.gameObject.SetActive(visible && !string.IsNullOrWhiteSpace(message));
|
||||
}
|
||||
|
||||
public void UpdateNoseGauge(int lieScore, int maxLieScore)
|
||||
{
|
||||
int safeMax = Mathf.Max(1, maxLieScore);
|
||||
float fillAmount = Mathf.Clamp01((float)lieScore / safeMax);
|
||||
|
||||
if (noseGaugeFill != null)
|
||||
{
|
||||
noseGaugeFill.fillAmount = fillAmount;
|
||||
}
|
||||
|
||||
if (noseValueText != null)
|
||||
{
|
||||
noseValueText.text = $"코 길이 {Mathf.RoundToInt(fillAmount * 100f)}%";
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowFinalResult(bool visible, string title, string description)
|
||||
{
|
||||
if (finalResultPanel != null)
|
||||
{
|
||||
finalResultPanel.SetActive(visible);
|
||||
}
|
||||
|
||||
if (finalTitleText != null)
|
||||
{
|
||||
finalTitleText.text = title;
|
||||
}
|
||||
|
||||
if (finalDescriptionText != null)
|
||||
{
|
||||
finalDescriptionText.text = description;
|
||||
}
|
||||
|
||||
SetChoiceButtonsInteractable(!visible);
|
||||
}
|
||||
|
||||
private void HandleConfirmClicked()
|
||||
{
|
||||
if (gameManager != null)
|
||||
{
|
||||
gameManager.ConfirmFinalResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowFinalResult(false, string.Empty, string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7f6a091efa74d648b3100bbbc3836ed
|
||||
25
Assets/My project/TruthFountain/Scripts/TruthQuestionData.cs
Normal file
25
Assets/My project/TruthFountain/Scripts/TruthQuestionData.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
[Serializable]
|
||||
public class TruthQuestionData
|
||||
{
|
||||
[Header("Question")]
|
||||
[TextArea(2, 5)]
|
||||
[SerializeField] private string questionText;
|
||||
|
||||
[TextArea(1, 3)]
|
||||
[SerializeField] private string hintText = "당신의 선택에 따라 코가 반응합니다.";
|
||||
|
||||
[Header("Choices")]
|
||||
[SerializeField] private TruthChoiceData[] choices;
|
||||
|
||||
public string QuestionText => questionText;
|
||||
public string HintText => hintText;
|
||||
public TruthChoiceData[] Choices => choices;
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(questionText) && choices != null && choices.Length > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e7d0f9c777e02c43ae6656a1c680fc7
|
||||
Reference in New Issue
Block a user