My program doesn't have bugs. It just develops random features.

Legacy:VitalOverdose/SFXPainful

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to: navigation, search
UT2004 :: Actor >> Emitter >>SFXpainful (Package: custom)

by VitalOverdose

Overview[edit]

This is a custom emitter actor that can track up to 20 particles from 1 of its emitters (ActiveEmitterNumb) and cause pain to any pawn they collide with. Using an iterator in this way to scan the particles locations is fairly stressful to the CPU so I've capped the particles to 20.

Properties[edit]

Note any changes to the properties will have to be made in the default properties by the mapper. Although you could write a custom spawner that can set the properties as the emitters are spawned (you cant change MaxParticles this way as its a constant)

ActiveEmitterNumb        - number of the emitter that is to be tracked.   
TimerFrequency           - Scanning frequency capped to 0.1 of a second max (10x Scans a second)                              
ScanSize                 - scan radius for each particle.
TheDamageType            - damagetype to apply on contact with a pawn.
DamageToVec              - damage amount for vehicles.
DamageToPawn             - damage amount for xpawns.
DamageToMonster          - Damage Amount for monsters.
VecCollisionFx           - Collision FX for vehicles.
PawnCollisionFx          - Collision FX for xpawns.
MonsterCollisionFx       - Collision FX for monsters.
VecCollisionSound        - collision Sound For vehicles.
PawnCollisionSound       - collision Sound For xpawns.
MonsterCollisionSound    - collision Sound For monsters.

Simulated Function PostBeginPlay()[edit]

OK here is where the capping takes place any value set outside the range of the programmer has set results in the value being set at its max for the scan size and for its min on the timerfrequency.

The call to destroy the actor if set wrong may seem a little harsh but its not possible to alter the maxparticle count after game-play has started as its a constant variable;

The timer is started at the end of this function. Set to run continuously being called every timerFrequency seconds. This could be called the Scan Rate for the tracked particles.

Simulated Function PostBeginPlay()
{
 Super.PostBeginPlay();
 
 If ( Emitters[ActiveEmitterNumb].MaxParticles > 20)
     Destroyed();
 
 if (ScanSize < 64 )
     ScanSize=64;
 
 if (TimerFrequency < 0.1)
     TimerFrequency = 0.1;
 
 SetTimer( TimerFrequency , true );
}

Simulated Function Timer()[edit]

Every time this function is called (once every Timerfrequency Seconds) the particles from the tracked emitter have their locations scanned with an iterator for any pawns. If one is found the valid ref for the pawn is bundled together with the velocity the particle was traveling at x 500 and set to the function ProcessCollision()

Simulated Function Timer()
{
 local Pawn           FoundPawn;
 Local Int            Counter;
 
 for ( Counter=0 ; Counter < Emitters[ActiveEmitterNumb].Particles.Length ; Counter++ )
     {
      foreach visiblecollidingActors(Class'Pawn', FoundPawn , ScanSize , Emitters[ActiveEmitterNumb].Particles[Counter].Location )
              {
               ProcessCollision (FoundPawn ,Emitters[ActiveEmitterNumb].Particles[Counter].Velocity*500);
              }
     }
}

Simulated Function ProcessCollision(Pawn CollidedWith, Vector ParticleMomentum)[edit]

Here the valid ref of the pawn the particle came in contact with is received along with a slightly increased velocity which comes in the form of a vector. Here the ProcessCollision is really just dealing out the pain ,sound and FX to the unlucky pawn who came in contact with the particle. As there's 3 different possible sets of sounds ,pain amount and fx (one of each for xpawn , monster and vec) it results in 3 lots of code. Ive used local variables to store what has to be played , spawned and ..er applied for each eventuality and then just applied it to one set of actions. This is about the only thing i could come up with to make this function slightly more efficient the just writing out 3 lots of the same code. If you know of a more efficient way of doing it please let me know. CaseSwitch may be more efficient IDK i haven't got round to using it yet.

Simulated function ProcessCollision(Pawn CollidedWith, vector Particlemomentum)
 
{
 
Local Int            ThePain;
 
local Emitter        SpawnedFX;
 
local Sound          CollisionSound;
 
local Class<emitter> Collisionfxclass< SEMI >
 
if ((CollidedWith.IsA('Vehicle')) && (DamageTovec !=0 ))     
   {
    ThePain = DamageTovec;
    if (VecCollisionSound!=None)
        CollisionSound=VecCollisionSound;
    if ( VecCollisionFx != None )
        Collisionfxclass = VecCollisionFx;
   }
 
else
 
if ((CollidedWith.IsA('monster')) && (DamageTomonster !=0 )) 
   {
    ThePain = DamageTomonster;
    if (MonsterCollisionSound!=None)
        CollisionSound=MonsterCollisionSound;
    if ( VecCollisionFx != None )
        Collisionfxclass = MonsterCollisionFx;
   }
 
else
 if ( (CollidedWith.IsA('xPawn') ) && (DamageToPawn !=0 ))    
    {
     ThePain = DamageToPawn;
     if (VecCollisionSound!=None)
         CollisionSound=PawnCollisionSound;
     if ( PawnCollisionFx != None )
         Collisionfxclass = PawnCollisionFx;
   }
 
 if ( CollisionSound != none )   // dealing out the pain,the fx,and the sound
     CollidedWith.PlaySound( CollisionSound );
 if ( Collisionfxclass != None )
     SpawnedFX = spawn(Collisionfxclass,CollidedWith,,CollidedWith.location,CollidedWith.rotation);
 if ( ThePain != 0 )
     CollidedWith.TakeDamage( DamageToPawn, Instigator, CollidedWith.Location , Particlemomentum, TheDamageType );
 
}

The Complete Script[edit]

//=============================================================================
 
// SFXPainful. - creates particles that hurts players
 
// By Vitaloverdose http://www.vitaloverdose.com
 
// updated oct 2007
 
//=============================================================================
 
class SFXPainful extends emitter
 
Placeable;
 
var    int                    TotalParticles;      // The mapper has no access to these
 
Var () int                    ActiveEmitterNumb;   // The mapper has no access to this property
 
Var () int                    TimerFrequency;      // The mapper has no access to this property
 
Var () int                    DamageToPawn;        // The mapper has no access to this property
 
Var () int                    DamageToVec;         // The mapper has no access to this property
 
Var () int                    DamageToMonster;     // The mapper has no access to this property
 
Var () float                  ScanSize;            // The mapper has no access to this property
 
var () Class<DamageType>      TheDamageType;       // The mapper has no access to this property
 
var () class <emitter>        MonsterCollisionFx;
 
var () class <emitter>        VecCollisionFx;
 
var () class <emitter>        PawnCollisionFx;
 
var () sound                  PawnCollisionSound;
 
var () Sound                  VecCollisionSound;
 
var () Sound                  MonsterCollisionSound;
 
Simulated Function PostBeginPlay()
 
{
 Super.PostBeginPlay();
 if ( Emitters[ActiveEmitterNumb].MaxParticles > 20)
     Destroyed();
 if (ScanSize < 64 )
     ScanSize=64;
 if (TimerFrequency < 0.1)
     TimerFrequency = 0.1;
 
SetTimer( TimerFrequency , true );
 
}
 
Simulated Function Timer()
 
{
 local Pawn           FoundPawn;
 Local Int            Counter;
 
 for ( Counter=0 ; Counter < Emitters[ActiveEmitterNumb].Particles.Length ; Counter++ )
      foreach visiblecollidingActors(Class'Pawn', FoundPawn , ScanSize , Emitters[ActiveEmitterNumb].Particles[Counter].Location )
               ProcessCollision (FoundPawn ,Emitters[ActiveEmitterNumb].Particles[Counter].Velocity*500);
 
}
 
Simulated function ProcessCollision(Pawn CollidedWith, vector Particlemomentum)
 
{
 
Local int            ThePain;
 
local emitter        SpawnedFX;
 
local sound          CollisionSound;
 
local class<emitter> Collisionfxclass< SEMI >
 
if ((CollidedWith.IsA('Vehicle')) && (DamageTovec    !=0 ))
   {
    ThePain = DamageTovec;
    if (VecCollisionSound!=None)
        CollisionSound=VecCollisionSound;
    if ( VecCollisionFx != None )
        Collisionfxclass = VecCollisionFx;
   }
 
else
 
if ((CollidedWith.IsA('monster')) && (DamageTomonster !=0 ))
   {
    ThePain = DamageTomonster;
    if (MonsterCollisionSound!=None)
        CollisionSound=MonsterCollisionSound;
    if ( VecCollisionFx != None )
        Collisionfxclass = MonsterCollisionFx;
   }
 
else
 if ( (CollidedWith.IsA('xPawn') ) && (DamageToPawn !=0 ))
    {
     ThePain = DamageToPawn;
     if (VecCollisionSound!=None)
         CollisionSound=PawnCollisionSound;
     if ( PawnCollisionFx != None )
         Collisionfxclass = PawnCollisionFx;
   }
 
                                               // dealing out the pain,the fx,and the sound
 if ( CollisionSound != none )
     CollidedWith.PlaySound( CollisionSound );
 if ( Collisionfxclass != None )
     SpawnedFX = spawn(Collisionfxclass,CollidedWith,,CollidedWith.location,CollidedWith.rotation);
 if ( ThePain != 0 )
     CollidedWith.TakeDamage( DamageToPawn, Instigator, CollidedWith.Location , Particlemomentum, TheDamageType );
 
}
 
defaultproperties
 
{
 
bNoDelete=False
 
RemoteRole=ROLE_SimulatedProxy
 
}

There may be some small alterations to this at a later date i have a few ideas of making it run more efficiently so more than 20 particles can be tracked. But its all going to need some fairly detailed testing before i make any serious changes to the way it works.

Optimization[edit]

Its totally untested as yet as to how many partials can be tracked without out any significant drop in game play speed. If anyone does get round to testing it please let me know.

Related Topcs[edit]

More custom emitter scripts[edit]

Discussion[edit]

DaWrecka: Have you considered making the emitter work server-side? If you could guarantee running on the server (which is required for dealing damage anyway) you can easily use Level.ControllerList. Since it's a linked list of nothing but controllers, you can vastly reduce the number of actors iterated through, since you can be sure that if Controller.Pawn != None, then the Pawn is a) a Pawn and not something else (obviously), b) Valid, and thus c) fair game. You're also not iterating through actors you wouldn't want damaged anyway like static meshes and brushes. The load will reduce vastly and consequently allow you to either use no cap or a much higher cap.

Vitaloverdose Sounds promising, ill look into it.