Initial commit
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user