Map mehanic ready to multiplayer
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user