Commit c5002782 authored by Rugeon's avatar Rugeon
Browse files

Changed the scripts and added NPBehave, compiles now! But the camera is zoomed...

Changed the scripts and added NPBehave, compiles now! But the camera is zoomed in so you can't see tanks
parent 554d4dc0
......@@ -48,6 +48,14 @@ namespace Complete
m_Tanks[i].m_PlayerNumber = i + 1;
m_Tanks[i].Setup();
}
foreach (TankManager tank in m_Tanks)
{
foreach (TankManager target in m_Tanks)
{
if (tank != target) tank.AddTarget(target.m_Instance);
}
}
}
......
......@@ -13,6 +13,10 @@ namespace Complete
public Color m_PlayerColor; // This is the color this tank will be tinted.
public Transform m_SpawnPoint; // The position and direction the tank will have when it spawns.
public Boolean m_NPC; // Is this AI controlled?
public int m_Behaviour; // Identifer for a behaviour tree
[HideInInspector] public int m_PlayerNumber; // This specifies which player this the manager for.
[HideInInspector] public string m_ColoredPlayerText; // A string that represents the player with their number colored to match their tank.
[HideInInspector] public GameObject m_Instance; // A reference to the instance of the tank when it is created.
......@@ -21,6 +25,7 @@ namespace Complete
private TankMovement m_Movement; // Reference to tank's movement script, used to disable and enable control.
private TankShooting m_Shooting; // Reference to tank's shooting script, used to disable and enable control.
private TankAI m_AI; // Reference to tank's AI script, used to disable and enable control.
private GameObject m_CanvasGameObject; // Used to disable the world space UI during the Starting and Ending phases of each round.
......@@ -29,11 +34,20 @@ namespace Complete
// Get references to the components.
m_Movement = m_Instance.GetComponent<TankMovement> ();
m_Shooting = m_Instance.GetComponent<TankShooting> ();
m_AI = m_Instance.GetComponent<TankAI> ();
m_CanvasGameObject = m_Instance.GetComponentInChildren<Canvas> ().gameObject;
// Set the player numbers to be consistent across the scripts.
m_Movement.m_PlayerNumber = m_PlayerNumber;
m_Shooting.m_PlayerNumber = m_PlayerNumber;
m_AI.m_PlayerNumber = m_PlayerNumber;
// Let the tank scripts know if they are AI contolled
m_Movement.m_NPC = m_NPC;
m_Shooting.m_NPC = m_NPC;
// Let tank AI know which behaviour to run
m_AI.m_Behaviour = m_Behaviour;
// Create a string using the correct color that says 'PLAYER 1' etc based on the tank's color and the player's number.
m_ColoredPlayerText = "<color=#" + ColorUtility.ToHtmlStringRGB(m_PlayerColor) + ">PLAYER " + m_PlayerNumber + "</color>";
......@@ -49,12 +63,17 @@ namespace Complete
}
}
public void AddTarget(GameObject target)
{
m_AI.AddTarget(target);
}
// Used during the phases of the game where the player shouldn't be able to control their tank.
// Used during the phases of the game where the player/AI shouldn't be able to control their tank.
public void DisableControl ()
{
m_Movement.enabled = false;
m_Shooting.enabled = false;
m_AI.enabled = false;
m_CanvasGameObject.SetActive (false);
}
......@@ -63,6 +82,9 @@ namespace Complete
// Used during the phases of the game where the player should be able to control their tank.
public void EnableControl ()
{
if (m_NPC) {
m_AI.enabled = true;
}
m_Movement.enabled = true;
m_Shooting.enabled = true;
......
using UnityEngine;
using NPBehave;
using System.Collections.Generic;
namespace Complete
{
/*
Example behaviour trees for the Tank AI. This is partial definition:
the core AI code is defined in TankAI.cs.
Use this file to specifiy your new behaviour tree.
*/
public partial class TankAI : MonoBehaviour
{
private Root CreateBehaviourTree() {
switch (m_Behaviour) {
case 1:
return SpinBehaviour(-0.05f, 1f);
case 2:
return TrackBehaviour();
default:
return new Root (new Action(()=> Turn(0.1f)));
}
}
/* Actions */
private Node StopTurning() {
return new Action(() => Turn(0));
}
private Node RandomFire() {
return new Action(() => Fire(UnityEngine.Random.Range(0.0f, 1.0f)));
}
/* Example behaviour trees */
// Constantly spin and fire on the spot
private Root SpinBehaviour(float turn, float shoot) {
return new Root(new Sequence(
new Action(() => Turn(turn)),
new Action(() => Fire(shoot))
));
}
// Turn to face your opponent and fire
private Root TrackBehaviour() {
return new Root(
new Service(0.2f, UpdatePerception,
new Selector(
new BlackboardCondition("targetOffCentre",
Operator.IS_SMALLER_OR_EQUAL, 0.1f,
Stops.IMMEDIATE_RESTART,
// Stop turning and fire
new Sequence(StopTurning(),
new Wait(2f),
RandomFire())),
new BlackboardCondition("targetOnRight",
Operator.IS_EQUAL, true,
Stops.IMMEDIATE_RESTART,
// Turn right toward target
new Action(() => Turn(0.2f))),
// Turn left toward target
new Action(() => Turn(-0.2f))
)
)
);
}
private void UpdatePerception() {
Vector3 targetPos = TargetTransform().position;
Vector3 localPos = this.transform.InverseTransformPoint(targetPos);
Vector3 heading = localPos.normalized;
blackboard["targetDistance"] = localPos.magnitude;
blackboard["targetInFront"] = heading.z > 0;
blackboard["targetOnRight"] = heading.x > 0;
blackboard["targetOffCentre"] = Mathf.Abs(heading.x);
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: de36e38e2676043d8b38b7ed362834ca
timeCreated: 1507583286
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using UnityEngine;
using NPBehave;
using System.Collections.Generic;
namespace Complete
{
/*
Script for the Tank AI. This is partial definition: behaviour trees
for this class are defined in Behaviours.cs
*/
public partial class TankAI : MonoBehaviour
{
public int m_PlayerNumber = 1; // Used to identify which tank belongs to which player. This is set by this tank's manager.
public int m_Behaviour = 0; // Used to select an AI behaviour in the Unity Inspector
private TankMovement m_Movement; // Reference to tank's movement script, used by the AI to control movement.
private TankShooting m_Shooting; // Reference to tank's shooting script, used by the AI to fire shells.
private List<GameObject> m_Targets; // List of enemy targets for this tank
private Root tree; // The tank's behaviour tree
private Blackboard blackboard; // The tank's behaviour blackboard
// Initialisation
private void Awake()
{
m_Targets = new List<GameObject>();
}
// Start behaviour tree
private void Start() {
Debug.Log("Initialising AI player " + m_PlayerNumber);
m_Movement = GetComponent<TankMovement> ();
m_Shooting = GetComponent<TankShooting> ();
tree = CreateBehaviourTree();
blackboard = tree.Blackboard;
#if UNITY_EDITOR
Debugger debugger = (Debugger)this.gameObject.AddComponent(typeof(Debugger));
debugger.BehaviorTree = tree;
#endif
tree.Start();
}
// Register an enemy target
public void AddTarget(GameObject target) {
m_Targets.Add(target);
}
// Get the transform for the first target
private Transform TargetTransform() {
if (m_Targets.Count > 0) {
return m_Targets[0].transform;
} else {
return null;
}
}
// ACTION: move the tank with a velocity between -1 and 1.
// -1: fast reverse
// 0: no change
// 1: fast forward
private void Move(float velocity) {
m_Movement.AIMove(velocity);
}
// ACTION: turn the tank with angular velocity between -1 and 1.
// -1: fast turn left
// 0: no change
// 1: fast turn right
private void Turn(float velocity) {
m_Movement.AITurn(velocity);
}
// ACTION: fire a shell with a force between 0 and 1
// 0: minimum force
// 1: maximum force
private void Fire(float force) {
m_Shooting.AIFire(force);
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: f2b48c7b359b342bba74422601342ee4
timeCreated: 1507543665
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
......@@ -12,6 +12,7 @@ namespace Complete
public AudioClip m_EngineDriving; // Audio to play when the tank is moving.
public float m_PitchRange = 0.2f; // The amount by which the pitch of the engine noises can vary.
public bool m_NPC; // Is shooting AI controlled?
private string m_MovementAxisName; // The name of the input axis for moving forward and back.
private string m_TurnAxisName; // The name of the input axis for turning.
......@@ -19,7 +20,11 @@ namespace Complete
private float m_MovementInputValue; // The current value of the movement input.
private float m_TurnInputValue; // The current value of the turn input.
private float m_OriginalPitch; // The pitch of the audio source at the start of the scene.
private ParticleSystem[] m_particleSystems; // References to all the particles systems used by the Tanks
private float m_AIMovement; // The current value of the AI's movement input.
private float m_AITurn; // The current value of the AI's turn input.
private void Awake ()
{
......@@ -35,6 +40,15 @@ namespace Complete
// Also reset the input values.
m_MovementInputValue = 0f;
m_TurnInputValue = 0f;
// We grab all the Particle systems child of that Tank to be able to Stop/Play them on Deactivate/Activate
// It is needed because we move the Tank when spawning it, and if the Particle System is playing while we do that
// it "think" it move from (0,0,0) to the spawn point, creating a huge trail of smoke
m_particleSystems = GetComponentsInChildren<ParticleSystem>();
for (int i = 0; i < m_particleSystems.Length; ++i)
{
m_particleSystems[i].Play();
}
}
......@@ -42,6 +56,12 @@ namespace Complete
{
// When the tank is turned off, set it to kinematic so it stops moving.
m_Rigidbody.isKinematic = true;
// Stop all particle system so it "reset" it's position to the actual one instead of thinking we moved when spawning
for(int i = 0; i < m_particleSystems.Length; ++i)
{
m_particleSystems[i].Stop();
}
}
......@@ -59,13 +79,17 @@ namespace Complete
private void Update ()
{
// Store the value of both input axes.
m_MovementInputValue = Input.GetAxis (m_MovementAxisName);
m_TurnInputValue = Input.GetAxis (m_TurnAxisName);
if (m_NPC) {
m_MovementInputValue = m_AIMovement;
m_TurnInputValue = m_AITurn;
} else {
m_MovementInputValue = Input.GetAxis (m_MovementAxisName);
m_TurnInputValue = Input.GetAxis (m_TurnAxisName);
}
EngineAudio ();
}
private void EngineAudio ()
{
// If there is no input (the tank is stationary)...
......@@ -101,7 +125,6 @@ namespace Complete
Turn ();
}
private void Move ()
{
// Create a vector in the direction the tank is facing with a magnitude based on the input, speed and the time between frames.
......@@ -123,5 +146,17 @@ namespace Complete
// Apply this rotation to the rigidbody's rotation.
m_Rigidbody.MoveRotation (m_Rigidbody.rotation * turnRotation);
}
public void AIMove(float move)
{
// Clamp the value to [-1,1]
m_AIMovement = (move > 1) ? 1 : (move < -1) ? -1 : move;
}
public void AITurn(float turn)
{
// Clamp the value to [-1,1]
m_AITurn = (turn > 1) ? 1 : (turn < -1) ? -1 : turn;
}
}
}
\ No newline at end of file
......@@ -16,18 +16,27 @@ namespace Complete
public float m_MaxLaunchForce = 30f; // The force given to the shell if the fire button is held for the max charge time.
public float m_MaxChargeTime = 0.75f; // How long the shell can charge for before it is fired at max force.
public bool m_NPC; // Is shooting AI controlled?
private string m_FireButton; // The input axis that is used for launching shells.
private float m_CurrentLaunchForce; // The force that will be given to the shell when the fire button is released.
private float m_ChargeSpeed; // How fast the launch force increases, based on the max charge time.
private bool m_Fired; // Whether or not the shell has been launched with this button press.
private int m_FiringState; // Current state of the firing sequence
private const int FIRING_INACTIVE = 0;
private const int FIRING_START = 1;
private const int FIRING_CHARGE = 2;
private const int FIRING_RELEASE = 3;
private bool m_AIFireRequest; // Does the AI want to fire?
private float m_AILaunchForce; // The AI's intended launch force
private void OnEnable()
{
// When the tank is turned on, reset the launch force and the UI
m_CurrentLaunchForce = m_MinLaunchForce;
m_AimSlider.value = m_MinLaunchForce;
m_FiringState = FIRING_INACTIVE;
}
......@@ -46,46 +55,91 @@ namespace Complete
// The slider should have a default value of the minimum launch force.
m_AimSlider.value = m_MinLaunchForce;
// If the max force has been exceeded and the shell hasn't yet been launched...
if (m_CurrentLaunchForce >= m_MaxLaunchForce && !m_Fired)
{
// ... use the max force and launch the shell.
m_CurrentLaunchForce = m_MaxLaunchForce;
Fire ();
}
// Otherwise, if the fire button has just started being pressed...
else if (Input.GetButtonDown (m_FireButton))
{
// ... reset the fired flag and reset the launch force.
m_Fired = false;
m_CurrentLaunchForce = m_MinLaunchForce;
// Change the clip to the charging clip and start it playing.
m_ShootingAudio.clip = m_ChargingClip;
m_ShootingAudio.Play ();
// Step forward the firing state machine
switch (m_FiringState) {
case FIRING_INACTIVE:
// AI control
if (m_NPC) {
// If AI has requested fire...
if (m_AIFireRequest) {
m_FiringState = FIRING_START;
}
// Player control
} else {
// If the fire button has just started being pressed...
if (Input.GetButtonDown (m_FireButton)) {
m_FiringState = FIRING_START;
}
}
break;
case FIRING_START:
// Reset the launch force.
m_CurrentLaunchForce = m_MinLaunchForce;
// Change the clip to the charging clip and start it playing.
m_ShootingAudio.clip = m_ChargingClip;
m_ShootingAudio.Play ();
// Automatically start charging
m_FiringState = FIRING_CHARGE;
break;
case FIRING_CHARGE:
// Increment the launch force and update the slider.
m_CurrentLaunchForce += m_ChargeSpeed * Time.deltaTime;
m_AimSlider.value = m_CurrentLaunchForce;
// If the max force has been exceeded and the shell hasn't yet been launched...
if (m_CurrentLaunchForce >= m_MaxLaunchForce)
{
// ... use the max force and launch the shell.
m_CurrentLaunchForce = m_MaxLaunchForce;
m_FiringState = FIRING_RELEASE;
}
// AI control
if (m_NPC) {
if (m_CurrentLaunchForce > m_AILaunchForce) {
m_FiringState = FIRING_RELEASE;
}
// Player control
} else {
/// if the fire button is released and the shell hasn't been launched yet...
if (Input.GetButtonUp (m_FireButton)) {
m_FiringState = FIRING_RELEASE;
}
}
break;
case FIRING_RELEASE:
m_FiringState = FIRING_INACTIVE;
m_AIFireRequest = false;
Fire();
break;
}
// Otherwise, if the fire button is being held and the shell hasn't been launched yet...
else if (Input.GetButton (m_FireButton) && !m_Fired)
{
// Increment the launch force and update the slider.
m_CurrentLaunchForce += m_ChargeSpeed * Time.deltaTime;
}
m_AimSlider.value = m_CurrentLaunchForce;
}
// Otherwise, if the fire button is released and the shell hasn't been launched yet...
else if (Input.GetButtonUp (m_FireButton) && !m_Fired)
{
// ... launch the shell.
Fire ();
public bool AIFire(float force) {
if (!m_AIFireRequest) {
m_AIFireRequest = true;
force = (force < 0) ? 0 : (force > 1) ? 1 : force;
m_AILaunchForce = m_MinLaunchForce + force * (m_MaxLaunchForce - m_MinLaunchForce);
return true;
} else {
return false;
}
}
private void Fire ()
{
// Set the fired flag so only Fire is only called once.
m_Fired = true;
// Create an instance of the shell and store a reference to it's rigidbody.
Rigidbody shellInstance =
Instantiate (m_Shell, m_FireTransform.position, m_FireTransform.rotation) as Rigidbody;
......
fileFormatVersion: 2
guid: 295004daf4522c446bbe4747bed6f101
folderAsset: yes
timeCreated: 1508240359
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 9c01bba4beb0e084ca41f4064620f636
folderAsset: yes
timeCreated: 1508240359
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 73af7b0c48aa04ee4903d34a7fdcf4cb
folderAsset: yes
timeCreated: 1457207424
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace NPBehave
{
[CustomEditor(typeof(Debugger))]
public class DebuggerEditor : Editor
{
public override void OnInspectorGUI()
{
GUILayout.Label("NPBehave Debugger", EditorStyles.centeredGreyMiniLabel);
if (GUILayout.Button("Open Debugger"))
{
DebuggerWindow.selectedDebugger = ((Debugger)target);
DebuggerWindow.selectedObject = DebuggerWindow.selectedDebugger.transform;
DebuggerWindow.ShowWindow();
}
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: fa7cc8848286f4e72889c81a587e0393
timeCreated: 1467467374
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace NPBehave
{
public class DebuggerWindow : EditorWindow
{
private const int nestedPadding = 10;
public static Transform selectedObject;