물품들 상호작용 달기
This commit is contained in:
Binary file not shown.
@@ -23,6 +23,9 @@ public class DialogCondition
|
||||
[Tooltip("골랐어야 하는 선택지 Code들 (전부 필요)")]
|
||||
public List<string> RequiredChoiceCodes = new();
|
||||
|
||||
[Tooltip("완료했어야 하는 미션 Id들 (전부 필요)")]
|
||||
public List<string> RequiredMissionIds = new();
|
||||
|
||||
[Tooltip("밟았어야 하는 이벤트존 Id들 (전부 필요)")]
|
||||
public List<string> RequiredZoneIds = new();
|
||||
|
||||
@@ -54,6 +57,10 @@ public bool IsMet(CharacterData affectionTarget)
|
||||
if (!string.IsNullOrEmpty(code) && !story.HasChosen(code))
|
||||
return false;
|
||||
|
||||
foreach (var missionId in RequiredMissionIds)
|
||||
if (!string.IsNullOrEmpty(missionId) && !story.IsMissionCompleted(missionId))
|
||||
return false;
|
||||
|
||||
foreach (var zoneId in RequiredZoneIds)
|
||||
if (!string.IsNullOrEmpty(zoneId) && !story.HasTriggeredZone(zoneId))
|
||||
return false;
|
||||
|
||||
@@ -82,6 +82,18 @@ public void RecordChoice(string code)
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
// ── 미션 이력 ────────────────────────────────────────────────
|
||||
public bool IsMissionCompleted(string missionId) => _state.CompletedMissions.Contains(missionId);
|
||||
|
||||
// 완료 기록. 처음 완료한 경우에만 true (진행도 보상 중복 방지용)
|
||||
public bool MarkMissionCompleted(string missionId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(missionId) || !_state.CompletedMissions.Add(missionId))
|
||||
return false;
|
||||
Changed?.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── 이벤트존 이력 ────────────────────────────────────────────
|
||||
public bool HasTriggeredZone(string zoneId) => _state.TriggeredZones.Contains(zoneId);
|
||||
|
||||
|
||||
8
Assets/02_Scripts/Mission.meta
Normal file
8
Assets/02_Scripts/Mission.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea41e03a449add349badcd8bcee6714d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
82
Assets/02_Scripts/Mission/Mission.cs
Normal file
82
Assets/02_Scripts/Mission/Mission.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
// 미션 하나. 자식의 MissionObjective들이 전부 완료되면 미션이 완료된다.
|
||||
// 시작은 Activate()를 UnityEvent(대화 노드 이벤트, 이벤트존, 버튼 등)로 호출하거나
|
||||
// ActivateOnStart를 켠다. 완료는 StoryState에 영구 기록되므로
|
||||
// DialogCondition의 RequiredMissionIds로 대화 활성화 조건에 쓸 수 있다.
|
||||
public class Mission : MonoBehaviour
|
||||
{
|
||||
public enum MissionState { Inactive, Active, Completed }
|
||||
|
||||
[Tooltip("StoryState에 기록되는 Id. 비워두면 오브젝트 이름 사용 (DialogCondition의 RequiredMissionIds에서 참조)")]
|
||||
[SerializeField] private string _id;
|
||||
|
||||
[Tooltip("씬 시작 시 자동으로 시작. 끄면 Activate()를 대화 노드 이벤트/존/버튼 등에서 호출할 것")]
|
||||
[SerializeField] private bool _activateOnStart;
|
||||
|
||||
[Tooltip("이 미션을 처음 완료하면 메인 진행도 +N")]
|
||||
[Min(0)] [SerializeField] private int _progressOnComplete;
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent OnActivated; // 미션 시작 시 (안내 대사, 미션 UI 표시 등)
|
||||
public UnityEvent OnCompleted; // 미션 완료 시 (보상 대사, VFX, UI 숨김 등)
|
||||
|
||||
public MissionState State { get; private set; } = MissionState.Inactive;
|
||||
public string Id => string.IsNullOrEmpty(_id) ? name : _id;
|
||||
|
||||
private MissionObjective[] _objectives;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_objectives = GetComponentsInChildren<MissionObjective>(true);
|
||||
foreach (var objective in _objectives)
|
||||
objective.Changed += Reevaluate;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// 저장된 상태에 이미 완료로 기록돼 있으면(이어하기 등) 이벤트 없이 완료 상태로 시작
|
||||
if (StoryManager.Instance.IsMissionCompleted(Id))
|
||||
State = MissionState.Completed;
|
||||
else if (_activateOnStart)
|
||||
Activate();
|
||||
}
|
||||
|
||||
// UnityEvent 연결용 — 대화의 '수락' 노드 이벤트 등에서 호출
|
||||
public void Activate()
|
||||
{
|
||||
if (State != MissionState.Inactive) return;
|
||||
if (StoryManager.Instance.IsMissionCompleted(Id))
|
||||
{
|
||||
State = MissionState.Completed;
|
||||
return;
|
||||
}
|
||||
|
||||
State = MissionState.Active;
|
||||
OnActivated?.Invoke();
|
||||
Reevaluate(); // 이미 조건이 갖춰져 있을 수도 있다 (물건을 미리 옮겨둔 경우)
|
||||
}
|
||||
|
||||
private void Reevaluate()
|
||||
{
|
||||
if (State != MissionState.Active) return;
|
||||
foreach (var objective in _objectives)
|
||||
if (!objective.IsComplete) return;
|
||||
Complete();
|
||||
}
|
||||
|
||||
private void Complete()
|
||||
{
|
||||
State = MissionState.Completed;
|
||||
|
||||
var story = StoryManager.Instance;
|
||||
bool firstTime = story.MarkMissionCompleted(Id);
|
||||
if (firstTime && _progressOnComplete > 0)
|
||||
story.MainProgress += _progressOnComplete;
|
||||
story.Save();
|
||||
|
||||
OnCompleted?.Invoke();
|
||||
Debug.Log($"[Mission] 완료: {Id}");
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/Mission/Mission.cs.meta
Normal file
2
Assets/02_Scripts/Mission/Mission.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d878a406b99ed614bbbce6a14c14f8c2
|
||||
16
Assets/02_Scripts/Mission/MissionObjective.cs
Normal file
16
Assets/02_Scripts/Mission/MissionObjective.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
// 미션 목표 하나의 베이스. Mission 오브젝트의 자식으로 두면 자동 수집된다.
|
||||
// 새 종류의 미션이 필요하면 이걸 상속해서 IsComplete 구현 + 상태가 바뀔 때
|
||||
// RaiseChanged()만 호출하면 된다 (예: 물건 옮기기, 특정 위치 방문, NPC에게 아이템 전달).
|
||||
public abstract class MissionObjective : MonoBehaviour
|
||||
{
|
||||
// 목표 달성 여부 — 되돌릴 수 있는 목표(물건을 도로 빼는 등)는 false로 돌아가도 된다.
|
||||
// 미션 완료는 "모든 목표가 동시에 true"인 순간 Mission 쪽에서 확정(래치)된다.
|
||||
public abstract bool IsComplete { get; }
|
||||
|
||||
// 달성 상태가 바뀔 때마다 발행 — Mission이 구독해서 완료를 재판정한다
|
||||
public event Action Changed;
|
||||
protected void RaiseChanged() => Changed?.Invoke();
|
||||
}
|
||||
2
Assets/02_Scripts/Mission/MissionObjective.cs.meta
Normal file
2
Assets/02_Scripts/Mission/MissionObjective.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6486c131b8f11624fbd409cea08cbbde
|
||||
67
Assets/02_Scripts/Mission/MoveItemsToZoneObjective.cs
Normal file
67
Assets/02_Scripts/Mission/MoveItemsToZoneObjective.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
// "지정한 물건들을 전부 이 존 안으로 옮기기" 목표.
|
||||
// 트리거 콜라이더가 붙은 존 오브젝트에 추가하고 물건들을 목록에 등록한다.
|
||||
// 물건 쪽에 Rigidbody가 있어야 트리거 이벤트가 발생한다 (XRGrabInteractable이면 이미 있음).
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class MoveItemsToZoneObjective : MissionObjective
|
||||
{
|
||||
[Tooltip("이 존 안으로 옮겨야 하는 물건들 (씬 오브젝트 루트)")]
|
||||
[SerializeField] private List<Transform> _requiredItems = new();
|
||||
|
||||
[Tooltip("들어온 개수가 바뀔 때마다 (현재 개수, 전체 개수) — 진행 UI 연결용")]
|
||||
public UnityEvent<int, int> OnCountChanged;
|
||||
|
||||
[Tooltip("대사 텍스트에서 {키}로 진행 상황을 쓰려면 키 이름 지정 (값 예: \"3/5\"). 비우면 안 씀")]
|
||||
[SerializeField] private string _dialogVariableKey;
|
||||
|
||||
private readonly HashSet<Transform> _inside = new();
|
||||
|
||||
public int Count => _inside.Count;
|
||||
public int Total => _requiredItems.Count;
|
||||
public override bool IsComplete => Count >= Total;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
var col = GetComponent<Collider>();
|
||||
if (!col.isTrigger)
|
||||
Debug.LogWarning($"[MoveItemsToZoneObjective] 콜라이더가 트리거가 아님: {name}");
|
||||
}
|
||||
|
||||
private void Start() => Publish(); // UI/대사 변수 초기값 (0/N)
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
var item = FindRequiredItem(other);
|
||||
if (item != null && _inside.Add(item)) Publish();
|
||||
}
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
var item = FindRequiredItem(other);
|
||||
if (item != null && _inside.Remove(item)) Publish();
|
||||
}
|
||||
|
||||
// 콜라이더에서 등록된 물건을 찾는다 (자식 콜라이더 대응 — 부모로 거슬러 올라가며 확인)
|
||||
private Transform FindRequiredItem(Collider col)
|
||||
{
|
||||
var t = col.attachedRigidbody != null ? col.attachedRigidbody.transform : col.transform;
|
||||
while (t != null)
|
||||
{
|
||||
if (_requiredItems.Contains(t)) return t;
|
||||
t = t.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void Publish()
|
||||
{
|
||||
_inside.RemoveWhere(t => t == null); // 파괴된 물건 정리
|
||||
if (!string.IsNullOrEmpty(_dialogVariableKey))
|
||||
DialogVariables.Set(_dialogVariableKey, $"{Count}/{Total}");
|
||||
OnCountChanged?.Invoke(Count, Total);
|
||||
RaiseChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c0d5ca4a604f5142873570997cb5fac
|
||||
@@ -11,6 +11,7 @@ public class StoryState
|
||||
public readonly HashSet<string> CompletedDialogs = new(); // 완료한 DialogGroup 이름
|
||||
public readonly HashSet<string> ChosenCodes = new(); // 골랐던 선택지 Code
|
||||
public readonly HashSet<string> TriggeredZones = new(); // 밟았던 이벤트 존(Timeline/Trigger) Id
|
||||
public readonly HashSet<string> CompletedMissions = new(); // 완료한 미션 Id
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
@@ -19,6 +20,7 @@ public void Clear()
|
||||
CompletedDialogs.Clear();
|
||||
ChosenCodes.Clear();
|
||||
TriggeredZones.Clear();
|
||||
CompletedMissions.Clear();
|
||||
}
|
||||
|
||||
// ── JSON 변환 ────────────────────────────────────────────────
|
||||
@@ -32,6 +34,7 @@ private class JsonData
|
||||
public List<string> CompletedDialogs = new();
|
||||
public List<string> ChosenCodes = new();
|
||||
public List<string> TriggeredZones = new();
|
||||
public List<string> CompletedMissions = new();
|
||||
}
|
||||
|
||||
public string ToJson()
|
||||
@@ -45,6 +48,7 @@ public string ToJson()
|
||||
data.CompletedDialogs.AddRange(CompletedDialogs);
|
||||
data.ChosenCodes.AddRange(ChosenCodes);
|
||||
data.TriggeredZones.AddRange(TriggeredZones);
|
||||
data.CompletedMissions.AddRange(CompletedMissions);
|
||||
return JsonUtility.ToJson(data, prettyPrint: true);
|
||||
}
|
||||
|
||||
@@ -60,6 +64,7 @@ public static StoryState FromJson(string json)
|
||||
state.CompletedDialogs.UnionWith(data.CompletedDialogs);
|
||||
state.ChosenCodes.UnionWith(data.ChosenCodes);
|
||||
state.TriggeredZones.UnionWith(data.TriggeredZones);
|
||||
state.CompletedMissions.UnionWith(data.CompletedMissions);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user