Game Development Community

Get the value of a Point3F saved on a player

by Bryce · in Torque Game Engine · 04/17/2010 (7:21 pm) · 10 replies

Hello children,

Example.
I've got a player. I saved a point on him by doing this:

%plr.thisPoint = "3 2 1";

How would I access this in the engine code and turn into a C++ Point3F? Like

Point3F aPoint = [WHATGOESHERE?]

Help is appreciated! I suck at engine code.

#1
04/17/2010 (8:10 pm)
Two options come to mind. You could access the object's dynamic field dictionary like this:
if (objectPtr->getFieldDictionary())
{
   // Query the dictionary for the string value of "thisPoint"
   const char* value = objectPtr->getFieldDictionary()->getFieldValue(StringTable->insert("thisPoint", false));
   // If "thisPoint" was found, convert to a Point3F
   if (value)
   {
      Point3F point;
      dSscanf(value, "%f %f %f", point.x, point.y, point.z);
      return point; // or whatever
   }
}

Or you could create a Player Class member variable for the point and map it to the script name "thisPoint" like this:
class Player: public ShapeBase
{
   // ... somewhere in here add
   Point3F mThisPoint;
   // you may also need to add
   public:
   static void initPersistFields();
void Player::initPersistFields()
{
   addField("thisPoint", TypePoint3F, Offset(mThisPoint, Player));
}
(That would be in player.h and player.cc, respectively)

Now the script field "thisPoint" is directly mapped to the Point3F mThisPoint.

... Why would you choose one method over the other? Well, the initPersistFields/addField approach is cleaner, especially if you wanted to access the point data from several different functions. On the other hand it is directly tied to a particular class (Player in this case), whereas the first approach will work with an object of any (script compatible) class; which I suppose could be useful, depending on what you want to do with it.
#2
04/17/2010 (9:44 pm)
Thanks for the reply, Scott, but let's try something else here...This doesn't seem to be working.

I'm trying to make iron sights on a weapon working over a network. My original implementation is to just alter the eyeOffset of the weapon image datablock to another field, called zoomEyeOffset.

I asked this forum question because my idea was to save the zoomed-in offset on the player when it was time to zoom in. That way, the image rendering code would add that offset to the rendered weapon. Player::RenderMountedImage doesn't seem to be able to read this information, no matter how I give it to it. Somehow the information is lost along the way, but I can't imagine what that possibly could be.

My next plan is to have some sort of switch on each player, like usingSights = true/false. When it comes time to render the mounted weapon image (Note...I'm using TGE 1.4.2!), we check to see if that player's usingSights switch is true or false. If they are not using the sights, render the weapon along the normal eyeOffset. Otherwise, if they are using the sights, render the weapon with zoomEyeOffset instead, so that the weapon is positioned properly on the screen. I hope I'm being clear, this is the part where I'm stuck. The engine code is difficult for me to understand, I'm honestly stumped by all this.

I thank anybody who has the patience to help me out on this.
#3
04/18/2010 (9:19 am)
Sounds like what you want is to add another field to the weapon image datablock.
struct ShapeBaseImageData: public GameBaseData {
   //... other stuff...
   Point3F zoomEyeOffset;
void ShapeBaseImageData::initPersistFields()
{
   // ... other fields
   addNamedField(zoomEyeOffset, TypePoint3F, ShapeBaseImageData);
Then you are going to need to transmit that field to the client via pack/unpackData like this:
void ShapeBaseImageData::packData(BitStream* stream)
{
   // .. other fields
   mathWrite(*stream, zoomEyeOffset);
void ShapeBaseImageData::unpackData(BitStream* stream)
{
   // .. other fields
   mathRead(*stream, &zoomEyeOffset);
(edit: Additional Note: It is vitally important that the order of the fields sent in packData matches exactly the order of the fields read in unpackData. If they are out of order the client will be disconnected when they attempt to download that datablock.)

Now the clients all have access to the same datablock value. The next step then is to decide how to use it. Sounds like you'll need to decide whether you are going to tie into the existing zoom functionality, or set up your own switch mechanism (which could involve adding a usingSights field to Player, then setting up scripts to toggle that via key press or some such). Tying into the existing zoom function is probably the easier way to go; unless you have reason to keep those actions seperate. If you choose to set up your own switch mechanism, keep in mind you will need to network that switch value in some way. (edit2: Actually that last part may not be necessary if it is an entirely client-side visual effect. So long as you don't consider it "cheating" if the client can use the zoomEyeOffset under any conditions. That part should only need to be networked if you want the server to be able to control when and where the client can use that function, otherwise it doesnt' matter.)
#4
04/18/2010 (8:09 pm)
Now I've run into an annoying issue. I added the zoomEyeOffset field as you suggested, and I've confirmed that it works, but I'm having trouble with the switch.

In a public section in class ShapeBase : public GameBase, I added

bool mUsingSights;

In ShapeBase::packUpdate, I added

stream->writeFlag( mUsingSights );

In ShapeBase::unPackUpdate in the correct spot, added
mUsingSights = stream->readFlag();

In ShapeBase::ShapeBase(), I added
mUsingSights = false;

Finally, I added in a function to activate this switch,

void ShapeBase::setUsingSights(bool use)
{
	if (use)
		mUsingSights = true;
	else
		mUsingSights = false;
}

And a ConsoleMethod for it
ConsoleMethod( ShapeBase, setUsingSights, void, 3, 3, "(bool useSights)")
{
   bool use = dAtob(argv[2]);
   if (object->isServerObject())
      object->setUsingSights(use);
}

However, when I call this function, it doesn't change mUsingSights. Everything compiled correctly, and when I echo out the values in ShapeBase::getRenderImageTransform (where I need the zoomEyeOffset number), it reads as 0. I know I'm missing something here...any thoughts?
#5
04/18/2010 (10:19 pm)
Where exactly in pack/unpackUpdate did you put mUsingSights?
#6
04/19/2010 (4:59 am)
// Group some of the uncommon stuff together.
   if (stream->writeFlag(mask & (NameMask | ShieldMask | CloakMask | InvincibleMask | SkinMask))) {
      if (stream->writeFlag(mask & CloakMask)) {
         // cloaking
         stream->writeFlag( mCloaked );
		 stream->writeFlag( mUsingSights );

         // piggyback control update
         stream->writeFlag(bool(getControllingClient()));

         // fading
         if(stream->writeFlag(mFading && mFadeElapsedTime >= mFadeDelay)) {
            stream->writeFlag(mFadeOut);
            stream->write(mFadeTime);
         }
         else
            stream->writeFlag(mFadeVal == 1.0f);
      }
      if (stream->writeFlag(mask & NameMask)) {
         con->packStringHandleU(stream, mShapeNameHandle);
      }
      if (stream->writeFlag(mask & ShieldMask)) {
         stream->writeNormalVector(mShieldNormal, ShieldNormalBits);
         stream->writeFloat( getEnergyValue(), EnergyLevelBits );
      }
      if (stream->writeFlag(mask & InvincibleMask)) {
         stream->write(mInvincibleTime);
         stream->write(mInvincibleSpeed);
      }

      if (stream->writeFlag(mask & SkinMask)) {

         con->packStringHandleU(stream, mSkinNameHandle);

      }
   }

if (stream->readFlag())
   {
      if(stream->readFlag())     // Cloaked and control
      {
         setCloakedState(stream->readFlag());
		 mUsingSights = stream->readFlag();
         mIsControlled = stream->readFlag();

         if (( mFading = stream->readFlag()) == true) {
            mFadeOut = stream->readFlag();
            if(mFadeOut)
               mFadeVal = 1.0f;
            else
               mFadeVal = 0;
            stream->read(&mFadeTime);
            mFadeDelay = 0;
            mFadeElapsedTime = 0;
         }
         else
            mFadeVal = F32(stream->readFlag());
      }
      if (stream->readFlag())  { // NameMask
         mShapeNameHandle = con->unpackStringHandleU(stream);
      }
      if(stream->readFlag())     // ShieldMask
      {
         // Cloaking, Shield, and invul masking
         Point3F shieldNormal;
         stream->readNormalVector(&shieldNormal, ShieldNormalBits);
         F32 energyPercent = stream->readFloat(EnergyLevelBits);
      }
      if (stream->readFlag()) {  // InvincibleMask
         F32 time, speed;
         stream->read(&time);
         stream->read(&speed);
         setupInvincibleEffect(time, speed);
      }

      if (stream->readFlag()) {  // SkinMask

         StringHandle skinDesiredNameHandle = con->unpackStringHandleU(stream);;

         if (mSkinNameHandle != skinDesiredNameHandle) {

            mSkinNameHandle = skinDesiredNameHandle;

            if (mShapeInstance) {

               mShapeInstance->reSkin(mSkinNameHandle);

               if (mSkinNameHandle.isValidString()) {

                  mSkinHash = _StringTable::hashString(mSkinNameHandle.getString());

               }

            }

         }

      }
   }
#7
04/19/2010 (5:41 am)
Are you sure you are calling your Console Method 'setUsingSights' from a server-side script (because if it's called from the client-side script, it will exit without any effect because of the 'if (object->isServerObject())' test).
#8
04/19/2010 (5:52 am)
That's worth double checking, what Nicolas said; but even if you're setting the value properly on the server it will not be sent to the client because you've bundled the update with the cloak update. That's ok. In fact it's a good way to handle it. However you will need to set the CloakMask bit to trigger the update.
void ShapeBase::setUsingSights(bool use)  
{  
   mUsingSights = use;
   setMaskBits(CloakMask); // <--
}
#9
04/19/2010 (6:00 am)
just a little explanation: See how your update is sent inside the conditional "if (stream->writeFlag(mask & CloakMask))"? Well, mask & CloakMask is not going to be true (the CloakMask bit will be 0 in mask) unless you turn that bit on with setMaskBits. This mechanism is designed to allow Torque to conserve bandwidth by not sending parts of a ghost update if that data has not changed.
#10
04/19/2010 (5:27 pm)
SetMaskBits did it! Thank you very much!