VSClone/Assets/Scripts/View/GameApp.cs

159 lines
6.0 KiB
C#

using System;
using System.IO;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using VS.Sim;
// 진입점 — 데이터 로드 · 저장 · 게임 진행 · 입력 · 화면 갱신
namespace VS.View
{
public partial class GameApp : MonoBehaviour
{
public static GameApp Instance { get; private set; }
public Tables T; public Game G;
public Canvas Canvas; public RectTransform Root, PopupLayer, FxLayer;
public Camera Cam;
public static string SavePathOverride; // 렌더 검사용 임시 세이브
public static Func<DateTime> ClockOverride;
public static RenderTexture RenderTarget; // 렌더 검사용
public bool Headless; // EditorRender: Update 진행 끔
float saveTimer, uiTimer;
string SavePath => SavePathOverride ?? Path.Combine(Application.persistentDataPath, "vs_save.json");
public static string ReadData(string name)
{
var ta = Resources.Load<TextAsset>("Data/" + name);
return ta != null ? ta.text : null;
}
bool inited;
void Awake() { if (Application.isPlaying) Init(); }
public static void Kill(GameObject go) { if (go == null) return; if (Application.isPlaying) Destroy(go); else DestroyImmediate(go); }
public void Init()
{
if (inited) return;
inited = true;
Instance = this;
Application.targetFrameRate = 60;
T = Tables.Load(ReadData);
SaveData s = null;
try { if (File.Exists(SavePath)) s = JsonUtility.FromJson<SaveData>(File.ReadAllText(SavePath)); }
catch (Exception e) { Debug.LogWarning("[VS] save load failed: " + e.Message); }
G = new Game(T, s, ClockOverride);
BuildCanvas();
BuildHud();
G.OfflineCheck();
HookEvents();
RefreshAll();
}
void BuildCanvas()
{
var camGo = new GameObject("UICamera");
camGo.transform.SetParent(transform, false);
Cam = camGo.AddComponent<Camera>();
Cam.clearFlags = CameraClearFlags.SolidColor; Cam.backgroundColor = new Color32(40, 28, 56, 255);
Cam.orthographic = true; Cam.cullingMask = 1 << 5; Cam.depth = 0; Cam.nearClipPlane = -10; Cam.farClipPlane = 1000;
if (RenderTarget != null) Cam.targetTexture = RenderTarget;
var cgo = new GameObject("Canvas", typeof(RectTransform));
cgo.layer = 5;
cgo.transform.SetParent(transform, false);
Canvas = cgo.AddComponent<Canvas>();
Canvas.renderMode = RenderMode.ScreenSpaceCamera; Canvas.worldCamera = Cam; Canvas.planeDistance = 100;
var sc = cgo.AddComponent<CanvasScaler>();
sc.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; sc.referenceResolution = new Vector2(1080, 1920);
sc.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; sc.matchWidthOrHeight = 0.5f;
cgo.AddComponent<GraphicRaycaster>();
Root = UI.Rect("Root", cgo.transform).Fill();
if (FindFirstObjectByType<EventSystem>() == null)
{
var es = new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
es.transform.SetParent(transform, false);
}
}
void Update()
{
if (Headless) return;
float dt = Mathf.Min(Time.unscaledDeltaTime, 0.25f);
G.Tick(dt);
TickBattle(dt);
UpdateInput(dt);
UpdateHudFx(dt);
uiTimer -= dt;
if (uiTimer <= 0f) { uiTimer = 0.1f; RefreshAll(); }
saveTimer += dt;
if (saveTimer > 10f) { saveTimer = 0; SaveNow(); }
}
/// 렌더 검사용 — 게임 진행 + 화면 연출을 dt 단위로 n 번
public void DebugTick(float dt, int n)
{
for (int i = 0; i < n; i++) { G.Tick(dt); TickBattle(dt); UpdateHudFx(dt); }
RefreshAll();
}
public void SaveNow()
{
try { G.Commit(); File.WriteAllText(SavePath, JsonUtility.ToJson(G.S)); }
catch (Exception e) { Debug.LogWarning("[VS] save failed: " + e.Message); }
}
void OnApplicationPause(bool p)
{
if (G == null) return;
if (p) { G.MarkAfkStamp(); SaveNow(); }
else { G.OfflineCheck(); afkShownThisSession = false; }
}
void OnApplicationQuit() { if (G == null) return; G.MarkAfkStamp(); SaveNow(); }
public void ResetSave()
{
try { if (File.Exists(SavePath)) File.Delete(SavePath); } catch { }
G = new Game(T, new SaveData(), ClockOverride);
HookEvents();
CloseAllPopups();
RebuildStage();
RefreshAll();
}
// ---------------- 입력: 무대 탭 · 길게 누르기(원작 OnOffActiveHold) ----------------
bool pressing; float pressTime; float holdAcc; bool holdActive;
public void OnStagePointerDown(Vector2 screenPos)
{
if (PopupOpen) return;
pressing = true; pressTime = 0; holdAcc = 0; holdActive = false;
DoTap(screenPos);
}
public void OnStagePointerUp() { pressing = false; holdActive = false; }
void UpdateInput(float dt)
{
if (!pressing) return;
pressTime += dt;
var r = T.Cfg.remote;
if (!r.OnOffActiveHold) return;
if (!holdActive && pressTime >= r.HoldActivateSeconds) { holdActive = true; holdAcc = 0; }
if (holdActive)
{
holdAcc += dt;
float iv = 1f / Mathf.Max(1, r.HoldTapsPerSecond);
while (holdAcc >= iv) { holdAcc -= iv; DoTap(Input.mousePosition); }
}
}
void DoTap(Vector2 screenPos)
{
bool coin = G.Tap();
OnTapVisual(screenPos, coin);
}
}
}