271 lines
9.6 KiB
C#
271 lines
9.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using MCPForUnity.Editor.Helpers;
|
|
using MCPForUnity.Runtime.Helpers;
|
|
using Newtonsoft.Json.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace MCPForUnity.Editor.Tools.Cameras
|
|
{
|
|
internal static class CameraHelpers
|
|
{
|
|
private static bool? _hasCinemachine;
|
|
private static Type _cmCameraType;
|
|
private static Type _cmBrainType;
|
|
|
|
internal static bool HasCinemachine
|
|
{
|
|
get
|
|
{
|
|
if (_hasCinemachine == null)
|
|
DetectCinemachine();
|
|
return _hasCinemachine.Value;
|
|
}
|
|
}
|
|
|
|
internal static Type CinemachineCameraType
|
|
{
|
|
get
|
|
{
|
|
if (_hasCinemachine == null)
|
|
DetectCinemachine();
|
|
return _cmCameraType;
|
|
}
|
|
}
|
|
|
|
internal static Type CinemachineBrainType
|
|
{
|
|
get
|
|
{
|
|
if (_hasCinemachine == null)
|
|
DetectCinemachine();
|
|
return _cmBrainType;
|
|
}
|
|
}
|
|
|
|
private static void DetectCinemachine()
|
|
{
|
|
_cmCameraType = UnityTypeResolver.ResolveComponent("CinemachineCamera");
|
|
_cmBrainType = UnityTypeResolver.ResolveComponent("CinemachineBrain");
|
|
_hasCinemachine = _cmCameraType != null && _cmBrainType != null;
|
|
}
|
|
|
|
internal static string GetCinemachineVersion()
|
|
{
|
|
if (!HasCinemachine || _cmCameraType == null)
|
|
return null;
|
|
|
|
try
|
|
{
|
|
var assembly = _cmCameraType.Assembly;
|
|
var version = assembly.GetName().Version;
|
|
return version?.ToString();
|
|
}
|
|
catch
|
|
{
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
internal static GameObject FindTargetGameObject(JObject @params)
|
|
{
|
|
var targetToken = @params["target"];
|
|
if (targetToken == null)
|
|
return null;
|
|
|
|
string searchMethod = ParamCoercion.CoerceString(
|
|
@params["searchMethod"] ?? @params["search_method"], "by_name");
|
|
|
|
if (targetToken.Type == JTokenType.Integer)
|
|
{
|
|
int instanceId = targetToken.Value<int>();
|
|
return GameObjectLookup.FindById(instanceId);
|
|
}
|
|
|
|
string targetStr = targetToken.ToString();
|
|
if (int.TryParse(targetStr, out int parsedId))
|
|
{
|
|
var byId = GameObjectLookup.FindById(parsedId);
|
|
if (byId != null) return byId;
|
|
}
|
|
|
|
return GameObjectLookup.FindByTarget(targetToken, searchMethod, true);
|
|
}
|
|
|
|
internal static GameObject ResolveGameObjectRef(object reference)
|
|
{
|
|
if (reference == null) return null;
|
|
|
|
if (reference is JToken jt)
|
|
{
|
|
if (jt.Type == JTokenType.Integer)
|
|
return GameObjectLookup.FindById(jt.Value<int>());
|
|
if (jt.Type == JTokenType.String)
|
|
{
|
|
string str = jt.ToString();
|
|
if (int.TryParse(str, out int id))
|
|
{
|
|
var byId = GameObjectLookup.FindById(id);
|
|
if (byId != null) return byId;
|
|
}
|
|
return GameObjectLookup.FindByTarget(jt, "by_name", true);
|
|
}
|
|
}
|
|
|
|
if (reference is string s)
|
|
{
|
|
if (int.TryParse(s, out int id))
|
|
{
|
|
var byId = GameObjectLookup.FindById(id);
|
|
if (byId != null) return byId;
|
|
}
|
|
var ids = GameObjectLookup.SearchGameObjects(
|
|
GameObjectLookup.SearchMethod.ByName, s, includeInactive: true, maxResults: 1);
|
|
return ids.Count > 0 ? GameObjectLookup.FindById(ids[0]) : null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
internal static Component FindCinemachineCamera(JObject @params)
|
|
{
|
|
if (!HasCinemachine) return null;
|
|
var go = FindTargetGameObject(@params);
|
|
return go != null ? go.GetComponent(CinemachineCameraType) : null;
|
|
}
|
|
|
|
internal static Component FindBrain()
|
|
{
|
|
if (!HasCinemachine || _cmBrainType == null)
|
|
return null;
|
|
|
|
return UnityFindObjectsCompat.FindAny(_cmBrainType) as Component;
|
|
}
|
|
|
|
internal static UnityEngine.Camera FindMainCamera()
|
|
{
|
|
var main = UnityEngine.Camera.main;
|
|
if (main != null) return main;
|
|
|
|
var allCams = UnityFindObjectsCompat.FindAll<UnityEngine.Camera>();
|
|
return allCams.Length > 0 ? allCams[0] : null;
|
|
}
|
|
|
|
internal static JObject ExtractProperties(JObject @params)
|
|
{
|
|
var props = @params["properties"] as JObject;
|
|
if (props != null) return props;
|
|
|
|
var propsStr = ParamCoercion.CoerceString(@params["properties"], null);
|
|
if (propsStr != null)
|
|
{
|
|
try { return JObject.Parse(propsStr); }
|
|
catch { return null; }
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
internal static object GetReflectionProperty(Component component, string propertyName)
|
|
{
|
|
if (component == null) return null;
|
|
var type = component.GetType();
|
|
var prop = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
|
|
return prop?.GetValue(component);
|
|
}
|
|
|
|
/// <summary>Read priority int from a CinemachineCamera component via SerializedObject.</summary>
|
|
internal static int ReadCinemachinePriority(Component cmCamera)
|
|
{
|
|
if (cmCamera == null) return 0;
|
|
using var so = new SerializedObject(cmCamera);
|
|
var priorityProp = so.FindProperty("Priority");
|
|
if (priorityProp == null) return 0;
|
|
var enabledProp = priorityProp.FindPropertyRelative("Enabled");
|
|
var valueProp = priorityProp.FindPropertyRelative("m_Value");
|
|
if (enabledProp != null && !enabledProp.boolValue) return 0;
|
|
return valueProp?.intValue ?? 0;
|
|
}
|
|
|
|
internal static bool SetReflectionProperty(Component component, string propertyName, object value)
|
|
{
|
|
if (component == null) return false;
|
|
var type = component.GetType();
|
|
var prop = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
|
|
if (prop == null || !prop.CanWrite) return false;
|
|
prop.SetValue(component, value);
|
|
return true;
|
|
}
|
|
|
|
internal static void SetTransformTarget(Component cmCamera, string propertyName, JToken targetRef)
|
|
{
|
|
if (cmCamera == null) return;
|
|
|
|
if (targetRef == null || targetRef.Type == JTokenType.Null)
|
|
{
|
|
SetReflectionProperty(cmCamera, propertyName, null);
|
|
return;
|
|
}
|
|
|
|
var go = ResolveGameObjectRef(targetRef);
|
|
if (go != null)
|
|
SetReflectionProperty(cmCamera, propertyName, go.transform);
|
|
}
|
|
|
|
internal static Type ResolveComponentType(string typeName)
|
|
{
|
|
return UnityTypeResolver.ResolveComponent(typeName);
|
|
}
|
|
|
|
internal static Component GetPipelineComponent(Component cmCamera, string stageName)
|
|
{
|
|
if (cmCamera == null) return null;
|
|
var type = cmCamera.GetType();
|
|
|
|
// CinemachineCamera.GetCinemachineComponent(CinemachineCore.Stage stage)
|
|
var stageEnumType = type.Assembly.GetType("Unity.Cinemachine.CinemachineCore+Stage")
|
|
?? type.Assembly.GetType("Unity.Cinemachine.CinemachineCore")?.GetNestedType("Stage");
|
|
|
|
if (stageEnumType == null) return null;
|
|
|
|
object stageEnum;
|
|
try { stageEnum = Enum.Parse(stageEnumType, stageName, true); }
|
|
catch { return null; }
|
|
|
|
var method = type.GetMethod("GetCinemachineComponent",
|
|
BindingFlags.Public | BindingFlags.Instance,
|
|
null, new[] { stageEnumType }, null);
|
|
|
|
if (method == null) return null;
|
|
return method.Invoke(cmCamera, new[] { stageEnum }) as Component;
|
|
}
|
|
|
|
internal static string GetFallbackSuggestion(string action)
|
|
{
|
|
return action switch
|
|
{
|
|
"set_body" or "set_aim" => "Use 'set_lens' and 'set_target' for basic camera configuration.",
|
|
"set_blend" => "Without Cinemachine, switch cameras by enabling/disabling Camera components.",
|
|
"set_noise" => "Camera shake without Cinemachine requires a custom script.",
|
|
"ensure_brain" => "CinemachineBrain requires the Cinemachine package. Basic Camera does not need a Brain.",
|
|
"get_brain_status" => "No CinemachineBrain available. Cinemachine package not installed.",
|
|
_ => "Install Cinemachine via Window > Package Manager."
|
|
};
|
|
}
|
|
|
|
internal static void MarkDirty(GameObject go)
|
|
{
|
|
if (go == null) return;
|
|
EditorUtility.SetDirty(go);
|
|
var prefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage();
|
|
if (prefabStage != null)
|
|
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(prefabStage.scene);
|
|
else
|
|
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(go.scene);
|
|
}
|
|
}
|
|
}
|