We're going to expand on what we learned about creating torches (extending an existing game object) and extend our own objects.
BattleMapEmitterEffect.uc
Let's start with a basic fireball.
Our torch was based on the EmitterSpawnable object, and extended to include movement and interactivity. This is a similar object extended from EmitterSpawnable, but we're only adding two features: a self-destruct timer (we only want it alive for a few seconds) and a camera shake. However, we've also made many of the values parameters so that they can be extended:
class BattleMapEmitterEffect extends EmitterSpawnable;
var float EffectLifespan;
var float EffectStarted;
var float ShakeScale;
var float OscillationDur;
var float PitchAmp;
var float PitchFreq;
var float YawAmp;
var float YawFreq;
var float RollAmp;
var float RollFreq;
simulated function PostBeginPlay()
{
EffectStarted = WorldInfo.TimeSeconds;
DoCameraEffects();
}
function Tick( float DeltaTime )
{
if ((WorldInfo.TimeSeconds - EffectStarted) > EffectLifespan)
self.Destroy();
}
function DoCameraEffects()
{
local CameraShake Shake;
local BattleMapPlayerController PC;
foreach WorldInfo.LocalPlayerControllers(class'BattleMapPlayerController', PC)
{
if (PC.PlayerCamera != None)
{
Shake = new class'CameraShake';
Shake.OscillationDuration = OscillationDur;
Shake.RotOscillation.Pitch.Amplitude = PitchAmp;
Shake.RotOscillation.Pitch.Frequency = PitchFreq;
Shake.RotOscillation.Yaw.Amplitude = YawAmp;
Shake.RotOscillation.Yaw.Frequency = YawFreq;
Shake.RotOscillation.Roll.Amplitude = RollAmp;
Shake.RotOscillation.Roll.Frequency = RollFreq;
PC.ClientPlayCameraShake(Shake, ShakeScale, false);
}
}
}
DefaultProperties
{
EffectLifespan=3.0
ShakeScale=1.0
OscillationDur=1.0
PitchAmp=20.0
PitchFreq=40.0
YawAmp=20.0
YawFreq=30.0
RollAmp=20.0
RollFreq=50.0
DrawScale=3.000000
Begin Object Name=ParticleSystemComponent0
Template=ParticleSystem'Envy_Effects.Particles.P_VH_Gib_Explosion'
ReplacementPrimitive=None
LightingChannels=(bInitialized=True,Dynamic=True)
LODLevel=1
End Object
ParticleSystemComponent=ParticleSystemComponent0
RemoteRole=ROLE_SimulatedProxy
bAlwaysRelevant=true
bReplicateMovement=true
bNetTemporary=true
bNoDelete=false
}
When we spawn this with a keystroke, it will throw down a screen shaking fireball wherever we're pointing.
With this as a base, creating other effects is simple. We extend our own object and swap out the particle system template. Below are three more effects: a force ball, a lightning bolt, and for the ranger in our group who fires Tomahawk cruise missiles instead of arrows, an earth-shattering nuclear explosion.
BattleMapEmitterEffectForce.uc
class BattleMapEmitterEffectForce extends BattleMapEmitterEffect;BattleMapEmitterEffectLightning.uc
DefaultProperties
{
DrawScale=1.000000
Begin Object Name=ParticleSystemComponent0
Template=ParticleSystem'P_WP_ShockRifle_Explo'
End Object
}
class BattleMapEmitterEffectLightning extends BattleMapEmitterEffect;BattleMapEmitterEffectNuke.uc
DefaultProperties
{
DrawScale=2.000000
Rotation=(Pitch=16384,Yaw=0,Roll=0)
Begin Object Name=ParticleSystemComponent0
Template=ParticleSystem'PS_Scorpion_Gun_Impact'
End Object
}
class BattleMapEmitterEffectNuke extends BattleMapEmitterEffect;
DefaultProperties
{
PitchAmp=150.0
PitchFreq=40.0
YawAmp=75.0
YawFreq=30.0
RollAmp=150.0
RollFreq=50.0
Begin Object Name=ParticleSystemComponent0
Template=ParticleSystem'P_VH_Death_SpecialCase_1_Base_Near'
End Object
}
BattleMapPlayerInput.uc
We already have a function for spawning objects (so far, just the torch) so we just need to add our new effects to BMSpawn():
exec function BMSpawn(string ObjectToSpawn)DefaultInput.ini
{
if (WorldInfo.NetMode == NM_Standalone || WorldInfo.NetMode == NM_ListenServer)
{
switch(ObjectToSpawn)
{
case "Torch":
Spawn(class'BattleMapTorch',Outer,,MouseOrigin,,,true );
break;
case "Fireball":
Spawn(class'BattleMapEmitterEffect',Outer,,MouseOrigin );
break;
case "Force":
Spawn(class'BattleMapEmitterEffectForce',Outer,,MouseOrigin );
break;
case "Lightning":
Spawn(class'BattleMapEmitterEffectLightning',Outer,,MouseOrigin );
break;
case "Nuke":
Spawn(class'BattleMapEmitterEffectNuke',Outer,,MouseOrigin );
break;
}
}
}
Finally, bind a key to the spawn function just like we did the torch:
-Bindings=(Name="F1",Command="GBA_ShowScores")
-Bindings=(Name="F2",Command="GBA_ShowMap")
-Bindings=(Name="F3",Command="GBA_ToggleMinimap")
.Bindings=(Name="F1",Command="BMSpawn Fireball")
.Bindings=(Name="F2",Command="BMSpawn Force")
.Bindings=(Name="F3",Command="BMSpawn Lightning")
.Bindings=(Name="F4",Command="BMSpawn Nuke")
BattleMapBlood.uc
We also need a way to mark creatures that have been "bloodied" during battle. The UDK has the perfect system for this: Decals. We'll create our own Decal actor to "spray" blood on the map. But first, we need some blood.
For simplicity's sake, I made a copy of the Bio_Splat_Decal_001 DecalMaterial (I liked the "wet" effect) and saved it to my BattleMapAssets package. Then I inserted a "Multiply" at the end to make it red instead of green:
Our extended DecalActorMovable object below has two extra features: it ensures the decal is always sprayed straight "down", and it adds a collision component so that we can detect it with the mouse:
class BattleMapBlood extends DecalActorMovable;
simulated function PostBeginPlay()
{
local Rotator newRot;
newRot.Pitch = -16384;
newRot.Yaw = Rand(65536);
newRot.Roll = -65536;
SetRotation(newRot);
}
DefaultProperties
{
Begin Object Name=NewDecalComponent
DecalMaterial=DecalMaterial'BattleMapAssets.Materials.Blood_Decal'
End Object
Begin Object Class=CylinderComponent Name=CollisionCylinder
CollisionRadius=+0064.000000
CollisionHeight=+0064.000000
BlockNonZeroExtent=true
BlockZeroExtent=true
BlockActors=true
CollideActors=true
End Object
CollisionComponent=CollisionCylinder
Components.Add(CollisionCylinder)
RemoteRole=ROLE_SimulatedProxy
bAlwaysRelevant=true
bReplicateMovement=true
bNetTemporary=false
bCanBeDamaged = false
bCollideActors = true
bBlockActors = true
bCollideWorld = false
bStatic=false
bNoDelete=false
bMovable=true
}
Since we're only using a single decal, the PostBeginPlay() function also randomly rotates the blood so that the same splat doesn't look mirrored all over the battle field. You could also randomly swap out the Material if you had several blood textures.
Don't forget to add the reference to our BattleMapPlayerInput.BMSpawn() function:
case "Blood":And bind a key to it in DefaultInput.ini:
Spawn(class'BattleMapBlood',Outer,,MouseOrigin );
break;
-Bindings=(Name="B",Command="GBA_ToggleSpeaking")BattleMapPlayerInput.uc
.Bindings=(Name="B",Command="BMSpawn Blood")
To wrap it up, we need a way to clean up our mess and delete torches and blood that we've spawned. We already know how to determine what object we're pointing at, so we just call that object's Destroy() function. Add the following function to BattleMapPlayerInput:
exec function BMDeleteObject()Add bind a key to BMDeleteObject() in DefaultInput.ini:
{
local BattleMapTorch To;
local BattleMapBlood Bl;
if (WorldInfo.NetMode == NM_Standalone || WorldInfo.NetMode == NM_ListenServer)
{
switch(ParseObjectName(ObjectUnderMouse))
{
case "Torch":
foreach DynamicActors(class'BattleMapTorch', To)
if (To.Name == ObjectUnderMouse)
{
To.Destroy();
}
break;
case "Blood":
foreach DynamicActors(class'BattleMapBlood', Bl)
if (Bl.Name == ObjectUnderMouse)
{
Bl.Destroy();
}
break;
}
}
}
.Bindings=(Name="Delete",Command="BMDeleteObject")Now, you can litter the battlefield with torches, blood and explosions while snuffing out your player's precious light sources.
BattleMap mode source files
Hey guys, completely amazing post, thanks so much.
ReplyDeleteI managed to get everything working except for the blood decal, and i cant understand why it isnt calling it, the rest of the effects and delete function work just fine, i save the blood decal with the right name in the right place, but still it wont appear when i press B, any ideas what could i had missed? (i changed the Input file too)
just made a weird discovery, if i spawn a torch i can spawn blood, but directly under the torch and nowhere else
ReplyDeleteI'm getting the same problem, everything is fine except for spawning blood which only works if i have a torch. The blood then spawns under the torch.
ReplyDeleteI love the nuke effect though, very cool.
i also noticed i can spawn blood near static meshes too, if the mouse tracker reads anything other than "world Info" is possible to spawn blood, but it doesnt always work.
ReplyDeletePS: the nuke effect will work lovely when the players roll a critic :D
Yes, I can spawn blood if I am hovering over meshes, tiggers or other objects (torch, blood).
ReplyDeleteOh and I fixed the offset mouse coordinates problem I was having with spawned objects and triggers. Objects would spawn down from the pointer location and triggers were shifted up and to the left. I had to disable the 'Constrain Aspect Ratio' setting for the Camera Actor to fix it. Now objects spawn directly under my pointer and triggers are located exactly where they are supposed to be. Thanks for the very cool effects.
One question - could we have a keyboard toggle for the HUD information (top left corner), I can then use UDK's in built hi-res screen capture console commands to capture some nice images for VTT (FGII) play.
OK. I think I have found the bug with the Blood splatter, well its fixed it for me anyway.
ReplyDeleteChange the following line in the DefaultProperties of the BattleMapBlood class, in BattleMapBlood.uc.
bCollideWorld = true
to
bCollideWord = false
Sweet, thanks! I'll check back when I have it running.
ReplyDeleteAbsolutely right, DrZeuss. Good catch! bCollideWorld should indeed be false for BattleMapBlood. That's what I get for testing on a terrain layer instead of a BSP.
ReplyDeleteOh right, thanks a lot mate, its fixed for me too, now i can go all Tarantino about it :D
ReplyDeleteI had a test run of the BattleMap yesterday with a few friends, it went down a storm, particularly the special effects I was able to throw in during the combat encounters. :) Thank you once again, may your dice rolls always be 20.
ReplyDeleteOne request, would it be possible to lock the scale and alignment of the grid once you have it initially setup? I only ask as zooming in/out of the maps (using C and Spacebar) currently requires the DM to rescale and realign the grid, this can be a little cumbersome especially when using large maps. Once again, many thanks and looking forward to the next post.