Compare commits
2 Commits
aa302b1d67
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 298e35c50b | |||
| f230b438ed |
@@ -0,0 +1,26 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
using SinoWms.Gameplay;
|
||||||
|
|
||||||
|
namespace SinoWms.EditorTools
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 미션 판정 매니저를 씬에 추가한다. 메뉴 <b>SinoWMS/Setup Mission</b>.
|
||||||
|
/// Play 후 모든 슬롯을 정답으로 채우면 완료 + 점수 표시.
|
||||||
|
/// </summary>
|
||||||
|
public static class MissionSetup
|
||||||
|
{
|
||||||
|
[MenuItem("SinoWMS/Setup Mission")]
|
||||||
|
public static void Setup()
|
||||||
|
{
|
||||||
|
var go = GameObject.Find("Mission") ?? new GameObject("Mission");
|
||||||
|
if (go.GetComponent<MissionManager>() == null)
|
||||||
|
go.AddComponent<MissionManager>();
|
||||||
|
|
||||||
|
EditorSceneManager.MarkSceneDirty(go.scene);
|
||||||
|
Selection.activeGameObject = go;
|
||||||
|
Debug.Log("[MissionSetup] 완료. Play 후 모든 슬롯을 정답으로 채우면 미션 완료.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9a8d41437743f4655996e49253cc72ad
|
||||||
@@ -39,21 +39,28 @@ namespace SinoWms.EditorTools
|
|||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
string objName = $"Box_{i + 1}";
|
string objName = $"Box_{i + 1}";
|
||||||
if (container.transform.Find(objName) != null) continue;
|
var existing = container.transform.Find(objName);
|
||||||
|
|
||||||
var box = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
GameObject box;
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
box = existing.gameObject;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
box = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||||
box.name = objName;
|
box.name = objName;
|
||||||
box.transform.SetParent(container.transform, false);
|
box.transform.SetParent(container.transform, false);
|
||||||
box.transform.localPosition = new Vector3(-3f + i * 2f, 0.5f, -20f);
|
|
||||||
box.transform.localScale = Vector3.one;
|
box.transform.localScale = Vector3.one;
|
||||||
box.GetComponent<Renderer>().sharedMaterial = MakeMaterial(colors[i]);
|
box.GetComponent<Renderer>().sharedMaterial = MakeMaterial(colors[i]);
|
||||||
|
box.AddComponent<Rigidbody>().mass = 1f;
|
||||||
var rb = box.AddComponent<Rigidbody>();
|
|
||||||
rb.mass = 1f;
|
|
||||||
|
|
||||||
box.AddComponent<Carryable>().displayName = names[i];
|
box.AddComponent<Carryable>().displayName = names[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 위치는 항상 갱신(동선 단축 — 플레이어 스폰 바로 남쪽)
|
||||||
|
box.transform.localPosition = new Vector3(-3f + i * 2f, 0.5f, -1f);
|
||||||
|
}
|
||||||
|
|
||||||
EditorSceneManager.MarkSceneDirty(player.scene);
|
EditorSceneManager.MarkSceneDirty(player.scene);
|
||||||
Selection.activeGameObject = player;
|
Selection.activeGameObject = player;
|
||||||
Debug.Log("[PickupDemoSetup] 완료. Play 후 박스에 다가가 E키로 집고 놓아 보세요.");
|
Debug.Log("[PickupDemoSetup] 완료. Play 후 박스에 다가가 E키로 집고 놓아 보세요.");
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ namespace SinoWms.EditorTools
|
|||||||
// 슬롯 위치와 기대 품목 (PickupDemoSetup의 박스 이름과 일치시켜 정답 구성)
|
// 슬롯 위치와 기대 품목 (PickupDemoSetup의 박스 이름과 일치시켜 정답 구성)
|
||||||
private static readonly (Vector3 pos, string expected)[] Slots =
|
private static readonly (Vector3 pos, string expected)[] Slots =
|
||||||
{
|
{
|
||||||
(new Vector3(-6f, 0.03f, 8f), "박스 A (생수)"),
|
(new Vector3(-3f, 0.03f, 1f), "박스 A (생수)"),
|
||||||
(new Vector3(-2f, 0.03f, 8f), "박스 B (우유)"),
|
(new Vector3(-1f, 0.03f, 1f), "박스 B (우유)"),
|
||||||
(new Vector3(2f, 0.03f, 8f), "박스 C (라면)"),
|
(new Vector3(1f, 0.03f, 1f), "박스 C (라면)"),
|
||||||
(new Vector3(6f, 0.03f, 8f), "박스 D (잡화)"),
|
(new Vector3(3f, 0.03f, 1f), "박스 D (잡화)"),
|
||||||
};
|
};
|
||||||
|
|
||||||
[MenuItem("SinoWMS/Setup Slots")]
|
[MenuItem("SinoWMS/Setup Slots")]
|
||||||
@@ -28,7 +28,12 @@ namespace SinoWms.EditorTools
|
|||||||
for (int i = 0; i < Slots.Length; i++)
|
for (int i = 0; i < Slots.Length; i++)
|
||||||
{
|
{
|
||||||
string slotName = $"Slot_{i + 1}";
|
string slotName = $"Slot_{i + 1}";
|
||||||
if (container.transform.Find(slotName) != null) continue;
|
var existing = container.transform.Find(slotName);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
existing.localPosition = Slots[i].pos; // 위치만 갱신
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var slotGo = new GameObject(slotName);
|
var slotGo = new GameObject(slotName);
|
||||||
slotGo.transform.SetParent(container.transform, false);
|
slotGo.transform.SetParent(container.transform, false);
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
using UnityEngine.InputSystem;
|
||||||
|
|
||||||
|
namespace SinoWms.Gameplay
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 씬의 모든 적치 슬롯을 감시해 미션 진행/완료를 판정한다.
|
||||||
|
/// 모든 슬롯이 정답으로 채워지면 완료 처리하고 점수(정답·오답·시간)를 표시한다.
|
||||||
|
/// </summary>
|
||||||
|
public class MissionManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] private float targetSeconds = 60f; // 시간 보너스 기준
|
||||||
|
private const int ScorePerSlot = 100;
|
||||||
|
private const int MistakePenalty = 25;
|
||||||
|
|
||||||
|
private PutawaySlot[] _slots;
|
||||||
|
private float _startTime;
|
||||||
|
private int _mistakes;
|
||||||
|
private bool _completed;
|
||||||
|
private int _finalScore;
|
||||||
|
private float _elapsedAtComplete;
|
||||||
|
|
||||||
|
private GUIStyle _hudStyle;
|
||||||
|
private GUIStyle _panelStyle;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
_slots = Object.FindObjectsByType<PutawaySlot>(FindObjectsSortMode.None);
|
||||||
|
_startTime = Time.time;
|
||||||
|
foreach (var s in _slots)
|
||||||
|
s.Changed += OnSlotChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
if (_slots == null) return;
|
||||||
|
foreach (var s in _slots)
|
||||||
|
if (s != null) s.Changed -= OnSlotChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (_completed && (Keyboard.current?.rKey.wasPressedThisFrame ?? false))
|
||||||
|
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSlotChanged(PutawaySlot slot)
|
||||||
|
{
|
||||||
|
if (_completed) return;
|
||||||
|
|
||||||
|
// 오답 적치는 누적 집계(교육 감점용)
|
||||||
|
if (slot.IsOccupied && !slot.IsCorrect)
|
||||||
|
_mistakes++;
|
||||||
|
|
||||||
|
int correct = 0;
|
||||||
|
foreach (var s in _slots)
|
||||||
|
if (s.IsCorrect) correct++;
|
||||||
|
|
||||||
|
if (correct == _slots.Length)
|
||||||
|
Complete(correct);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Complete(int correct)
|
||||||
|
{
|
||||||
|
_completed = true;
|
||||||
|
_elapsedAtComplete = Time.time - _startTime;
|
||||||
|
|
||||||
|
int score = correct * ScorePerSlot - _mistakes * MistakePenalty;
|
||||||
|
if (_elapsedAtComplete < targetSeconds)
|
||||||
|
score += Mathf.RoundToInt((1f - _elapsedAtComplete / targetSeconds) * 50f);
|
||||||
|
_finalScore = Mathf.Max(0, score);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int CorrectCount()
|
||||||
|
{
|
||||||
|
int c = 0;
|
||||||
|
foreach (var s in _slots)
|
||||||
|
if (s != null && s.IsCorrect) c++;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
_hudStyle ??= new GUIStyle(GUI.skin.box) { fontSize = 18, alignment = TextAnchor.MiddleCenter };
|
||||||
|
_panelStyle ??= new GUIStyle(GUI.skin.box) { fontSize = 22, alignment = TextAnchor.MiddleCenter };
|
||||||
|
|
||||||
|
if (_slots == null || _slots.Length == 0) return;
|
||||||
|
|
||||||
|
if (!_completed)
|
||||||
|
{
|
||||||
|
string hud = $"적치 {CorrectCount()}/{_slots.Length} 오답 {_mistakes}";
|
||||||
|
GUI.Label(new Rect(Screen.width / 2f - 130f, 64f, 260f, 32f), hud, _hudStyle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float w = 360f, h = 160f;
|
||||||
|
var rect = new Rect(Screen.width / 2f - w / 2f, Screen.height / 2f - h / 2f, w, h);
|
||||||
|
string text =
|
||||||
|
$"미션 완료!\n\n점수 {_finalScore}점\n" +
|
||||||
|
$"소요 {_elapsedAtComplete:0}초 · 오답 {_mistakes}건\n\nR: 다시 하기";
|
||||||
|
GUI.Label(rect, text, _panelStyle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 27f48d56c8f68491ba5b07b4887165cc
|
||||||
@@ -21,6 +21,9 @@ namespace SinoWms.Gameplay
|
|||||||
private static readonly Color Correct = new Color(0.3f, 0.8f, 0.4f);
|
private static readonly Color Correct = new Color(0.3f, 0.8f, 0.4f);
|
||||||
private static readonly Color Wrong = new Color(0.85f, 0.3f, 0.3f);
|
private static readonly Color Wrong = new Color(0.85f, 0.3f, 0.3f);
|
||||||
|
|
||||||
|
/// <summary>적치/제거 등 상태가 바뀌면 발생(미션 판정용).</summary>
|
||||||
|
public event System.Action<PutawaySlot> Changed;
|
||||||
|
|
||||||
public Carryable Occupant { get; private set; }
|
public Carryable Occupant { get; private set; }
|
||||||
public bool IsOccupied => Occupant != null;
|
public bool IsOccupied => Occupant != null;
|
||||||
public bool IsCorrect =>
|
public bool IsCorrect =>
|
||||||
@@ -55,6 +58,7 @@ namespace SinoWms.Gameplay
|
|||||||
{
|
{
|
||||||
if (pad == null) return;
|
if (pad == null) return;
|
||||||
pad.material.color = !IsOccupied ? Empty : (IsCorrect ? Correct : Wrong);
|
pad.material.color = !IsOccupied ? Empty : (IsCorrect ? Correct : Wrong);
|
||||||
|
Changed?.Invoke(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user