Game Development Community

dev|Pro Game Development Curriculum

Jetpack - 1.4 / 1.5 Compliant

by Tim Heldna · 11/13/2006 (4:44 pm) · 56 comments

This resource will allow you to add a jetting functionality to your player. This enables the player to press a mouse button, or keyboard input and begin to fly.

It also adds the ability of movement whilst jumping or jetting when in the air.

It supports the use of energy. When energy is drained you will not be able to jet until it has regenerated. This of course is optional. If desired, you may permit players to jet indefinitely. This is controlled through script.

Although this has a similar feel to the Tribes jetting functionality, it is not the same. It does not support skiing.

I do not take credit for the code used in this resource. I merely used the Jet Pack Love resource, combined with the Air Control resource and updated it to work with 1.4. & 1.5. I also cleaned some code and did some reformatting.

Files affected:

Source Code
player.cc
player.h

Script
player.cs
default.bind.cs

Install Instructions:

All code changes have been encased with:
// Added for player jetting start
   // Added for player jetting end

player.cc

Find
maxJumpSpeed = 2 * minJumpSpeed;

Underneath that add
// Added for player jetting start
   jetJumpForce = 75 * 5;
   jetJumpEnergyDrain = 0;
   jetMinJumpEnergy = 0;
   jetJumpSurfaceAngle = 78;
   jetMinJumpSpeed = 20;
   jetMaxJumpSpeed = 100;
   // Added for player jetting end

Find
groundImpactShakeFalloff = 10.0;

Underneath that add
// Added for player jetting start
   airControl=0.0f;
   // Added for player jetting end

Find
if (minJumpEnergy < jumpEnergyDrain)
      minJumpEnergy = jumpEnergyDrain;

Underneath that add
// Added for player jetting start
   if (jetMinJumpEnergy < jetJumpEnergyDrain)
      jetMinJumpEnergy = jetJumpEnergyDrain;
   // Added for player jetting end

Find
addField("jumpDelay", TypeS32, Offset(jumpDelay, PlayerData));

Underneath that add
// Added for player jetting start
   addField("jetJumpForce", TypeF32, Offset(jetJumpForce, PlayerData));
   addField("jetJumpEnergyDrain", TypeF32, Offset(jetJumpEnergyDrain, PlayerData));
   addField("jetMinJumpEnergy", TypeF32, Offset(jetMinJumpEnergy, PlayerData));
   addField("jetMinJumpSpeed", TypeF32, Offset(jetMinJumpSpeed, PlayerData));
   addField("jetMaxJumpSpeed", TypeF32, Offset(jetMaxJumpSpeed, PlayerData));
   addField("jetJumpSurfaceAngle", TypeF32, Offset(jetJumpSurfaceAngle, PlayerData));
   // Added for player jetting end

Find
addField("groundImpactShakeFalloff",   TypeF32,       Offset(groundImpactShakeFalloff,    PlayerData));

Underneath that add
// Added for player jetting start
   addField("airControl", TypeF32, Offset(airControl,PlayerData));
   // Added for player jetting end

Find
stream->writeInt(jumpDelay,JumpDelayBits);

Underneath that add
// Added for player jetting start
   stream->write(jetJumpForce);
   stream->write(jetJumpEnergyDrain);
   stream->write(jetMinJumpEnergy);
   stream->write(jetMinJumpSpeed);
   stream->write(jetMaxJumpSpeed);
   stream->write(jetJumpSurfaceAngle);
   // Added for player jetting end

Find
stream->write(groundImpactShakeFalloff);

Underneath that add
// Added for player jetting start
   stream->write(airControl);
   // Added for player jetting end

Find
jumpDelay = stream->readInt(JumpDelayBits);

Underneath that add
// Added for player jetting start
   stream->read(&jetJumpForce);
   stream->read(&jetJumpEnergyDrain);
   stream->read(&jetMinJumpEnergy);
   stream->read(&jetMinJumpSpeed);
   stream->read(&jetMaxJumpSpeed);
   stream->read(&jetJumpSurfaceAngle);
   // Added for player jetting end

Find
stream->read(&groundImpactShakeFalloff);

Underneath that add
// Added for player jetting start
   stream->read(&airControl);
   // Added for player jetting end

This next bit is a little tricky. It's a trap for young players, people always seem to get confused as to where the code starts and ends. Make sure all your opening and closing brackets line up! Please take some extra care with the next few step. Read slowly and carefully. I'll try to make it as easy as possible for you.

Find
// Adjust the players's requested dir. to be parallel
      // to the contact surface.
      F32 pvl = pv.len();

Directly underneath, replace the next if statement with this
// Removed for player jetting start
/*
      if (pvl) {
         VectorF nn;
         mCross(pv,VectorF(0,0,1),&nn);
         nn *= 1 / pvl;
         VectorF cv = contactNormal;
         cv -= nn * mDot(nn,cv);
         pv -= cv * mDot(pv,cv);
         pvl = pv.len();
      }
*/
// Removed for player jetting end

I tend to comment code out rather than deleting it, makes life easier when debugging.

Directly underneath the section of code you just commented out, and above this
// Convert to acceleration
      if (pvl)
         pv *= moveSpeed / pvl;
      VectorF runAcc = pv - (mVelocity + acc);
      F32 runSpeed = runAcc.len();

Add this
// Added for player jetting start
      if(mJetting)
      {
         pvl = moveVec.len();
         if (pvl)
         {
            VectorF nn;
            mCross(pv,VectorF(0,0,0),&nn);
            nn *= 1 / pvl;
            VectorF cv;
            cv.set(0,0,0);
            cv -= nn * mDot(nn,cv);
            pv -= cv * mDot(pv,cv);
            pvl = pv.len();
         }
      }
      else
      {
         if (pvl)
         {
            VectorF nn;
            mCross(pv,VectorF(0,0,1),&nn);
            nn *= 1 / pvl;
            VectorF cv = contactNormal;
            cv -= nn * mDot(nn,cv);
            pv -= cv * mDot(pv,cv);
            pvl = pv.len();
         }
      }
// Added for player jetting end

Now just a few lines below find this
else
      mContactTimer++;

And replace those two lines of code with
// Removed for player jetting start
//   else
//      mContactTimer++;
// Removed for player jetting end

// Added for player jetting start
   else 
   {
      mContactTimer++;

      if (!inLiquid && mDataBlock->airControl > 0.0f) 
      {
         VectorF pv;
         pv = moveVec;

         F32 pvl = pv.len();

         if (pvl)
            pv *= moveSpeed / pvl;
            VectorF runAcc = pv - acc;
            runAcc.z = 0;
            runAcc.x = runAcc.x * mDataBlock->airControl;
            runAcc.y = runAcc.y * mDataBlock->airControl;
            F32 runSpeed = runAcc.len();

            F32 maxAcc = (mDataBlock->runForce / mMass) * TickSec * mDataBlock->airControl;

            if (runSpeed > maxAcc)
               runAcc *= maxAcc / runSpeed;
               acc += runAcc;
      }
   }
// Added for player jetting end

If you've followed my instructions correctly, you should find this directly underneath the section of code you just pasted in.
// Acceleration from Jumping
   if (move->trigger[2] && !isMounted() && canJump())

Directly after the closing brace { of above mentioned if statement, E.g.
setActionThread((mVelocity.len() < 0.5)?
            PlayerData::StandJumpAnim: PlayerData::JumpAnim, true, false, true);
         mJumpSurfaceLastContact = JumpSkipContactsMax;
      }
   } [b]<-- this is the last closing bracket[/b]


Add
// Added for player jetting start
   if (move->trigger[1] && !isMounted() && canJetJump())
   {
      mJetting = true;

      // Scale the jump impulse base on maxJumpSpeed
      F32 zSpeedScale = mVelocity.z;

         if (zSpeedScale <= mDataBlock->jetMaxJumpSpeed)
         {
            zSpeedScale = (zSpeedScale <= mDataBlock->jetMinJumpSpeed)? 1:
            1 - (zSpeedScale - mDataBlock->jetMinJumpSpeed) / (mDataBlock->jetMaxJumpSpeed - mDataBlock->jetMinJumpSpeed);

            // Desired jump direction
            VectorF pv = moveVec;
            F32 len = pv.len();

            if (len > 0)
               pv *= 1 / len;

            // If we are facing into the surface jump up, otherwise
            // jump away from surface.
            F32 dot = mDot(pv,mJumpSurfaceNormal);
            F32 impulse = mDataBlock->jetJumpForce / mMass;

            if (dot <= 0)
               acc.z += mJumpSurfaceNormal.z * impulse * zSpeedScale;

            else
            {
               acc.x += pv.x * impulse * dot;
               acc.y += pv.y * impulse * dot;
               acc.z += mJumpSurfaceNormal.z * impulse * zSpeedScale;
            }
            mEnergy -= mDataBlock->jetJumpEnergyDrain;
	    setActionThread((mVelocity.len() < 0.5)?            
	    // PlayerData::StandJumpAnim: PlayerData::JumpAnim, true, false, true); // Removed as it was causing people problems with the jump animation whilst jetting  
         }
   }

   else
   {
      mJetting = false;
   }

   if (mJetting)
   {
      F32 newEnergy = mEnergy - mDataBlock->minJumpEnergy;
      if (newEnergy < 0)
      {
         newEnergy = 0;
         mJetting = false;
      }
   mEnergy = newEnergy;
   }
// Added for player jetting end

Tricky part is over.

Find this if statement
// Container buoyancy & drag
   if (mBuoyancy != 0)
   {     // Applying buoyancy when standing still causing some jitters-
      if (mBuoyancy > 1.0 || !mVelocity.isZero() || !runSurface)
         mVelocity.z -= mBuoyancy * mGravity * mGravityMod * TickSec;
   }

And change it to look like this
// Container buoyancy & drag
   if (mBuoyancy != 0)
   {     // Applying buoyancy when standing still causing some jitters-

      // Removed for player jetting start
      //if (mBuoyancy > 1.0 || !mVelocity.isZero() || !runSurface)
      // Removed for player jetting end
      
      // Added for player jetting start
      if (mBuoyancy > 1.0 || !runSurface)
      // Added for player jetting end
         mVelocity.z -= mBuoyancy * mGravity * mGravityMod * TickSec;
   }

Find this function
bool Player::canJump()
{
   return mState == MoveState && mDamageState == Enabled && !isMounted() && !mJumpDelay && mEnergy >= mDataBlock->minJumpEnergy && mJumpSurfaceLastContact < JumpSkipContactsMax;
}

Directly underneath add this new function
// Added for player jetting start
bool Player::canJetJump()
{
   return mState == MoveState && mDamageState == Enabled && !isMounted() && mEnergy >= mDataBlock->jetMinJumpEnergy;
}
// Added for player jetting end

That's all for player.cc

player.h

Find
S32 jumpDelay;

Underneath that add
// Added for player jetting start
   F32 jetJumpForce;
   F32 jetJumpEnergyDrain; // Energy per jump
   F32 jetMinJumpEnergy;
   F32 jetMinJumpSpeed;
   F32 jetMaxJumpSpeed;
   F32 jetJumpSurfaceAngle; // Angle vertical degrees
   // Added for player jetting end

Find
F32 footSplashHeight;

Underneath that add
// Added for player jetting start
   F32 airControl;
   // Added for player jetting end

Find
void updateState();

Underneath that add
// Added for player jetting start
   bool mJetting;
   // Added for player jetting end

Find
bool  canJump();

Underneath that add
// Added for player jetting start
   bool canJetJump();
   // Added for player jetting end

That takes care of all head code changes. Recompile now.


Now we only have some minor scripting additions to make.

player.cs

Anywhere in the PlayerData datablock add

// Added for player jetting start
   airControl         = 0.3;
   jetJumpForce       = (8.3 * 10);
   jetJumpEnergyDrain = 0.8;
   jetMinJumpEnergy   = 0;
   // Added for player jetting end

default.bind.cs

Anywhere you like add

// Added for player jetting start
function mouseJet(%val)
{
   $mvTriggerCount1++;
}

moveMap.bind(mouse, button1, mouseJet);
// Added for player jetting end

That will bind jetting to the right mouse button. Feel free to change the binding to anything you like.

Make sure you delete your client\config.cs file for the new keybinding to take affect.

Season the datablock values inside the PlayerData datablock to your liking.

This concludes today's code snippet, enjoy!
Page«First 1 2 3 Next»
#42
03/03/2009 (3:25 am)
Have you added the script part to bind a key or a mouse button to activate it?

Gabriel
#43
09/02/2009 (12:19 pm)
Is there any way you can make this happen only if you hit a trigger of some sort?
#44
09/02/2009 (12:38 pm)
@Megan

Just add this to your trigger call back script:
$mvTriggerCount1++;
Which is what is executed in the function bound to the mouse input.

Gabriel
#45
09/28/2009 (11:46 am)
Thanks Gabriel!

However now I'm having another problem, it won't jet. Everything compiled just fine but it won't work when I press the button. Any pointers?
#46
10/19/2009 (11:49 am)
I need help!!

My code is in, no errors, but it still won't work when I press the button. I know it's recognizing when I press the button, but nothing happens. Is there any way to debug the code?
#47
10/19/2009 (11:55 am)
Try adding a few break points in the blocks of code that you added (see above). Then try stepping through to see what is happening.

I assume that you have rebuilt Torque after these modifications and are using the newly built executable.

Gabriel
#48
10/21/2009 (11:34 am)
Yes I am using the newly built torgue. However do you mean adding a break point in the torque script or in the engine code? and if it is the engine code how do you do it?
#49
10/22/2009 (12:47 pm)
In Visual Studio placing a break point in the code usually requires placing the cursor on the line of code you wish to break at, and then pressing F9. You will then have to do build a debug configuration and execute it under Visual Studio. When the execution reaches the specified line of code it will break and switch back to Visual Studio, allowing you to inspect the data and code processes at and after that point.

Out of curiosity though, your account does not list that you own any TGE products?

Gabriel
#50
10/28/2009 (11:55 am)
Thanks, I'll try it out. Well technically I didn't buy anything. The torque I'm using is owned by my school.
#51
12/13/2009 (7:54 pm)
I'm working on this resouce and coming up with a linking issues any recomendations?

7>player.obj : error LNK2019: unresolved external symbol "public: bool __thiscall Player::canJump(void)" (?canJump@Player@@QAE_NXZ) referenced in function "protected: void __thiscall Player::updateMove(struct Move const *)" (?updateMove@Player@@IAEXPBUMove@@@Z)
7>../example/torqueDemo_DEBUG.exe : fatal error LNK1120: 1 unresolved externals
#52
01/29/2010 (11:41 pm)
Has anyone tried to implement this in T3D?
#53
01/31/2010 (1:09 am)
Anyone?

Bueller?

#54
04/20/2013 (1:56 am)
does any1 have the modified files as per post?
#55
04/20/2013 (2:06 am)
can any1 send me the modified files?
pls pls pls
#56
04/20/2013 (9:00 am)
would it be possible to add the files? again?
Page«First 1 2 3 Next»