Game Development Community

Manual creation of Projectile

by Michael Perry · in Torque Game Engine · 01/31/2007 (7:52 am) · 4 replies

Hey all. I'm attempting to create a projectile in the engine.

I read this thread Create projectile in C, and am having trouble with the approach.

For my first run through, I'll be calculating the velocity and positioning the way starter.fps works. The final code needs to take in those values as parameters to use.

Projectiles are created in the WeaponImage::onFire(%this, %obj, %slot) script code:
%projectile = %this.projectile;

   // Determine initial projectile velocity based on the 
   // gun's muzzle point and the object's current velocity
   %muzzleVector = %obj.getMuzzleVector(%slot);
   %objectVelocity = %obj.getVelocity();
   %muzzleVelocity = VectorAdd(
      VectorScale(%muzzleVector, %projectile.muzzleVelocity),
      VectorScale(%objectVelocity, %projectile.velInheritFactor));

   // Create the projectile object
   %p = new (%this.projectileType)() {
      dataBlock        = %projectile;
      initialVelocity  = %muzzleVelocity;
      initialPosition  = %obj.getMuzzlePoint(%slot);
      sourceObject     = %obj;
      sourceSlot       = %slot;
      client           = %obj.client;
   };
   
   MissionCleanup.add(%p);

Here is what I'm doing in the engine:
bool Factory::CreateProjectile(Point3F location, Point3F velocity, U16 shooterID, U16 projType)
{
	ShapeBase* shooter = dynamic_cast<ShapeBase*>(Sim::findObject(shooterID));

	if(shooter != NULL)
	{
		// Play the animation, muzzle flash, and sound effect, but don't make a projectile
		shooter->simulateFire(0);
		
		// Create projectile data
		ProjectileData projData;
		projData.projectileShapeName = "../data/shapes/weapons/common/tracer.dts";
		projData.isBallistic = true;
		projData.muzzleVelocity = 150;
		projData.velInheritFactor = 0.3;
		projData.lifetime = 5000;
		projData.fadeDelay = 5000;
		projData.gravityMod = 0.80;
		projData.hasLight = true;
		projData.lightRadius = 1;
		projData.lightColor = ColorF(1.0, 0.0, 0.0);
		projData.armingDelay = 0;

		// Determine initial projectile velocity, until we start getting data from PDUs
		Point3F muzzleVector;
		shooter->getMuzzleVector(0, &muzzleVector);
		Point3F objectVelocity = shooter->getVelocity();		
		Point3F muzzleVelocity = (muzzleVector * projData.muzzleVelocity) + (objectVelocity * projData.velInheritFactor);

		// Make the missile, and fire it off
		SimObjectPtr<Projectile> missile = new Projectile();
		missile->setDataBlock(&projData);
		
                [b]// The following are modifier functions I wrote, that simply set the [/b]
                [b]//  projectiles mCurrentVelocity, mCurrentPosition, mSourceObjectID, ect[/b]
                missile->setCurrentVelocity(muzzleVelocity);
		missile->setCurrentPosition(muzzleVector);
		missile->setSourceObject(shooterID);
		missile->setSlot(0);
                
                [b]// After registerObject is called, the game freezes, with no error messages[/b]
		missile->registerObject();
	}
	else
		return 0;

	return 1;
}


As the comment in the code states, after I register the missile the game will freeze with no error messages.

So, did I screw up the initialization? Am I using the wrong classes? Any help would be appreciated and thanks in advance!

*EDIT* - I also have these concerns:
    [li]Will the collisions still work[li]Will the projectile be cleaned up properly[li]Do I need to assign a client?
%p = new (%this.projectileType)() {
      dataBlock        = %projectile;
      initialVelocity  = %muzzleVelocity;
      initialPosition  = %obj.getMuzzlePoint(%slot);
      sourceObject     = %obj;
      sourceSlot       = %slot;
      client           = %obj.client;  [b]/// <== How would I get this in engine, and do I need to?[/b]
   };

#1
02/01/2007 (8:43 am)
I suppose I can call a custom script function from the engine, passing in the velocity, position, ect.

The script function would look similar to my first code block, just with different parameters, and not called via the weaponimage.

I'll test this later, but for the sake of understanding the engine a little better, and solving the original problem, I'd love to get this code working.
#2
02/01/2007 (8:53 am)
Short Answer: Your datablock goes out of scope immediately after you leave the method, and is no longer available for the engine. In addition, it's never networked since that only happens during mission load.

Long Answer:

One of the first things you missed is that you never actually created the datablock you defined...it's simply a locally scoped dataset. It needs to be both assigned memory with a new statement, and registered with the simulation itself.

You also don't want to be creating datablocks on the fly during runtime, since they will not be networked to the clients (which happens during mission load).

In the script utlization of projectiles, %obj.client is:

--a dynamic field (which means c++ doesn't have access to it)
--a gameplay mechanism--it's only used for scoring. Since once the projectile is created and placed in the simulation, it becomes an object of it's own, with no direct connection to the player that fired the weapon that created the projectile, we keep track of which client that was at creation time, so when the projectile destroys a target, we can credit the original firing player with the kill.

In general, this is how I would do what you seem to be wanting to do:

--create all my projectile datablocks in script, per usual. Have them in the mission file so they can be transmitted to the client(s).
--create my projectile in c++, using setDataBlock() to assign the (already created and networked) datablock.
--not worry about the %obj.score, but if scoring is a game mechanic I need, use a similar mechanic.
#3
02/01/2007 (9:27 am)
Again, thanks very much for your help Stephen. I'll make the recommended changes and see how it all works out.

One aspect I'm not sure how to code is the setDataBlock() routine, which takes in a GameBaseData* as a parameter. I've never tried grabbing a script datablock from C code before, so this is an unknown operation to me.

Would I use Sim::getDataBlockGroup()? or Sim::findObject()? Or something else?
#4
02/01/2007 (9:51 am)
Off the top of my head, since by definition all datablocks are named objects, you would use something like the Sim::findObject("datablock name");, but it's possible that setDataBlock may let you send the name of hte datablock directly, and it does the lookup.