Tactical Shooter AI and Dark Tonic

Here, I’m going to share how I integrated Core GameKit by Dark Tonic and
Tactical Shooter AI by Squared55 so that I can have all the game action I need for my project alongside Ultimate FPS by Opsive. I have a number of script changes so that Tactical Shooter AI will Spawn and Despawn with Core GameKit. This should also work with Pool Boss, but you will need to change the using statement at the top of the scripts and maybe a few other functions (Core GameKit is worth the money)!

I’ve used Core GameKit by Dark Tonic for a few years now and I really like how it handles a ton of features. I’ve used it just for spawning and despawning (Pool Boss), Wave Management, Global Variables (like score, levels), Killables, and Triggered events…basically all the features. I’ve used Pool Boss by itself. I’ve also used Master Audio which is very handy.

I’ve also used Tactical Shooter AI alongside UFPS for a while. It works great out of the box.

UFPS Integration page UFPS – Tactical AI and Core Game Kit

Tactical Shooter AI by Squared55
Core GameKit by Dark Tonic

HealthScript Edits = I created a new float to handle reseting the Health.

//I added the below at the top with the other using statements
using DarkTonic.CoreGameKit;				////ADD THIS FOR CGK - line 11, 41-42,208

//Line 11
public float defaultHealth=0f;

//Line 41 - This is inside the Awake
defaultHealth = health;

//I added the below at the bottom of the script
//CGK Spawning
		void OnSpawned()
		{
			this.enabled = true;
			health=defaultHealth;
			if(rotateToAimGunScript)
				rotateToAimGunScript.enabled = true;
			if(animator)
				animator.enabled = true;
			if(gunScript)
				gunScript.enabled = true;
			//Enable the ragdoll
			int i;
			for(i = 0; i < rigidbodies.Length; i++)
			{
				rigidbodies[i].isKinematic = true;
			}
		}
		void OnDespawned()
		{
			StopAllCoroutines();
		}

AnimationScript – Basically I moved Start into OnSpawned, plus I stop all Coroutines just in case. I also call a ResetRagdoll from the new script on the Ragdoll, and I changed the animator weight to 0.6 float otherwise the soldier leans backwards after being respawned.  Make sure you create the new TacticalAIRagdollDespawner script at the bottom of this page before you edit this script.

//I added the below to the Using statements at the top
using DarkTonic.CoreGameKit;//ADD THIS FOR CGK

//Comment out Start, copy 87-129 into OnSpawned

//In Awake below SetHashes(); add the two lines below
if(animator.gameObject.GetComponent<TacticalAIRagdollDespawner>()==null)
	animator.gameObject.AddComponent<TacticalAIRagdollDespawner>();

//Below are my OnSpawned and OnDespawned methods
///
<summary>
		/// The below OnSpawned and OnDespawned are using Dark Tonic CGK pool boss
		/// </summary>

		void OnSpawned()
		{
			//Reset the Ragdoll
			animator.GetComponent<TacticalAIRagdollDespawner>().ResetRagdoll();
			//BELOW I fix the animator to the correct weight
			//because 1 is too high with the animators that come with Tactical Shooter AI
			//If using your own custom, then just test it out with this, or remove the below two lines.
			int upperLayerIndex = animator.GetLayerIndex("UpperBody");
			animator.SetLayerWeight(upperLayerIndex,0.6f);
			//Move everything from Start

			//Set offset of mesh
			if (myAIBodyTransform)
			{
				bodyOffset = myAIBodyTransform.localPosition;
				bodyOffset.x *= transform.localScale.x;
				bodyOffset.y *= transform.localScale.y;
				bodyOffset.z *= transform.localScale.z;
				myAIBodyTransform.parent = null;
			}
			else
			{
				Debug.LogWarning("No transform set for 'myAIBodyTransform'.  Please assign a transform in the inspector!");
				this.enabled = false;
			}

			//Inititate Hashes and stuff
			navi = myBaseScript.GetAgent();
			minDistToCrouch = minDistToCrouch * minDistToCrouch;
			myTransform = transform;

			lerpSpeed = myBaseScript.runSpeed;

			//Check to make sure we have all of our scripts assigned
			if (!myBaseScript)
			{
				Debug.LogWarning("No Base Script found!  Please add one in the inspector!");
				this.enabled = false;
			}
			else if (maxMovementSpeed < 0)
			{
				maxMovementSpeed = myBaseScript.runSpeed;
			}

			if (!animator)
			{
				Debug.LogWarning("No animator component found!  Please add one in the inspector!");
				this.enabled = false;
			}
			else
			{
				animator.speed = animatorSpeed;
			}
		}
		void OnDespawned()
		{
			//Move the Ragdoll back to the Parent Agent
			if(myAIBodyTransform.parent == null)
			{
				Debug.Log("This moved ragdoll to this parent "+this.transform.name);
				myAIBodyTransform.SetParent(this.gameObject.transform);
			}
			StopAllCoroutines();
		}

TargetScript – I basically moved Start and Awake to OnSpawned. I commented out Start and Awake. I commented out OnDestroy on lines 151-155 (but it’s fine if you don’t).

//I added the below to the top of the script with the Using Statements
using DarkTonic.CoreGameKit;//ADD THIS FOR CGK - Move start and awake to OnSpawned, Commentout Start and awake, Commentout OnDestroy 151-155

//Below are what my OnSpawned and OnDestroy looks like
		///
<summary>
		/// The below OnSpawned and OnDespawned are using Dark Tonic CGK pool boss
		/// </summary>

		void OnSpawned()
		{
			//FROM AWAKE
			if (!healthScriptHolder)
				healthScriptHolder = gameObject;

			layerMask = TacticalAI.ControllerScript.currentController.GetLayerMask();

			if (!targetObjectTransform)
				targetObjectTransform = transform;

			if (!myLOSTarget)
				myLOSTarget = targetObjectTransform;

			//Add ourselves to the list of targets
			if (TacticalAI.ControllerScript.currentController)
				myUniqueID = TacticalAI.ControllerScript.currentController.AddTarget(myTeamID, targetObjectTransform, this);
			else
				UnityEngine.Debug.LogWarning("No AI Controller Found!");

			if (!eyeTransform)
				eyeTransform = targetObjectTransform;

			effectiveFOV = myFieldOfView / 2;
			maxDistToNoticeTarget = maxDistToNoticeTarget*maxDistToNoticeTarget;

			if (myAIBaseScript)
			{
				myAIBaseScript.SetTargetObj(this);
			}
	//END FROM AWAKE

			//FROM START - Start some loops
			if (myAIBaseScript)
			{
				StartCoroutine(LoSLoop());
				StartCoroutine(TargetSelectionLoop());
				//StartCoroutine(CountDownToTargetExperation());
			}
			//END FROM START
		}
		void OnDespawned()
		{
			StopAllCoroutines();
		}

CustomAIBehavior Script – basically commented out Start, moved it to OnSpawned, and added OnSpawned and OnDespawned right below the current Start

//I added the below to the using statements
using DarkTonic.CoreGameKit;		//ADD THIS FOR CGK - Moved Start to OnSpawned, Comment out Start lines 42-45, OnSpawned and OnDespawned to line 46

//I added the below on line 46 right below Start
		//CGK
		void OnSpawned()
		{
			//Moved from Start
			ApplyBehaviour();
		}
		void OnDespawned()
		{
			KillBehaviour();
			StopAllCoroutines();
		}
		//END CGK

BaseScript Stuff – I commented out Start, made another change and put start information into OnSpawned

//I added the below to the using statements
using DarkTonic.CoreGameKit;				//ADD THIS FOR CGK -
//Comment out Start 189-210, Copy 191-208 into OnSpawned,
//Edits around line 757,

//Find the below - around line 757 and put this
//CGK                GameObject.Destroy(animationScript.myAIBodyTransform.gameObject, timeUntilBodyIsDestroyedAfterDeath);
//CGK                GameObject.Destroy(gameObject);
				animationScript.myAIBodyTransform.gameObject.SendMessage("AIDeath",timeUntilBodyIsDestroyedAfterDeath,SendMessageOptions.DontRequireReceiver);
				animationScript.myAIBodyTransform.gameObject.SendMessage("SetParent",this.gameObject.transform,SendMessageOptions.DontRequireReceiver);
				myTargetScript.RemoveThisTargetFromPLay();
				animationScript.enabled = false;

//Below are what my OnSpawned and OnDespawned look like
		///
<summary>
		/// The below OnSpawned and OnDespawned are using Dark Tonic CGK pool boss
		/// </summary>

		void OnSpawned()
		{
			//FROM START
			GetDefaultBehaviours();
			if (TacticalAI.ControllerScript.currentController != null)
			{
				layerMask = TacticalAI.ControllerScript.currentController.GetLayerMask();
			}
			else
			{
				this.enabled = false;
			}
			//StartCoroutine(WaitBetweenDodges());
			if (!ControllerScript.pMode)
			{
				StartCoroutine("AICycle");
			}
			else
			{
				StartCoroutine("PerformanceAICycle");
			}
			//DONE FROM START

			navmeshInterfaceClass = gameObject.GetComponent<NavmeshInterface>();
			navI = navmeshInterfaceClass;
			navI.Initialize(gameObject);
			myTransform = transform;
			animationScript.enabled=true;

			timeUntilNextDodge = timeBetweenLoSDodges * Random.value;
			dodgeClearHeightCheckPos = Vector3.zero;
			dodgeClearHeightCheckPos.y = dodgingClearHeight;

			distFromTargetToSprint = distFromTargetToSprint * distFromTargetToSprint;
			meleeRange = meleeRange * meleeRange;
			if (idleSpeed > runSpeed)
			{
				idleSpeed = runSpeed;
			}
			if (headLookScript)
			{
				headLookScript.Deactivate();
			}
		}
		void OnDespawned()
		{
			StopAllCoroutines();
		}

I created the below script and put it on the Ragdoll child. Basically this will reparent the Ragdoll.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using DarkTonic.CoreGameKit;
using TacticalAI;

public class TacticalAIRagdollDespawner : MonoBehaviour {

	float TimeToWaitUntilDead=0f;
	Transform myParent=null;
	//This function is called by TacticalAI.BaseScript
	public void AIDeath(float timeToWait){
		TimeToWaitUntilDead = timeToWait;
		StartCoroutine("AIDeathGo");
	}
	//This function is called by TacticalAI.BaseScript
	public void SetParent(Transform parentTransform){
		myParent=parentTransform;
	}
	IEnumerator AIDeathGo ()
	{
		yield return new WaitForSeconds(TimeToWaitUntilDead);
		myParent.position = Vector3.zero;
		myParent.rotation = Quaternion.identity;
		this.transform.SetParent(myParent);
		this.transform.position = Vector3.zero;
		this.transform.rotation = Quaternion.identity;
		PoolBoss.Despawn(myParent);
	}

	///
<summary>
	/// Initialize the Ragdoll by getting all the components and resetting the Ragdoll on each Start/Spawn
	/// </summary>

	Dictionary<Transform, Quaternion> TransformRotations = new Dictionary<Transform, Quaternion>();
	Dictionary<Transform, Vector3> TransformPositions = new Dictionary<Transform, Vector3>();
	protected List<Transform> m_Transforms = null;
	protected List<Rigidbody> m_Rigidbodies = null;
	Quaternion m_Rot;
	Vector3 m_Pos;

	protected List<Transform> Transforms
	{
		get
		{
			if (m_Transforms == null)
			{
				m_Transforms = new List<Transform>();
				foreach (Rigidbody r in Rigidbodies)
				{
					m_Transforms.Add(r.transform);
				}
			}
			return m_Transforms;
		}
	}
	protected List<Rigidbody> Rigidbodies
	{
		get
		{
			if (m_Rigidbodies == null)
				m_Rigidbodies = new List<Rigidbody>(GetComponentsInChildren<Rigidbody>());
			return m_Rigidbodies;
		}
	}

	//Function saves the start position of the Ragdoll
	void Start()
	{
		SaveRagdollStart();
	}
	public void SaveRagdollStart()
	{
		foreach (Transform t in Transforms)
		{
			if (!TransformRotations.ContainsKey(t))
				TransformRotations.Add(t.transform, t.localRotation);
			if (!TransformPositions.ContainsKey(t))
				TransformPositions.Add(t.transform, t.localPosition);
		}
	}
	//Function resets the ragdoll to the start position
	public void ResetRagdoll()
	{
		foreach (Transform t in Transforms)
		{
			if (TransformRotations.TryGetValue(t, out m_Rot))
				t.localRotation = m_Rot;
			if (TransformPositions.TryGetValue(t, out m_Pos))
				t.localPosition = m_Pos;
		}
	}
}

HeaderWP

Advertisements