Squashed commit of the following:

commit 428243f4a6
Author: AsanoRema <asanorema2036@gmail.com>
Date:   Sat Aug 29 13:43:13 2026 +0300

    Squashed commit of the following:

    commit 68e78fa464
    Author: AsanoRema <asanorema2036@gmail.com>
    Date:   Sat Aug 29 13:41:49 2026 +0300

        Updated .gitignore

commit 9f87122a34
Author: AsanoRema <asanorema2036@gmail.com>
Date:   Sat Aug 29 13:34:57 2026 +0300

    Added Dohya

commit f6a7547e84
Author: AsanoRema <asanorema2036@gmail.com>
Date:   Fri Aug 21 17:59:13 2026 +0300

    Fixed

commit b98901c366
Author: AsanoRema <asanorema2036@gmail.com>
Date:   Tue Aug 18 17:37:22 2026 +0300

    Added new particle Package

commit bb2f4a3b16
Author: AsanoRema <asanorema2036@gmail.com>
Date:   Tue Aug 18 17:17:48 2026 +0300

    Map mehanic ready to multiplayer

commit c01ff800f6
Author: AsanoRema <asanorema2036@gmail.com>
Date:   Tue Aug 18 16:16:19 2026 +0300

    Squashed commit of the following:

    commit 173bbb165c
    Author: henjkey <henjkey@gmail.com>
    Date:   Fri Aug 14 18:51:23 2026 +0500

        Mappp

    commit d559ab338a
    Author: henjkey <henjkey@gmail.com>
    Date:   Thu Aug 13 18:42:41 2026 +0500

        Map

        Controller
        Coordinates
        Target System
        Crosshair System
        Radar System
This commit is contained in:
2026-08-29 13:45:55 +03:00
parent 68e78fa464
commit ffa1064063
3344 changed files with 4722286 additions and 105458 deletions
@@ -0,0 +1,167 @@
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(MapCoordinateSystem))]
public sealed class MapCoordinateSystemEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
EditorGUILayout.HelpBox(
"Select MapCoordinates to display its grid and live crosshair coordinates in the Scene view. " +
"This visualization is not rendered in the Game view or in builds.",
MessageType.Info);
}
private void OnSceneGUI()
{
MapCoordinateSystem coordinateSystem = (MapCoordinateSystem)target;
if (!coordinateSystem.ShowSceneVisualization || !coordinateSystem.HasValidCorners())
return;
DrawGrid(coordinateSystem);
DrawCorners(coordinateSystem);
DrawCrosshairs(coordinateSystem);
DrawTargets(coordinateSystem);
DrawPlayers(coordinateSystem);
}
private static void DrawGrid(MapCoordinateSystem coordinateSystem)
{
int divisions = coordinateSystem.GridDivisions;
GUIStyle labelStyle = CreateLabelStyle(coordinateSystem.BoundaryColor);
for (int index = 0; index <= divisions; index++)
{
float normalized = (float)index / divisions;
Vector3 verticalStart = Vector3.Lerp(
coordinateSystem.BottomLeft.position,
coordinateSystem.BottomRight.position,
normalized);
Vector3 verticalEnd = Vector3.Lerp(
coordinateSystem.TopLeft.position,
coordinateSystem.TopRight.position,
normalized);
Vector3 horizontalStart = Vector3.Lerp(
coordinateSystem.BottomLeft.position,
coordinateSystem.TopLeft.position,
normalized);
Vector3 horizontalEnd = Vector3.Lerp(
coordinateSystem.BottomRight.position,
coordinateSystem.TopRight.position,
normalized);
bool isBoundary = index == 0 || index == divisions;
Handles.color = isBoundary ? coordinateSystem.BoundaryColor : coordinateSystem.GridColor;
Handles.DrawLine(verticalStart, verticalEnd);
Handles.DrawLine(horizontalStart, horizontalEnd);
if (!coordinateSystem.ShowCoordinateLabels)
continue;
float horizontalCoordinate = coordinateSystem.MaximumCoordinates.x * normalized;
float verticalCoordinate = coordinateSystem.MaximumCoordinates.y * normalized;
Handles.Label(verticalStart, horizontalCoordinate.ToString("0.##"), labelStyle);
Handles.Label(horizontalStart, verticalCoordinate.ToString("0.##"), labelStyle);
}
}
private static void DrawCorners(MapCoordinateSystem coordinateSystem)
{
Handles.color = coordinateSystem.BoundaryColor;
DrawCorner(coordinateSystem.BottomLeft, "(0, 0)", coordinateSystem);
DrawCorner(
coordinateSystem.BottomRight,
$"({coordinateSystem.MaximumCoordinates.x:0.##}, 0)",
coordinateSystem);
DrawCorner(
coordinateSystem.TopLeft,
$"(0, {coordinateSystem.MaximumCoordinates.y:0.##})",
coordinateSystem);
DrawCorner(
coordinateSystem.TopRight,
$"({coordinateSystem.MaximumCoordinates.x:0.##}, " +
$"{coordinateSystem.MaximumCoordinates.y:0.##})",
coordinateSystem);
}
private static void DrawCorner(
Transform corner,
string label,
MapCoordinateSystem coordinateSystem)
{
float size = coordinateSystem.MarkerSize * HandleUtility.GetHandleSize(corner.position);
Handles.SphereHandleCap(0, corner.position, Quaternion.identity, size, EventType.Repaint);
if (coordinateSystem.ShowCoordinateLabels)
Handles.Label(corner.position, label, CreateLabelStyle(coordinateSystem.BoundaryColor));
}
private static void DrawCrosshairs(MapCoordinateSystem coordinateSystem)
{
MapController[] controllers =
Object.FindObjectsByType<MapController>(FindObjectsSortMode.None);
foreach (MapController controller in controllers)
{
if (controller.CoordinateSystem != coordinateSystem || controller.CrosshairVisual == null)
continue;
Vector3 position = controller.CrosshairVisual.position;
Vector2 coordinates = coordinateSystem.WorldPositionToCoordinates(position);
float size = coordinateSystem.MarkerSize * 1.35f * HandleUtility.GetHandleSize(position);
Handles.color = Color.yellow;
Handles.SphereHandleCap(0, position, Quaternion.identity, size, EventType.Repaint);
Handles.Label(
position,
$"Crosshair ({coordinates.x:0.00}, {coordinates.y:0.00})",
CreateLabelStyle(Color.yellow));
}
}
private static void DrawTargets(MapCoordinateSystem coordinateSystem)
{
if (!coordinateSystem.ShowCoordinateLabels)
return;
MapTarget[] targets = Object.FindObjectsByType<MapTarget>(FindObjectsSortMode.None);
foreach (MapTarget mapTarget in targets)
{
if (mapTarget.CoordinateSystem != null && mapTarget.CoordinateSystem != coordinateSystem)
continue;
Vector3 position = mapTarget.transform.position;
Vector2 coordinates = coordinateSystem.WorldPositionToCoordinates(position);
Handles.Label(
position,
$"{mapTarget.name} ({coordinates.x:0.00}, {coordinates.y:0.00})",
CreateLabelStyle(Color.red));
}
}
private static void DrawPlayers(MapCoordinateSystem coordinateSystem)
{
MapPlayer[] players = Object.FindObjectsByType<MapPlayer>(FindObjectsSortMode.None);
foreach (MapPlayer mapPlayer in players)
{
if (mapPlayer.CoordinateSystem != null && mapPlayer.CoordinateSystem != coordinateSystem)
continue;
Vector3 position = mapPlayer.transform.position;
Vector2 coordinates = coordinateSystem.WorldPositionToCoordinates(position);
Handles.Label(
position,
$"MapPlayer ({coordinates.x:0.00}, {coordinates.y:0.00})",
CreateLabelStyle(Color.green));
}
}
private static GUIStyle CreateLabelStyle(Color color)
{
GUIStyle style = new(EditorStyles.boldLabel);
style.normal.textColor = color;
return style;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c020017978644e9be71242ece0e6011
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,307 @@
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";
private const string EnemyBalancePath =
"Assets/CONTENT/FEATURES/MapRadar/Configs/MapEnemyBalance.asset";
[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);
MapEnemyBalanceConfig enemyBalance = GetOrCreateEnemyBalance();
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);
SetObject(map, "enemyBalance", enemyBalance);
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, enemy balance and base health are connected.",
mapController);
}
private static MapEnemyBalanceConfig GetOrCreateEnemyBalance()
{
MapEnemyBalanceConfig config =
AssetDatabase.LoadAssetAtPath<MapEnemyBalanceConfig>(EnemyBalancePath);
if (config != null)
return config;
config = ScriptableObject.CreateInstance<MapEnemyBalanceConfig>();
AssetDatabase.CreateAsset(config, EnemyBalancePath);
EditorUtility.SetDirty(config);
AssetDatabase.SaveAssets();
return config;
}
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;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3807965ea93055647ad20aa098cd2032
@@ -0,0 +1,25 @@
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(MapTargetController))]
public sealed class MapTargetControllerEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
EditorGUILayout.Space();
using (new EditorGUI.DisabledScope(!Application.isPlaying))
{
if (GUILayout.Button("Start Next Wave"))
((MapTargetController)target).StartNextWave();
}
if (!Application.isPlaying)
{
EditorGUILayout.HelpBox(
"Start Next Wave becomes available in Play Mode.",
MessageType.Info);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 09f50d05321d420f86622f0dc7a6fa14