I love the smell of UnrealEd crashing in the morning. – tarquin

Legacy:Static Function

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

Static functions are functions which may be called without an object or actor. Instead, they are called on the class itself. They carry certain restrictions. See also Function syntax.

Syntax[edit]

static <other function modifiers> function/operator <type> <function name> ( <parameters> )
{
  ...
}

Static functions which are declared as final are automatically simulated as well. The question of "simulated" or not doesn't apply to static functions, since, technically, a static function is called in context of a class, not an Actor, and a class is a non-Actor object.

Calling A Static Function[edit]

Static functions can be called like any other function or using this special syntax:

class'ClassName'.static.FunctionName(...);

In other words, they can be called without an object of that class available. You can also substitute a class variable, like so:

RunTimeClass.static.FunctionName(...);

(RunTimeClass is a variable of type "class": see Peppers And Pepper Grinders.)

Restrictions Of Static Functions[edit]

Static functions can only access variables passed to them as parameters or the default values of variables (logical, since there might not exist any actors of that class).

The only function calls that can be made from a static function are other static functions. However, if you have a valid reference to an object instance, that object is still capable of calling functions, like so:

static function CallNonStaticFunctions( actor Other )
 
{
  local Projectile P;
 
  if( Other != None )
  {
    Other.BroadcastMessage("You have to let it all go Neo, fear, doubt, and disbelief.");
 
    //You can access any variable belonging to the actor as well.
    Other.Log("Level ="@Other.Level);
 
    //You can also call iterators:
    foreach Other.AllActors( class'Projectile', P )
      P.Destroy;
  }

Uses and Implentation[edit]

Static functions have few uses, but they do exist - they can be used for a couple of things in my experience:

  1. A class that contains "global" functions - for instance math functions that you may want to be used by various classes that are not derived from a single parent class that can be readily modified. By that I mean something like a pawn and a projectile that both require the same function - although you can subclass actor and put the functions there, pawn and projectile both provide native code that would be lost if you subclassed actor then made your own pawn/projectile class. So this can be used to access those functions.
  2. A highly flexible predefined variable. This is how I use static functions - meshes are bundled with their animations so that the actual animation name is not used. A "meshcontroller" static object has static functions which return the appropriate animation and mesh for that meshcontroller. Then, instead of setting a mesh for an actor, you give your classes a class<meshcontroller> and then use the static function to return animation names within the PlayAnim call and the mesh can be returned and set in postbeginplay. You can even use this to have random animations on meshes that have multiple animations and not on others that have only one - it's all modular and transparent to the class that's actually calling the playanim.

Here is an example of the above:

  LoopAnim( MyMesh.static.GetAttackAnim(), MyMesh.static.GetAttackAnimRate() );

And in the meshcontroller class:

  const numAttackAnims=3;
 
  // Didn't use an array to make this easier to expand by subclasses.
  var name AttackAnim1, AttackAnim2, AttackAnim3;
  var float AttackAnimRate1, AttackAnimRate2, AttackAnimRate3;
 
  static function name  GetAttackAnim()
  {
    local int Temp;
 
    Temp = INT( FRand() * numAttackAnims );
    switch( Temp )
    {
      case 0: return  Default.AttackAnim1; break;
      case 1: return  Default.AttackAnim2; break;
      case 2: return  Default.AttackAnim3; break;
      default: return Default.AttackAnim1; break;
    }
  }
 
  etc...

The problem with that is that if you have multiple attack animations that are random you might not get the matching animrate and attackanim, so you could remedy that as follows:

  Temp = MyMesh.static.RandomizeAttack();
  LoopAnim( MyMesh.static.GetAttackAnim( Temp ), MyMesh.static.GetAttackAnimRate( Temp ) );

And in the meshcontroller class:

  const numAttackAnims=3;
 
  // Didn't use an array to make this easier to expand by subclasses.
  var name AttackAnim1, AttackAnim2, AttackAnim3;
  var float AttackAnimRate1, AttackAnimRate2, AttackAnimRate3;
 
  static function int   RandomizeAttack()  { return INT( FRand() * numAttackAnims; }
  static function name  GetAttackAnim( int AnimNum )
  {
    switch( AnimNum )
    {
      case 0: return  Default.AttackAnim1; break;
      case 1: return  Default.AttackAnim2; break;
      case 2: return  Default.AttackAnim3; break;
      default: return Default.AttackAnim1; break;
    }
  }
 
 
  etc...

That, however would be slightly hack-ish. Here's an even better way.

  MyMesh.static.PlayAttackAnim( self );

And in the meshcontroller class:

  const numAttackAnims=3;
 
  // Didn't use an array to make this easier to expand by subclasses.
  var name AttackAnim1, AttackAnim2, AttackAnim3;
  var float AttackAnimRate1, AttackAnimRate2, AttackAnimRate3;
 
  static function PlayAttackAnim( actor HaplessVictim )
  {
    local int Temp;
 
    Temp = INT( FRand() * numAttackAnims );
    switch( Temp )
    {
      case 0:  HaplessVictim.PlayAnim( Default.AttackAnim1, Default.AttackAnimRate1 ); break;
      case 1:  HaplessVictim.PlayAnim( Default.AttackAnim2, Default.AttackAnimRate2 ); break;
      case 2:  HaplessVictim.PlayAnim( Default.AttackAnim3, Default.AttackAnimRate3 ); break;
      default: HaplessVictim.PlayAnim( Default.AttackAnim1, Default.AttackAnimRate1 ); break;
    }
  }