290 lines
12 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|