diff --git a/FishDice/Assets/FishDice/Demo/FishDice.Demo.asmdef b/FishDice/Assets/FishDice/Demo/FishDice.Demo.asmdef index b36e76f..9ae8787 100644 --- a/FishDice/Assets/FishDice/Demo/FishDice.Demo.asmdef +++ b/FishDice/Assets/FishDice/Demo/FishDice.Demo.asmdef @@ -2,7 +2,8 @@ "name": "FishDice.Demo", "rootNamespace": "FishDice.Demo", "references": [ - "FishDice.Runtime" + "FishDice.Runtime", + "UnityEngine.UI" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/FishDice/Assets/FishDice/Demo/LuckyDiceDemoController.cs b/FishDice/Assets/FishDice/Demo/LuckyDiceDemoController.cs index 7d39684..acb60a6 100644 --- a/FishDice/Assets/FishDice/Demo/LuckyDiceDemoController.cs +++ b/FishDice/Assets/FishDice/Demo/LuckyDiceDemoController.cs @@ -1,142 +1,307 @@ -using System.Collections.Generic; +using System; +using System.Collections; +using FishDice.LuckyDice; +using UnityEngine.EventSystems; using UnityEngine; +using UnityEngine.UI; +using Random = UnityEngine.Random; namespace FishDice.Demo { public sealed class LuckyDiceDemoController : MonoBehaviour { - private static readonly LuckyDiceDemoScenario[] Scenarios = - { - new LuckyDiceDemoScenario("Normal Reward", "Numbers pay the base reward.", 0, 1), - new LuckyDiceDemoScenario("Clover Bonus", "A number plus Clover grants reward and bonus.", 0, 5), - new LuckyDiceDemoScenario("Lucky Dice: Rocket", "Two Clovers enter Lucky Dice and resolve Slap Down.", 5, 5, 0), - new LuckyDiceDemoScenario("Lucky Dice: Thief", "Two Clovers enter Lucky Dice and resolve Treasure Heist.", 5, 5, 1) - }; + private static readonly string[] NormalPreviewFaces = { "2", "3", "4", "5", "6", "Clover" }; + private static readonly string[] LuckyPreviewFaces = { "Rocket", "Thief" }; + public const string BuiltinFontResourcePath = "LegacyRuntime.ttf"; - private readonly List _history = new List(); - private Vector2 _scroll; - private LuckyDiceDemoSnapshot _current; - private GUIStyle _titleStyle; - private GUIStyle _labelStyle; - private GUIStyle _summaryStyle; + private readonly SystemRandomSource _flowRandom = new SystemRandomSource(new System.Random()); + private Text _titleText; + private Text _statusText; + private Text _detailText; + private Text _rewardText; + private Text[] _normalDiceTexts; + private Text[] _luckyDiceTexts; + private Button _primaryButton; + private Text _primaryButtonText; + private Coroutine _flowRoutine; + private LuckyDiceDemoSnapshot _lastSnapshot; + private Font _uiFont; private void Awake() { - RunScenario(Scenarios[0]); + BuildUi(); + ShowReady(); } - private void OnGUI() + private void StartRoll() { - EnsureStyles(); - - var margin = 24f; - var width = Mathf.Min(780f, Screen.width - margin * 2f); - var height = Mathf.Min(620f, Screen.height - margin * 2f); - GUILayout.BeginArea(new Rect(margin, margin, width, height), GUI.skin.box); - GUILayout.Label("FishDice Lucky Dice Demo", _titleStyle); - GUILayout.Label("Runtime path: normal roll -> combo facts -> rule actions -> optional Lucky Dice target mode.", _labelStyle); - GUILayout.Space(10f); - - GUILayout.BeginHorizontal(); - if (GUILayout.Button("Random Roll", GUILayout.Height(34f))) + if (_flowRoutine != null) { - RunRandom(); + StopCoroutine(_flowRoutine); } - foreach (var scenario in Scenarios) + _flowRoutine = StartCoroutine(PlayRollFlow()); + } + + private IEnumerator PlayRollFlow() + { + _primaryButton.interactable = false; + _primaryButtonText.text = "投掷中"; + _rewardText.text = string.Empty; + _detailText.text = string.Empty; + SetLuckyDiceVisible(false); + SetStatus("摇动普通骰子"); + + _lastSnapshot = LuckyDiceDemoRunner.RunRandom(_flowRandom); + yield return AnimateDice(_normalDiceTexts, NormalPreviewFaces, 0.9f); + ApplyDiceTexts(_normalDiceTexts, _lastSnapshot.NormalFaces); + SetStatus("组合判定"); + _detailText.text = _lastSnapshot.ComboLine; + yield return new WaitForSeconds(0.55f); + + if (_lastSnapshot.OutcomeKind == "lucky_dice") { - if (GUILayout.Button(scenario.Title, GUILayout.Height(34f))) + SetStatus("触发 Lucky Dice"); + _rewardText.text = "双 Clover 命中"; + yield return new WaitForSeconds(0.45f); + SetLuckyDiceVisible(true); + SetStatus("揭示特殊骰子"); + yield return AnimateDice(_luckyDiceTexts, LuckyPreviewFaces, 1.0f); + ApplyDiceTexts(_luckyDiceTexts, _lastSnapshot.LuckyDiceSlotKeys); + } + + ShowResult(_lastSnapshot); + _flowRoutine = null; + } + + private IEnumerator AnimateDice(Text[] diceTexts, string[] previewFaces, float duration) + { + var elapsed = 0f; + while (elapsed < duration) + { + for (var i = 0; i < diceTexts.Length; i++) { - RunScenario(scenario); + diceTexts[i].text = previewFaces[Random.Range(0, previewFaces.Length)]; } + + yield return new WaitForSeconds(0.08f); + elapsed += 0.08f; } - GUILayout.EndHorizontal(); - - GUILayout.Space(12f); - DrawSnapshot(_current); - GUILayout.Space(12f); - DrawHistory(); - GUILayout.EndArea(); } - private void RunScenario(LuckyDiceDemoScenario scenario) + private void ShowReady() { - SetCurrent(LuckyDiceDemoRunner.RunScenario(scenario)); + _titleText.text = "Lucky Dice"; + SetStatus("准备投骰"); + _detailText.text = "普通骰子会随机落点,命中双 Clover 后进入 Lucky Dice。"; + _rewardText.text = string.Empty; + ApplyDiceTexts(_normalDiceTexts, new[] { "?", "?" }); + ApplyDiceTexts(_luckyDiceTexts, new[] { "?", "?", "?" }); + SetLuckyDiceVisible(false); + _primaryButton.interactable = true; + _primaryButtonText.text = "投骰"; } - private void RunRandom() + private void ShowResult(LuckyDiceDemoSnapshot snapshot) { - SetCurrent(LuckyDiceDemoRunner.RunRandom()); + SetStatus(ResolveResultStatus(snapshot)); + _detailText.text = $"{snapshot.FaceLine}\n{snapshot.RuleLine}\n{snapshot.EffectLine}"; + _rewardText.text = snapshot.TargetModeLine == "Target Mode: -" + ? snapshot.Summary + : $"{snapshot.Summary}\n{snapshot.TargetModeLine}"; + _primaryButton.interactable = true; + _primaryButtonText.text = "再投一次"; } - private void SetCurrent(LuckyDiceDemoSnapshot snapshot) + private static string ResolveResultStatus(LuckyDiceDemoSnapshot snapshot) { - _current = snapshot; - _history.Insert(0, snapshot); - if (_history.Count > 8) + switch (snapshot.OutcomeKind) { - _history.RemoveAt(_history.Count - 1); + case "lucky_dice": + return "进入目标玩法"; + case "clover_bonus": + return "获得 Clover Bonus"; + case "normal_reward": + return "获得普通奖励"; + default: + return "本轮结束"; } } - private void DrawSnapshot(LuckyDiceDemoSnapshot snapshot) + private void BuildUi() { - if (snapshot == null) + EnsureEventSystem(); + + var canvasObject = new GameObject("LuckyDiceCanvas"); + canvasObject.transform.SetParent(transform, false); + var canvas = canvasObject.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvas.pixelPerfect = true; + var scaler = canvasObject.AddComponent(); + scaler.uiScaleMode = CanvasScaler.ScaleMode.ConstantPixelSize; + scaler.scaleFactor = 1f; + scaler.referencePixelsPerUnit = 100f; + canvasObject.AddComponent(); + + var root = CreatePanel("Root", canvasObject.transform, new Color(0.06f, 0.09f, 0.13f, 1f)); + Stretch(root.rectTransform); + + var board = CreatePanel("Board", root.transform, new Color(0.12f, 0.16f, 0.22f, 0.96f)); + Center(board.rectTransform, new Vector2(760f, 600f)); + + _titleText = CreateText("Title", board.transform, "Lucky Dice", 34, FontStyle.Bold, Color.white); + SetRect(_titleText.rectTransform, new Vector2(0f, 194f), new Vector2(680f, 48f)); + + _statusText = CreateText("Status", board.transform, string.Empty, 24, FontStyle.Bold, new Color(1f, 0.84f, 0.38f)); + SetRect(_statusText.rectTransform, new Vector2(0f, 142f), new Vector2(680f, 40f)); + + _normalDiceTexts = new[] + { + CreateDiceText("NormalDiceA", board.transform, new Vector2(-96f, 64f), new Color(0.96f, 0.97f, 1f)), + CreateDiceText("NormalDiceB", board.transform, new Vector2(96f, 64f), new Color(0.96f, 0.97f, 1f)) + }; + + _luckyDiceTexts = new[] + { + CreateDiceText("LuckySlotA", board.transform, new Vector2(-150f, -48f), new Color(1f, 0.86f, 0.46f)), + CreateDiceText("LuckySlotB", board.transform, new Vector2(0f, -48f), new Color(1f, 0.86f, 0.46f)), + CreateDiceText("LuckySlotC", board.transform, new Vector2(150f, -48f), new Color(1f, 0.86f, 0.46f)) + }; + + _detailText = CreateText("Detail", board.transform, string.Empty, 16, FontStyle.Normal, new Color(0.82f, 0.88f, 0.94f)); + SetRect(_detailText.rectTransform, new Vector2(0f, -142f), new Vector2(650f, 86f)); + + _rewardText = CreateText("Reward", board.transform, string.Empty, 19, FontStyle.Bold, new Color(0.55f, 0.92f, 0.75f)); + SetRect(_rewardText.rectTransform, new Vector2(0f, -210f), new Vector2(650f, 56f)); + + _primaryButton = CreateButton("PrimaryButton", board.transform, "投骰", new Vector2(0f, -254f), new Vector2(190f, 48f)); + _primaryButton.onClick.AddListener(StartRoll); + } + + private static void EnsureEventSystem() + { + if (FindObjectOfType() != null) { return; } - GUILayout.Label(snapshot.Title, _summaryStyle); - GUILayout.Label(snapshot.Description, _labelStyle); - GUILayout.Space(6f); - GUILayout.Label(snapshot.FaceLine, _labelStyle); - GUILayout.Label(snapshot.ComboLine, _labelStyle); - GUILayout.Label(snapshot.RuleLine, _labelStyle); - GUILayout.Label(snapshot.EffectLine, _labelStyle); - GUILayout.Label(snapshot.LuckyDiceLine, _labelStyle); - GUILayout.Label(snapshot.TargetModeLine, _labelStyle); - GUILayout.Space(6f); - GUILayout.Label(snapshot.Summary, _summaryStyle); + var eventSystemObject = new GameObject("EventSystem"); + eventSystemObject.AddComponent(); + eventSystemObject.AddComponent(); } - private void DrawHistory() + private Text CreateDiceText(string name, Transform parent, Vector2 anchoredPosition, Color faceColor) { - GUILayout.Label("Recent Rolls", _summaryStyle); - _scroll = GUILayout.BeginScrollView(_scroll, GUILayout.Height(180f)); - foreach (var snapshot in _history) - { - GUILayout.Label($"{snapshot.Title} | {snapshot.FaceLine} | {snapshot.Summary}", _labelStyle); - } - GUILayout.EndScrollView(); + var background = CreatePanel(name, parent, new Color(0.2f, 0.25f, 0.33f, 1f)); + SetRect(background.rectTransform, anchoredPosition, new Vector2(116f, 84f)); + + var text = CreateText(name + "Text", background.transform, "?", 24, FontStyle.Bold, faceColor); + Stretch(text.rectTransform); + return text; } - private void EnsureStyles() + private Button CreateButton(string name, Transform parent, string label, Vector2 anchoredPosition, Vector2 size) { - if (_titleStyle != null) + var image = CreatePanel(name, parent, new Color(0.92f, 0.56f, 0.16f, 1f)); + SetRect(image.rectTransform, anchoredPosition, size); + var button = image.gameObject.AddComponent