/// <summary>
    /// Function to apply the task of this primative node to change the world state
    /// </summary>
    public void applyTask(HTN_WorldState worldState)
    {
        Task taskName = this.taskName;

        // use switch of the task name to check precondition (maybe have a better way to do it??)
        switch (taskName)
        {
        //case Task.ROOT_TASK:
        //return true;  // since root task has not precondition

        // Primitive Tasks:
        case Task.MOVE_TO_CRATE:
            worldState.isInFrontOfObstacle = true;
            break;

        case Task.MOVE_TO_ROCK:
            worldState.isInFrontOfObstacle = true;
            break;

        case Task.THROW_CRATE:

        case Task.THROW_ROCK:

        case Task.WANDER:
            break;      // wander won't have a effect on the Game state
        }
    }
示例#2
0
 /// <summary>
 /// Function to update the world state in this planner
 /// </summary>
 /// <param name="ws"></param>
 public void updateWorldState(HTN_WorldState ws)
 {
     this.currentWorldState.isInAttackRange              = ws.isInAttackRange;
     this.currentWorldState.isInFrontOfObstacle          = ws.isInFrontOfObstacle;
     this.currentWorldState.hasCratesInRange             = ws.hasCratesInRange;
     this.currentWorldState.isTheChosenObstacleDestroyed = ws.isTheChosenObstacleDestroyed;
 }
    /// <summary>
    /// Function to verify the precondition of the node using the current worldState and the Task name (enum)
    /// </summary>
    /// <param name="worldState"></param>
    /// <returns>Return true if the preconditions are satisfied by the current World State, otherwise return false</returns>
    public bool verifyPrecondition(HTN_WorldState worldState)
    {
        Task taskName = this.taskName;

        /*
         * // use switch of the task name to check precondition (maybe have a better way to do it??)
         * switch (taskName)
         * {
         *  //case Task.ROOT_TASK:
         *      //return true;  // since root task has not precondition
         *
         *  // Method Tasks:
         *  case Task.DISTANCE_ATTACK:
         *      // if the task is the Method: DISTANCE_ATTACK, check the world state
         *      if (worldState.isInAttackRange && worldState.hasObstablesOnMap)
         *      {
         *          return true;
         *      }
         *      else
         *      {
         *          return false;
         *      }
         *  case Task.CRATE_ATTACK:
         *      // if the task is the Method: DISTANCE_ATTACK, check the world state
         *      if (worldState.isInAttackRange && worldState.hasObstablesOnMap)
         *      {
         *          return true;
         *      }
         *      else
         *      {
         *          return false;
         *      }
         *  case Task.ROCK_ATTACK:
         *      // if the task is the Method: DISTANCE_ATTACK, check the world state
         *      if (worldState.isInAttackRange && worldState.hasObstablesOnMap)
         *      {
         *          return true;
         *      }
         *      else
         *      {
         *          return false;
         *      }
         *  case Task.IDLE:
         *      // if the task is the Method: IDLE, return always true, since it doesn't depend on the World State
         *      return true;
         * }
         */
        //return false;  // for unexcepted error

        // for simplify the tree, we only put precondition for primitive task, so every method will be valid
        return(true);
    }
示例#4
0
    /// <summary>
    /// Funtion to return the first method that the World State satisfies its precondition
    /// (This function will only be called by a Compound node)
    /// </summary>
    /// <param name="worldState"></param>
    /// <returns></returns>
    public HTN_MethodNode findSatisfiedMethod(HTN_WorldState worldState)
    {
        // iterate all the chilren of this compound method (which should always be Method nodes)
        foreach (HTN_MethodNode method in this.children)
        {
            // return the first Method node which its precondition is satisfied by the current World State
            if (method.verifyPrecondition(worldState))
            {
                return(method);
            }
        }

        // if not found, return null
        return(null);
    }
示例#5
0
    /// <summary>
    /// Funtion to return all methods that the World State satisfies its precondition
    /// (This function will only be called by a Compound node)
    /// </summary>
    /// <param name="worldState"></param>
    /// <returns></returns>
    public ArrayList findSatisfiedMethods(HTN_WorldState worldState)
    {
        ArrayList satisfiedMethods = new ArrayList();

        // iterate all the chilren of this compound method (which should always be Method nodes)
        foreach (HTN_MethodNode method in this.children)
        {
            // return the first Method node which its precondition is satisfied by the current World State
            if (method.verifyPrecondition(worldState))
            {
                satisfiedMethods.Add(method);
            }
        }


        return(satisfiedMethods);
    }
    // --------------------------

    // Start is called before the first frame update
    void Start()
    {
        navMeshAgent             = this.gameObject.GetComponent <NavMeshAgent>();
        navMeshAgent.autoBraking = false;                    // disable auto braking for continuous movement

        this.monsterYellingText.gameObject.SetActive(false); // disable UI yelling text

        // generate an HTN tree for this monster
        rootNode = generateHTNtree();

        // generate a World State
        HTN_WorldState ws = new HTN_WorldState(false, false, false, false, false);  // set all false initially, let the update function to update WorldState

        this.worldState = ws;

        // create a HTN planner
        planner = new HTN_Planner(this, worldState);
    }
    /// <summary>
    /// Function to update the world state for this monster's HTN
    /// </summary>
    /// <param name="ws"></param>
    void updateWorldState(HTN_WorldState ws)
    {
        // update isInAttackRange
        if (Vector3.Distance(this.gameObject.transform.position, player.gameObject.transform.position) <= attackRange)
        {
            ws.isInAttackRange = true;
        }
        else
        {
            ws.isInAttackRange = false;
        }

        // update isInAttackRange
        if (Vector3.Distance(this.gameObject.transform.position, player.gameObject.transform.position) <= yellingRange)
        {
            ws.isInYellingRange = true;
        }
        else
        {
            ws.isInYellingRange = false;
        }


        // update hasCratesInRange, check if there is crate in attack range
        bool hasCrates = false;

        foreach (GameObject obstacle in gameInitalizer.getObstacleList())
        {
            if (obstacle.tag == "Crate" && Vector3.Distance(this.gameObject.transform.position, obstacle.transform.position) <= attackRange)
            {
                hasCrates = true;
                break;
            }
        }
        if (hasCrates)
        {
            ws.hasCratesInRange = true;
        }
        else
        {
            ws.hasCratesInRange = false;
        }


        // update isInFrontOfObstacle
        if (this.obstacleToBeThrown)
        {
            if (Vector3.Distance(this.gameObject.transform.position, this.obstacleToBeThrown.transform.position) <= 6f)
            {
                ws.isInFrontOfObstacle = true;
            }
            else
            {
                ws.isInFrontOfObstacle = false;
            }
        }

        // update isTheChosenCrateDestroyed
        if (this.obstacleToBeThrown)
        {
            // if the current chosen obstacle is destroyed, change the world state isTheChosenCrateDestroyed to true
            if (this.obstacleToBeThrown.gameObject == null)
            {
                ws.isTheChosenObstacleDestroyed = true;
                //this.obstacleToBeThrown = null;
            }
            else
            {
                ws.isTheChosenObstacleDestroyed = false;
            }
        }

        // updata the world state in planner
        this.planner.updateWorldState(this.worldState);
    }
    /// <summary>
    /// Function to verify the precondition of the node using the current worldState and the Task name (enum)
    /// </summary>
    /// <param name="worldState"></param>
    /// <returns>Return true if the preconditions are satisfied by the current World State, otherwise return false</returns>
    public bool verifyPrecondition(HTN_WorldState worldState)
    {
        Task taskName = this.taskName;

        // use switch of the task name to check precondition (maybe have a better way to do it??)
        switch (taskName)
        {
        //case Task.ROOT_TASK:
        //return true;  // since root task has not precondition

        // Primitive Tasks:
        case Task.MOVE_TO_CRATE:
            // if the task is the Primitive task: MOVE_TO_CRATE, check the world state
            if (worldState.isInAttackRange && worldState.hasCratesInRange)
            {
                return(true);
            }
            else
            {
                return(false);
            }


        case Task.MOVE_TO_ROCK:
            // if the task is the Primitive task: MOVE_TO_ROCK, check the world state
            if (worldState.isInAttackRange)
            {
                return(true);
            }
            else
            {
                return(false);
            }

        case Task.THROW_CRATE:
            // if the task is the Primitive task: THROW_CRATE, check the world state
            if (worldState.isInFrontOfObstacle && worldState.isInAttackRange && !worldState.isTheChosenObstacleDestroyed)
            {
                return(true);
            }
            else
            {
                return(false);
            }

        case Task.THROW_ROCK:
            // if the task is the Primitive task: THROW_ROCK, check the world state
            if (worldState.isInFrontOfObstacle && worldState.isInAttackRange && !worldState.isTheChosenObstacleDestroyed)
            {
                return(true);
            }
            else
            {
                return(false);
            }

        case Task.YELLING:
            // if the task is the Primitive task: YELLING, check the world state
            if (worldState.isInYellingRange)
            {
                return(true);
            }
            else
            {
                return(false);
            }

        case Task.WANDER:
            return(true);         // return always true for wander task
        }

        return(false);  // for unexcepted error
    }
示例#9
0
    /// <summary>
    /// Function to generate a plan for a given root node of HTN tree
    /// </summary>
    /// <param name="rootNode"></param>
    public void planning(HTN_node rootNode)
    {
        HTN_WorldState   state         = this.currentWorldState.clone(); // make a copy of the current state
        ArrayList        finalPlanList = new ArrayList();
        Stack <HTN_node> tasksNodes    = new Stack <HTN_node>();

        // stack for store Method node for backtracking
        Stack <ArrayList> backTrackingMethods = new Stack <ArrayList>();  // each list in this stack is a list of methods
        // stack for store planList for backtracking
        Stack <ArrayList>      backTrackingPlan = new Stack <ArrayList>();
        Stack <HTN_WorldState> states           = new Stack <HTN_WorldState>(); // store the copy of states for backtracking


        tasksNodes.Push(rootNode);  // put the root node into the task stack

        // while the stack is not empty
        while (!(tasksNodes.Count == 0))
        {
            HTN_node taskNode = tasksNodes.Pop();

            // if the node is a compound node
            if (taskNode is HTN_CompoundNode)
            {
                ArrayList methods = ((HTN_CompoundNode)taskNode).findSatisfiedMethods(this.currentWorldState);
                //HTN_MethodNode method = ((HTN_CompoundNode)taskNode).findSatisfiedMethod(this.currentWorldState);
                HTN_MethodNode method = (HTN_MethodNode)methods[0];
                methods.RemoveAt(0);

                if (method != null)
                {
                    // store the plan and the next method node for backtracking when needed
                    backTrackingPlan.Push(new ArrayList(finalPlanList));  // clone the current plan list
                    backTrackingMethods.Push(methods);
                    states.Push(state.clone());

                    // push the subtasks of this method into the list (push backward since we want the first subtask to be pop next)
                    for (int i = method.getChildren().Count - 1; i >= 0; i--)
                    {
                        tasksNodes.Push((HTN_node)method.getChildren()[i]);
                    }
                }
                else
                {
                    // restore to the saved backtracking state
                    state         = states.Pop();
                    finalPlanList = backTrackingPlan.Pop();
                    tasksNodes.Clear();  // empty the tasksNode stack

                    // pop a methods list
                    ArrayList btMethods = backTrackingMethods.Pop();
                    while (btMethods.Count == 0)
                    {
                        btMethods = backTrackingMethods.Pop();
                    }

                    // get the first method in that list
                    HTN_MethodNode btMethodNode = (HTN_MethodNode)btMethods[0];
                    btMethods.RemoveAt(0);  // delete that method from the list
                    if (btMethods.Count > 0)
                    {
                        backTrackingMethods.Push(btMethods);  // put the method list back
                    }
                    // push the subtasks of this method into the list (push backward since we want the first subtask to be pop next)
                    for (int i = btMethodNode.getChildren().Count - 1; i >= 0; i--)
                    {
                        tasksNodes.Push((HTN_node)btMethodNode.getChildren()[i]);
                    }
                }
            }
            else  // primative node
            {
                // if the precondition is met
                if (((HTN_PrimitiveNode)taskNode).verifyPrecondition(state))
                {
                    // apply the effect of this task on the world state
                    ((HTN_PrimitiveNode)taskNode).applyTask(state);
                    finalPlanList.Add(taskNode);  // add this taskNode to the plan list

                    // if this primitive task is the last in the stack, our planning is finish
                    if (tasksNodes.Count == 0)
                    {
                        break;
                    }
                }
                else
                {
                    // restore to the saved backtracking state
                    state         = states.Pop();
                    finalPlanList = backTrackingPlan.Pop();
                    tasksNodes.Clear();  // empty the tasksNode stack

                    // pop a methods list
                    ArrayList btMethods = backTrackingMethods.Pop();
                    while (btMethods.Count == 0)
                    {
                        btMethods = backTrackingMethods.Pop();
                    }

                    // get the first method in that list
                    HTN_MethodNode btMethodNode = (HTN_MethodNode)btMethods[0];
                    btMethods.RemoveAt(0);  // delete that method from the list
                    if (btMethods.Count > 0)
                    {
                        backTrackingMethods.Push(btMethods);  // put the method list back
                    }
                    // push the subtasks of this method into the list (push backward since we want the first subtask to be pop next)
                    for (int i = btMethodNode.getChildren().Count - 1; i >= 0; i--)
                    {
                        tasksNodes.Push((HTN_node)btMethodNode.getChildren()[i]);
                    }
                }
            }
        }

        this.plan = finalPlanList;  // set the current plan to the final plan list
    }
示例#10
0
    private float timer = 0f;                 // using for applying 1second cool-down between each plan

    //private bool hasReplanning = false;  // used for update UI text

    public HTN_Planner(System.Object agent, HTN_WorldState ws)
    {
        this.agent             = agent;
        this.currentWorldState = ws;
    }