Monday, October 15, 2012

Cookbook: Typecasting and utilizing class hierarchy

Even unrealscript enables you to utilize inheritance and class hierarchy of your custom classes. So it is a good idea to design your actors/object in a way, that as much of the shared functionality and variables are in parental classes (to avoid copying the same code over and over) and be able to access that basic functionality after typecasting to a parent class (for example useful for storing all differing actors with same parent in one array and accessing them by their parent class...

Let me show you all of this in an example:

Let's say we need a couple of types of platforms, one that turns on some lights, one that opens doors, one that gives player some points. What they have in common is a mesh and slight movement upon touch (to emulate being used). Additionally there is a class that stores a set of references to such platforms and handles global resetting, changing of states, etc.

All the common functionality goes into some PlatformBase.uc:


   1:  class PlatformBase extends Actor
   2:      placeable;
   3:   
   4:  //our mesh
   5:  var (StaticMesh) StaticMeshComponent StaticMesh;
   6:   
   7:  /** movement offset from base position */
   8:  var(Game) vector vMoveOffset;
   9:   
  10:  //========================================================================
  11:  function TriggerAction()
  12:  {
  13:     //move
  14:     SetLocation(Location + vMoveOffset);
  15:  }
  16:   
  17:  //========================================================================
  18:  simulated event Reset()
  19:  {
  20:     //reset
  21:     SetLocation(OrigLocation);
  22:     super.Reset();
  23:  }

In the inherited classes you only need to add additional functionality, all the functionality of parental class is intact and can be used:

PlatformPoints.uc:

   1:  class PlatformPoints extends PlatformBase
   2:      placeable;
   3:   
   4:  //========================================================================
   5:  function TriggerAction()
   6:  {
   7:     super.TriggerAction(); //call parent method (move platform)
   8:     
   9:     PlayerController.AddPoints(100); //add some points
  10:  }

PlatformLight.uc:

   1:  class PlatformLight extends PlatformBase
   2:      placeable;
   3:   
   4:  var(Light) const LightComponent    Light;
   5:   
   6:  //========================================================================
   7:  function TriggerAction()
   8:  {
   9:     super.TriggerAction(); //call parent method (move platform)
  10:     
  11:     Light.SetEnabled(true); //enable referenced light
  12:  }
  13:   
  14:  //========================================================================
  15:  simulated event Reset()
  16:  {
  17:     super.Reset(); //call parent method
  18:     
  19:     Light.SetEnabled(false); //reset light state
  20:  }

PlatformDoor.uc:

   1:  class PlatformDoor extends PlatformBase
   2:      placeable;
   3:   
   4:  //========================================================================
   5:  function TriggerAction()
   6:  {
   7:     super.TriggerAction(); //call parent method (move platform)
   8:     
   9:     //trigger event with myself as instigator
  10:     TriggerEventClass(class'SeqEvent_DoorOpen', self);
  11:  }
  12:   
  13:  //========================================================================
  14:  simulated event Reset()
  15:  {
  16:     super.Reset(); //call parent method
  17:     
  18:     //trigger event with myself as instigator
  19:     TriggerEventClass(class'SeqEvent_DoorClose', self);
  20:  }

The handler class than collects references to all platforms by their parental class and can access all the basic functionality or typecast it to inherited class to use additional functionality. And remember accessing basic functionality of class referenced by its parental class actually calls the overriden method of actual class!


   1:  class PlatformHandler extends Actor;
   2:   
   3:  //references to all platforms
   4:  var array<PlatformBase>    Platforms;
   5:   
   6:  //========================================================================
   7:  event PostBeginPlay()
   8:  {
   9:     local PlatformBase platform;
  10:   
  11:     //iterate over all actors with exact or parental class of PlatformBase
  12:     foreach WorldInfo.DynamicActors(class'PlatformBase', platform)
  13:     {
  14:        Platforms.AddItem(platform); //store reference
  15:     }
  16:  }
  17:   
  18:  //========================================================================
  19:  function ResetActors()
  20:  {
  21:     local PlatformBase platform;
  22:     local PlatformDoor door;
  23:   
  24:     //reset all platforms
  25:     foreach Platforms(platform)
  26:     {
  27:        //call the reset 
  28:        //(this will call actual reset of real platform class type!)
  29:        platform.Reset();
  30:   
  31:        //you can even typecast to some inherited type
  32:        door =  (PlatformDoor)platform;
  33:   
  34:        if (door != none) //success, this one is really platformDoor
  35:        {
  36:           //call method unique for PlatformDoor!
  37:           door.StringToHUD("door opening!");
  38:        }
  39:     }
  40:  }

No comments:

Post a Comment