Files
rudder-unity-sdk/Assets/LiveOpsExamples/Editor/CozyCollectorBake.Scene.cs
T

259 lines
9.2 KiB
C#
Raw Normal View History

2026-08-12 14:03:44 +03:00
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();
}
}
}