diff --git a/Assets/_Project/Definitions/Towers/BasicTower.asset b/Assets/_Project/Definitions/Towers/BasicArrowTower.asset similarity index 84% rename from Assets/_Project/Definitions/Towers/BasicTower.asset rename to Assets/_Project/Definitions/Towers/BasicArrowTower.asset index e32563a..5f00eaf 100644 --- a/Assets/_Project/Definitions/Towers/BasicTower.asset +++ b/Assets/_Project/Definitions/Towers/BasicArrowTower.asset @@ -10,11 +10,11 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 7b353a757b6e6774d97e6fb8ba138fcc, type: 3} - m_Name: BasicTower + m_Name: BasicArrowTower m_EditorClassIdentifier: Assembly-CSharp::TD.Towers.TowerDefinition - DisplayName: Basic Arrow - Description: Targets ground and air. Fires single-target arrows. The bread and butter - of any maze. + DisplayName: Basic Arrow Tower + Description: Single-target tower that fires arrows at ground and air enemies. The + backbone of any maze. FootprintSize: {x: 2, y: 2} GoldCost: 25 BuildTime: 0.5 diff --git a/Assets/_Project/Definitions/Towers/BasicTower.asset.meta b/Assets/_Project/Definitions/Towers/BasicArrowTower.asset.meta similarity index 100% rename from Assets/_Project/Definitions/Towers/BasicTower.asset.meta rename to Assets/_Project/Definitions/Towers/BasicArrowTower.asset.meta diff --git a/Assets/_Project/Prefabs/Towers/Tower_Basic.prefab b/Assets/_Project/Prefabs/Towers/Tower_BasicArrow.prefab similarity index 94% rename from Assets/_Project/Prefabs/Towers/Tower_Basic.prefab rename to Assets/_Project/Prefabs/Towers/Tower_BasicArrow.prefab index 42defa0..1e6b505 100644 --- a/Assets/_Project/Prefabs/Towers/Tower_Basic.prefab +++ b/Assets/_Project/Prefabs/Towers/Tower_BasicArrow.prefab @@ -69,8 +69,9 @@ GameObject: - component: {fileID: 9137031893466587143} - component: {fileID: 805962841523123163} - component: {fileID: 8853488620519990682} + - component: {fileID: 5360241685303413177} m_Layer: 0 - m_Name: Tower_Basic + m_Name: Tower_BasicArrow m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -100,7 +101,7 @@ MeshFilter: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6482414459531823157} - m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} + m_Mesh: {fileID: 0} --- !u!23 &4028055828417179692 MeshRenderer: m_ObjectHideFlags: 0 @@ -241,6 +242,21 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::TD.Combat.TowerRangeIndicator rangeProjector: {fileID: 8255517343120954594} projectionDepth: 50 +--- !u!114 &5360241685303413177 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6482414459531823157} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 93bc533262394e1e8b122bbfad89391a, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::TD.Visuals.ConeMesh + sides: 16 + radius: 0.5 + height: 1 --- !u!1 &7580197837852108944 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/_Project/Prefabs/Towers/Tower_Basic.prefab.meta b/Assets/_Project/Prefabs/Towers/Tower_BasicArrow.prefab.meta similarity index 100% rename from Assets/_Project/Prefabs/Towers/Tower_Basic.prefab.meta rename to Assets/_Project/Prefabs/Towers/Tower_BasicArrow.prefab.meta diff --git a/Assets/_Project/Scripts/Visuals.meta b/Assets/_Project/Scripts/Visuals.meta new file mode 100644 index 0000000..367f7c2 --- /dev/null +++ b/Assets/_Project/Scripts/Visuals.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 16f3da2efd0d4431888e3f04056646c2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Project/Scripts/Visuals/ConeMesh.cs b/Assets/_Project/Scripts/Visuals/ConeMesh.cs new file mode 100644 index 0000000..d1790a5 --- /dev/null +++ b/Assets/_Project/Scripts/Visuals/ConeMesh.cs @@ -0,0 +1,126 @@ +// Assets/_Project/Scripts/Visuals/ConeMesh.cs +using UnityEngine; + +namespace TD.Visuals +{ + /// + /// Procedurally generates a cone mesh and assigns it to the GameObject's + /// . Unity ships no built-in cone primitive, so this + /// fills that gap for tower visuals (e.g. the Basic Arrow Tower). + /// + /// + /// Visual only. No networking, no collision — the mesh is purely cosmetic. + /// The existing MeshRenderer + material (and any owner/paint tinting via + /// TowerInstance.tintedRenderers) keep working unchanged. + /// + /// Pivot. The cone is centred on the local origin: its base ring sits at + /// y = -height/2 and its apex at y = +height/2, matching the vertical + /// extents of a unit cube so it sits on a build tile the same way the old cube did. + /// + /// Edit-time. [ExecuteAlways] rebuilds the mesh in the editor so the + /// cone is visible without entering play mode. The generated mesh is flagged + /// HideFlags.DontSave so it is never written out as a project asset. + /// + [ExecuteAlways] + [RequireComponent(typeof(MeshFilter))] + public class ConeMesh : MonoBehaviour + { + [Tooltip("Number of radial segments around the base. Higher = smoother circle.")] + [Range(3, 64)] + [SerializeField] private int sides = 16; + + [Tooltip("Radius of the cone's base in local units.")] + [Min(0.001f)] + [SerializeField] private float radius = 0.5f; + + [Tooltip("Height of the cone from base to apex in local units.")] + [Min(0.001f)] + [SerializeField] private float height = 1f; + + private Mesh mesh; + + private void OnEnable() => Rebuild(); + + private void OnValidate() + { + // OnValidate can fire before OnEnable in the editor; guard the component. + if (isActiveAndEnabled) Rebuild(); + } + + private void OnDisable() + { + // Clean up the generated mesh so it doesn't linger when the object is destroyed. + if (mesh != null) + { + if (Application.isPlaying) Destroy(mesh); + else DestroyImmediate(mesh); + mesh = null; + } + } + + private void Rebuild() + { + var filter = GetComponent(); + if (filter == null) return; + + if (mesh == null) + { + mesh = new Mesh { name = "ProceduralCone", hideFlags = HideFlags.DontSave }; + } + + BuildCone(mesh); + filter.sharedMesh = mesh; + } + + private void BuildCone(Mesh target) + { + target.Clear(); + + float halfHeight = height * 0.5f; + + // Vertex layout: + // [0 .. sides-1] base ring + // [sides] apex + // [sides + 1] base centre (for the bottom cap) + int baseRingStart = 0; + int apexIndex = sides; + int baseCentreIndex = sides + 1; + + var vertices = new Vector3[sides + 2]; + for (int i = 0; i < sides; i++) + { + float angle = (i / (float)sides) * Mathf.PI * 2f; + vertices[baseRingStart + i] = new Vector3( + Mathf.Cos(angle) * radius, + -halfHeight, + Mathf.Sin(angle) * radius); + } + vertices[apexIndex] = new Vector3(0f, halfHeight, 0f); + vertices[baseCentreIndex] = new Vector3(0f, -halfHeight, 0f); + + // Side faces (base ring -> apex) + base cap (fan from centre). + // Wind both so they face outward / downward respectively. + var triangles = new int[sides * 3 * 2]; + int t = 0; + for (int i = 0; i < sides; i++) + { + int next = (i + 1) % sides; + + // Side triangle, outward-facing. + triangles[t++] = baseRingStart + i; + triangles[t++] = apexIndex; + triangles[t++] = baseRingStart + next; + + // Base cap triangle, downward-facing. + triangles[t++] = baseCentreIndex; + triangles[t++] = baseRingStart + next; + triangles[t++] = baseRingStart + i; + } + + target.vertices = vertices; + target.triangles = triangles; + target.RecalculateNormals(); + target.RecalculateBounds(); + } + } +} diff --git a/Assets/_Project/Scripts/Visuals/ConeMesh.cs.meta b/Assets/_Project/Scripts/Visuals/ConeMesh.cs.meta new file mode 100644 index 0000000..2db0b9d --- /dev/null +++ b/Assets/_Project/Scripts/Visuals/ConeMesh.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 93bc533262394e1e8b122bbfad89391a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: