Legacy:Extending States

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to navigation Jump to search

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:

<uscript> 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"
 }

} </uscript>

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:

<uscript line> class SbA extends Actor;

function Test() { Log("A" @GetStateName() ,name); }

function PostBeginPlay() { Test(); GotoState('SBase'); Test(); GotoState('SExtended'); Test(); Destroy(); }

state SBase { function Test() { Log("A.Base" @GetStateName() ,name); }

}

state SExtended extends SBase { }

/* Log: SbA: A SbA SbA: A.Base SBase SbA: A.Base SExtended

  • /

</uscript>This actor behaves as expected.

Lets override the test function in a subclass:

<uscript line> class SbB extends SbA;

state SBase { function Test() { Log("B.Base" @GetStateName() ,name); } }

state SExtended { }

/* Log: SbB: A SbB SbB: B.Base SBase SbB: A.Base SExtended

  • /

</uscript>

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:Legacy Tutorial

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