Tuesday, June 26, 2012

Platformer Kit: SPG_Weapon class (UnrealScript)

The SPG_Weapon class represents the weapon used in the Platformer Kit. It is based on the Link Gun weapon of Unreal Tournament, but only has the option of shooting with projectile.

If you do not know about the Weapon class first read the article: "Weapon Class (UnrealScript)".

The variables of the SPG_Weapon class are available for changing in the editor.
class SPG_Weapon extends Weapon;

// Name of the socket which represents the muzzle socket
var(Weapon) const Name MuzzleSocketName;

// Particle system representing the muzzle flash
var(Weapon) const ParticleSystemComponent MuzzleFlash;

// Projectile classes that this weapon fires. DisplayName lets the editor show this as WeaponProjectiles
var(Weapon) const array< class<Projectile> > Projectiles<DisplayName=Weapon Projectiles>;

// Sounds to play back when the weapon is fired
var(Weapon) const array<SoundCue> WeaponFireSounds;

An Archetype was also created for SPG_Weapon class. The image below shows the values ​​of some variables.


The SPG_Weapon class uses two Sockets. One to define the position in the 3D model of the weapon where the projectile is fired. The other Socket belongs to the SPG_PlayerPawn class and defines the position where the weapon must stay relative to the player. To understand more about Sockets, read the article at UDN: "Skeletal Mesh Sockets".

In the PostBeginPlay() function, the Particle System Component represented by the "MuzzleFlash" variable is attached to the Socket of the weapon. The weapon is attached to the player in the ClientGivenTo() function.
simulated event PostBeginPlay()
{
    local SkeletalMeshComponent SkeletalMeshComponent;

    Super.PostBeginPlay();

    if (MuzzleFlash != None)
    {
        SkeletalMeshComponent = SkeletalMeshComponent(Mesh);
        if (SkeletalMeshComponent != None && SkeletalMeshComponent.GetSocketByName(MuzzleSocketName) != None)
        {
            SkeletalMeshComponent.AttachComponentToSocket(MuzzleFlash, MuzzleSocketName);
        }
    }
}

One function that is executed when the weapon is fired is the PlayFireEffects(). It is responsible for activating the Particles System and play the weapon firing sound.

For more information about Particles Systems, read the article at UDN: "Particles Systems".

Below is the image when the weapon is fired and the code of the PlayFireEffects() function.

simulated function PlayFireEffects(byte FireModeNum, optional vector HitLocation)
{
    if (MuzzleFlash != None)
    {
        // Activate the muzzle flash
        MuzzleFlash.ActivateSystem();
    }

    // Play back weapon fire sound if FireModeNum is within the array bounds and if the 
    // weapon fire sound in that array index is not none
    if (FireModeNum < WeaponFireSounds.Length && WeaponFireSounds[FireModeNum] != None && Instigator != None)
    {
        Instigator.PlaySound(WeaponFireSounds[FireModeNum]);
    }
}

The other function that is executed when the weapon is fired is the ProjectileFire(). It is responsible for creating a new projectile and start it in the correct position and direction.

The position of the projectile is obtained from the GetWeaponStartTraceLocation() function of the SPG_PlayerPawn class, which is represented in the code by the variable Instigator.
simulated function Projectile ProjectileFire()
{
    local Vector SpawnLocation;
    local Rotator SpawnRotation;
    local class<Projectile> ProjectileClass;
    local Projectile SpawnedProjectile;

    // tell remote clients that we fired, to trigger effects
    IncrementFlashCount();

    // Only allow servers to spawn projectiles
    if (Role == ROLE_Authority)
    {
        // This is where we would spawn the projectile
        SpawnLocation = Instigator.GetWeaponStartTraceLocation();

        // This is the rotation we should spawn the projectile
        SpawnRotation = GetAdjustedAim(SpawnLocation);

        // Get the projectile class
        ProjectileClass = GetProjectileClass();
        if (ProjectileClass != None)
        {
            // Spawn the projectile setting the projectile's owner to myself
            SpawnedProjectile = Spawn(ProjectileClass, Self,, SpawnLocation, SpawnRotation);

            // Check if we've spawned the projectile, and that it isn't going to be deleted
            if (SpawnedProjectile != None && !SpawnedProjectile.bDeleteMe)
            {
                // Initialize the projectile
                SpawnedProjectile.Init(Vector(SpawnRotation));
            }
        }
      
        // Return it up the line
        return SpawnedProjectile;
    }

    return None;
}