159 lines
6.0 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|