| Home Page | Recent Changes

Extending States

States can be extended in the same way as classes. A state that extends another inherits functions of its parent state, and can of course override them or add new ones.

Extending states is probably most useful within a single class. With a child class, there is already inheritance: the Child's state Foo inherits the Parent's state Foo anyway. Extending states is useful if you have a state Foo and you also want a state FooSpecialCase, perhaps.

Example

(needs to be rewritten, as is overly complex...)

Suppose we have a class Monster and a subclass BugEyedMonster.

Monster has the following states:

  • Idle
  • Attacking

BugEyedMonster inherits these states. We can do the following:

Add to an Inherited State

e.g. Add a function RollEyes to the Attacking state. (though I'm not sure what happens to label code outside of functions. help!)

AFAIK label code outside of functions are simply executed when control is passed there via the Goto() command. Epic pretty much always have a set of function calls after the label which invariably change the state of the object to make it do more stuff. When defining a state they place all of the functions within the state first and then have a series of labels after the functions that do appropriate "stuff". In subclasses it looks like you can redefine labels to "override them" but you can never call up to the label defined in the superclass once it has been overidden in the subclass's state. – EntropicLqd

Add a New State

e.g. RunningAway (because BugEyedMonsters are very cowardly)

This state will inherit functions from BugEyedMonster's null state

Extend a State

e.g. ScratchingButt extends Idle. We'd want most of the functionality of Idle, but with an extra occasional animation. We'd also have to add to our inherited Idle state to sometimes switch to ScratchingButt depending on circumstances.

I've probably got it all wrong, but if it makes Mych laugh enough he may fix it. :-)Tarquin

So if we consider a function present in Monster, eg Trigger, we now have this in several flavours:

In Monster we have:

  • Trigger() in null state
    • Idle.Trigger() inherits null.Trigger unless we override
    • Atacking.Trigger() inherits null.Trigger unless we override

In BugEyedMonster we have

  • Trigger() in null state, inherits Monster.Trigger unless we override
    • Idle.Trigger() – this is the confusing one. see below
    • Atacking.Trigger()
    • ScratchingButt.Trigger() inherits Idle.Trigger() unless we override

from UnrealScript Language Reference/States

The scoping rules, which resolves these complex situations, are:

  • If the object is in a state, and an implementation of the function exists somewhere in that state (either in the actor?s class or in some parent class), the most-derived state version of the function is called.
  • Otherwise, the most-derived non-state version of the function is called.

Examples

Taken from a Discussion on UnrealScript Language Test:

Wormbo: What I mean with "super states" is:

class SomeClass extends Actor;

// Super() compiler bug:
function A()
{
  // super call to sibling class function that doesn't exist in this or the super class
  Super(DestroyableTrigger).SpawnEffects(); // compiles and even executes! (spawns some visual effects)
}

// Super states:

// new function, does not exist in Actor
function X()
{
  log("SomeClass global X");
}

// new state, does not exist in Actor
state BaseState
{
  function X()
  {
    log("SomeClass BaseState X");
    // Super.X() -> "Error, Unknown Function 'X' in 'Class Engine.Actor'"
    Global.X(); // logs the same
  }
}

state ExtendedState extends BaseState
{
  function X()
  {
    log("SomeClass ExtendedState X");
    Super.X();  // "SomeClass BaseState X"
    Global.X(); // "SomeClass global X"
  }
}

state AnotherExtendedState extends ExtendedState
{
  function X()
  {
    log("SomeClass AnotherExtendedState X");
    Super.X();  // "SomeClass ExtendedState X"
    // it's not possible to call "SomeClass BaseState X" directly through a Super(Something).X() construction:
    // Super(BaseState).X() -> "Error, Bad class name 'BaseState'"
    // Super(SomeClass.BaseState).X() -> "Error, Missing ')' in 'super(classname)'"
    Global.X(); // "SomeClass global X"
  }
}

If the object is in state AnotherExtendedState, it will log the following when X is called:

 SomeClass AnotherExtendedState X
 SomeClass ExtendedState X
 SomeClass BaseState X
 SomeClass global X
 SomeClass global X
 SomeClass global X

The calling structure looks like this:

AnotherExtendedState.X()
  +- ExtendedState.X()
  |    +- BaseState.X()
  |    |    +- global X()
  |    +- global X()
  +- global X()

OlympusMons: Ahh yes thats very, very clever wormbo :D How did you ever come up with that concept, might be handy for some AI or something.

Wormbo: PlayerController already uses the concept of extending states within the sme class with BaseSpectating being the base state for Spectating, AttractMode and WaitingForPawn. The Bot class makes extensive use of extending states as well:

MoveToGoal
  +- MoveToGoalWithEnemy
  |    +- Fallback
  |    |    +- Retreating
  |    +- Charging
  |    +- VehicleCharging
  |    +- Hunting
  +- MoveToGoalNoEnemy
       +- Roaming
NoGoal
  +- RestFormation

Base state function overriding and extended states

There's a catch with behaviour of extended states when a base state function in subclass is overriden. Suppose we have class like this one:

00001  class SbA extends Actor;
00002  
00003  function Test()
00004  {
00005      Log("A" @GetStateName() ,name);
00006  }
00007  
00008  function PostBeginPlay()
00009  {
00010      Test();
00011      GotoState('SBase'); Test();
00012      GotoState('SExtended'); Test();
00013      Destroy();
00014  }
00015  
00016  state SBase
00017  {
00018      function Test()
00019      {
00020          Log("A.Base" @GetStateName() ,name);
00021      }
00022      
00023  }
00024  
00025  state SExtended extends SBase
00026  {
00027  }
00028  
00029  /*
00030  Log:
00031  SbA: A SbA
00032  SbA: A.Base SBase
00033  SbA: A.Base SExtended
00034  */

This actor behaves as expected.

Lets override the test function in a subclass:

00001  class SbB extends SbA;
00002  
00003  state SBase
00004  {
00005      function Test()
00006      {
00007          Log("B.Base" @GetStateName() ,name);
00008      }
00009  }
00010  
00011  state SExtended
00012  {
00013  }
00014  
00015  /*
00016  Log:
00017  SbB: A SbB
00018  SbB: B.Base SBase
00019  SbB: A.Base SExtended
00020  */

Notice that in SExtended state SbB.SBase.Test() wasn't called.

Workarounds:

Duplicate the function so it's in SbB.SBase and SbB.SExtended?

Related Topics

Discussion


Category Tutorial

Category To Do – Revise tutorial fill in with uscript examples.

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

FAQs

Help Desk

Mapping Topics

Mapping Lessons

UnrealEd Interface

UnrealScript Topics

UnrealScript Lessons

Making Mods

Class Tree

Modeling Topics

Chongqing Page

Log In