using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using PinballUncountable;
namespace PinballUncountable.Editor
{
///
/// 在 Play 模式下录制 Dome 内小球的物理轨迹,烘焙为可回放数据。
/// 使用方式:进入 Play 模式 → 选中带 PinballUncountableDome 的物体 → 菜单 Pinball Uncountable / Bake Dome Trajectories
///
public static class PinballUncountableDomeBaker
{
private const float SampleInterval = 0.02f; // 50fps,与回放一致
private const float BakeDuration = 5f;
private static PinballUncountableDome _dome;
private static List[] _samples;
private static float _nextSampleTime;
private static float _bakeEndTime;
private static int _seed;
[MenuItem("Pinball Uncountable/Bake Dome Trajectories", true)]
private static bool ValidateBake()
{
return Application.isPlaying && GetDomeFromSelection() != null;
}
[MenuItem("Pinball Uncountable/Bake Dome Trajectories")]
private static void Bake()
{
var dome = GetDomeFromSelection();
if (dome == null)
{
Debug.LogWarning("[DomeBaker] 请先在 Hierarchy 中选中带有 PinballUncountableDome 的物体,并处于 Play 模式。");
return;
}
StartBake(dome, (int)System.DateTime.Now.Ticks);
}
///
/// 使用指定种子烘焙一帧(可用于批量生成多组变体)
///
public static void BakeWithSeed(PinballUncountableDome dome, int seed)
{
if (dome == null || !Application.isPlaying)
{
Debug.LogWarning("[DomeBaker] BakeWithSeed 需在 Play 模式下且 dome 非空。");
return;
}
StartBake(dome, seed);
}
private static PinballUncountableDome GetDomeFromSelection()
{
var go = Selection.activeGameObject;
if (go == null) return null;
return go.GetComponent() ?? go.GetComponentInChildren();
}
private static void StartBake(PinballUncountableDome dome, int seed)
{
CleanupBakeState();
_dome = dome;
_seed = seed;
var so = new SerializedObject(dome);
var ballsProp = so.FindProperty("balls");
var xRange = so.FindProperty("xRange").floatValue;
var yRange = so.FindProperty("yRange").floatValue;
int n = ballsProp.arraySize;
if (n == 0)
{
Debug.LogError("[DomeBaker] Dome 的 balls 数组为空。");
CleanupBakeState();
return;
}
_dome.SetBakePhysicsCollisionEnabled(true);
Random.InitState(seed);
var rbs = new Rigidbody2D[n];
for (int i = 0; i < n; i++)
{
var refProp = ballsProp.GetArrayElementAtIndex(i);
rbs[i] = refProp.objectReferenceValue as Rigidbody2D;
if (rbs[i] != null)
{
rbs[i].simulated = true;
rbs[i].velocity = Vector2.zero;
rbs[i].angularVelocity = 0f;
rbs[i].AddForce(new Vector2(Random.Range(-xRange, xRange), Random.Range(0, yRange)), ForceMode2D.Impulse);
}
}
_samples = new List[n];
for (int i = 0; i < n; i++)
_samples[i] = new List();
_nextSampleTime = Time.time;
_bakeEndTime = Time.time + BakeDuration;
EditorApplication.update += RecordUpdate;
Debug.Log($"[DomeBaker] 开始录制 Dome 轨迹,种子={seed},时长={BakeDuration}s,球数={n}");
}
private static void RecordUpdate()
{
if (_dome == null || _samples == null)
{
CleanupBakeState();
return;
}
var so = new SerializedObject(_dome);
var ballsProp = so.FindProperty("balls");
int n = ballsProp.arraySize;
while (Time.time >= _nextSampleTime && _nextSampleTime < _bakeEndTime)
{
for (int i = 0; i < n && i < _samples.Length; i++)
{
var rb = ballsProp.GetArrayElementAtIndex(i).objectReferenceValue as Rigidbody2D;
if (rb != null)
_samples[i].Add(rb.transform.position);
}
_nextSampleTime += SampleInterval;
}
if (Time.time >= _bakeEndTime)
{
EditorApplication.update -= RecordUpdate;
FinishBake();
}
}
private static void FinishBake()
{
if (_dome == null || _samples == null) return;
var variant = new DomeKickVariant
{
trajectories = new SerializableDomeTrajectory[_samples.Length]
};
for (int i = 0; i < _samples.Length; i++)
variant.trajectories[i] = new SerializableDomeTrajectory(_samples[i].ToArray());
string path = "Assets/ABPackage/pkgDownloads/EventPinballUncountable/EventPinballUncountableCowBoy/Data/DomeBaked.asset";
var existing = AssetDatabase.LoadAssetAtPath(path);
if (existing != null)
{
var list = new List(existing.variants ?? System.Array.Empty());
list.Add(variant);
existing.variants = list.ToArray();
EditorUtility.SetDirty(existing);
AssetDatabase.SaveAssets();
Debug.Log($"[DomeBaker] 已追加 1 组轨迹到 {path},当前共 {existing.variants.Length} 组。");
}
else
{
var asset = ScriptableObject.CreateInstance();
asset.variants = new[] { variant };
AssetDatabase.CreateAsset(asset, path);
AssetDatabase.SaveAssets();
Debug.Log($"[DomeBaker] 已创建烘焙资源: {path},1 组轨迹。将此项赋给 Dome 的 Baked Data 即可使用回放。");
}
CleanupBakeState();
}
private static void CleanupBakeState()
{
EditorApplication.update -= RecordUpdate;
if (_dome != null)
{
_dome.StopBalls();
_dome.SetBakePhysicsCollisionEnabled(false);
}
_dome = null;
_samples = null;
}
}
}