Damage and Destruction: Part3 - OnHit Damage Effects
by Michael Hall · 05/11/2011 (2:58 pm) · 2 comments
This time we look at adding some effects when an object is struck. This will allow customization of "onHit" effects for each and every object you wish to set these up for -- think sparks, chips, shards, splinters, etc. These will be independent of the projectile and explosion effects.
During the early betas I had added some of the damage functionality these Resources are showing to the FPS Example, but over time they were stripped out in the interest of simplifying things. The example here is also how the player blood spurt effect was made to work when a player/bot was hit by a projectile.
Note that the following will not work for TSStatic shapes since they do not take advantage of the shapeBase damage handling or offer any script interaction.
Lets take our BouncingBoulder example from the previous Resources and as it takes damage or is hit by a projectile make it throw out some rock chips. We'll do this by using a particle effect and trigger it to emit by modifying the object's damage method. The new bit of code is shown between the dashed-line comments.
The way in which the particle emitter to use is hardcoded in our example Damage method is not my preferred way of doing things. I find it more useful to move such things into the datablock which makes customizing the damage effects (particles, sounds, etc) on an object by object basis as simple as adding and reading a field in the object's datablock -- but that's different lesson in itself... maybe later.
Next up, possibly sound effects (clanks, squeaks, pings, crumpts, etc.) to go along with our onHit-style damage effects, or maybe damage and destruction for vehicles... who knows :D
During the early betas I had added some of the damage functionality these Resources are showing to the FPS Example, but over time they were stripped out in the interest of simplifying things. The example here is also how the player blood spurt effect was made to work when a player/bot was hit by a projectile.
Note that the following will not work for TSStatic shapes since they do not take advantage of the shapeBase damage handling or offer any script interaction.
Lets take our BouncingBoulder example from the previous Resources and as it takes damage or is hit by a projectile make it throw out some rock chips. We'll do this by using a particle effect and trigger it to emit by modifying the object's damage method. The new bit of code is shown between the dashed-line comments.
function BouncingBoulder::damage(%data, %obj, %sourceObject, %position, %amount, %damageType)
{
//echo("BouncingBoulder::damage("@ %data.getName() @", "@ %obj @", "@ %sourceObject @", "@ %position @", "@ %amount@", "@ %damageType@")");
// -------------------------------------------------------------------------
// Place a new particleEmitter node (DamageFxNode) at the position of impact
// and use the emitter defined for our damageFX.
// -------------------------------------------------------------------------
%particles = new ParticleEmitterNode()
{
position = %position;
rotation = "1 0 0 0";
scale = "1 1 1";
dataBlock = "DamageFxNode";
emitter = "BoulderChipsEmitter";
velocity = "1";
};
MissionCleanup.add(%particles);
// -------------------------------------------------------------------------
// There's no need to calculate damage for an object that's already destroyed
if (%obj.isDestroyed())
{
//echo("object already destroyed, returning");
return;
}
// We can check for certain damageTypes and/or scale the damage for
// different types here before applying damage. Another option here would be
// to simply return if the %damagetype !$= "ExplosionDamage") or whatever type
// of damage you wish capable of causing destruction.
%obj.applyDamage(%amount);
}The comment says what we're going to do so ensure that you have a particle effect to use as well as the DamageFXNode emitter node. This is just a generic node that our script code will place in the world as a means of programatically creating new particle effects during run-time:// For damageFx when an object is struck by a projectile and damaged
datablock ParticleEmitterNodeData(DamageFxNode)
{
timeMultiple = 1.0;
};And an example BouldChips particle system:// ----------------------------------------------------------------------------
// BouncingBoulder onHit-style damageFX
// ----------------------------------------------------------------------------
datablock ParticleData(BoulderChipsParticle)
{
dragCoefficient = "0.9";
windCoefficient = "0.6";
gravityCoefficient = "0.8";
inheritedVelFactor = "0.2";
constantAcceleration = "0";
lifetimeMS = "640";
lifetimeVarianceMS = "320";
spinSpeed = "0";
spinRandomMin = "-200";
spinRandomMax = "200";
useInvAlpha = true;
textureName = "art/shapes/particles/dustParticle";//stonedebris";
colors[0] = "0.4 0.4 0.4 1";
colors[1] = "0.4 0.4 0.4 1";
sizes[0] = "0.08";
sizes[1] = "0.16";
times[0] = "0";
times[1] = "1";
};
datablock ParticleEmitterData(BoulderChipsEmitter)
{
ejectionPeriodMS = 5;
periodVarianceMS = 0;
ejectionVelocity = 2.4;
velocityVariance = 0.8;
ejectionOffset = 0.8;
thetaMin = 0;
thetaMax = 60;
phiReferenceVel = 0;
phiVariance = 360;
overrideAdvances = false;
orientParticles = false;
lifetimeMS = 75;
particles = "BoulderChipsParticle";
};You may find it useful to disable or reduce the stock Rocketlauncher explosion effects if you find that you're not seeing the BoulderChips when you shoot the BouncingBoulder.The way in which the particle emitter to use is hardcoded in our example Damage method is not my preferred way of doing things. I find it more useful to move such things into the datablock which makes customizing the damage effects (particles, sounds, etc) on an object by object basis as simple as adding and reading a field in the object's datablock -- but that's different lesson in itself... maybe later.
Next up, possibly sound effects (clanks, squeaks, pings, crumpts, etc.) to go along with our onHit-style damage effects, or maybe damage and destruction for vehicles... who knows :D
About the author
Been dabbling with game-programming since the age of 10 when I got my first computer, a Commodore. Got serious about game-development after modding Tribes for several years. Doesn't sleep much. Drinks rum. Teaches guitar. Plays cello.
#2
Any more than one is only needed if you want to have halfspeed or hyperspeed versions of certain particle effects. You can consolidate the ParticleEmitterNodeData's into as few examples as you wish -- just like your trigger timing example.
05/11/2011 (4:20 pm)
Yeah, the time multiplier is it's only field. Most of the time you don't need one since the majority of particle systems are placed through the Editor or from projectile & explosions and other miscellaneous events. Any more than one is only needed if you want to have halfspeed or hyperspeed versions of certain particle effects. You can consolidate the ParticleEmitterNodeData's into as few examples as you wish -- just like your trigger timing example.

Associate Steve Acaster
[YorkshireRifles.com]
Does "particleEmitterNodeData" hold anything else but the "timeMulitple"? Cause you could save on datablocks (1024 limit right?) by having various "particleEmitters" share the same "NodeData" - I expect that 99% of the time any "emitter" would want to use "timeMultiple = 1.0;".
Obviously not in this resource 'cos you need to demonstrate the link, but I was just thinking ...
Seems the same with triggers, each trigger needs a datablock, but if all commands on trigger actions fire a function from the individual triggerObject rather than the datablock, the only datablocks you need are ones which use different tick periods.
Originally I'd had seperate datablocks for triggers with their own scripts, but have since got them using shared datablocks based on tick (stock 500m/s, custom 1000m/s, 3000m/s, etc) and firing a function from the worldEditor properties, saves on a ton of datablocks.