Search Unity

C# constructor, instantiation and properties, read last post.

Discussion in 'Scripting' started by HAlbera, Apr 26, 2015.

  1. HAlbera

    HAlbera

    Joined:
    Jun 7, 2013
    Posts:
    63
    Hello all!

    I'm creating a task list system for the AI in my game, I understand there are other alternatives such as FSMs and Goal Oriented systems but I'm set on creating my own task system.

    So far I have a class that contains all the stuff I'll need for the system.

    Inside that class I have 2 more classes, one defines what a task is (Task) and one that defines a task manager.

    Inside the task manager it contains a list (of type Task) and a number of functions I'll need for manipulating this list.

    Now, Task has a 'public delegate void TaskFunction();' what I want to do is store a method in that, that in turn will be handled by the task manager. Obviously I have declared it as 'public TaskFunction taskFunction;' So far so good.

    In a separate script I want to store a function in it. void MoveToNextWaypoint(MoveState m) is an example of the functions I want to add to a task and it is contained in the script that will be creating and assigning the new tasks. Here is what I'm trying to do.

    Code (CSharp):
    1. AITask.Task newTask = new AITask.Task();
    2.         newTask.priority = 10;
    3.         newTask.taskLoop = true;
    4.         newTask.taskFunction = MoveToNextWaypoint(MoveState.Jogging);
    I get the error 'error CS0029: Cannot implicitly convert type `void' to `AITask.Task.TaskFunction''

    From my understanding, I can't store this method in there because it has a parameter and therefore has the wrong signature?

    I've tried a few different things including casting it and ive read the delegate reference on msdn but it's still going over my head.

    Thanks in advance!

    Halbera.

    EDIT: After reading this I tried changing the function type of void MoveTo... to AITask.Task.TaskFunction, now apparently not all code paths return a value. I don't need it to return a value, just be void?
     
  2. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    If you want the method to return nothing then it will be void. I would need to see more information as well as the full error message. Post the full script that is causing the error and the error message so people can help you more effectively.
     
  3. HAlbera

    HAlbera

    Joined:
    Jun 7, 2013
    Posts:
    63
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEditor;
    3.  
    4. public class CivillianBehaviour : MonoBehaviour {
    5.  
    6.     public GameObject TESTTARGET;
    7.  
    8.     //NAVIGATION AGENT
    9.     public NavMeshAgent NAV;
    10.  
    11.     //ANIMATION CONTROLLER
    12.     Animator ANIM;
    13.     public enum MoveState {Idle, Walking, Jogging}
    14.     public MoveState moveState;
    15.     //STATE SETUP
    16.     delegate void CurrentState();
    17.     CurrentState currentState;
    18.  
    19.     //WAYPOINT SYSTEM
    20.     public List<Vector3> WAYPOINTS;
    21.     public NavMeshPath NEXTWAYPOINT;
    22.     float waypointAccuracy = 0.1f;
    23.  
    24.     //TASKLIST
    25.     public AITask.TaskManager tm;
    26.  
    27.     // Use this for initialization
    28.     void Start () {
    29.         currentState = Idle;
    30.         NAV = GetComponent<NavMeshAgent>();
    31.         ANIM = GetComponent<Animator>();
    32.         NEXTWAYPOINT = new NavMeshPath();
    33.         tm = new AITask.TaskManager();
    34.     }
    35.  
    36.     // Update is called once per frame
    37.     void Update () {
    38.         AITask.Task newTask = new AITask.Task();
    39.         newTask.priority = 10;
    40.         newTask.taskLoop = true;
    41.         newTask.taskFunction = MoveToNextWaypoint(MoveState.Jogging);
    42.  
    43.         //TODO state transitions
    44.      
    45.         //SETTING THE STATE VARIABLES AND EXECUTING THE CURRENT STATE
    46.         //currentState();
    47.         ANIM.SetInteger("moveState",(int)moveState);
    48.     }
    49.  
    50.     //SPECIAL STATE BEHAVIOUR (Animation and Movement already handled in move functions eg. MoveToNextWaypoint(MoveState m);
    51.     void Idle(){
    52.  
    53.     }
    54.  
    55.     void Walking(){
    56.  
    57.     }
    58.  
    59.     void Jogging(){
    60.  
    61.     }
    62.  
    63.     //TASK FUNCTIONS
    64.  
    65.     AITask.Task.TaskFunction MoveToNextWaypoint(MoveState m){
    66.         if (NAV.hasPath){
    67.             if (Vector3.Distance (NAV.nextPosition, WAYPOINTS[0]) <= waypointAccuracy){
    68.                 WAYPOINTS.RemoveAt(0);
    69.                 NAV.ResetPath ();
    70.             }
    71.         }
    72.         else {
    73.             if (WAYPOINTS.Count > 0){
    74.                 NAV.CalculatePath(WAYPOINTS[0],NEXTWAYPOINT);
    75.                 NAV.SetPath(NEXTWAYPOINT);
    76.                 moveState = m;
    77.             }        
    78.         }
    79.     }
    80.  
    81.     //ANIMATION
    82.     void OnAnimatorMove(){
    83.         //SET NAVAGENT SPEED TO ANIMATION MOVEMENT SPEED
    84.         if (moveState != MoveState.Idle)
    85.         {
    86.             //Debug.Log ("MOVING");
    87.             //set the navAgent's velocity to the velocity of the animation clip currently playing
    88.             NAV.velocity = ANIM.deltaPosition / Time.deltaTime;
    89.  
    90.             //set the rotation in the direction of movement
    91.             transform.rotation = Quaternion.LookRotation(NAV.desiredVelocity);
    92.         }
    93.     }
    94. }
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.Linq;
    6.  
    7. public class AITask{
    8.  
    9.     public class Task {
    10.         public int priority {
    11.             get { return priority;}
    12.             set { priority = value;}
    13.         }
    14.         //Does this task need to loop? (use for low priority tasks?)
    15.         public bool taskLoop = false;
    16.  
    17.         //Set this to delete the task.
    18.         public bool Complete;
    19.  
    20.         public delegate void TaskFunction();
    21.         public TaskFunction taskFunction;
    22.  
    23.         //TYPES OF TASK
    24.  
    25.         public void doNothing(){
    26.             Debug.Log ("doing nothing");
    27.         }
    28.  
    29.         public void findFood(){
    30.             //TODO: find food code
    31.             Debug.Log ("finding food");
    32.         }
    33.  
    34.         //Do the assigned (or not) task.
    35.         public void DoTask(){
    36.             if (taskFunction!=null){
    37.                 taskFunction();
    38.             }
    39.             else {
    40.                 Debug.LogError("Task has no function assigned!");
    41.             }
    42.         }
    43.  
    44.     }
    45.  
    46.     public class TaskManager {
    47.  
    48.         public List<Task> TaskList;
    49.  
    50.         public TaskManager(){
    51.  
    52.         }
    53.  
    54.         public delegate void CurrentTask();
    55.  
    56.         public void Update(){
    57.             if (TaskList.Count > 0){
    58.                 //sort the list by priority.
    59.                 TaskList = TaskList.OrderBy(x => x.priority).ToList();
    60.                 //if the task reports itself as complete
    61.                 if (TaskList[0].Complete){
    62.                     //delete the task
    63.                     TaskList.RemoveAt (0);
    64.                     return;
    65.                 }
    66.                 if (TaskList[0] != null){
    67.                     TaskList[0].DoTask();
    68.                 }
    69.                 else {
    70.                     Debug.LogWarning("Task List - Deleted Task");
    71.                 }
    72.             }
    73.             else {
    74.                 Debug.LogWarning ("Task List - Empty!");
    75.             }
    76.          
    77.         }
    78.  
    79.         void ClearList(){
    80.             TaskList.Clear ();
    81.         }
    82.     }
    83.  
    84. }
    Assets/Civillian/CivillianBehaviour.cs(67,34): error CS0161: `CivillianBehaviour.MoveToNextWaypoint(CivillianBehaviour.MoveState)': not all code paths return a value


    I think it's maybe that fact the AITask has no access or idea about what CivillianBehaviour is doing regarding the nav calcs and stuff?

    Cheers.

    Halbera.

    EDIT: Should also point out there is a lot of stuff in there thats not actually doing anything yet, its a WIP obviously.
     
    Last edited: Apr 26, 2015
  4. pws-devs

    pws-devs

    Joined:
    Feb 2, 2015
    Posts:
    63
    The moment you use the delegate keyword, it is more like member variable than a function although it is still a function. So for a member variable you need to treat it as though it is expected to return something, although technically it's not supposed to return anything with void. How I would suggest is that you redo the delegate passing in a custom object as a parameter, which the custom object would wrap whatever other parameters you need for individual tasks.
     
    HAlbera likes this.
  5. HAlbera

    HAlbera

    Joined:
    Jun 7, 2013
    Posts:
    63
    Sorry im not following completely. So using a delegate isnt the best way of doing what i want.

    My understanding is you suggest creating another class that describes what i want it to do and assign that class its behaviour when i instantiate it as a new task?

    Sorry im not 100% on what you mean.

    Is there no way of using a delegate that doesnt care about a return type or possibly just return something arbitrary?

    Thanks for your reply!

    Halbera.
     
  6. pws-devs

    pws-devs

    Joined:
    Feb 2, 2015
    Posts:
    63
    In short, yes. I don't think that you should be using a delegate for doing a task. Delegates are more for event-driven stuff and not really iterating stuff, like what you are trying to do. So a better use of delegate would be when the task end you fire of a TaskComplete event that is a delegate, such that whoever is listening to it would know that the task is complete.
     
    HAlbera likes this.
  7. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,535
    First, I don't agree with 'pws devs', delegates aren't only for events. I use them for all sorts of things... including systems somewhat kind of similarish to what you're attempting... though I'm noticing some oddities in your code, probably stemming more from a misunderstanding of how these constructs are supposed to be syntactically structured in C#.

    1) delegates are references to a function, not calling a function. To get a reference to a function you do NOT close the parenthesis at the end. Those parens tell the compiler you're calling a function, where as if you don't have the parens, you're saying you want a reference to the function.

    You're calling MoveToNextWaypoint in your first example, and MoveToNextWaypoint is a void returning function (void is the return type of a function that doesn't return anything...) You're not saying assign the function reference, you're assigning what the function returns upon completion, which is nothing/void.

    You're supposed to say:

    Code (csharp):
    1.  
    2. newTask.taskFunction = MoveToNextWaypoint;
    3.  
    Thing is, you can't do that either, because you designed Task to accept a delegate that has NO parameters. That's the way delegates work... a function NEEDS its parameters to operate, how is the TaskManager supposed to know what to pass into the function if it doesn't even know the shape of the function... this is the whole point of the 'delegate' type. It acts as the 'interface' of the function. It allows the code to understand the shape of functions, just like interfaces allow the code to understand the shape of objects.

    Thing is, in your example, it appears you expect the same parameter to be passed in no matter what. You can get around your design by using a anonymous function.

    Code (csharp):
    1.  
    2. newTask.taskFunction = () => MoveToNextWaypoint(MoveState.Jogging);
    3.  
    What you're doing here is creating a function that calls a function with a specific parameter. The new anonymous function has zero parameters and returns nothing/void, so it matches the shape expected by the 'taskFunction' property, and therefore will work.

    2) will be followed in another post
     
    HAlbera likes this.
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,535
    2)

    WHY IN GODS NAME is Task and TaskManager inside a class named AITask? AITask isn't anything? Like, it literally serves zero purpose... except maybe to serve as a naming convention.

    Now instead of saying:

    Code (csharp):
    1.  
    2. var task = new Task();
    3.  
    You instead have to say:

    Code (csharp):
    1.  
    2. var task = new AITask.Task();
    3.  
    ... have you not heard of namespaces?

    https://msdn.microsoft.com/en-us/library/z2kcy19k.aspx

    AITask literally serves no other purpose. So why make it a class? What happens if I create an AITask object?

    Code (csharp):
    1.  
    2. var obj = new AITask();
    3.  
    Nothing... that's it. Nothing. AITask has no purpose.
     
    HAlbera likes this.
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,535
    3) now, as for your Task/TaskManager thing.

    What is this for? What are you trying to accomplish with it?

    In your example you post, well... you create an instance of TaskManager, but you never call any of the functions on it. It never has update called on it. You do know that only MonoBehaviours that are attached to GameObjects will implicitly have a function called 'Update' called automatically on it, right?

    Furthermore, you're every frame adding a task (on Update inside your CivillianBehaviour) to the TaskManager you never actually operate. Thing is... you never remove it either. All you're going to do is create the same task stacked on top of itself forever... infinitely... yeah, that's not going to go well.

    None of these flaws have really anything to do with delegates. Like I said, I use delegates a lot for very similar things. It's just that your design in general lacks really any general structure to it that makes sense.

    So I ask again, what are you attempting to accomplish?

    See, a class should perform a... well... a task of some sort. That task, or job, should be easily described. So describe it...
     
    HAlbera, pws-devs and Fajlworks like this.
  10. pws-devs

    pws-devs

    Joined:
    Feb 2, 2015
    Posts:
    63
    Well explained. I can't explain as well as you do. But then, it's kinda hard for a beginner to swallow down if you noticed, you are trying to explain delegates to a beginner. I just do an approach of "just don't use delegates for anything else" to explain to a beginner.
     
  11. HAlbera

    HAlbera

    Joined:
    Jun 7, 2013
    Posts:
    63
    Wow, thanks for taking the time to respond! I'm pretty much ditching the whole idea of delegates until I have learnt them and fully understand why they are useful. I just had it in my head that a delegate could work for what I was trying. I have re-worked my idea a little in the mean time to come up with what I think is a more elegant solution (for a beginner.)

    I am having a probably very simple problem with my new approach. Here are the full classes.

    Task.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. public abstract class Task {
    6.  
    7.     private bool Initialised = false;
    8.  
    9.     public int Priority { get; set; }
    10.     public int TaskID { get; set; }
    11.  
    12.     //Reference to the GameObject that is using this task. Required!
    13.     public GameObject ThisGameObject { get; set; }
    14.  
    15.     public enum TaskState {
    16.  
    17.         DoingNothing,
    18.         Executing,
    19.         Finished,
    20.         Failed = -1
    21.  
    22.     }
    23.  
    24.     public TaskState taskState;
    25.  
    26.     //Constructor, checks the task has been setup properly.
    27.     public Task (){
    28.  
    29.     }
    30.     void Main(){
    31.         if (!Initialised){
    32.             TaskSetupCheck();
    33.             Initialised = true;
    34.         }
    35.     }
    36.  
    37.     private void TaskSetupCheck(){
    38.         if (Priority == 0 || TaskID == 0 || ThisGameObject == null){
    39.             Debug.LogWarning("Task was not setup correctly - TaskID (int) and Priority (int) need values and GameObject reference is needed...");
    40.             taskState = TaskState.Failed;
    41.         }
    42.     }
    43.  
    44.     //Execute() needs to be called in update of the TaskManager.
    45.     public abstract void Execute();
    46.  
    47. }
    48.  
    49. public class MoveTask : Task {
    50.  
    51.     private bool Initialised = false;
    52.  
    53.     public Vector3 DestinationPosition { get; set; }
    54.     public NavMeshAgent Agent;
    55.  
    56.     public MoveTask (){
    57.  
    58.     }
    59.  
    60.     void Main(){
    61.         if (!Initialised){
    62.             MoveTaskSetupCheck();
    63.             Initialised = true;
    64.         }
    65.     }
    66.  
    67.     private void MoveTaskSetupCheck(){
    68.         if (DestinationPosition == null || Agent == null){
    69.             Debug.LogWarning("MoveTask was not setup correctly - DestinationPosition (Vector3) needs a value and NavMeshAgent reference is needed...");
    70.             taskState = TaskState.Failed;
    71.         }
    72.     }
    73.  
    74.     //Execute() needs to be called in update of the TaskManager.
    75.     public override void Execute(){
    76.         taskState = TaskState.Executing;
    77.         Agent.SetDestination(DestinationPosition);
    78.  
    79.     }
    80. }
    81.  
    82. public class Eat : Task {
    83.  
    84.     public GameObject Food { get; set; }
    85.  
    86.     //Execute() needs to be called in update of the TaskManager.
    87.     public override void Execute(){
    88.  
    89.     }
    90. }
    TaskManager.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5.  
    6. public class TaskManager : MonoBehaviour {
    7.  
    8.     public List<Task> TaskList;
    9.  
    10.     void Start(){
    11.         TaskList = new List<Task>();
    12.     }
    13.  
    14.     void SortListByPriority(){
    15.         if (TaskList .Count > 0){
    16.             TaskList = TaskList.OrderBy(x => x.Priority).ToList();
    17.         }
    18.     }
    19.  
    20.     void Update(){
    21.         //If the TaskList isn't empty;
    22.         if (TaskList.Count > 0){
    23.             //If the next task in TaskList is flagged as failed then delete it.
    24.             if (TaskList[0].taskState == Task.TaskState.Failed || TaskList[0].taskState == Task.TaskState.Finished){
    25.                 TaskList.RemoveAt(0);
    26.                 Debug.Log ("RemovedTask");
    27.             }
    28.             //Else continue with processing the task list.
    29.             else {
    30.                 TaskList[0].Execute();
    31.             }
    32.         }
    33.     }
    34.  
    35. }
    Testies.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4.  
    5. public class Testies : MonoBehaviour {
    6.  
    7.     TaskManager taskManager;
    8.  
    9.     MoveTask mt;
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.         taskManager = GetComponent<TaskManager>();
    14.         mt = new MoveTask(){
    15.             Priority = 1,
    16.             TaskID = 001,
    17.             ThisGameObject = gameObject,
    18.             Agent = GetComponent<NavMeshAgent>(),
    19.             DestinationPosition = new Vector3(0,0,0)
    20.         };
    21.  
    22.         Debug.Log (mt.TaskID);
    23.  
    24.         taskManager.TaskList.Add(mt); << THIS LINE.
    25.     }
    26.  
    27.     // Update is called once per frame
    28.     void Update () {
    29.  
    30.     }
    31. }
    NullReferenceException: Object reference not set to an instance of an object
    Testies.Start () (at Assets/Testies.cs:24)

    The Testies.cs and the TaskManager.cs scripts are both attached to a GameObject in the scene.

    Obviously the Testies.cs script is purely for trying stuff out with the Task system. What makes my brain hurt is that I have them both attached to the main camera before moving them over to a simple capsule. I had no compile errors before switching them over. I just can't see what I've missed?

    Thankyou again for the replies!

    EDIT: I've removed the waypoint system just to get my head around the task system initially. I'm fairly sure I can plot a waypoint system using this new task system much more easily anyhow.

    In response to your asking what the point of it is =] I just need a way to stack up things that an AI controlled character needs to do in a very simple way, but it has to include priority sorting.

    The end goal is to be able to create a new 'task' with all the properties its needs, then let the task manager of that ai to sort out what it needs the most. If its hunger level gets low, create a new 'task' with a certain priority. If it again gets lower, search and find the existing task and if it exists raise its priority. If the AI gets damaged or hurt or it needs a cuddle, make a task... Im not looking for sentience, just a good simple todo list.

    I want the priority system to add just a dash of humanity to the AI.

    Halbera.
     
    Last edited: May 9, 2015
  12. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,535
    So your problem is most like that List<Task> is not serializable, since Task is not serializable. This means that Unity when deserializing isn't going to create an instance of the list for you.

    This means the only list that gets created is the one that is done in 'Start' of TaskManager... but you try to access it in 'Start' of 'Testies' ( ::snicker::, I see what you did there...). Here's the thing, you don't know which order Start is really going to be called (unless you manually set the execution order).

    This is what 'Awake' is for. Awake should initialize everything local to a class, Start should access other scripts. This way the order doesn't matter... you just know all Awakes are called before all Starts.
     
    HAlbera likes this.
  13. HAlbera

    HAlbera

    Joined:
    Jun 7, 2013
    Posts:
    63
    Okay so I've just got back to the computer (I work shifts) and read your reply lordofduct. I have moved all my initialisations to Awake() and it now works perfectly. You have been a massive help. While I'll probably never actually setup tasks in Start() more likely dynamically at runtime, this has taught me good practise for my coding. So again, thank you.

    Coincidently this might explain why moving the scripts to a different gameobject caused my code to break. I assume the execution order of scripts is linked to the order in which they are attached in the inspector. With your help I'm not going to suffer from that problem again.

    Many thanks lordofduct!

    Halbera.

    EDIT: Okay, it looks like I'm having more problems with the order things are executed.

    Does a unity c# script run Main() at all? I have put a Debug.Log in there and it seems to not be called. I really don't want to inherit from Monobehaviour for these scripts just to use Awake and Start. I just want to use the c# constructors and Main().

    The contructor seems to be called BEFORE the properties have been set by the script that instantiates that class. How the hell do I instantiate the class, set the properties and THEN check if those properties have been set?
     
    Last edited: May 11, 2015
  14. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,535
    Awake and Start are only for MonoBehaviours attached to a GameObject. If the class you're creating is never attached to a GameObject, don't inherit from MonoBehaviour, and don't use Awake and Start, instead use the constructor function.

    Of course, this means you won't have access to Update, and other messages. And instead you'll have to code some way to send those messages to it if they need them.
     
    HAlbera likes this.
  15. HAlbera

    HAlbera

    Joined:
    Jun 7, 2013
    Posts:
    63
    Right, well this was far more in depth than I had hoped it would get but I've finally achieved something that feels good. I couldn't get my head around what is being called first and blah blah. I also realised that there was NO reason to have a enum Task.TaskState, that was silly. I also learnt that the NavMeshAgent needs a little time to compute its path and nearly slammed my face into the desk working that out, I assume it calls a coroutine of some kind to calculate a path.

    What I will do Is rework the NavMesh stuff and try not to use set destination, rather use the calculatePath and hasPath functions to better control the flow of the program.

    What I have now works, and it works beautifully, except I get an out of range exception when the task list is empty, but that does not break the game and I'm assuming it's purely to do with the fact I'm removing tasks from the list then trying to access them again.

    As it is now though, it works!

    TaskManager.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5.  
    6. public class TaskManager : MonoBehaviour {
    7.  
    8.     public List<Task> TaskList;
    9.  
    10.     void Awake(){
    11.         TaskList = new List<Task>();
    12.     }
    13.  
    14.     void Start(){
    15.  
    16.     }
    17.  
    18.     void SortListByPriority(){
    19.         if (TaskList .Count > 0){
    20.             TaskList = TaskList.OrderBy(x => x.Priority).ToList();
    21.         }
    22.         else return;
    23.     }
    24.  
    25.     void Update(){
    26.         //If the TaskList isn't empty;
    27.         if (TaskList.Count > 0){
    28.             //If this task is not initialised, initialise it.
    29.             if (!TaskList[0].Initialised){
    30.                 TaskList[0].Initialise();
    31.             }
    32.             //If it is initialised and valid...
    33.             if (TaskList[0].Initialised && TaskList[0].Valid){
    34.  
    35.                 //AND decalres itself finished...
    36.                 if (TaskList[0].Finished ()){
    37.                     //Remove it.
    38.                     Debug.Log ("Task finished, removing from list!");
    39.                     TaskList.RemoveAt(0);
    40.                 }
    41.                 //Otherwise, call execute on the task.
    42.                 else {
    43.                     TaskList[0].Execute ();
    44.                 }
    45.  
    46.                 //If the Task is reporting itself as totally invalid, then remove it!
    47.                 if (!TaskList[0].Valid){
    48.                     Debug.LogWarning("Invalid Task, removing from list!");
    49.                     TaskList.RemoveAt(0);
    50.                 }
    51.             }
    52.         }
    53.         else {
    54.             Debug.Log ("Task List Empty");
    55.         }
    56.     }
    57.    
    58. }
    59.  
    Task.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. public abstract class Task {
    6.  
    7.     //Returns true if the conditions declared by the Task are met.
    8.     public abstract bool Valid { get; }
    9.  
    10.     //Returns true if the Task has had Initialise() called.
    11.     public bool Initialised { get; set; }
    12.  
    13.     //Used for sorting Tasks.
    14.     public int Priority { get; set; }
    15.     public int TaskID { get; set; }
    16.  
    17.     //Reference to the GameObject that is using this Task. Required!
    18.     public GameObject ThisGameObject { get; set; }
    19.  
    20.     //Constructor.
    21.     public Task(){
    22.         Initialised = false;
    23.     }
    24.  
    25.     //To be called before Execute or the Task will (probably) fail, depending on whether the task needs to initialise at all.
    26.     public abstract void Initialise();
    27.  
    28.     //Allows the TaskManager to check if a task has finished, each task defines it's own rules as to what finished means.
    29.     public abstract bool Finished();  
    30.  
    31.     //Execute() needs to be called in update of the TaskManager.
    32.     public abstract void Execute();
    33.  
    34. }
    35.  
    36. public class MoveTask : Task {
    37.  
    38.     //The Game World Coordinates for the NavMeshAgent to head towards.
    39.     public Vector3 DestinationPosition { get; set; }
    40.     //Agent reference.
    41.     public NavMeshAgent Agent;
    42.  
    43.     //Constructor
    44.     public MoveTask(){
    45.         Initialised = false;
    46.     }
    47.  
    48.     //Called to check if the task has been setup correctly, returns true if everything seems right.
    49.     private bool SetupCheck(){
    50.         if (Agent == null || Priority == 0 || TaskID == 0 || ThisGameObject == null){
    51.             Debug.LogWarning("MoveTask was not setup correctly...");
    52.             return false;
    53.         }
    54.         else {
    55.             return true;
    56.         }
    57.     }
    58.  
    59.     //This tasks implementation of Valid() simply relays the output of the function SetupCheck() waste of a call right now but maybe useful for later Tasks?
    60.     public override bool Valid{
    61.         get {
    62.             if (!SetupCheck()){
    63.             return false;
    64.             }
    65.             else {
    66.             return true;
    67.             }
    68.         }
    69.     }
    70.     //This Tasks implementation of Initilise() simply sets the NavMeshAgents 'DestinationPosition'.
    71.     public override void Initialise (){
    72.         Agent.SetDestination(DestinationPosition);
    73.         //IMPORTANT that this is now set to true. The TaskManager relies on this variable.
    74.         Initialised = true;
    75.     }
    76.  
    77.     //Execute() needs to be called in update of the TaskManager. Setting the destination doesn't need to be done in each update,
    78.     //but might need to change later to check for updates to the destination. As it is, this Task will only move to a fixed point.
    79.     public override void Execute(){
    80.         Debug.Log (Agent.remainingDistance);
    81.     }
    82.  
    83.     bool HasReachedDestination(){
    84.         //IMPORTANT! If the agent is still calculating its route then leave it alone or program flow will be F***ed.
    85.         if (!Agent.pathPending){
    86.             //If the path is NOT complete or totally invalid, return false.
    87.             if (Agent.pathStatus == NavMeshPathStatus.PathInvalid || Agent.pathStatus == NavMeshPathStatus.PathPartial){
    88.                 Debug.Log ("Path invalid or incomplete");
    89.                 return false;
    90.             }
    91.             //If the path is complete (valid) and it is within 0.1f of the target then return true.
    92.             if (Agent.pathStatus == NavMeshPathStatus.PathComplete && Agent.remainingDistance < 0.1f){
    93.                 Debug.Log ("Path complete and dist close");
    94.                 return true;
    95.             }
    96.             return false;
    97.         }
    98.         //Otherwise, just return false and allow the agent to continue processing or moving.
    99.         else return false;      
    100.     }
    101.  
    102.     //This Task defines if it is finished purely by the return of HasReachedDestination(). Note 'HasReachedDe...' currently cannot tell if it hasnt reached
    103.     //its destination because it is blocked or unreachable, possibly need to just set this Task to invalid if the spot is blocked and allow the AI to set
    104.     //itself another Task when needed.
    105.     public override bool Finished(){
    106.         if (HasReachedDestination()){
    107.             return true;
    108.         }
    109.         else return false;
    110.     }
    111.  
    112. }
    113.  
    114. //Nothing implemented yet.
    115. public class Eat : Task {
    116.    
    117.     public GameObject Food { get; set; }
    118.  
    119.     public override bool Valid {
    120.         get {
    121.             throw new NotImplementedException ();
    122.         }
    123.     }
    124.  
    125.     public override void Initialise ()
    126.     {
    127.         throw new NotImplementedException ();
    128.     }
    129.  
    130.     public override bool Finished (){
    131.         throw new NotImplementedException ();
    132.     }
    133.  
    134.     //Execute() needs to be called in update of the TaskManager.
    135.     public override void Execute(){
    136.  
    137.     }
    138. }
    139.  
    Testies.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4.  
    5. public class Testies : MonoBehaviour {
    6.  
    7.     TaskManager taskManager;
    8.  
    9.     MoveTask mt;
    10.  
    11.     // Use this for initialization
    12.  
    13.     void Awake(){
    14.         taskManager = GetComponent<TaskManager>();
    15.  
    16.     }
    17.     void Start () {
    18.         Invoke("NewTask",0.5f);
    19.  
    20.     }
    21.     //Just for testing, constructs a new task after half a second and adds it to the list.
    22.     void NewTask(){
    23.         mt = new MoveTask(){
    24.             Priority = 1,
    25.             TaskID = 001,
    26.             ThisGameObject = gameObject,
    27.             Agent = GetComponent<NavMeshAgent>(),
    28.             DestinationPosition = new Vector3(0,0,0)
    29.         };
    30.         taskManager.TaskList.Add(mt);
    31.     }
    32.  
    33.     // Update is called once per frame
    34.     void Update () {
    35.    
    36.     }
    37. }
    38.  
    Thanks again lordofduct!

    Halbera.