Moved from swarm to NavMesh - need to fix the enemy navigation! Review the animation controller to see if anything is being triggered.
This commit is contained in:
@@ -1,10 +1,17 @@
|
||||
using Synty.AnimationBaseLocomotion.Samples;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
[RequireComponent(typeof(EnemyRagdoll))]
|
||||
[RequireComponent(typeof(NavMeshAgent))]
|
||||
public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
public enum EnemyType { Horde, Siege }
|
||||
[Header("Targeting Priorities")]
|
||||
public EnemyType type = EnemyType.Horde;
|
||||
[Range(0f, 1f)]
|
||||
public float hordeBarrierFocusChance = 0.2f;
|
||||
[Header("Stats")]
|
||||
public int health = 30;
|
||||
private bool isStunned = false;
|
||||
@@ -19,47 +26,166 @@ public class EnemyAI : MonoBehaviour
|
||||
[Header("Blocking")]
|
||||
[Range(0f, 1f)]
|
||||
public float blockChance = 0.15f; // 15% chance to block by default
|
||||
[HideInInspector] public Transform currentTarget;
|
||||
|
||||
private Animator anim;
|
||||
private Rigidbody rb;
|
||||
private EnemyRagdoll ragdoll;
|
||||
private NavMeshAgent agent;
|
||||
private Fortification targetFortification;
|
||||
private bool isDead = false;
|
||||
public GameObject stunVFX;
|
||||
private GameObject activeStunVFX;
|
||||
|
||||
void Start()
|
||||
{
|
||||
anim = GetComponent<Animator>();
|
||||
rb = GetComponent<Rigidbody>();
|
||||
ragdoll = GetComponent<EnemyRagdoll>();
|
||||
agent = GetComponent<NavMeshAgent>();
|
||||
|
||||
rb.isKinematic = true;
|
||||
rb.useGravity = false;
|
||||
|
||||
EnemySwarmManager.Instance.RegisterEnemy(transform);
|
||||
anim.SetFloat("MoveSpeed", 3.5f);
|
||||
EnemySwarmManager.Instance.RegisterEnemy(this);
|
||||
//anim.SetFloat("MoveSpeed", agent.speed);
|
||||
|
||||
// Add a random offset to the initial attack timer so the mob doesn't attack in perfectly unified sync
|
||||
lastAttackTime = Time.time + Random.Range(0f, attackCooldown);
|
||||
//AssignTarget();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (isDead || isStunned) return;
|
||||
// PlayerController.Instance comes from the core script setup
|
||||
if (SamplePlayerAnimationController.Instance != null)
|
||||
|
||||
// Retargeting check if a barrier is broken
|
||||
if (targetFortification != null && targetFortification.IsDestroyed)
|
||||
{
|
||||
Vector3 offset = SamplePlayerAnimationController.Instance.transform.position - transform.position;
|
||||
|
||||
// sqrMagnitude is significantly faster for the CPU than Vector3.Distance
|
||||
if (offset.sqrMagnitude <= attackRange * attackRange)
|
||||
targetFortification = null;
|
||||
AssignTarget();
|
||||
}
|
||||
|
||||
if (currentTarget != null)
|
||||
{
|
||||
Vector3 offset = currentTarget.position - transform.position;
|
||||
float effectiveRange = (targetFortification != null) ? attackRange * 1.5f : attackRange;
|
||||
|
||||
// --- THE FIX: Pure mathematical distance check ---
|
||||
if (offset.sqrMagnitude <= effectiveRange * effectiveRange)
|
||||
{
|
||||
agent.isStopped = true;
|
||||
anim.SetFloat("MoveSpeed", 0f);
|
||||
|
||||
// Face the target manually
|
||||
Vector3 direction = offset.normalized;
|
||||
direction.y = 0;
|
||||
|
||||
// Prevent Unity error if direction is perfectly zero
|
||||
if (direction.sqrMagnitude > 0.001f)
|
||||
{
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), Time.deltaTime * 5f);
|
||||
}
|
||||
|
||||
if (Time.time - lastAttackTime > attackCooldown)
|
||||
{
|
||||
PerformAttack();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
agent.isStopped = false;
|
||||
agent.SetDestination(currentTarget.position);
|
||||
|
||||
// Fallback: If agent gets stuck briefly, keep the run animation playing smoothly
|
||||
float currentSpeed = agent.velocity.magnitude;
|
||||
if (currentSpeed < 0.1f && !agent.pathPending) currentSpeed = agent.speed;
|
||||
|
||||
anim.SetFloat("MoveSpeed", currentSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TRIGGERED VIA ANIMATION EVENT ON THE ENEMY ATTACK CLIP ---
|
||||
public void ExecuteEnemyHit()
|
||||
{
|
||||
if (isDead || currentTarget == null) return;
|
||||
|
||||
Vector3 offset = currentTarget.position - transform.position;
|
||||
float effectiveRange = (targetFortification != null) ? attackRange * 1.5f : attackRange;
|
||||
|
||||
if (offset.sqrMagnitude <= effectiveRange * effectiveRange)
|
||||
{
|
||||
if (targetFortification != null && !targetFortification.IsDestroyed)
|
||||
{
|
||||
targetFortification.TakeDamage(attackDamage);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Safely grab the player health without relying on the old PlayerController script
|
||||
PlayerHealth pHealth = currentTarget.GetComponent<PlayerHealth>();
|
||||
if (pHealth != null)
|
||||
{
|
||||
pHealth.TakeDamage(attackDamage);
|
||||
Debug.Log("<color=red>Enemy landed a hit on the Player!</color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private void AssignTarget()
|
||||
{
|
||||
bool prioritizeBarrier = (type == EnemyType.Siege) || (Random.value < hordeBarrierFocusChance);
|
||||
|
||||
if (prioritizeBarrier)
|
||||
{
|
||||
targetFortification = FindNearestFortification();
|
||||
if (targetFortification != null)
|
||||
{
|
||||
currentTarget = targetFortification.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPlayerTarget();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPlayerTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void FindPlayerTarget()
|
||||
{
|
||||
GameObject player = GameObject.FindGameObjectWithTag("Player");
|
||||
if (player != null)
|
||||
{
|
||||
currentTarget = player.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("<color=red>EnemyAI could not find the Player! Ensure your Player Prefab has the Tag set to 'Player'.</color>");
|
||||
}
|
||||
}
|
||||
|
||||
private Fortification FindNearestFortification()
|
||||
{
|
||||
Fortification nearest = null;
|
||||
float minDist = float.MaxValue;
|
||||
|
||||
foreach (Fortification fort in Fortification.AllFortifications)
|
||||
{
|
||||
if (fort.IsDestroyed) continue; // Ignore rubble
|
||||
|
||||
float dist = (transform.position - fort.transform.position).sqrMagnitude;
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
nearest = fort;
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
private void PerformAttack()
|
||||
{
|
||||
lastAttackTime = Time.time;
|
||||
@@ -68,19 +194,6 @@ public class EnemyAI : MonoBehaviour
|
||||
anim.SetTrigger("Attack");
|
||||
}
|
||||
|
||||
// --- TRIGGERED VIA ANIMATION EVENT ON THE ENEMY ATTACK CLIP ---
|
||||
public void ExecuteEnemyHit()
|
||||
{
|
||||
if (isDead || SamplePlayerAnimationController.Instance == null) return;
|
||||
|
||||
// Simple check to see if the player is still in range when the attack lands
|
||||
Vector3 offset = SamplePlayerAnimationController.Instance.transform.position - transform.position;
|
||||
if (offset.sqrMagnitude <= attackRange * attackRange)
|
||||
{
|
||||
SamplePlayerAnimationController.Instance.GetComponent<PlayerHealth>().TakeDamage(attackDamage);
|
||||
}
|
||||
}
|
||||
|
||||
public void TakeDamage(int damage, Vector3 hitSource, float knockbackForce)
|
||||
{
|
||||
if (isDead) return;
|
||||
@@ -121,8 +234,9 @@ public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
isDead = true;
|
||||
|
||||
EnemySwarmManager.Instance.RemoveEnemy(transform);
|
||||
EnemySwarmManager.Instance.RemoveEnemy(this);
|
||||
ragdoll.ActivateRagdoll(hitSource, knockbackForce);
|
||||
Destroy(stunVFX);
|
||||
Destroy(gameObject, 3f);
|
||||
this.enabled = false;
|
||||
}
|
||||
@@ -136,6 +250,10 @@ public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
isStunned = true;
|
||||
anim.SetTrigger("Stun"); // Requires a "Stun" trigger in your Animator
|
||||
if (stunVFX != null && activeStunVFX == null)
|
||||
{
|
||||
activeStunVFX = Instantiate(stunVFX, transform.position, Quaternion.identity, transform);
|
||||
}
|
||||
|
||||
// Detach from the Burst Compiler so they stop sliding toward you
|
||||
EnemySwarmManager.Instance.PauseEnemyMovement(transform);
|
||||
@@ -146,6 +264,7 @@ public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
isStunned = false;
|
||||
anim.SetTrigger("Recover"); // Requires a "Recover" trigger in your Animator
|
||||
Destroy(activeStunVFX);
|
||||
|
||||
// Reattach to the Burst Compiler so they resume the chase
|
||||
EnemySwarmManager.Instance.ResumeEnemyMovement(transform);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Jobs;
|
||||
using Unity.Jobs;
|
||||
@@ -25,6 +26,7 @@ public class WaveDefinition
|
||||
public class EnemySwarmManager : MonoBehaviour
|
||||
{
|
||||
public static EnemySwarmManager Instance;
|
||||
private List<EnemyAI> activeEnemiesList = new List<EnemyAI>();
|
||||
|
||||
[Header("Swarm Settings")]
|
||||
public float enemySpeed = 3.5f;
|
||||
@@ -34,7 +36,7 @@ public class EnemySwarmManager : MonoBehaviour
|
||||
public Transform[] spawnPoints;
|
||||
public float timeBetweenWaves = 5.0f;
|
||||
[Tooltip("Delay between individual enemy spawns to prevent lag spikes")]
|
||||
public float spawnStagger = 0.05f;
|
||||
public float spawnStagger = 0.05f;
|
||||
|
||||
private int currentWaveIndex = 0;
|
||||
private int activeEnemyCount = 0;
|
||||
@@ -49,8 +51,8 @@ public class EnemySwarmManager : MonoBehaviour
|
||||
void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
transformAccessArray = new TransformAccessArray(1000);
|
||||
targetPositions = new NativeArray<float3>(1, Allocator.Persistent);
|
||||
transformAccessArray = new TransformAccessArray(1000);
|
||||
//targetPositions = new NativeArray<float3>(1, Allocator.Persistent);
|
||||
}
|
||||
|
||||
void Start()
|
||||
@@ -73,9 +75,9 @@ public class EnemySwarmManager : MonoBehaviour
|
||||
{
|
||||
isSpawning = true; // Prevent accidental wave-clears during cooldown
|
||||
Debug.Log($"<color=cyan>Next wave starting in {timeBetweenWaves} seconds...</color>");
|
||||
|
||||
|
||||
yield return new WaitForSeconds(timeBetweenWaves);
|
||||
|
||||
|
||||
StartCoroutine(SpawnWaveRoutine(waves[currentWaveIndex]));
|
||||
}
|
||||
|
||||
@@ -91,11 +93,11 @@ public class EnemySwarmManager : MonoBehaviour
|
||||
{
|
||||
// Pick a random spawn point from the array
|
||||
Transform randomSpawnPoint = spawnPoints[UnityEngine.Random.Range(0, spawnPoints.Length)];
|
||||
|
||||
|
||||
// Add a tiny random offset so they don't spawn perfectly inside each other
|
||||
Vector3 spawnPos = randomSpawnPoint.position + new Vector3(
|
||||
UnityEngine.Random.Range(-2f, 2f),
|
||||
0f,
|
||||
UnityEngine.Random.Range(-2f, 2f),
|
||||
0f,
|
||||
UnityEngine.Random.Range(-2f, 2f)
|
||||
);
|
||||
|
||||
@@ -107,35 +109,38 @@ public class EnemySwarmManager : MonoBehaviour
|
||||
}
|
||||
|
||||
isSpawning = false;
|
||||
|
||||
|
||||
// Failsafe: Check if the player somehow killed them all before the spawning even finished
|
||||
CheckWaveClear();
|
||||
}
|
||||
|
||||
public void RegisterEnemy(Transform enemyTransform)
|
||||
public void RegisterEnemy(EnemyAI enemy) // <-- Changed parameter from Transform to EnemyAI
|
||||
{
|
||||
movementJobHandle.Complete();
|
||||
transformAccessArray.Add(enemyTransform);
|
||||
|
||||
transformAccessArray.Add(enemy.transform);
|
||||
activeEnemiesList.Add(enemy); // Keep a parallel list on the main thread
|
||||
|
||||
activeEnemyCount++;
|
||||
if (UIManager.Instance != null) UIManager.Instance.UpdateEnemiesRemaining(activeEnemyCount);
|
||||
}
|
||||
|
||||
public void RemoveEnemy(Transform enemyTransform)
|
||||
public void RemoveEnemy(EnemyAI enemy) // <-- Changed parameter from Transform to EnemyAI
|
||||
{
|
||||
movementJobHandle.Complete();
|
||||
for (int i = 0; i < transformAccessArray.length; i++)
|
||||
int index = activeEnemiesList.IndexOf(enemy);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
if (transformAccessArray[i] == enemyTransform)
|
||||
{
|
||||
transformAccessArray.RemoveAtSwapBack(i);
|
||||
|
||||
activeEnemyCount--;
|
||||
if (UIManager.Instance != null) UIManager.Instance.UpdateEnemiesRemaining(activeEnemyCount);
|
||||
|
||||
CheckWaveClear();
|
||||
break;
|
||||
}
|
||||
// Swap-back deletion keeps arrays perfectly aligned and highly performant
|
||||
transformAccessArray.RemoveAtSwapBack(index);
|
||||
|
||||
activeEnemiesList[index] = activeEnemiesList[activeEnemiesList.Count - 1];
|
||||
activeEnemiesList.RemoveAt(activeEnemiesList.Count - 1);
|
||||
|
||||
activeEnemyCount--;
|
||||
if (UIManager.Instance != null) UIManager.Instance.UpdateEnemiesRemaining(activeEnemyCount);
|
||||
|
||||
CheckWaveClear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,11 +171,19 @@ public class EnemySwarmManager : MonoBehaviour
|
||||
|
||||
// --- BURST COMPILER MOVEMENT ---
|
||||
|
||||
void Update()
|
||||
/*void Update()
|
||||
{
|
||||
if (SamplePlayerAnimationController.Instance == null || transformAccessArray.length == 0) return;
|
||||
|
||||
targetPositions[0] = SamplePlayerAnimationController.Instance.transform.position;
|
||||
// --- NEW TARGETING LOGIC ---
|
||||
// Feed every single enemy's unique target into the NativeArray before the Job runs
|
||||
for (int i = 0; i < activeEnemiesList.Count; i++)
|
||||
{
|
||||
if (activeEnemiesList[i].currentTarget != null)
|
||||
targetPositions[i] = activeEnemiesList[i].currentTarget.position;
|
||||
else
|
||||
targetPositions[i] = SamplePlayerAnimationController.Instance.transform.position; // Fallback
|
||||
}
|
||||
|
||||
SwarmJob swarmJob = new SwarmJob
|
||||
{
|
||||
@@ -180,21 +193,9 @@ public class EnemySwarmManager : MonoBehaviour
|
||||
};
|
||||
|
||||
movementJobHandle = swarmJob.Schedule(transformAccessArray);
|
||||
}
|
||||
}*/
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
movementJobHandle.Complete();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
movementJobHandle.Complete();
|
||||
if (transformAccessArray.isCreated) transformAccessArray.Dispose();
|
||||
if (targetPositions.IsCreated) targetPositions.Dispose();
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
/*[BurstCompile]
|
||||
struct SwarmJob : IJobParallelForTransform
|
||||
{
|
||||
[ReadOnly] public NativeArray<float3> targetPos;
|
||||
@@ -204,7 +205,8 @@ public class EnemySwarmManager : MonoBehaviour
|
||||
public void Execute(int index, TransformAccess transform)
|
||||
{
|
||||
float3 currentPos = transform.position;
|
||||
float3 dir = targetPos[0] - currentPos;
|
||||
// The Job now looks up the SPECIFIC target for this specific index!
|
||||
float3 dir = targetPos[index] - currentPos;
|
||||
|
||||
if (math.lengthsq(dir) > 4.0f)
|
||||
{
|
||||
@@ -215,7 +217,42 @@ public class EnemySwarmManager : MonoBehaviour
|
||||
transform.rotation = targetRot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/*void LateUpdate()
|
||||
{
|
||||
movementJobHandle.Complete();
|
||||
}*/
|
||||
|
||||
/*void OnDestroy()
|
||||
{
|
||||
movementJobHandle.Complete();
|
||||
if (transformAccessArray.isCreated) transformAccessArray.Dispose();
|
||||
if (targetPositions.IsCreated) targetPositions.Dispose();
|
||||
}*/
|
||||
|
||||
/* [BurstCompile]
|
||||
struct SwarmJob : IJobParallelForTransform
|
||||
{
|
||||
[ReadOnly] public NativeArray<float3> targetPos;
|
||||
public float speed;
|
||||
public float dt;
|
||||
|
||||
public void Execute(int index, TransformAccess transform)
|
||||
{
|
||||
float3 currentPos = transform.position;
|
||||
float3 dir = targetPos[0] - currentPos;
|
||||
|
||||
if (math.lengthsq(dir) > 4.0f)
|
||||
{
|
||||
dir = math.normalize(dir);
|
||||
transform.position = currentPos + (dir * speed * dt);
|
||||
|
||||
quaternion targetRot = quaternion.LookRotationSafe(dir, math.up());
|
||||
transform.rotation = targetRot;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
public void PauseEnemyMovement(Transform enemyTransform)
|
||||
{
|
||||
movementJobHandle.Complete();
|
||||
|
||||
68
Assets/Scripts/Fortification.cs
Normal file
68
Assets/Scripts/Fortification.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class Fortification : MonoBehaviour
|
||||
{
|
||||
// Global list so Siege enemies can easily find targets
|
||||
public static List<Fortification> AllFortifications = new List<Fortification>();
|
||||
|
||||
[Header("Health Settings")]
|
||||
public float maxHealth = 500f;
|
||||
public float currentHealth;
|
||||
|
||||
[Header("Visual Stages (Assign GameObjects)")]
|
||||
public GameObject fullyBuiltModel;
|
||||
public GameObject damagedModel;
|
||||
public GameObject ruinedModel;
|
||||
|
||||
private Collider barrierCollider;
|
||||
public bool IsDestroyed => currentHealth <= 0;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
barrierCollider = GetComponent<Collider>();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
AllFortifications.Add(this);
|
||||
currentHealth = maxHealth;
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
AllFortifications.Remove(this);
|
||||
}
|
||||
|
||||
public void TakeDamage(float amount)
|
||||
{
|
||||
if (IsDestroyed) return;
|
||||
|
||||
currentHealth -= amount;
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
public void Repair(float amount)
|
||||
{
|
||||
if (currentHealth >= maxHealth) return;
|
||||
|
||||
currentHealth += amount;
|
||||
currentHealth = Mathf.Min(currentHealth, maxHealth); // Cap at max
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
private void UpdateVisuals()
|
||||
{
|
||||
// Swap models based on health thresholds
|
||||
fullyBuiltModel.SetActive(currentHealth > maxHealth * 0.5f);
|
||||
damagedModel.SetActive(currentHealth > 0 && currentHealth <= maxHealth * 0.5f);
|
||||
ruinedModel.SetActive(IsDestroyed);
|
||||
|
||||
// When destroyed, turn the collider into a trigger so the horde can pour through the rubble
|
||||
if (barrierCollider != null)
|
||||
{
|
||||
barrierCollider.isTrigger = IsDestroyed;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Fortification.cs.meta
Normal file
2
Assets/Scripts/Fortification.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a75b96ae329b2e1479de939a6edfa4b1
|
||||
@@ -11,15 +11,18 @@ public class Pickup : MonoBehaviour
|
||||
|
||||
[Header("Health Settings")]
|
||||
public int healAmount = 50;
|
||||
public GameObject healthVFX;
|
||||
|
||||
[Header("Strength Settings")]
|
||||
public float strengthMultiplier = 2.0f; // Doubles damage and knockback
|
||||
public float strengthDuration = 10f;
|
||||
public GameObject strengthVFX;
|
||||
|
||||
[Header("Stun Settings")]
|
||||
public float stunRadius = 8f;
|
||||
public float stunDuration = 5f;
|
||||
public LayerMask enemyLayer;
|
||||
public GameObject stunVFX;
|
||||
|
||||
void Update()
|
||||
{
|
||||
@@ -47,15 +50,18 @@ public class Pickup : MonoBehaviour
|
||||
case PickupType.Health:
|
||||
PlayerHealth health = player.GetComponent<PlayerHealth>();
|
||||
if (health != null) health.Heal(healAmount);
|
||||
Instantiate(healthVFX, player.transform.position + Vector3.up * 0.1f, player.transform.rotation, player.transform);
|
||||
break;
|
||||
|
||||
case PickupType.Strength:
|
||||
SyntyPlayerCombat combat = player.GetComponentInChildren<SyntyPlayerCombat>();
|
||||
SyntyPlayerCombat combat = player.GetComponent<SyntyPlayerCombat>();
|
||||
if (combat != null) combat.ApplyStrengthBuff(strengthMultiplier, strengthDuration);
|
||||
Instantiate(strengthVFX, transform.position, transform.rotation);
|
||||
break;
|
||||
|
||||
case PickupType.StunBomb:
|
||||
ExecuteStunBomb(transform.position);
|
||||
Instantiate(stunVFX, transform.position, transform.rotation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
65
Assets/Scripts/PlayerBuilder.cs
Normal file
65
Assets/Scripts/PlayerBuilder.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using UnityEngine;
|
||||
using Synty.AnimationBaseLocomotion.Samples.InputSystem; // Required to see the InputReader
|
||||
|
||||
public class PlayerBuilder : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
public InputReader inputReader;
|
||||
|
||||
[Header("Repair Settings")]
|
||||
public float repairRange = 4.0f;
|
||||
public float repairHealthPerSecond = 100f;
|
||||
public LayerMask fortificationLayer;
|
||||
|
||||
private bool isHoldingRepair = false;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
// Subscribe to the broadcasts when the script turns on
|
||||
if (inputReader != null)
|
||||
{
|
||||
inputReader.onRepairActivated += StartRepairing;
|
||||
inputReader.onRepairDeactivated += StopRepairing;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
// Unsubscribe when turned off to prevent memory leaks
|
||||
if (inputReader != null)
|
||||
{
|
||||
inputReader.onRepairActivated -= StartRepairing;
|
||||
inputReader.onRepairDeactivated -= StopRepairing;
|
||||
}
|
||||
}
|
||||
|
||||
// These trigger the moment the InputReader hears a button press
|
||||
private void StartRepairing() { isHoldingRepair = true; }
|
||||
private void StopRepairing() { isHoldingRepair = false; }
|
||||
|
||||
void Update()
|
||||
{
|
||||
// If the switch is ON, continuously repair every frame
|
||||
if (isHoldingRepair)
|
||||
{
|
||||
RepairNearbyFortification();
|
||||
}
|
||||
}
|
||||
|
||||
private void RepairNearbyFortification()
|
||||
{
|
||||
Collider[] hits = Physics.OverlapSphere(transform.position, repairRange, fortificationLayer);
|
||||
|
||||
foreach (Collider hit in hits)
|
||||
{
|
||||
Fortification fort = hit.GetComponentInParent<Fortification>();
|
||||
|
||||
if (fort != null && fort.currentHealth < fort.maxHealth)
|
||||
{
|
||||
fort.Repair(repairHealthPerSecond * Time.deltaTime);
|
||||
//add in repair audio and VFX
|
||||
break; // Only repair one barrier at a time
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/PlayerBuilder.cs.meta
Normal file
2
Assets/Scripts/PlayerBuilder.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 745f9f574fe2dec4ba6adbc589f53c53
|
||||
Reference in New Issue
Block a user