chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
313
Assets/Scripts/Managers/MinimapObjectMarker.cs
Normal file
313
Assets/Scripts/Managers/MinimapObjectMarker.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// MinimapObjectMarker - Creates minimap markers for cash bags and vaults.
|
||||
/// Uses different shapes than character markers for differentiation.
|
||||
/// CashBag = Square, Vaults = Diamond
|
||||
///
|
||||
/// IMPORTANT: You MUST create a layer called "Minimap" in Unity Project Settings
|
||||
/// and configure your Main Camera to NOT render this layer.
|
||||
/// </summary>
|
||||
public class MinimapObjectMarker : MonoBehaviour
|
||||
{
|
||||
public enum ObjectType
|
||||
{
|
||||
CashBag,
|
||||
PlayerVault,
|
||||
EnemyVault
|
||||
}
|
||||
|
||||
[Header("Marker Settings")]
|
||||
[SerializeField] private float heightOffset = 3f;
|
||||
[SerializeField] private float markerSize = 5f;
|
||||
[SerializeField] private ObjectType objectType = ObjectType.CashBag;
|
||||
|
||||
[Header("Colors")]
|
||||
[SerializeField] private Color cashBagColor = new Color(1f, 0.85f, 0f, 1f); // Gold/Yellow
|
||||
[SerializeField] private Color playerVaultColor = new Color(0.2f, 0.8f, 0.2f, 1f); // Green
|
||||
[SerializeField] private Color enemyVaultColor = new Color(0.9f, 0.2f, 0.2f, 1f); // Red
|
||||
|
||||
[Header("Layer Settings")]
|
||||
[SerializeField] private string minimapLayerName = "Minimap";
|
||||
|
||||
private GameObject markerObject;
|
||||
private MeshRenderer markerRenderer;
|
||||
private bool hasConfiguredLayer = false;
|
||||
private bool _markerCreated = false;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
AutoDetectType();
|
||||
|
||||
if (!_markerCreated)
|
||||
CreateMarker();
|
||||
|
||||
UpdateMarkerColor();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// Re-create marker if it was destroyed (e.g. pooled object returned and re-enabled)
|
||||
if (markerObject == null)
|
||||
{
|
||||
_markerCreated = false;
|
||||
AutoDetectType();
|
||||
CreateMarker();
|
||||
UpdateMarkerColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
markerObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// Hide marker when the source object is disabled (pooled/despawned)
|
||||
if (markerObject != null)
|
||||
markerObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void AutoDetectType()
|
||||
{
|
||||
// Check if this is a CashBag
|
||||
if (GetComponent<CashBag>() != null)
|
||||
{
|
||||
objectType = ObjectType.CashBag;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a TeamVault
|
||||
TeamVault vault = GetComponent<TeamVault>();
|
||||
if (vault != null)
|
||||
{
|
||||
objectType = vault.TeamTag == "Player" ? ObjectType.PlayerVault : ObjectType.EnemyVault;
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (markerObject == null) return;
|
||||
|
||||
// Ensure layer is correctly set
|
||||
if (!hasConfiguredLayer)
|
||||
{
|
||||
ConfigureLayer();
|
||||
}
|
||||
|
||||
// Marker is parented, so local position keeps it above owner
|
||||
// No need to update position every frame — it follows automatically
|
||||
// Only update rotation for the correct shape appearance
|
||||
if (objectType == ObjectType.CashBag)
|
||||
{
|
||||
markerObject.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
markerObject.transform.localRotation = Quaternion.Euler(90f, 45f, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMarker()
|
||||
{
|
||||
// GUARD: prevent duplicate markers
|
||||
if (_markerCreated && markerObject != null) return;
|
||||
|
||||
// Also destroy any stale marker children from previous lifecycle
|
||||
for (int i = transform.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
var child = transform.GetChild(i);
|
||||
if (child.name.Contains("MinimapObjectMarker"))
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
// Use different heights per type to prevent Z-fighting
|
||||
float h = heightOffset;
|
||||
switch (objectType)
|
||||
{
|
||||
case ObjectType.CashBag: h = heightOffset; break;
|
||||
case ObjectType.PlayerVault: h = heightOffset + 1f; break;
|
||||
case ObjectType.EnemyVault: h = heightOffset + 2f; break;
|
||||
}
|
||||
|
||||
markerObject = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||
markerObject.name = $"{gameObject.name}_MinimapObjectMarker";
|
||||
|
||||
// Parent to owner so it follows position automatically
|
||||
markerObject.transform.SetParent(transform, false);
|
||||
markerObject.transform.localPosition = Vector3.up * h;
|
||||
|
||||
// Initial rotation based on type
|
||||
if (objectType == ObjectType.CashBag)
|
||||
markerObject.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
|
||||
else
|
||||
markerObject.transform.localRotation = Quaternion.Euler(90f, 45f, 0f);
|
||||
|
||||
markerObject.transform.localScale = Vector3.one * markerSize;
|
||||
|
||||
// Remove collider
|
||||
Collider col = markerObject.GetComponent<Collider>();
|
||||
if (col != null) Destroy(col);
|
||||
|
||||
// Configure layer
|
||||
ConfigureLayer();
|
||||
|
||||
// Get renderer and create material
|
||||
markerRenderer = markerObject.GetComponent<MeshRenderer>();
|
||||
|
||||
Material markerMaterial = new Material(Shader.Find("Sprites/Default"));
|
||||
if (markerMaterial != null)
|
||||
{
|
||||
markerRenderer.material = markerMaterial;
|
||||
}
|
||||
|
||||
_markerCreated = true;
|
||||
Debug.Log($"[MinimapObjectMarker] Created {objectType} marker for {gameObject.name}");
|
||||
}
|
||||
|
||||
private void ConfigureLayer()
|
||||
{
|
||||
int minimapLayer = LayerMask.NameToLayer(minimapLayerName);
|
||||
|
||||
if (minimapLayer == -1)
|
||||
{
|
||||
if (!hasConfiguredLayer)
|
||||
{
|
||||
Debug.LogError($"[MinimapObjectMarker] CRITICAL: Layer '{minimapLayerName}' not found! " +
|
||||
"Please create this layer in Project Settings > Tags and Layers. " +
|
||||
"Main Camera must EXCLUDE this layer from its Culling Mask!");
|
||||
}
|
||||
// Use layer 31 as fallback
|
||||
markerObject.layer = 31;
|
||||
}
|
||||
else
|
||||
{
|
||||
markerObject.layer = minimapLayer;
|
||||
hasConfiguredLayer = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMarkerColor()
|
||||
{
|
||||
if (markerRenderer == null) return;
|
||||
|
||||
Color targetColor;
|
||||
|
||||
switch (objectType)
|
||||
{
|
||||
case ObjectType.CashBag:
|
||||
targetColor = cashBagColor;
|
||||
break;
|
||||
case ObjectType.PlayerVault:
|
||||
targetColor = playerVaultColor;
|
||||
break;
|
||||
case ObjectType.EnemyVault:
|
||||
targetColor = enemyVaultColor;
|
||||
break;
|
||||
default:
|
||||
targetColor = Color.white;
|
||||
break;
|
||||
}
|
||||
|
||||
markerRenderer.material.color = targetColor;
|
||||
|
||||
// Set texture — required for Sprites/Default in URP to render visibly
|
||||
Texture2D tex = (objectType == ObjectType.CashBag)
|
||||
? GetSquareTexture()
|
||||
: GetDiamondTexture();
|
||||
markerRenderer.material.mainTexture = tex;
|
||||
}
|
||||
|
||||
// ─── Texture Generation (cached statics, same pattern as MinimapMarker) ───
|
||||
|
||||
private static Texture2D _cachedSquareTex;
|
||||
private static Texture2D _cachedDiamondTex;
|
||||
|
||||
private static Texture2D GetSquareTexture()
|
||||
{
|
||||
if (_cachedSquareTex == null)
|
||||
_cachedSquareTex = GenerateSquareTexture();
|
||||
return _cachedSquareTex;
|
||||
}
|
||||
|
||||
private static Texture2D GetDiamondTexture()
|
||||
{
|
||||
if (_cachedDiamondTex == null)
|
||||
_cachedDiamondTex = GenerateDiamondTexture();
|
||||
return _cachedDiamondTex;
|
||||
}
|
||||
|
||||
private static Texture2D GenerateSquareTexture()
|
||||
{
|
||||
int size = 64;
|
||||
Texture2D tex = new Texture2D(size, size);
|
||||
Color[] colors = new Color[size * size];
|
||||
float border = 4f;
|
||||
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
bool inside = x >= border && x < size - border && y >= border && y < size - border;
|
||||
colors[y * size + x] = inside ? Color.white : Color.clear;
|
||||
}
|
||||
}
|
||||
tex.SetPixels(colors);
|
||||
tex.Apply();
|
||||
return tex;
|
||||
}
|
||||
|
||||
private static Texture2D GenerateDiamondTexture()
|
||||
{
|
||||
int size = 64;
|
||||
Texture2D tex = new Texture2D(size, size);
|
||||
Color[] colors = new Color[size * size];
|
||||
float center = size / 2f;
|
||||
float halfSize = center - 4f;
|
||||
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
float dx = Mathf.Abs(x - center);
|
||||
float dy = Mathf.Abs(y - center);
|
||||
float dist = dx + dy; // Manhattan distance → diamond shape
|
||||
|
||||
if (dist <= halfSize - 1.5f)
|
||||
colors[y * size + x] = Color.white;
|
||||
else if (dist <= halfSize + 1.5f)
|
||||
{
|
||||
float alpha = 1f - Mathf.Clamp01((dist - halfSize + 1.5f) / 3f);
|
||||
colors[y * size + x] = new Color(1, 1, 1, alpha);
|
||||
}
|
||||
else
|
||||
colors[y * size + x] = Color.clear;
|
||||
}
|
||||
}
|
||||
tex.SetPixels(colors);
|
||||
tex.Apply();
|
||||
return tex;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (markerObject != null)
|
||||
{
|
||||
Destroy(markerObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetType(ObjectType type)
|
||||
{
|
||||
objectType = type;
|
||||
UpdateMarkerColor();
|
||||
}
|
||||
|
||||
public void SetVisible(bool visible)
|
||||
{
|
||||
if (markerObject != null)
|
||||
{
|
||||
markerObject.SetActive(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user