FX- and Bezier projectiles

Scripts and code that enhance the gameplay functionality of the engine.
7 posts Page 1 of 1
LukasPJ
Site Admin
Posts: 357
Joined: Tue Feb 03, 2015 7:25 pm
 
by LukasPJ » Wed Feb 18, 2015 10:30 pm
Hey guys, this resource adds 2 new Projectile classes that is especially useful for effects.

The first is the "FXProjectile" which both adds the OnlyCollideWithTarget and the Homing features. They should be self explanatory. Furthermore this projectile class is more inheritance friendly and makes it easier to add new projectile classes such as the "BezierProjectile" which makes the projectile fly from A to B following a bezier curve.

The FXProjectile can be downloaded here.
The BezierProjectile can be downloaded here.

Example of use:

datablock BezierProjectileData(DefaultBezierProjectile)
{
projectileShapeName = "";

muzzleVelocity = 10;
velInheritFactor = 0;
armingDelay = 0;
particleEmitter = DefaultBezierProjectileEmitter;
lifetime = 2000;
fadeDelay = 2000;
isBallistic = false;
bounceElasticity = 0;
bounceFriction = 0;
gravityMod = 0;
hasLight = false;
lightRadius = 3;
lightColor = "0.8 0.8 1.0";

bezierweights = "15 0 25";
};

function ThrowBezierProjectile(%start, %end, %data, %ignoreCol, %weights)
{
%vel = VectorSub(%end, %start);
%vel = VectorNormalize(%vel);
%vel = VectorScale(%vel, %data.muzzleVelocity);
%proj = new BezierProjectile() {
initialPosition = %start;
initialVelocity = %vel;
homing = false;
BezierWeights = %weights;
targetPosition = %end;
datablock = %data;
};
}


And video of that function here (mute it):


Lastly, this old video of mine shows a use case for the projectiles:
Steve_Yorkshire
Posts: 207
Joined: Tue Feb 03, 2015 10:30 pm
 
by Steve_Yorkshire » Thu Feb 19, 2015 5:49 pm
Cool stuff! Also cool music! badapda-da!
Gibby
Posts: 72
Joined: Wed Feb 11, 2015 2:40 pm
by Gibby » Sun Feb 22, 2015 6:10 pm
@Lucas: Thanks for this! Worked perfectly out of the box!
buckmaster
Steering Committee
Steering Committee
Posts: 321
Joined: Thu Feb 05, 2015 1:02 am
by buckmaster » Sun Feb 22, 2015 9:30 pm
Steve_Yorkshire
Posts: 207
Joined: Tue Feb 03, 2015 10:30 pm
 
by Steve_Yorkshire » Wed Oct 28, 2015 6:41 pm
Noticed a small "jitter" on collision when using FXProjectile with homing, targetObject and onlyCollideWithTarget. Doesn't seem to do anything else, just doesn't delete/vanish on impact. Did look at the code but couldn't see an obvious reason.
LukasPJ
Site Admin
Posts: 357
Joined: Tue Feb 03, 2015 7:25 pm
 
by LukasPJ » Thu Oct 29, 2015 1:55 am
Steve_Yorkshire wrote:Noticed a small "jitter" on collision when using FXProjectile with homing, targetObject and onlyCollideWithTarget. Doesn't seem to do anything else, just doesn't delete/vanish on impact. Did look at the code but couldn't see an obvious reason.

Remind me to look into this over the weekend :P
Steve_Yorkshire
Posts: 207
Joined: Tue Feb 03, 2015 10:30 pm
 
by Steve_Yorkshire » Wed Nov 04, 2015 5:54 pm
Had another look at it with a clear head and realised why it's not working.

The "onlyCollideWithTarget" var is set to teleport the projectile to the target at the end of it's lifetime and does not actually collide with it ... which is ... er ... odd. Might have been useful for whatever you originally envisioned but ... odd ... for general projectile use. The "onlyCollideWithTarget" var doesn't feature anywhere else except as a negative with collision checking.
void FXProjectile::simulate( F32 dt )
{
if ( isServerObject() && mCurrTick >= mDataBlock->lifetime )
{
if(mTargetObject && mOnlyCollideWithTarget && mHoming)
{
VectorF normal = -getVelocity();
normal.normalize();
onCollision( getPosition(), normal, mTargetObject );
explode( getPosition(), normal, mTargetObject->getTypeMask() );
}
deleteObject();
return;
}


So I commented that out and put in a positive check for targetObject, OnlyCollideWithTarget and homing vars. Nothing fancy, just working as normies would expect projectile to. So here's what the new simulate function looks like.
void FXProjectile::simulate( F32 dt )
{
/* //yorks out - why Lucas why!? :P
if ( isServerObject() && mCurrTick >= mDataBlock->lifetime )
{
if(mTargetObject && mOnlyCollideWithTarget && mHoming)
{
VectorF normal = -getVelocity();
normal.normalize();
onCollision( getPosition(), normal, mTargetObject );
explode( getPosition(), normal, mTargetObject->getTypeMask() );
}
deleteObject();
return;
}
*/

if ( mHasExploded )
return;

// ... otherwise, we have to do some simulation work.
RayInfo rInfo;
Point3F oldPosition;
Point3F newPosition;

updatePosition(dt, oldPosition, newPosition);

if(!(mTargetObject && mOnlyCollideWithTarget && mHoming))
{
// disable the source objects collision reponse for a short time while we
// determine if the projectile is capable of moving from the old position
// to the new position, otherwise we'll hit ourself
bool disableSourceObjCollision = (mSourceObject.isValid() && mCurrTick <= SourceIdTimeoutTicks);

if ( disableSourceObjCollision )
mSourceObject->disableCollision();

disableCollision();

// Determine if the projectile is going to hit any object between the previous
// position and the new position. This code is executed both on the server
// and on the client (for prediction purposes). It is possible that the server
// will have registered a collision while the client prediction has not. If this
// happens the client will be corrected in the next packet update.
// Raycast the abstract PhysicsWorld if a PhysicsPlugin exists.
bool hit = false;

if ( mPhysicsWorld )
hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce );
else
hit = getContainer()->castRay(oldPosition, newPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo);

if ( hit )
{
// make sure the client knows to bounce
if ( isServerObject() && ( rInfo.object->getTypeMask() & csmStaticCollisionMask ) == 0 )
setMaskBits( BounceMask );

// Next order of business: do we explode on this hit?
if ( mCurrTick > mDataBlock->armingDelay || mDataBlock->armingDelay == 0 )
{
if(!(mTargetObject && mOnlyCollideWithTarget && rInfo.object != mTargetObject) || !mOnlyCollideWithTarget)
{
MatrixF xform( true );
xform.setColumn( 3, rInfo.point );
setTransform( xform );
mCurrPosition = rInfo.point;
mCurrVelocity = Point3F::Zero;

// Get the object type before the onCollision call, in case
// the object is destroyed.
U32 objectType = rInfo.object->getTypeMask();

// re-enable the collision response on the source object since
// we need to process the onCollision and explode calls
if ( disableSourceObjCollision )
mSourceObject->enableCollision();

// Ok, here is how this works:
// onCollision is called to notify the server scripts that a collision has occurred, then
// a call to explode is made to start the explosion process. The call to explode is made
// twice, once on the server and once on the client.
// The server process is responsible for two things:
// 1) setting the ExplosionMask network bit to guarantee that the client calls explode
// 2) initiate the explosion process on the server scripts
// The client process is responsible for only one thing:
// 1) drawing the appropriate explosion

// It is possible that during the processTick the server may have decided that a hit
// has occurred while the client prediction has decided that a hit has not occurred.
// In this particular scenario the client will have failed to call onCollision and
// explode during the processTick. However, the explode function will be called
// during the next packet update, due to the ExplosionMask network bit being set.
// onCollision will remain uncalled on the client however, therefore no client
// specific code should be placed inside the function!
onCollision( rInfo.point, rInfo.normal, rInfo.object );
explode( rInfo.point, rInfo.normal, objectType );
// break out of the collision check, since we've exploded
// we don't want to mess with the position and velocity
}
}
else
{
if ( mDataBlock->isBallistic )
{
// Otherwise, this represents a bounce. First, reflect our velocity
// around the normal...
Point3F bounceVel = mCurrVelocity - rInfo.normal * (mDot( mCurrVelocity, rInfo.normal ) * 2.0);
mCurrVelocity = bounceVel;

// Add in surface friction...
Point3F tangent = bounceVel - rInfo.normal * mDot(bounceVel, rInfo.normal);
mCurrVelocity -= tangent * mDataBlock->bounceFriction;

// Now, take elasticity into account for modulating the speed of the grenade
mCurrVelocity *= mDataBlock->bounceElasticity;

// Set the new position to the impact and the bounce
// will apply on the next frame.
//F32 timeLeft = 1.0f - rInfo.t;
newPosition = oldPosition = rInfo.point + rInfo.normal * 0.05f;
}
}
}

// re-enable the collision response on the source object now
// that we are done processing the ballistic movement
if ( disableSourceObjCollision )
mSourceObject->enableCollision();
enableCollision();
}
else//yorks new down here for testing onlyCollideWithTarget when homing and there is a targetObject
{
bool disableSourceObjCollision = (mSourceObject.isValid() && mCurrTick <= SourceIdTimeoutTicks);

if (disableSourceObjCollision)
mSourceObject->disableCollision();

disableCollision();

bool hit = false;

if (mPhysicsWorld)
hit = mPhysicsWorld->castRay(oldPosition, newPosition, &rInfo, Point3F(newPosition - oldPosition) * mDataBlock->impactForce);
else
hit = getContainer()->castRay(oldPosition, newPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo);

if (hit)
{
// make sure the client knows to bounce
if (isServerObject() && (rInfo.object->getTypeMask() & csmStaticCollisionMask) == 0)
setMaskBits(BounceMask);

// Next order of business: do we explode on this hit?
if (mCurrTick > mDataBlock->armingDelay || mDataBlock->armingDelay == 0)
{
if (mTargetObject && mOnlyCollideWithTarget && rInfo.object == mTargetObject)
{
MatrixF xform(true);
xform.setColumn(3, rInfo.point);
setTransform(xform);
mCurrPosition = rInfo.point;
mCurrVelocity = Point3F::Zero;

// Get the object type before the onCollision call, in case
// the object is destroyed.
U32 objectType = rInfo.object->getTypeMask();

// re-enable the collision response on the source object since
// we need to process the onCollision and explode calls
if (disableSourceObjCollision)
mSourceObject->enableCollision();

onCollision(rInfo.point, rInfo.normal, rInfo.object);
explode(rInfo.point, rInfo.normal, objectType);
}
}
else
{
if (mDataBlock->isBallistic)
{
Point3F bounceVel = mCurrVelocity - rInfo.normal * (mDot(mCurrVelocity, rInfo.normal) * 2.0);
mCurrVelocity = bounceVel;

Point3F tangent = bounceVel - rInfo.normal * mDot(bounceVel, rInfo.normal);
mCurrVelocity -= tangent * mDataBlock->bounceFriction;

mCurrVelocity *= mDataBlock->bounceElasticity;

newPosition = oldPosition = rInfo.point + rInfo.normal * 0.05f;
}
}
}

if (disableSourceObjCollision)
mSourceObject->enableCollision();
enableCollision();
}//end of yorks

if ( isClientObject() )
{
emitParticles( mCurrPosition, newPosition, mCurrVelocity, U32( dt * 1000.0f ) );
updateSound();
}

mCurrDeltaBase = newPosition;
mCurrBackDelta = mCurrPosition - newPosition;
mCurrPosition = newPosition;

MatrixF xform( true );
xform.setColumn( 3, mCurrPosition );
setTransform( xform );
}


And here's a video of some of it in action! :D
7 posts Page 1 of 1

Who is online

Users browsing this forum: No registered users and 1 guest