I'm a doctor, not a mechanic

Legacy:VitalOverdose/DestructibleMover

From Unreal Wiki, The Unreal Engine Documentation Site
< Legacy:VitalOverdose
Revision as of 16:10, 19 November 2007 by Sweavo (Talk)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
UT2004 :: Actor >> Mover >> Destructible (Package: custom)


by VitalOverdose

Part of Vital'sPMT

Overview

A mover that can simulate being destroyed.

I say SIMULATE because to tell you the truth its just gets hidden as creating and destroying actors is not the most efficient way of making something vanish. Most seemingly destructible actors in unreal just simply get hidden , their collision turned off and an emitterfx spawned to simulate any debris. With this actor from being removed completely the actor then times itself so it knows when to switch back on again (simulating a respawn).

This is a very efficient system but its not without its problems, namely the lighting. As most shading on the bsp and static meshes in the game is calculated before the game is run and sort of 'baked on' the the textures used in level if a static/semi static object is removed then the shadows that were made by the object stay where they are. It is for this reason the movers in UT don't cast shadows.

The only way around it is to use dynamic lighting which sucks up a lot of the games available CPU power. Its not impossible to use dynamic lighting fx in your level otherwise they wouldn't be there ....use sparingly.

User Properties

(these properties are available to the mapper in unrealed)

DestroyAtKey    - The key number at which the mover will vanish. 
                  Note: Remember the mover has to come back past the number again so for each  
                  complete move opening,opened,closing,closed the mover will end up vanishing twice
                  if RebirthTime < ((movetime*Numkeys+stayopentime) - (DestroyAtKey x movetime))    
RebirthTime     - The length of time the mover stays hidden for.
                  Note this is an INT/Integer variable (whole number) so you cant have any half 
                  seconds.

Internal Variables

(Properties not settable by the mapper in unrealed)

bnetgame        - To help sort out what code to run in the timer;
BaseFourCounter - in the original game code there are two timer() speeds the mover gets updated with,
                  one of them is every second and the other is every 4 seconds.
                  Basefourcounter get subtracted every timer call. When it reaches 0
                  It is reset to 4 and the original code form the timer function is allowed to do 
                  its thing.
Timercounter    - hold the amount of seconds until the mover reapears after vanishing 

–The Functions–

Function BeginPlay()

(generic:called by the system just asthe actor enters gameplay)

Orig Script

In this function Ive had to include all of the code from the parent function as the bit i want to get to is buried right in the middle of the class. So what Ive done here is just stripped out the bit Ive changed so i can explain a bit about how Ive done it. This is not the full function, if you look at the completed script at the bottom of the page you will see the full code for this function.;-

	// timer updates real position every second in network play
	if ( Level.NetMode != NM_Standalone )
	{
		if ( Level.NetMode == NM_Client && bClientAuthoritative )
			settimer(4.0, true);
		else
			settimer(1.0, true);
		if ( Role < ROLE_Authority )
			return;
	}
code continues...

-As you can see the timer can either be set to tick away at once a second or every 4 seconds. But we need the timer to tick once every second always, this is one way to do it;- ===Function BeginPlay() (generic:called by system as the actor enters gameplay). ====Modified Script

simulated function BeginPlay()        
{
// <-<-there's more code in the full function that should go in here.
 
 if ( Level.NetMode != NM_Standalone )                 
    {
     bNetGame = true;                     // using a Boolean as a flag as it quick to check 
     if ( Level.NetMode == NM_Client && bClientAuthoritative )
        {
         BaseFourCounter == 4;    // Base4counter is set to 4.This property acts as a timer and a flag 2 let timer()
        }			  // know to include the original code from the timer() function every 4 cycles.
    }
 
 settimer(1, true);               // Timer is set to the standard timing rate of once per second.
 
 if ( (bNetGame ) Role < ROLE_Authority ))         
    {
     Return;
    }   
 
//  <-<-<- in the full function displayed in the completed script at the bottom of the page code continues...

===Event KeyFrameReached() (generic: called by this actor when it has reached a ketfame position) This is another function from the parent class that contains some important code. So the new code has to contain no returns or things will start to malfunction. The code from the parent class is simply added with the .super function.

event KeyFrameREached()
{
 
 if (( Keynum == DestroyAtKey ) && (bhidden==false))
    {
     bHidden = True;
     Setcollision( false , false , false );
     TimerCounter=RebirthTime;
 
     if (Base4counter = -1)                // this will only be true once. After that 
	 Base4counter = 4;
    }
super.keyframereached();
}

Function Timer()

(generic: Initialised by the programmer but called by the system)

This function is split up into 4 parts;-

  • process timercounter
  • check Bnetgame + if not exit at this point
  • Process Bas4Counter
  • code from parent class is executed here
function timer()
{
 if ( TimerCounter > 0 ) 
    {
     TimerCounter -= 1;
     if (TimerCounter == 0)
        {
         bHidden = False;
         SetCollision( true , true , true );
        }
    }
 
 if ( !bNetGame )
       Return;
 
 if ( BaseFourCounter > 0)
    {
     BaseFourCounter -= 1
     if ( basefourcounter != 0 )
         return;
 
     Basefourcounter=4;
    }
 super.Timer()
}

DefaultProperties

I very rarley use default properties in uscript but this time i decided it would be better as leaving the DestroyAtKey at the default of 0 could cause problems if people dont realise that this dosent mean the hide/unde is disabled.

defaultproperties
{
DestroyAtKey=-1                 
RebirthTime=30 
}

The Completed Script;

Here is the completed script for now;-

Note:I haven't finished fully testing this script yet..so there may be a few changes to it.

If anyone has any problems with it let me know.

//-----------------------------------------------------------
 
// DestructibleMover by VitalOverdose
 
// jan 2006, updated Feb 2006
 
//-----------------------------------------------------------
 
class Destructible Extends Mover;
 
Var() int                    DestroyAtKey;
 
Var() float                  RebirthTime;
 
var bool                     bBasefour;
 
var int                      BaseFourCounter;
 
var bool                     bnetgame;
 
var int                      Timercounter;                                                     // When mover enters gameplay.
 
simulated function BeginPlay()
 
{
 local AntiPortalActor AntiPortalA;
 
 
 if (AntiPortalTag != '')
    {
 
	 foreach AllActors( class'AntiPortalActor',AntiPortalA,AntiPortalTag )
                      {
 
			           AntiPortals.Length                  = AntiPortals.Length + 1;
 
			           AntiPortals[AntiPortals.Length - 1] = AntiPortalA;
 
		              }
 
	}
 
if ( Level.NetMode != NM_Standalone )                 
    {
     bNetGame = true;                     // using a boolean as a flag as it quick to check 
     if ( Level.NetMode == NM_Client && bClientAuthoritative )
        {
         BaseFourCounter = 4;    // Base4counter is set to 4.This property acts as a timer and a flag 2 let timer()
        }			  // know to include the original code from the timer() function every 4 cycles.
    }                             
 
 settimer(1, true);               // Timer is set to the standard timing rate of once persecond.
 
 if ( (bNetGame ) &&( Role < ROLE_Authority ))         
    Return;
 
 RealPosition = Location;
 RealRotation = Rotation;
 Super.BeginPlay();                                    // Init key info.
 KeyNum         = Clamp( KeyNum, 0, ArrayCount(KeyPos)-1 );
 PhysAlpha      = 0.0;
 StartKeyNum    = KeyNum;
 Move( BasePos + KeyPos[KeyNum] - Location );         // Set initial location.
 SetRotation( BaseRot + KeyRot[KeyNum] );             // Initial rotation.
 if ( ReturnGroup == '' )                             
     ReturnGroup = tag;
 
 Leader   = None;
 Follower = None;
 
}
 
event KeyFrameREached()
 
{
 if ( Keynum == DestroyAtKey ) 
    {
     bHidden = True;
     Setcollision( false , false , false );
     TimerCounter=RebirthTime;
    }
 
super.keyframereached();
 
}
 
function timer()
 
{
 if ( TimerCounter > 0 ) 
    {
     TimerCounter -= 1;
     if (TimerCounter == 0)
        {
          bHidden = False;
          SetCollision( true , true , true );
        }
    }
 
 if ( !bNetGame )
    Return;
 
 if ( BaseFourCounter > 0)
    {
     BaseFourCounter -= 1;
     if ( basefourcounter != 0 )
          return;
    }
 super.Timer();
 
}
 
defaultproperties
 
{
 
DestroyAtKey=-1
 
RebirthTime=30 
 
}


Related Topics


Discussion