Initial commit
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
using LiveOpsExamples;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LiveOpsExamples.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Bakes the Cozy Collector ScriptableObject assets. Idempotent: writes to
|
||||
/// fixed asset paths and overwrites whatever is already there.
|
||||
/// </summary>
|
||||
public static partial class CozyCollectorBake
|
||||
{
|
||||
private const string SpriteLibraryAssetPath = "Assets/LiveOpsExamples/CozyCollectorSpriteLibrary.asset";
|
||||
private const string SpriteFolderPath = "Assets/LiveOpsExamples/Sprites";
|
||||
|
||||
[MenuItem("Tools/LiveOps/Bake Cozy Collector Assets")]
|
||||
public static void BakeAssets()
|
||||
{
|
||||
var library = AssetDatabase.LoadAssetAtPath<CozyCollectorSpriteLibrary>(SpriteLibraryAssetPath);
|
||||
if (library == null)
|
||||
{
|
||||
library = ScriptableObject.CreateInstance<CozyCollectorSpriteLibrary>();
|
||||
AssetDatabase.CreateAsset(library, SpriteLibraryAssetPath);
|
||||
}
|
||||
|
||||
var serialized = new SerializedObject(library);
|
||||
AssignLibrarySprite(serialized, "background", "cozy-camp-bg.png");
|
||||
AssignLibrarySprite(serialized, "player", "player.png");
|
||||
AssignLibrarySprite(serialized, "moonberry", "moonberry.png");
|
||||
AssignLibrarySprite(serialized, "acorn", "acorn.png");
|
||||
AssignLibrarySprite(serialized, "star", "star.png");
|
||||
AssignLibrarySprite(serialized, "snack", "snack.png");
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
EditorUtility.SetDirty(library);
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log($"Cozy Collector sprite library baked: {SpriteLibraryAssetPath}");
|
||||
}
|
||||
|
||||
private static void AssignLibrarySprite(SerializedObject serialized, string fieldName, string fileName)
|
||||
{
|
||||
var spritePath = $"{SpriteFolderPath}/{fileName}";
|
||||
var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(spritePath);
|
||||
if (sprite == null)
|
||||
{
|
||||
Debug.LogError($"CozyCollectorBake: sprite not found at {spritePath}");
|
||||
return;
|
||||
}
|
||||
|
||||
var property = serialized.FindProperty(fieldName);
|
||||
if (property == null)
|
||||
{
|
||||
Debug.LogError($"CozyCollectorBake: field '{fieldName}' not found on {nameof(CozyCollectorSpriteLibrary)}");
|
||||
return;
|
||||
}
|
||||
|
||||
property.objectReferenceValue = sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 640a29c33f5384caebacc7de21235ab8
|
||||
@@ -0,0 +1,166 @@
|
||||
using System.IO;
|
||||
using LiveOpsExamples;
|
||||
using TMPro;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LiveOpsExamples.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Bakes the Cozy Collector gameplay prefabs. Idempotent: every prefab is
|
||||
/// rebuilt from code and saved over the same fixed asset path.
|
||||
/// </summary>
|
||||
public static partial class CozyCollectorBake
|
||||
{
|
||||
private const string PrefabFolderPath = "Assets/LiveOpsExamples/Prefabs";
|
||||
private const string SnackSpritePath = "Assets/LiveOpsExamples/Sprites/snack.png";
|
||||
|
||||
private static readonly Color AccentColor = ParseHexColor("#FFD27D");
|
||||
|
||||
[MenuItem("Tools/LiveOps/Bake Cozy Collector Prefabs")]
|
||||
public static void BakePrefabs()
|
||||
{
|
||||
EnsureAssetFolder(PrefabFolderPath);
|
||||
|
||||
SaveBakedPrefab(BuildSnackPrefab(), $"{PrefabFolderPath}/Snack.prefab");
|
||||
SaveBakedPrefab(BuildScorePopupPrefab(), $"{PrefabFolderPath}/ScorePopup.prefab");
|
||||
SaveBakedPrefab(BuildCollectBurstPrefab(), $"{PrefabFolderPath}/CollectBurst.prefab");
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log($"Cozy Collector prefabs baked under {PrefabFolderPath}");
|
||||
}
|
||||
|
||||
private static GameObject BuildSnackPrefab()
|
||||
{
|
||||
var go = new GameObject("Snack");
|
||||
|
||||
var collider = go.AddComponent<CircleCollider2D>();
|
||||
collider.isTrigger = true;
|
||||
collider.radius = 0.3f;
|
||||
|
||||
var renderer = go.AddComponent<SpriteRenderer>();
|
||||
var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(SnackSpritePath);
|
||||
if (sprite == null)
|
||||
{
|
||||
Debug.LogError($"CozyCollectorBake: sprite not found at {SnackSpritePath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.sprite = sprite;
|
||||
// Phaser setDisplaySize(52, 52) at 100 PPU -> 0.52 x 0.52 world units.
|
||||
var scale = new Vector3(
|
||||
0.52f / sprite.bounds.size.x,
|
||||
0.52f / sprite.bounds.size.y,
|
||||
1f);
|
||||
go.transform.localScale = scale;
|
||||
// Phaser pickup radius = 0.42 * 52px ~= 0.22 world units; a circle
|
||||
// collider's world radius scales by the largest transform axis.
|
||||
collider.radius = 0.22f / Mathf.Max(scale.x, scale.y);
|
||||
}
|
||||
|
||||
go.AddComponent<Snack>();
|
||||
return go;
|
||||
}
|
||||
|
||||
private static GameObject BuildScorePopupPrefab()
|
||||
{
|
||||
var go = new GameObject("ScorePopup");
|
||||
|
||||
var label = go.AddComponent<TextMeshPro>();
|
||||
AssignDefaultFont(label);
|
||||
label.text = "+10";
|
||||
label.fontSize = 2.5f;
|
||||
label.alignment = TextAlignmentOptions.Center;
|
||||
label.color = Color.white;
|
||||
|
||||
var view = go.AddComponent<ScorePopupView>();
|
||||
WireObjectFields(view, ("label", label));
|
||||
return go;
|
||||
}
|
||||
|
||||
private static GameObject BuildCollectBurstPrefab()
|
||||
{
|
||||
var go = new GameObject("CollectBurst");
|
||||
var particles = go.AddComponent<ParticleSystem>();
|
||||
|
||||
var main = particles.main;
|
||||
main.duration = 0.5f;
|
||||
main.loop = false;
|
||||
main.playOnAwake = true;
|
||||
main.startLifetime = 0.45f;
|
||||
main.startSpeed = 2f;
|
||||
main.startSize = 0.15f;
|
||||
main.gravityModifier = 0f;
|
||||
main.startColor = AccentColor;
|
||||
main.stopAction = ParticleSystemStopAction.Destroy;
|
||||
|
||||
var emission = particles.emission;
|
||||
emission.rateOverTime = 0f;
|
||||
emission.SetBursts(new[] { new ParticleSystem.Burst(0f, 9) });
|
||||
|
||||
var shape = particles.shape;
|
||||
shape.shapeType = ParticleSystemShapeType.Sphere;
|
||||
shape.radius = 0.1f;
|
||||
|
||||
var renderer = particles.GetComponent<ParticleSystemRenderer>();
|
||||
renderer.renderMode = ParticleSystemRenderMode.Billboard;
|
||||
var material = AssetDatabase.GetBuiltinExtraResource<Material>("Default-Particle.mat");
|
||||
if (material == null)
|
||||
material = AssetDatabase.GetBuiltinExtraResource<Material>("Sprites-Default.mat");
|
||||
if (material == null)
|
||||
Debug.LogWarning("CozyCollectorBake: no builtin particle material resolved, assign one manually.");
|
||||
else
|
||||
renderer.sharedMaterial = material;
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
private static Color ParseHexColor(string hex)
|
||||
{
|
||||
return ColorUtility.TryParseHtmlString(hex, out var color) ? color : Color.magenta;
|
||||
}
|
||||
|
||||
private static void EnsureAssetFolder(string folderPath)
|
||||
{
|
||||
if (AssetDatabase.IsValidFolder(folderPath))
|
||||
return;
|
||||
|
||||
var parent = Path.GetDirectoryName(folderPath)?.Replace('\\', '/');
|
||||
if (!string.IsNullOrEmpty(parent))
|
||||
EnsureAssetFolder(parent);
|
||||
AssetDatabase.CreateFolder(parent, Path.GetFileName(folderPath));
|
||||
}
|
||||
|
||||
private static void SaveBakedPrefab(GameObject go, string path)
|
||||
{
|
||||
PrefabUtility.SaveAsPrefabAsset(go, path);
|
||||
Object.DestroyImmediate(go);
|
||||
}
|
||||
|
||||
private static void WireObjectFields(Object component, params (string Field, Object Value)[] fields)
|
||||
{
|
||||
var serialized = new SerializedObject(component);
|
||||
foreach (var (field, value) in fields)
|
||||
{
|
||||
var property = serialized.FindProperty(field);
|
||||
if (property == null)
|
||||
{
|
||||
Debug.LogError($"CozyCollectorBake: field '{field}' not found on {component.GetType().Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
property.objectReferenceValue = value;
|
||||
}
|
||||
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
private static void AssignDefaultFont(TMP_Text text)
|
||||
{
|
||||
if (TMP_Settings.defaultFontAsset != null)
|
||||
text.font = TMP_Settings.defaultFontAsset;
|
||||
else
|
||||
Debug.LogWarning("CozyCollectorBake: TMP default font asset is not set, assign a font manually.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d0f038b4a90c45a8a95bf4f37221e78
|
||||
@@ -0,0 +1,258 @@
|
||||
using System.Collections.Generic;
|
||||
using RudderSdk.Unity;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LiveOpsExamples.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Bakes the Cozy Collector demo scene (world + camera + game setup) and saves it
|
||||
/// to <see cref="ScenePath"/>. UI is built by the CozyCollectorBake.Ui part.
|
||||
/// Re-running overwrites the same scene asset.
|
||||
/// </summary>
|
||||
public static partial class CozyCollectorBake
|
||||
{
|
||||
private const string ScenePath = "Assets/LiveOpsExamples/LiveOpsCozyCollector.unity";
|
||||
private const string BackgroundSpritePath = "Assets/LiveOpsExamples/Sprites/cozy-camp-bg.png";
|
||||
private const string PlayerSpritePath = "Assets/LiveOpsExamples/Sprites/player.png";
|
||||
private const string ConfigurationPath = "Assets/LiveOpsLocal.asset";
|
||||
private const string SpriteLibraryPath = "Assets/LiveOpsExamples/CozyCollectorSpriteLibrary.asset";
|
||||
private const string SnackPrefabPath = "Assets/LiveOpsExamples/Prefabs/Snack.prefab";
|
||||
private const string ScorePopupPrefabPath = "Assets/LiveOpsExamples/Prefabs/ScorePopup.prefab";
|
||||
private const string CollectBurstPrefabPath = "Assets/LiveOpsExamples/Prefabs/CollectBurst.prefab";
|
||||
|
||||
// The Phaser field is 1280x720 px at 100 PPU -> 12.8x7.2 world units.
|
||||
private const float FieldWidthUnits = 12.8f;
|
||||
private const float FieldHeightUnits = 7.2f;
|
||||
|
||||
private static readonly Color CameraBackgroundColor = new Color(0.10f, 0.08f, 0.13f);
|
||||
|
||||
[MenuItem("Tools/LiveOps/Bake Cozy Collector (All)")]
|
||||
public static void BakeAll()
|
||||
{
|
||||
BakeAssets();
|
||||
BakePrefabs();
|
||||
BakeScene();
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
[MenuItem("Tools/LiveOps/Bake Cozy Collector Scene")]
|
||||
public static void BakeScene()
|
||||
{
|
||||
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
|
||||
BuildSceneCamera();
|
||||
BuildSceneBackground();
|
||||
|
||||
var world = BuildSceneWorld(out var playerRenderer, out var spawner, out var snacksParent);
|
||||
var controller = BuildSceneGameSetup(world);
|
||||
|
||||
WireSceneWorld(world, playerRenderer, spawner, controller);
|
||||
WireSceneSpawner(spawner, snacksParent);
|
||||
|
||||
BuildUi(controller, world);
|
||||
|
||||
EditorSceneManager.SaveScene(scene, ScenePath);
|
||||
AddSceneToBuildSettings();
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
Debug.Log($"[CozyCollectorBake] Scene saved to {ScenePath}.");
|
||||
}
|
||||
|
||||
private static void BuildSceneCamera()
|
||||
{
|
||||
var cameraGo = new GameObject("Main Camera");
|
||||
cameraGo.tag = "MainCamera";
|
||||
cameraGo.transform.position = new Vector3(0f, 0f, -10f);
|
||||
|
||||
var camera = cameraGo.AddComponent<Camera>();
|
||||
camera.orthographic = true;
|
||||
camera.orthographicSize = FieldHeightUnits / 2f;
|
||||
camera.clearFlags = CameraClearFlags.SolidColor;
|
||||
camera.backgroundColor = CameraBackgroundColor;
|
||||
// Game field occupies the left 75% of the screen; the right side hosts the UI rail.
|
||||
camera.rect = new Rect(0f, 0f, 0.75f, 1f);
|
||||
}
|
||||
|
||||
private static void BuildSceneBackground()
|
||||
{
|
||||
var backgroundGo = new GameObject("Background");
|
||||
var renderer = backgroundGo.AddComponent<SpriteRenderer>();
|
||||
renderer.sortingOrder = -10;
|
||||
|
||||
var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(BackgroundSpritePath);
|
||||
if (sprite == null)
|
||||
{
|
||||
Debug.LogError($"[CozyCollectorBake] Background sprite not found at {BackgroundSpritePath}.");
|
||||
return;
|
||||
}
|
||||
|
||||
renderer.sprite = sprite;
|
||||
var size = sprite.bounds.size;
|
||||
backgroundGo.transform.localScale = new Vector3(
|
||||
size.x > 0f ? FieldWidthUnits / size.x : 1f,
|
||||
size.y > 0f ? FieldHeightUnits / size.y : 1f,
|
||||
1f);
|
||||
}
|
||||
|
||||
private static GameWorld BuildSceneWorld(
|
||||
out SpriteRenderer playerRenderer,
|
||||
out SnackSpawner spawner,
|
||||
out Transform snacksParent)
|
||||
{
|
||||
var worldGo = new GameObject("World");
|
||||
var world = worldGo.AddComponent<GameWorld>();
|
||||
|
||||
var playerGo = new GameObject("Player");
|
||||
playerGo.transform.SetParent(worldGo.transform, false);
|
||||
playerRenderer = playerGo.AddComponent<SpriteRenderer>();
|
||||
playerRenderer.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(PlayerSpritePath);
|
||||
|
||||
var colliderRadius = 0.3f;
|
||||
if (playerRenderer.sprite == null)
|
||||
{
|
||||
Debug.LogError($"[CozyCollectorBake] Player sprite not found at {PlayerSpritePath}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Phaser setDisplaySize(82, 108) at 100 PPU -> 0.82 x 1.08 world units.
|
||||
var bounds = playerRenderer.sprite.bounds.size;
|
||||
var scale = new Vector3(0.82f / bounds.x, 1.08f / bounds.y, 1f);
|
||||
playerGo.transform.localScale = scale;
|
||||
// Phaser body is ~0.45x0.55 world units; a circle collider's world
|
||||
// radius scales by the largest transform axis.
|
||||
colliderRadius = 0.24f / Mathf.Max(scale.x, scale.y);
|
||||
}
|
||||
|
||||
var body = playerGo.AddComponent<Rigidbody2D>();
|
||||
body.bodyType = RigidbodyType2D.Kinematic;
|
||||
body.simulated = true;
|
||||
body.gravityScale = 0f;
|
||||
|
||||
// Non-trigger collider so the snacks' trigger colliders fire OnTriggerEnter2D.
|
||||
var collider = playerGo.AddComponent<CircleCollider2D>();
|
||||
collider.radius = colliderRadius;
|
||||
collider.isTrigger = false;
|
||||
|
||||
var snacksGo = new GameObject("Snacks");
|
||||
snacksGo.transform.SetParent(worldGo.transform, false);
|
||||
snacksParent = snacksGo.transform;
|
||||
|
||||
var spawnerGo = new GameObject("Spawner");
|
||||
spawnerGo.transform.SetParent(worldGo.transform, false);
|
||||
spawner = spawnerGo.AddComponent<SnackSpawner>();
|
||||
|
||||
return world;
|
||||
}
|
||||
|
||||
private static CozyCollectorController BuildSceneGameSetup(GameWorld world)
|
||||
{
|
||||
var setupGo = new GameObject("GameSetup");
|
||||
|
||||
var rudder = setupGo.AddComponent<Rudder>();
|
||||
var configuration = AssetDatabase.LoadAssetAtPath<RudderConfiguration>(ConfigurationPath);
|
||||
if (configuration == null)
|
||||
{
|
||||
Debug.LogError($"[CozyCollectorBake] RudderConfiguration not found at {ConfigurationPath}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSceneReference(rudder, "_configuration", configuration);
|
||||
}
|
||||
|
||||
var controller = setupGo.AddComponent<CozyCollectorController>();
|
||||
SetSceneReference(controller, "world", world);
|
||||
return controller;
|
||||
}
|
||||
|
||||
private static void WireSceneWorld(
|
||||
GameWorld world,
|
||||
SpriteRenderer playerRenderer,
|
||||
SnackSpawner spawner,
|
||||
CozyCollectorController controller)
|
||||
{
|
||||
SetSceneReference(world, "player", playerRenderer);
|
||||
SetSceneReference(world, "spawner", spawner);
|
||||
SetSceneReference(world, "controller", controller);
|
||||
// moveMin/moveMax keep the component defaults (-6,-3.2)/(6,3.2).
|
||||
|
||||
var scorePopupPrefab = LoadScenePrefabComponent<ScorePopupView>(ScorePopupPrefabPath);
|
||||
if (scorePopupPrefab != null)
|
||||
{
|
||||
SetSceneReference(world, "scorePopupPrefab", scorePopupPrefab);
|
||||
}
|
||||
|
||||
var collectBurstPrefab = LoadScenePrefabComponent<ParticleSystem>(CollectBurstPrefabPath);
|
||||
if (collectBurstPrefab != null)
|
||||
{
|
||||
SetSceneReference(world, "collectBurstPrefab", collectBurstPrefab);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WireSceneSpawner(SnackSpawner spawner, Transform snacksParent)
|
||||
{
|
||||
var snackPrefab = LoadScenePrefabComponent<Snack>(SnackPrefabPath);
|
||||
if (snackPrefab != null)
|
||||
{
|
||||
SetSceneReference(spawner, "prefab", snackPrefab);
|
||||
}
|
||||
|
||||
var library = AssetDatabase.LoadAssetAtPath<CozyCollectorSpriteLibrary>(SpriteLibraryPath);
|
||||
if (library == null)
|
||||
{
|
||||
Debug.LogError($"[CozyCollectorBake] Sprite library not found at {SpriteLibraryPath}. " +
|
||||
"Run Tools/LiveOps/Bake Cozy Collector (All) first.");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSceneReference(spawner, "library", library);
|
||||
}
|
||||
|
||||
SetSceneReference(spawner, "parent", snacksParent);
|
||||
// spawnMin/spawnMax keep the component defaults.
|
||||
}
|
||||
|
||||
private static void AddSceneToBuildSettings()
|
||||
{
|
||||
var scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
|
||||
foreach (var entry in scenes)
|
||||
{
|
||||
if (entry.path == ScenePath)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
scenes.Add(new EditorBuildSettingsScene(ScenePath, true));
|
||||
EditorBuildSettings.scenes = scenes.ToArray();
|
||||
}
|
||||
|
||||
private static T LoadScenePrefabComponent<T>(string path) where T : Component
|
||||
{
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
var component = prefab != null ? prefab.GetComponent<T>() : null;
|
||||
if (component == null)
|
||||
{
|
||||
Debug.LogError($"[CozyCollectorBake] Prefab with {typeof(T).Name} not found at {path}. " +
|
||||
"Run Tools/LiveOps/Bake Cozy Collector (All) first; the scene is saved with empty references.");
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
private static void SetSceneReference(Object target, string propertyName, Object value)
|
||||
{
|
||||
var serialized = new SerializedObject(target);
|
||||
var property = serialized.FindProperty(propertyName);
|
||||
if (property == null)
|
||||
{
|
||||
Debug.LogError($"[CozyCollectorBake] Property '{propertyName}' not found on {target.GetType().Name}.");
|
||||
return;
|
||||
}
|
||||
|
||||
property.objectReferenceValue = value;
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b06747933b8c458b8262e8516634b14
|
||||
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using LiveOpsExamples;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace LiveOpsExamples.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds the Cozy Collector UI: a GameObject with a PanelRenderer (PanelSettings +
|
||||
/// CozyCollectorUI.uxml) and all panel components wired through serialized
|
||||
/// references. The PanelSettings asset is created programmatically when missing.
|
||||
/// Called from <see cref="CozyCollectorBake.BakeScene"/>.
|
||||
/// </summary>
|
||||
public static partial class CozyCollectorBake
|
||||
{
|
||||
private const string UiFolderPath = "Assets/LiveOpsExamples/UI";
|
||||
private const string UiPanelSettingsPath = UiFolderPath + "/CozyCollectorPanelSettings.asset";
|
||||
private const string UiDocumentAssetPath = UiFolderPath + "/CozyCollectorUI.uxml";
|
||||
private const string UiLogRowTemplatePath = UiFolderPath + "/LogRow.uxml";
|
||||
private const string UiToastTemplatePath = UiFolderPath + "/ToastRow.uxml";
|
||||
private const string UiShopOfferRowTemplatePath = UiFolderPath + "/ShopOfferRow.uxml";
|
||||
private const string UiBackpackItemRowTemplatePath = UiFolderPath + "/BackpackItemRow.uxml";
|
||||
private const string UiLeaderboardRowTemplatePath = UiFolderPath + "/LeaderboardRow.uxml";
|
||||
|
||||
private static void BuildUi(CozyCollectorController controller, GameWorld world)
|
||||
{
|
||||
var panelSettings = GetOrCreatePanelSettings();
|
||||
var visualTree = LoadUiAsset<VisualTreeAsset>(UiDocumentAssetPath);
|
||||
|
||||
var uiGo = new GameObject("UI");
|
||||
var panelRenderer = uiGo.AddComponent<PanelRenderer>();
|
||||
panelRenderer.panelSettings = panelSettings;
|
||||
if (visualTree != null)
|
||||
{
|
||||
panelRenderer.visualTreeAsset = visualTree;
|
||||
}
|
||||
|
||||
WirePanel(uiGo.AddComponent<SessionCardPanel>(), panelRenderer, controller);
|
||||
WirePanel(uiGo.AddComponent<RemoteConfigPanel>(), panelRenderer, controller);
|
||||
|
||||
var eventInspector = uiGo.AddComponent<EventInspectorPanel>();
|
||||
WirePanel(eventInspector, panelRenderer, controller);
|
||||
WireTemplate(eventInspector, "logRowTemplate", UiLogRowTemplatePath);
|
||||
|
||||
var hud = uiGo.AddComponent<HudPanel>();
|
||||
WirePanel(hud, panelRenderer, controller);
|
||||
SetSceneReference(hud, "world", world);
|
||||
|
||||
var toasts = uiGo.AddComponent<ToastsPanel>();
|
||||
WirePanel(toasts, panelRenderer, controller);
|
||||
WireTemplate(toasts, "toastTemplate", UiToastTemplatePath);
|
||||
|
||||
WirePanel(uiGo.AddComponent<BootOverlayPanel>(), panelRenderer, controller);
|
||||
WirePanel(uiGo.AddComponent<SetupOverlayPanel>(), panelRenderer, controller);
|
||||
|
||||
var shop = uiGo.AddComponent<ShopModalPanel>();
|
||||
WirePanel(shop, panelRenderer, controller);
|
||||
WireTemplate(shop, "shopOfferRowTemplate", UiShopOfferRowTemplatePath);
|
||||
|
||||
var backpack = uiGo.AddComponent<BackpackModalPanel>();
|
||||
WirePanel(backpack, panelRenderer, controller);
|
||||
WireTemplate(backpack, "backpackItemRowTemplate", UiBackpackItemRowTemplatePath);
|
||||
var spriteLibrary = AssetDatabase.LoadAssetAtPath<CozyCollectorSpriteLibrary>(SpriteLibraryPath);
|
||||
if (spriteLibrary == null)
|
||||
{
|
||||
Debug.LogError($"[CozyCollectorBake] Sprite library not found at {SpriteLibraryPath}. " +
|
||||
"Run Tools/LiveOps/Bake Cozy Collector (All) first.");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSceneReference(backpack, "spriteLibrary", spriteLibrary);
|
||||
}
|
||||
|
||||
WirePanel(uiGo.AddComponent<OfferModalPanel>(), panelRenderer, controller);
|
||||
|
||||
var roundResult = uiGo.AddComponent<RoundResultModalPanel>();
|
||||
WirePanel(roundResult, panelRenderer, controller);
|
||||
WireTemplate(roundResult, "leaderboardRowTemplate", UiLeaderboardRowTemplatePath);
|
||||
}
|
||||
|
||||
private static void WirePanel(MonoBehaviour panel, PanelRenderer panelRenderer, CozyCollectorController controller)
|
||||
{
|
||||
SetSceneReference(panel, "panelRenderer", panelRenderer);
|
||||
SetSceneReference(panel, "controller", controller);
|
||||
}
|
||||
|
||||
private static void WireTemplate(MonoBehaviour panel, string fieldName, string templatePath)
|
||||
{
|
||||
var template = LoadUiAsset<VisualTreeAsset>(templatePath);
|
||||
if (template != null)
|
||||
{
|
||||
SetSceneReference(panel, fieldName, template);
|
||||
}
|
||||
}
|
||||
|
||||
private static T LoadUiAsset<T>(string path) where T : Object
|
||||
{
|
||||
var asset = AssetDatabase.LoadAssetAtPath<T>(path);
|
||||
if (asset == null)
|
||||
{
|
||||
Debug.LogError($"[CozyCollectorBake] {typeof(T).Name} not found at {path}. " +
|
||||
"The scene is saved with an empty reference.");
|
||||
}
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
private static PanelSettings GetOrCreatePanelSettings()
|
||||
{
|
||||
var settings = AssetDatabase.LoadAssetAtPath<PanelSettings>(UiPanelSettingsPath);
|
||||
if (settings != null)
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
|
||||
EnsureAssetFolder(UiFolderPath);
|
||||
|
||||
settings = ScriptableObject.CreateInstance<PanelSettings>();
|
||||
settings.scaleMode = PanelScaleMode.ScaleWithScreenSize;
|
||||
settings.referenceResolution = new Vector2Int(1600, 900);
|
||||
settings.screenMatchMode = PanelScreenMatchMode.MatchWidthOrHeight;
|
||||
settings.match = 0.5f;
|
||||
|
||||
var defaultTheme = LoadDefaultRuntimeTheme();
|
||||
if (defaultTheme != null)
|
||||
{
|
||||
settings.themeStyleSheet = defaultTheme;
|
||||
}
|
||||
|
||||
AssetDatabase.CreateAsset(settings, UiPanelSettingsPath);
|
||||
return settings;
|
||||
}
|
||||
|
||||
private static ThemeStyleSheet LoadDefaultRuntimeTheme()
|
||||
{
|
||||
var themeUtility = Type.GetType("UnityEditor.UIElements.ThemeUtility, UnityEditor.UIElementsModule");
|
||||
var property = themeUtility?.GetProperty("builtInDefaultRuntimeTheme", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
return property?.GetValue(null) as ThemeStyleSheet;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ffeef7dc98af46a8b8bf186f4e2073b
|
||||
Reference in New Issue
Block a user