private void FilterFireTargetList(TargetList targets) { if (this.fireControllerLayerMask.value == 0) { return; } Vector3 position; if (this.fireCtrl.emitter != null) { position = this.fireCtrl.emitter.position; } else { position = this.fireCtrl.transform.position; } LayerMask mask = this.fireControllerLayerMask; this.FilterTargetList(targets, mask, position, Color.yellow); }
/// <summary> /// Remove an object from the list explicitly. /// This works even if the object is still in range, effectivley hiding it from the /// perimiter. /// </summary> /// <param name="target">The Target to remove</param> /// <returns>True if somethign was removed</returns> public bool Remove(Target target) { // Quit if nothing was removed if (!this.targets.Remove(target)) { return(false); } // Silence errors on game exit / unload clean-up if (target.transform == null || this.transform == null || this.transform.parent == null) { return(false); } #if UNITY_EDITOR if (this.targetTracker.debugLevel > DEBUG_LEVELS.Off) // If on at all { Debug.Log(string.Format ( "{0} : Target Removed - {1}", this.targetTracker, target.targetable )); } #endif // Trigger the delegate execution for this event if (this.targetTracker.onNotDetectedDelegates != null) { this.targetTracker.onNotDetectedDelegates(this.targetTracker, target); } target.targetable.OnNotDetected(this.targetTracker); // Trigger target update: Sorting, filtering, events. // This uses AddRange internally to copy the targets so '=' is safe this.targetTracker.targets = this.targets; return(true); }
private void OnFire() { if (this.debugLevel > DEBUG_LEVELS.Off) { string arg = string.Format("Firing on: {0}\nHitEffects{1}", this.targetsString, this.effectsOnTarget.ToString()); UnityEngine.Debug.Log(string.Format("{0}: {1}", this, arg)); } TargetList targetList = new TargetList(); foreach (Target current in this.targets) { Target target = new Target(current); target.fireController = this; targetList.Add(target); switch (this.notifyTargets) { case FireController.NOTIFY_TARGET_OPTIONS.Direct: target.targetable.OnHit(this.effectsOnTarget, target); this.SpawnAmmunition(target, false, false); break; case FireController.NOTIFY_TARGET_OPTIONS.PassToProjectile: this.SpawnAmmunition(target, true, true); break; case FireController.NOTIFY_TARGET_OPTIONS.UseProjectileEffects: this.SpawnAmmunition(target, true, false); break; } if (this.notifyTargets > FireController.NOTIFY_TARGET_OPTIONS.Off && this.debugLevel > DEBUG_LEVELS.Off) { UnityEngine.Debug.DrawLine(this.emitter.position, target.transform.position, Color.red); } } this.targets = targetList; if (this.onFireDelegates != null) { this.onFireDelegates(this.targets); } }
protected void FilterTrackerTargetList(TargetTracker source, TargetList targets) { // Quit if the mask is set to nothing == OFF if (this.targetTrackerLayerMask.value == 0) { return; } Vector3 fromPos; if (this.tracker.area != null) { fromPos = this.tracker.area.transform.position; } else { fromPos = this.tracker.transform.position; } LayerMask mask = this.targetTrackerLayerMask; this.FilterTargetList(targets, mask, fromPos, Color.red); }
private void FilterFireTargetList(TargetList targets) { // Quit if the mask is set to nothing == OFF if (this.fireControllerLayerMask.value == 0) { return; } Vector3 fromPos; if (this.fireCtrl.emitter != null) { fromPos = this.fireCtrl.emitter.position; } else { fromPos = this.fireCtrl.transform.position; } LayerMask mask = this.fireControllerLayerMask; this.FilterTargetList(targets, mask, fromPos, Color.yellow); }
private void FilterTargetList(TargetList targets, LayerMask mask, Vector3 fromPos, Color debugLineColor) { List <Target> list = new List <Target>(targets); foreach (Target current in list) { bool flag = false; Vector3 position = current.targetable.xform.position; if (this.testMode == LineOfSightModifier.TEST_MODE.SixPoint) { foreach (Vector3 current2 in new List <Vector3> { new Vector3(position.x + this.radius, position.y, position.z), new Vector3(position.x, position.y + this.radius, position.z), new Vector3(position.x, position.y, position.z + this.radius), new Vector3(position.x - this.radius, position.y, position.z), new Vector3(position.x, position.y - this.radius, position.z), new Vector3(position.x, position.y, position.z - this.radius) }) { flag = Physics.Linecast(fromPos, current2, mask); if (!flag) { break; } } } else { flag = Physics.Linecast(fromPos, position, mask); } if (flag) { targets.Remove(current); } } }
/// <summary> /// Clears the entire list explicitly /// This works even if the object is still in range, effectivley hiding it from the /// perimiter. /// </summary> public void Clear() { // Trigger the delegate execution for this event foreach (Target target in this.targets) { target.targetable.OnNotDetected(this.targetTracker); } this.targets.Clear(); #if UNITY_EDITOR if (this.targetTracker.debugLevel > DEBUG_LEVELS.Normal) // If on at all { Debug.Log(string.Format ( "{0} : All Targets Cleared!", this.targetTracker )); } #endif // Trigger target update: Sorting, filtering, events. // This uses AddRange internally to copy the targets so '=' is safe this.targetTracker.targets = this.targets; }
public TargetList(TargetList targetList) : base(targetList) { }
protected void FilterFireTargetList(TargetList targets) { // Get the position expected to be used to fire from. Vector3 fromPos = this.origin.position; #if UNITY_EDITOR var debugRemoveNames = new List <string>(); #endif this.iterTargets.Clear(); this.iterTargets.AddRange(targets); float dist; for (int i = 0; i < iterTargets.Count; i++) { this.currentTarget = iterTargets[i]; dist = this.currentTarget.targetable.GetDistToPos(fromPos); // Skip if the target is in the distance range if (dist > this.minDistance && dist < this.maxDistance) { #if UNITY_EDITOR if (this.debugLevel > DEBUG_LEVELS.Off) { Debug.DrawLine ( fromPos, this.currentTarget.targetable.transform.position, Color.green, 0.01f ); } #endif continue; } #if UNITY_EDITOR if (this.debugLevel > DEBUG_LEVELS.Off) { Debug.DrawLine ( fromPos, this.currentTarget.targetable.transform.position, Color.red, 0.01f ); } #endif targets.Remove(this.currentTarget); #if UNITY_EDITOR debugRemoveNames.Add(this.currentTarget.targetable.name); #endif } #if UNITY_EDITOR if (this.debugLevel == DEBUG_LEVELS.High && debugRemoveNames.Count > 0) { string msg = string.Format ( "Holding fire due to distance: {0}", string.Join(", ", debugRemoveNames.ToArray()) ); Debug.Log(string.Format("{0}: {1}", this, msg)); } #endif }
/// <summary> /// Handles all firing events including target aquisition and firing. /// Events are: /// OnStart() : /// Runs once when the firing system first becomes active /// OnUpdate() : /// Runs each frame while the firing system is active /// OnTargetUpdate() : /// Runs each frame while tracking a target (there is at least one target.) /// OnIdleUpdate() : /// Runs each frame while the firing system is idle (no targets) /// OnFire() : /// Runs when it is time to fire. /// /// Counter Behavior Notes: /// * If there are no targets. the counter will keep running up. /// This means the next target to enter will be fired upon /// immediatly. /// /// * The counter is always active so if the last target exits, then a /// new target enters right after that, there may still be a wait. /// </summary> private IEnumerator FiringSystem() { // While (true) because of the timer, we want this to run all the time, not // start and stop based on targets in range if (this.initIntervalCountdownAtZero) { this.fireIntervalCounter = 0; } else { this.fireIntervalCounter = this.interval; } this.targets.Clear(); this.OnStart(); // EVENT TRIGGER while (true) { // if there is no target, counter++, handle idle behavior, and // try next frame. // Will init this.targets for child classes as well. this.targets = this.targetTracker.targets; if (this.targets.Count != 0) { // Let the delegate filter a copy of the list just for the OnFire // Test. We still want this.targets to remain as is. // Do this in here to still trigger OnTargetUpdate var targetsCopy = new TargetList(); targetsCopy.AddRange(this.targets); if (this.onPreFireDelegates != null) { this.onPreFireDelegates(targetsCopy); } // if all is right, fire if (targetsCopy.Count != 0 && // Incase of pre-fire delegate changes this.fireIntervalCounter <= 0 && this.isLockedOnTarget) // Always true if WaitForAlignment is OFF { this.OnFire(); this.fireIntervalCounter = this.interval; // Reset } else if (this.debugLevel > DEBUG_LEVELS.Off) { // Just for debug. Show a gizmo line to each target being tracked // OnFire() has another color, so keep this here where this // won't overlay the OnFired line. foreach (Target target in targets) { Debug.DrawLine ( this.emitter.position, target.transform.position, Color.gray ); } } // Update event while tracking a target this.OnTargetUpdate(targets); // EVENT TRIGGER } else { // Update event while NOT tracking a target this.OnIdleUpdate(); // EVENT TRIGGER } this.fireIntervalCounter -= Time.deltaTime; // Update event no matter what this.OnUpdate(); // EVENT TRIGGER // Stager calls to get Target (the whole system actually) yield return(null); } }
/// <summary> /// A delegate run by the EventTrigger component OnFire /// </summary> protected void OnEventTriggerFire(TargetList targets) { // A great place for an explosion effect to be triggered! }
/// <summary> /// Handles all firing events including target aquisition and firing. /// Events are: /// OnStart() : /// Runs once when the firing system first becomes active /// OnUpdate() : /// Runs each frame while the firing system is active /// OnTargetUpdate() : /// Runs each frame while tracking a target (there is at least one target.) /// OnIdleUpdate() : /// Runs each frame while the firing system is idle (no targets) /// OnFire() : /// Runs when it is time to fire. /// /// Counter Behavior Notes: /// * If there are no targets. the counter will keep running up. /// This means the next target to enter will be fired upon /// immediatly. /// /// * The counter is always active so if the last target exits, then a /// new target enters right after that, there may still be a wait. /// </summary> protected IEnumerator FiringSystem() { if (this.targetTracker == null) { this.targetTracker = this.GetComponent <TargetTracker>(); if (this.targetTracker == null) { // Give it a frame to see if this.targetTracker is being set by code. yield return(null); if (this.targetTracker == null) { throw new MissingComponentException ( "FireControllers must be on the same GameObject as a TargetTracker " + "or have it's targetTracker property set by code or drag-and-drop " + "in the inspector." ); } } } // While (true) because of the timer, we want this to run all the time, not // start and stop based on targets in range if (this.initIntervalCountdownAtZero) { this.fireIntervalCounter = 0; } else { this.fireIntervalCounter = this.interval; } this.targets.Clear(); this.OnStart(); // EVENT TRIGGER this.keepFiring = true; // Can be turned off elsewhere to kill the firing system while (this.keepFiring) { // if there is no target, counter++, handle idle behavior, and // try next frame. // Will init this.targets for child classes as well. this.targets = new TargetList(this.targetTracker.targets); if (this.targets.Count != 0) { if (this.fireIntervalCounter <= 0) { // Let the delegate filter a copy of the list just for the OnFire // Test. We still want this.targets to remain as is. // Do this in here to still trigger OnTargetUpdate this.targetsCopy.Clear(); this.targetsCopy.AddRange(this.targets); if (targetsCopy.Count != 0 && this.onPreFireDelegates != null) { this.onPreFireDelegates(this.targetsCopy); } // If all is right, fire // Check targetsCopy incase of pre-fire delegate changes if (targetsCopy.Count != 0) { this.Fire(); this.fireIntervalCounter = this.interval; // Reset } } // Update event while tracking a target this.OnTargetUpdate(this.targets); // EVENT TRIGGER } else { // Update event while NOT tracking a target this.OnIdleUpdate(); // EVENT TRIGGER } this.fireIntervalCounter -= Time.deltaTime; // Update event no matter what this.OnUpdate(); // EVENT TRIGGER // Stager calls to get Target (the whole system actually) yield return(null); } // Wipe out the target list when stopped this.targets.Clear(); }
/// <summary> /// Fire on the targets /// </summary> protected void Fire() { #if UNITY_EDITOR // Log a message to show what is being fired on if (this.debugLevel > DEBUG_LEVELS.Off) { string[] names = new string[this.targets.Count]; for (int i = 0; i < this.targets.Count; i++) { names[i] = this.targets[i].transform.name; } string msg = string.Format ( "Firing on: {0}\nEventInfo: {1}", string.Join(", ", names), this.eventInfoList.ToString() ); Debug.Log(string.Format("{0}: {1}", this, msg)); } #endif // // Create a new list of targets which have this fire controller reference. // var targetCopies = new TargetList(); Target newTarget; foreach (Target target in this.targets) { // Can't edit a struct in a foreach loop, so need to copy and store newTarget = new Target(target); newTarget.fireController = this; // Add reference. null before this targetCopies.Add(newTarget); } // Write the result over the old target list. This is for output so targets // which are handled at all by this target tracker are stamped with a // reference. this.targets = targetCopies; // // Hnadle delivery // foreach (Target target in this.targets) { switch (this.notifyTargets) { case NOTIFY_TARGET_OPTIONS.Direct: target.targetable.OnHit(this.eventInfoList, target); break; case NOTIFY_TARGET_OPTIONS.PassInfoToEventTrigger: this.SpawnEventTrigger(target, true); break; case NOTIFY_TARGET_OPTIONS.UseEventTriggerInfo: this.SpawnEventTrigger(target, false); break; } } #if UNITY_EDITOR // When in the editor, if debugging, draw a line to each hit target. if (this.debugLevel > DEBUG_LEVELS.Off && this.notifyTargets > NOTIFY_TARGET_OPTIONS.Off) { foreach (Target target in this.targets) { Debug.DrawLine(this.spawnEventTriggerAtTransform.position, target.transform.position, Color.red); } } #endif // Trigger the delegates if (this.onFireDelegates != null) { this.onFireDelegates(this.targets); } }
private IEnumerator Detonate() { if (this.debugLevel > DEBUG_LEVELS.Off) { string msg = "Detonating..."; Debug.Log(string.Format("Detonator ({0}): {1}", this.name, msg)); } // Wait for next frame to begin to be sure targets have been propegated // This also makes this loop easier to manage. yield return(new WaitForFixedUpdate()); // Because of physics...matters? // START EVENT if (this.OnDetonatingDelegates != null) { this.OnDetonatingDelegates(); } // Keep track of targets which have already been processed so they // aren't hit twice var processedTargetList = new TargetList(); this.range = Vector3.zero; float timer = 0; float progress = 0; // Normalized mount processed 0-1 // The timer can exit the loop if used while (true) { // UPDATE EVENT if (this.OnDetonatingUpdateDelegates != null) { this.OnDetonatingUpdateDelegates(progress); } // Exit? if (timer >= this.durration) { break; } timer += Time.deltaTime; progress = timer / this.durration; this.range = this.maxRange * progress; // Build a list of targets in range which have NOT been processed yet. var newTargets = new TargetList(); foreach (Target target in this.targets) { if (!processedTargetList.Contains(target)) { newTargets.Add(target); } } if (newTargets.Count > 0) { if (this.debugLevel > DEBUG_LEVELS.Off) { string msg = string.Format("Detonation hitting targets: {0}", this.targets); Debug.Log(string.Format("Detonator ({0}): {1}", this.name, msg)); } foreach (Target target in newTargets) { target.targetable.OnHit ( this.effectsOnTarget, target, this.perimeter.collider ); processedTargetList.Add(target); } } yield return(new WaitForFixedUpdate()); // Because of physics...matters? } // Prevent being run more than once in a frame where it has already // been destroyed. if (!this.gameObject.activeInHierarchy) { yield break; // Same as return } PathologicalGames.InstanceManager.Despawn(this.transform); }
protected void FilterTargetList(TargetList targets, LayerMask mask, Vector3 fromPos, Color debugLineColor) { #if UNITY_EDITOR var debugRemoveNames = new List <string>(); #endif Vector3 toPos; bool isNotLOS; var iterTargets = new List <Target>(targets); Collider targetColl; foreach (Target target in iterTargets) { isNotLOS = false; if (this.testMode == TEST_MODE.BoundingBox) { targetColl = target.targetable.coll; // This solution works with rotation pretty well Matrix4x4 mtx = target.targetable.transform.localToWorldMatrix; Vector3 ext = targetColl.bounds.extents * 0.5f; var bboxPnts = new Vector3[8]; bboxPnts[0] = mtx.MultiplyPoint3x4(ext); bboxPnts[1] = mtx.MultiplyPoint3x4(new Vector3(-ext.x, ext.y, ext.z)); bboxPnts[2] = mtx.MultiplyPoint3x4(new Vector3(ext.x, ext.y, -ext.z)); bboxPnts[3] = mtx.MultiplyPoint3x4(new Vector3(-ext.x, ext.y, -ext.z)); bboxPnts[4] = mtx.MultiplyPoint3x4(new Vector3(ext.x, -ext.y, ext.z)); bboxPnts[5] = mtx.MultiplyPoint3x4(new Vector3(-ext.x, -ext.y, ext.z)); bboxPnts[6] = mtx.MultiplyPoint3x4(new Vector3(ext.x, -ext.y, -ext.z)); bboxPnts[7] = mtx.MultiplyPoint3x4(-ext); for (int i = 0; i < bboxPnts.Length; i++) { isNotLOS = Physics.Linecast(fromPos, bboxPnts[i], mask); // Quit loop at first positive test if (isNotLOS) { #if UNITY_EDITOR if (this.debugLevel > DEBUG_LEVELS.Off) { Debug.DrawLine(fromPos, bboxPnts[i], debugLineColor, 0.01f); } #endif continue; } else { break; } } } else { toPos = target.targetable.transform.position; isNotLOS = Physics.Linecast(fromPos, toPos, mask); #if UNITY_EDITOR if (isNotLOS && this.debugLevel > DEBUG_LEVELS.Off) { Debug.DrawLine(fromPos, toPos, debugLineColor, 0.01f); } #endif } if (isNotLOS) { targets.Remove(target); #if UNITY_EDITOR debugRemoveNames.Add(target.targetable.name); #endif } } #if UNITY_EDITOR if (this.debugLevel == DEBUG_LEVELS.High && debugRemoveNames.Count > 0) { Debug.Log("Holding fire for LOS: " + string.Join(",", debugRemoveNames.ToArray())); } #endif }
/// <summary> /// Destroys the projectile on impact and finds objects in range to /// affect if they share the same tag as target. /// </summary> public void DetonateProjectile() { // Prevent being run more than once in a frame where it has already // been destroyed. if (!this.gameObject.activeInHierarchy) { return; } // Build a new list of targets depending on the options used var targetList = new TargetList(); if (this.areaHit) { // This is turned back off OnDisable() (base class) this.perimeter.enabled = true; targetList.AddRange(this.targets); // Add all targets in range } else { if (this.target != Target.Null) { targetList.Add(this.target); // Add projectile target } } if (this.debugLevel > DEBUG_LEVELS.Off) { string msg = string.Format("Detonating with targets: {0}", targetList); Debug.Log(string.Format("Projectile ({0}): {1}", this.name, msg)); } // Create a new list of targets which have this target tracker reference. // This is for output so targets which are handled at all by this Projectile // are stamped with a reference. var targetCopies = new TargetList(); Target target; foreach (Target inTarget in targetList) { if (inTarget == Target.Null) { continue; } // Can't edit a struct in a foreach loop, so need to copy and store target = new Target(inTarget); target.projectile = this; // Add reference. null before t targetCopies.Add(target); switch (this.notifyTargets) { case NOTIFY_TARGET_OPTIONS.Direct: target.targetable.OnHit(this.effectsOnTarget, target, this.collider); break; } // Just for debug. Show a gizmo line when firing if (this.debugLevel > DEBUG_LEVELS.Off) { Debug.DrawLine ( this.xform.position, target.transform.position, Color.red ); } } switch (this.notifyTargets) { case NOTIFY_TARGET_OPTIONS.Direct: this.SpawnDetonatorPrefab(false); break; case NOTIFY_TARGET_OPTIONS.PassToDetonator: this.SpawnDetonatorPrefab(true); break; } // Trigger delegates if (this.OnDetonationDelegates != null) { this.OnDetonationDelegates(targetCopies); } // Clean-up in case this instance is used in a pooling system like PoolManager this.target = Target.Null; InstanceManager.Despawn(this.transform); }
private void FilterTargetList(TargetList targets, LayerMask mask, Vector3 fromPos, Color debugLineColor) { #if UNITY_EDITOR var debugRemoveNames = new List <string>(); #endif Vector3 toPos; bool isNotLOS; var iterTargets = new List <Target>(targets); foreach (Target target in iterTargets) { isNotLOS = false; toPos = target.targetable.xform.position; if (this.testMode == TEST_MODE.SixPoint) { var sweep = new List <Vector3>(); sweep.Add(new Vector3(toPos.x + this.radius, toPos.y, toPos.z)); sweep.Add(new Vector3(toPos.x, toPos.y + this.radius, toPos.z)); sweep.Add(new Vector3(toPos.x, toPos.y, toPos.z + this.radius)); sweep.Add(new Vector3(toPos.x - this.radius, toPos.y, toPos.z)); sweep.Add(new Vector3(toPos.x, toPos.y - this.radius, toPos.z)); sweep.Add(new Vector3(toPos.x, toPos.y, toPos.z - this.radius)); foreach (Vector3 pos in sweep) { isNotLOS = Physics.Linecast(fromPos, pos, mask); #if UNITY_EDITOR if (this.debugLevel > DEBUG_LEVELS.Off) { Debug.DrawLine(fromPos, pos, debugLineColor, 0.01f); } #endif // Quit loop at first positive test if (isNotLOS) { continue; } else { break; } } } else { isNotLOS = Physics.Linecast(fromPos, toPos, mask); #if UNITY_EDITOR if (this.debugLevel > DEBUG_LEVELS.Off) { Debug.DrawLine(fromPos, toPos, debugLineColor, 0.01f); } #endif } if (isNotLOS) { targets.Remove(target); #if UNITY_EDITOR debugRemoveNames.Add(target.targetable.name); #endif } } #if UNITY_EDITOR if (this.debugLevel == DEBUG_LEVELS.High && debugRemoveNames.Count > 0) { Debug.Log("Holding fire for LOS: " + string.Join(",", debugRemoveNames.ToArray())); } #endif }