Initial Unity 6 scaffold for Cat Kirby Adventures.

Add Phase A greybox vertical slice: third-person movement, forest path, checkpoints, story triggers, and light ability gate.
This commit is contained in:
ifontosh
2026-09-08 17:09:27 +08:00
commit 1c6c9b2a70
44 changed files with 2553 additions and 0 deletions
@@ -0,0 +1,107 @@
using UnityEngine;
namespace KirbyAdventures
{
/// <summary>
/// Ensures a single bootstrap exists and builds the Phase A greybox level.
/// </summary>
public class GameBootstrap : MonoBehaviour
{
static bool _initialized;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void AutoBootstrap()
{
if (_initialized) return;
if (FindFirstObjectByType<GameBootstrap>() != null)
{
_initialized = true;
return;
}
var go = new GameObject("GameBootstrap");
go.AddComponent<GameBootstrap>();
_initialized = true;
}
void Awake()
{
DontDestroyOnLoad(gameObject);
if (GameFlow.Instance == null)
{
var flowGo = new GameObject("GameFlow");
flowGo.AddComponent<GameFlow>();
DontDestroyOnLoad(flowGo);
}
if (FindFirstObjectByType<CheckpointSystem>() == null)
{
var cp = new GameObject("CheckpointSystem");
cp.AddComponent<CheckpointSystem>();
DontDestroyOnLoad(cp);
}
BuildUi();
Level01ForestBuilder.Build();
}
void BuildUi()
{
var dialogue = DialogueUI.EnsureExists();
var pause = CreateOverlay("PauseOverlay", "ПАУЗА\nEsc — продолжить", new Color(0f, 0f, 0f, 0.65f));
pause.SetActive(false);
var win = CreateOverlay("WinOverlay", "Ворота открыты!\nДеревня ждёт Кирби.\n\nПобеда среза Phase A", new Color(0.05f, 0.2f, 0.1f, 0.8f));
win.SetActive(false);
if (GameFlow.Instance != null)
{
GameFlow.Instance.SetPauseOverlay(pause);
GameFlow.Instance.SetWinOverlay(win);
}
// silence unused
_ = dialogue;
}
static GameObject CreateOverlay(string name, string text, Color bg)
{
var canvasGo = new GameObject(name);
var canvas = canvasGo.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 100;
canvasGo.AddComponent<UnityEngine.UI.CanvasScaler>().uiScaleMode =
UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize;
canvasGo.AddComponent<UnityEngine.UI.GraphicRaycaster>();
DontDestroyOnLoad(canvasGo);
var panel = new GameObject("Panel");
panel.transform.SetParent(canvasGo.transform, false);
var img = panel.AddComponent<UnityEngine.UI.Image>();
img.color = bg;
var rt = panel.GetComponent<RectTransform>();
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = Vector2.zero;
rt.offsetMax = Vector2.zero;
var textGo = new GameObject("Text");
textGo.transform.SetParent(panel.transform, false);
var uiText = textGo.AddComponent<UnityEngine.UI.Text>();
uiText.text = text;
uiText.alignment = TextAnchor.MiddleCenter;
uiText.fontSize = 36;
uiText.color = Color.white;
uiText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
if (uiText.font == null)
uiText.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
var trt = textGo.GetComponent<RectTransform>();
trt.anchorMin = new Vector2(0.1f, 0.2f);
trt.anchorMax = new Vector2(0.9f, 0.8f);
trt.offsetMin = Vector2.zero;
trt.offsetMax = Vector2.zero;
return canvasGo;
}
}
}
+86
View File
@@ -0,0 +1,86 @@
using UnityEngine;
namespace KirbyAdventures
{
/// <summary>
/// Global game state: pause, win, light ability unlock.
/// </summary>
public class GameFlow : MonoBehaviour
{
public static GameFlow Instance { get; private set; }
public bool IsPaused { get; private set; }
public bool HasWon { get; private set; }
public bool LightAbilityUnlocked { get; private set; }
GameObject _pauseOverlay;
GameObject _winOverlay;
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
public void SetPauseOverlay(GameObject overlay) => _pauseOverlay = overlay;
public void SetWinOverlay(GameObject overlay) => _winOverlay = overlay;
public void UnlockLightAbility()
{
LightAbilityUnlocked = true;
Debug.Log("[GameFlow] Light ability unlocked.");
}
public void TogglePause()
{
if (HasWon) return;
SetPaused(!IsPaused);
}
public void SetPaused(bool paused)
{
IsPaused = paused;
Time.timeScale = paused ? 0f : 1f;
if (_pauseOverlay != null)
_pauseOverlay.SetActive(paused);
Cursor.lockState = paused ? CursorLockMode.None : CursorLockMode.Locked;
Cursor.visible = paused;
}
public void TriggerWin()
{
if (HasWon) return;
HasWon = true;
IsPaused = true;
Time.timeScale = 0f;
if (_winOverlay != null)
_winOverlay.SetActive(true);
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
Debug.Log("[GameFlow] Level complete — village gate opened.");
}
void Update()
{
var kb = UnityEngine.InputSystem.Keyboard.current;
if (kb != null && kb.escapeKey.wasPressedThisFrame)
TogglePause();
}
void OnDestroy()
{
if (Instance == this)
{
Time.timeScale = 1f;
Instance = null;
}
}
}
}