Decorator not getting any update tick

VeryBerry

New member
Hi,

I wanted to know the rationale behind Decorators not getting the update and Decorate function being called only after the child is executed.
This is quite limiting in comparison with the implementation of decorator in UE4.

I wanted to propose the following call flow. instead of decorators getting an update tick, the Decorate function is to be called after each child update tick so the decorator be able to stop or alter child execution status in each tick ( for example a timeout decorator could be very simply implemented with such functionality. the current decorator for timeout only takes effect after the child is executed so if the child's task is taking too long, like a moveTo or a follow task, it cannot be stopped )
 
Also I have noticed the canExecute is not causing the task to fail if it returns false. this is problematic since it creates scenarios that cannot be distinguished

Currently:
  • decorator cannot execute -> decorator returns success
  • decorator can execute and task returns success -> decorator returns success
  • decorator can execute and task returns failure -> decorator returns failure
How I expect it to work
  • decorator cannot execute -> decorator returns failure
  • decorator can execute and task returns success -> decorator returns success
  • decorator can execute and task returns failure -> decorator returns failure

when a decorator cannot execute a child returning a failure should be default for composite such as a sequence that cannot go through
also conceptually it is more coherent and easier to understand. since returning success means both decorator and task were successful
 
With the current design the decorator only updates after the child has returned a non-running status. I do like your suggestion, but at this point I want to wait until version 2 before making a possibly breaking change.

In your situation I recommend downloading the runtime source and making the modification to the behavior manager. This will prevent you from having to wait.
 
Thanks a lot. didn't know I can get the runtime source code. amazing I already have it working
The modification is quite minor.

Firstly we would need an enumerator in order to select the order of calling the decorator
Code:
    /// <summary>
    /// Describes when a parent task gonna call its Decorate function
    /// </summary>
    public enum DecorateExecutionOrder
    {
        /// <summary>
        /// Gets executed after the child task is executed and has any result
        /// besides <seealso cref="TaskStatus.Inactive"/> or <seealso cref="TaskStatus.Running"/>
        /// </summary>
        AfterChildExecuted,

        /// <summary>
        /// Gets executed after each time task update function gets called.
        /// </summary>
        AfterChildUpdated
    }

The parent task needs to extend to indicate how the decorator function needs to be called
Code:
        /// <summary>
        /// Gets the how this parent will execute the decorate function
        /// </summary>
        public virtual DecorateExecutionOrder GetDecorateExecutionOrder()
        {
            return DecorateExecutionOrder.AfterChildExecuted;
        }

A tiny change in BehaviorManager.cs is needed but i don't know if I am allowed to post it here

assuming that is done you would need a new decorator base class
Code:
using BehaviorDesigner.Runtime.Tasks;

/// <summary>
/// This is a base class for decorators which need to evaluate and/or change the status of their child task
/// on each tick. the Decorate function of the derived class would be called on each tick of the child task
/// </summary>
public abstract class ContinousDecorator : Decorator
{
    /// <summary>
    /// Gets the execution result for the child of the decorator.
    /// For the derived class this value is up to date after OnChildExecuted is called on the base class
    /// </summary>
    public TaskStatus childExecutionResult
    {
        get;
        protected set;
    }

    public override bool CanExecute()
    {
        return childExecutionResult == TaskStatus.Inactive || childExecutionResult == TaskStatus.Running;
    }

    public override void OnChildExecuted( TaskStatus childStatus )
    {
        childExecutionResult = childStatus;
    }

    public override void OnEnd()
    {
        childExecutionResult = TaskStatus.Inactive;
    }

    public override DecorateExecutionOrder GetDecorateExecutionOrder()
    {
        return DecorateExecutionOrder.AfterChildUpdated;
    }
}


now this is how I used ContinousDecorator to make a timeout
Code:
public class Timeout : ContinousDecorator
{
    [Tooltip( "The maximum time the child task has to execute" )]
    public float maxExecutionTime = 1f;

    private float taskStartingTime;

    public override void OnStart()
    {
        taskStartingTime = Time.time;
    }

    public override TaskStatus Decorate( TaskStatus status )
    {
        return taskStartingTime + maxExecutionTime >= Time.time ? status : TaskStatus.Failure;
    }
}
 
Top