Files
FORT-BO/Assets/CONTENT/FEATURES/MapRadar/Editor/MapRadarNetworkSetup.cs
T

290 lines
12 KiB
C#

using Unity.Netcode;
using Unity.Netcode.Components;
using UnityEditor;
using UnityEngine;
public static class MapRadarNetworkSetup
{
private const string LeverPrefabPath =
"Assets/CONTENT/Game/Prefabs/Interaction/PhysicalLever.prefab";
[MenuItem("FORT-BO/Map Radar/Configure Network Prototype")]
public static void ConfigureNetworkPrototype()
{
MapController mapController = Object.FindFirstObjectByType<MapController>();
if (mapController == null)
{
Debug.LogError("MapController was not found in the open scene.");
return;
}
MapCoordinateSystem coordinateSystem = mapController.CoordinateSystem;
if (coordinateSystem == null || !coordinateSystem.HasValidCorners())
{
Debug.LogError("MapCoordinateSystem is not configured.", mapController);
return;
}
Undo.RegisterFullObjectHierarchyUndo(mapController.transform.root.gameObject,
"Configure network map prototype");
GetOrAdd<NetworkObject>(mapController.gameObject);
NetworkBaseHealth baseHealth = GetOrAdd<NetworkBaseHealth>(mapController.gameObject);
MapControlBinder binder = GetOrAdd<MapControlBinder>(mapController.gameObject);
Transform controlsRoot = GetOrCreateChild(
mapController.transform.root,
"Map Controls");
MapBasis basis = CreateBasis(coordinateSystem);
NetworkPhysicalWheelInteractable horizontalWheel = CreateWheel(
controlsRoot,
"Map Horizontal Wheel",
basis.ControlCenter - basis.Horizontal * 1.8f,
basis.Rotation,
"Aim horizontal");
NetworkPhysicalWheelInteractable verticalWheel = CreateWheel(
controlsRoot,
"Map Vertical Wheel",
basis.ControlCenter - basis.Horizontal * 0.6f,
basis.Rotation,
"Aim vertical");
NetworkPhysicalLeverInteractable fireLever = CreateFireLever(
controlsRoot,
basis.ControlCenter + basis.Horizontal * 0.8f,
basis.Rotation);
NetworkButtonInteractable scanButton = CreateScanButton(
controlsRoot,
basis.ControlCenter + basis.Horizontal * 2f,
basis.Rotation);
SerializedObject map = new(mapController);
SetObject(map, "roundGameLoop",
Object.FindFirstObjectByType<RoundGameLoopController>());
SetObject(map, "baseHealth", baseHealth);
map.ApplyModifiedPropertiesWithoutUndo();
SerializedObject binding = new(binder);
SetObject(binding, "mapController", mapController);
SetObject(binding, "horizontalWheel", horizontalWheel);
SetObject(binding, "verticalWheel", verticalWheel);
SetObject(binding, "fireLever", fireLever);
SetObject(binding, "scanButton", scanButton);
binding.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(mapController);
EditorUtility.SetDirty(binder);
EditorUtility.SetDirty(baseHealth);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(
mapController.gameObject.scene);
UnityEditor.SceneManagement.EditorSceneManager.SaveOpenScenes();
Selection.activeObject = controlsRoot.gameObject;
Debug.Log(
"[Map] Network prototype configured: X/Y wheels, fire lever, " +
"scan button and base health are connected.",
mapController);
}
private static NetworkPhysicalWheelInteractable CreateWheel(
Transform parent,
string name,
Vector3 position,
Quaternion rotation,
string interactionLabel)
{
Transform root = GetOrCreateChild(parent, name);
root.SetPositionAndRotation(position, rotation);
root.localScale = Vector3.one;
GetOrAdd<NetworkObject>(root.gameObject);
NetworkTransform networkTransform = GetOrAdd<NetworkTransform>(root.gameObject);
networkTransform.AuthorityMode = NetworkTransform.AuthorityModes.Server;
networkTransform.UseUnreliableDeltas = true;
Transform legacyVisual = root.Find("Wheel Visual");
Transform visual = root.Find("Wheel Pivot");
if (visual == null)
{
if (legacyVisual != null)
Object.DestroyImmediate(legacyVisual.gameObject);
visual = new GameObject("Wheel Pivot").transform;
visual.SetParent(root, false);
}
visual.localPosition = Vector3.zero;
visual.localRotation = Quaternion.identity;
visual.localScale = Vector3.one;
Transform disk = visual.Find("Wheel Disk");
if (disk == null)
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
cylinder.name = "Wheel Disk";
disk = cylinder.transform;
disk.SetParent(visual, false);
}
disk.localPosition = Vector3.zero;
disk.localRotation = Quaternion.Euler(90f, 0f, 0f);
disk.localScale = new Vector3(0.48f, 0.08f, 0.48f);
Transform handle = visual.Find("Handle");
if (handle == null)
{
GameObject handleObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
handleObject.name = "Handle";
handle = handleObject.transform;
handle.SetParent(visual, false);
Object.DestroyImmediate(handleObject.GetComponent<Collider>());
}
handle.localPosition = new Vector3(0f, 0.65f, 0f);
handle.localRotation = Quaternion.identity;
handle.localScale = new Vector3(0.14f, 0.4f, 0.14f);
NetworkPhysicalWheelInteractable wheel =
GetOrAdd<NetworkPhysicalWheelInteractable>(root.gameObject);
SerializedObject serializedWheel = new(wheel);
SetObject(serializedWheel, "wheelVisual", visual);
SetVector3(serializedWheel, "neutralLocalEulerAngles", Vector3.zero);
SetString(serializedWheel, "interactionLabel", interactionLabel);
SetVector3(serializedWheel, "localRotationAxis", Vector3.forward);
SetVector3(serializedWheel, "localReferenceDirection", Vector3.up);
SetFloat(serializedWheel, "minimumAngle", -180f);
SetFloat(serializedWheel, "maximumAngle", 180f);
SetFloat(serializedWheel, "initialNormalizedValue", 0.5f);
SetFloat(serializedWheel, "maximumInteractionDistance", 3.5f);
SetFloat(serializedWheel, "maximumControlDistance", 4f);
serializedWheel.ApplyModifiedPropertiesWithoutUndo();
return wheel;
}
private static NetworkPhysicalLeverInteractable CreateFireLever(
Transform parent,
Vector3 position,
Quaternion rotation)
{
Transform existing = parent.Find("Map Fire Lever");
GameObject root;
if (existing != null)
{
root = existing.gameObject;
}
else
{
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(LeverPrefabPath);
if (prefab == null)
throw new MissingReferenceException($"Lever prefab not found: {LeverPrefabPath}");
root = (GameObject)PrefabUtility.InstantiatePrefab(prefab, parent);
root.name = "Map Fire Lever";
}
root.transform.SetPositionAndRotation(position, rotation);
root.transform.localScale = Vector3.one * 0.55f;
NetworkPhysicalLeverInteractable lever =
root.GetComponent<NetworkPhysicalLeverInteractable>();
SerializedObject serializedLever = new(lever);
SetString(serializedLever, "interactionLabel", "Fire");
SetBool(serializedLever, "initialPositionIsMaximum", false);
SetBool(serializedLever, "returnToInitialPositionAfterSwitch", true);
SetFloat(serializedLever, "automaticReturnDelay", 0.2f);
SetFloat(serializedLever, "maximumInteractionDistance", 3.5f);
SetFloat(serializedLever, "maximumControlDistance", 4f);
serializedLever.ApplyModifiedPropertiesWithoutUndo();
return lever;
}
private static NetworkButtonInteractable CreateScanButton(
Transform parent,
Vector3 position,
Quaternion rotation)
{
Transform root = GetOrCreateChild(parent, "Map Scan Button");
root.SetPositionAndRotation(position, rotation);
root.localScale = Vector3.one;
GetOrAdd<NetworkObject>(root.gameObject);
NetworkTransform networkTransform = GetOrAdd<NetworkTransform>(root.gameObject);
networkTransform.AuthorityMode = NetworkTransform.AuthorityModes.Server;
Transform visual = root.Find("Button Visual");
if (visual == null)
{
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "Button Visual";
visual = cube.transform;
visual.SetParent(root, false);
}
visual.localPosition = Vector3.zero;
visual.localRotation = Quaternion.identity;
visual.localScale = new Vector3(0.55f, 0.55f, 0.18f);
NetworkButtonInteractable button = GetOrAdd<NetworkButtonInteractable>(root.gameObject);
SerializedObject serializedButton = new(button);
SetString(serializedButton, "interactionLabel", "Scan map");
SetFloat(serializedButton, "maximumInteractionDistance", 3.5f);
serializedButton.ApplyModifiedPropertiesWithoutUndo();
return button;
}
private static MapBasis CreateBasis(MapCoordinateSystem coordinates)
{
Vector3 bottomLeft = coordinates.BottomLeft.position;
Vector3 horizontalEdge = coordinates.BottomRight.position - bottomLeft;
Vector3 verticalEdge = coordinates.TopLeft.position - bottomLeft;
Vector3 horizontal = horizontalEdge.normalized;
Vector3 vertical = verticalEdge.normalized;
Vector3 normal = Vector3.Cross(horizontal, vertical).normalized;
Vector3 bottomCenter = bottomLeft + horizontalEdge * 0.5f;
Vector3 controlCenter = bottomCenter - vertical * 0.85f + normal * 0.55f;
return new MapBasis(
horizontal,
controlCenter,
Quaternion.LookRotation(normal, vertical));
}
private static Transform GetOrCreateChild(Transform parent, string name)
{
Transform existing = parent.Find(name);
if (existing != null)
return existing;
GameObject child = new(name);
child.transform.SetParent(parent, false);
Undo.RegisterCreatedObjectUndo(child, $"Create {name}");
return child.transform;
}
private static T GetOrAdd<T>(GameObject target) where T : Component
{
T component = target.GetComponent<T>();
return component != null ? component : Undo.AddComponent<T>(target);
}
private static void SetObject(SerializedObject target, string property, Object value) =>
target.FindProperty(property).objectReferenceValue = value;
private static void SetString(SerializedObject target, string property, string value) =>
target.FindProperty(property).stringValue = value;
private static void SetFloat(SerializedObject target, string property, float value) =>
target.FindProperty(property).floatValue = value;
private static void SetBool(SerializedObject target, string property, bool value) =>
target.FindProperty(property).boolValue = value;
private static void SetVector3(SerializedObject target, string property, Vector3 value) =>
target.FindProperty(property).vector3Value = value;
private readonly struct MapBasis
{
public readonly Vector3 Horizontal;
public readonly Vector3 ControlCenter;
public readonly Quaternion Rotation;
public MapBasis(Vector3 horizontal, Vector3 controlCenter, Quaternion rotation)
{
Horizontal = horizontal;
ControlCenter = controlCenter;
Rotation = rotation;
}
}
}