Game Development Community

Iron Sights

by Chris Byars · in Torque Game Engine · 07/20/2006 (6:16 pm) · 18 replies

I use an iron sight system for some of my weapons that simply changes the eyeOffSet. (Necessary for bitmap oriented scope overlays to move the weapon to the center to see flare/smoke effects, and necessary for the handgun to actually look through the sights of the handgun) Client calls commandToServer, which gets the mounted image that the player is holding, and calls the corresponding WeaponZoom method. WeaponZoom for making it centered perfectly and aligned to fire straight, WeaponUnZoom to return it to normal when you're done zooming.

function serverCmdWeaponZoom(%client)
{
   %currentWeapon = %client.player.getMountedImage($WeaponSlot);
   %currentWeapon.WeaponZoom(%client.player);
}

function serverCmdWeaponUnZoom(%client)
{
   %currentWeapon = %client.player.getMountedImage($WeaponSlot);
   %currentWeapon.WeaponUnZoom(%client.player);
}

And in the corresponding weapon *.cs file:
function handgunImage::WeaponZoom(%this, %obj, %slot)
{
   %this.eyeOffSet = "0.002 0.8 -0.019";
}

function handgunImage::WeaponUnZoom(%this, %obj, %slot)
{
   %this.eyeOffSet = "0.085 0.57 -0.09";
}

Unfortunately this type of setup does not work networked. Works for all the clients, no problem, however when a client calls their zoom, the server player's eyeOffSet gets modified as well if they're using the same weapon. How should I set something like this up to work per client rather than modify the overall ShapeBaseImageData value?

Thanks for any information.

#1
07/21/2006 (7:29 am)
You'd have to seperate eyeOffset from the datablock to the object.
#2
07/21/2006 (7:48 am)
I have a solution for this but I have a 'Complete FPS Weapon Solutions Pack' nearing completion and subsequent release. My pack includes code as well as models (amongst other things) and I am a little hesitant to release code from my pack, sorry.

I feel bad so I'll give you a hint.

- Get rid of:
function handgunImage::WeaponZoom(%this, %obj, %slot)
{
   %this.eyeOffSet = "0.002 0.8 -0.019";
}

function handgunImage::WeaponUnZoom(%this, %obj, %slot)
{
   %this.eyeOffSet = "0.085 0.57 -0.09";
}

And replace it with a function derived from ShapeBase. This function should switch between some eyeOffset values specied in your weapon's Image DataBlock.

Good luck, sorry I couldn't be of any further help. Maybe someone else will help you if you're still stuck.

- Tim
#3
07/24/2006 (3:32 pm)
Alright, I can't seem to figure this one out. (Things like this really throw me.) I can't conceptualize how it would work. :/

Quote:And replace it with a function derived from ShapeBase. This function should switch between some eyeOffset values specied in your weapon's Image DataBlock.

So your function would require the eyeOffSet values already stated in the datablock ShapeBaseImageData(handgunImage), and it would switch between them depending on what the client calls. How would this be any different than me changing the image's eyeOffSet. Would still affect the server player, no?
#4
07/24/2006 (5:27 pm)
Tim is being silly, just listen to what Stefan said. You need to move the variable to be defined in the object instead of the datablock, so you can change it dynamically.
#5
07/24/2006 (6:15 pm)
Is there a way to do this in script, like Tim's undisclosed method? I'm rather incompetant in the source. :)
#6
07/24/2006 (9:05 pm)
No. Tim is talking about adding a function in source I think, he's just confusing terms by saying Datablock. It's not very hard at all to move a value from datablock to object and be able to update. You really should learn at least that much by now.

Though really I think eyeOffset is a retarded half hack and you should not use it at all and have your animations match up so it looks like it supposed to in first person. That's what all the modern games are starting to do now anyways.
#7
07/24/2006 (9:11 pm)
You do it your way, I do it my way; it gets the job done in the way I like it. I very much prefer the eyeOffSet LOD way I manage first and third person in TGE/TSE. First and third (including the player) can be syncronized perfectly, while having more control over the weapon handling in first person.

I do know enough to do this in the source (negate my last post), I'm just inquiring for other options/insight.
#8
07/24/2006 (11:28 pm)
I'm not being silly and I haven't made any changes to source to get this working and I'm not confusing terms ;)
#9
07/25/2006 (12:04 am)
How can you possibly change a value that is read in source from the datablock from script? Unless you have some crazy hack involving swapping the mounted ShapeImage, I can't think of any way.
#10
07/25/2006 (12:26 am)
Ok C2 (and Paul), here's a crude way of achieving what you need. It's not a crazy hack (maybe a little) and it will work across a network. Not the most elegant way of doing things but far from the worse. Buy my weapon pack (when released) if you want professional code : P

Start on the client.

Add in your keybinding:
moveMap.bind(mouse0, "button2", toggleEyeOffset);

Function to server.
function toggleEyeOffset( %val )
{
   if ( %val == 1 )
      commandToServer( 'AdjustEyeOffset' );
   else if ( %val == 0 )
      commandToServer( 'ReturnEyeOffset' );
}

Last thing for the client, a couple of functions to handle adjusting and returning offset when instructed by the server.
function clientCmdAdjustEyeOffset(%client,%curWeapon,%offset)
{
   %curWeapon.eyeOffset = %offset;
   echo("-->Adjusting EyeOffset to " @ %offset);
}

function clientCmdReturnEyeOffset(%client,%curWeapon,%offset)
{
   %curWeapon.eyeOffset = %offset;
   echo("-->Returning EyeOffset to " @ %offset);
}

Now to the server.
function serverCmdAdjustEyeOffset(%client,%val)
{
   %curWeapon = %client.player.getMountedImage($WeaponSlot);
   %offset = %curWeapon.adjustedEyeOffset;
   %canAdjustEyeOffset = %curWeapon.adjustEyeOffset;

   if(%canAdjustEyeOffset)
   {
      commandToClient(%client,'AdjustEyeOffset',%client,%curWeapon,%offset);
      %weaponName = %curWeapon.item.getName();
      echo("-->Current Weapon is " @ %weaponName);
   }

   else if(!%canAdjustEyeOffset)
   {
      echo("-->Weapon does not have adjustable eyeOffset");
      return;
   }
}

function serverCmdReturnEyeOffset(%client,%val)
{
   %curWeapon = %client.player.getMountedImage($WeaponSlot);
   %offset = %curWeapon.normalEyeOffset;
   %canAdjustEyeOffset = %curWeapon.adjustEyeOffset;

   if(%canAdjustEyeOffset)
   {
   commandToClient(%client,'ReturnEyeOffset',%client,%curWeapon,%offset);
   }

   else if(!%canAdjustEyeOffset)
   {
      return;
   }
}

Finally, add some new values to each weapon script that is to have an adjustable eyeOffset.

This goes in the ShapeBaseImageData section.
datablock ShapeBaseImageData(desertEagleImage)
{
   // Basic Item properties
   shapeFile         = "~/data/shapes/weapon/desertEagle/weapon.dts";
   emap              = true;

   // Specify mount point & offset for 3rd person, and eye offset
   // for first person rendering.
   mountPoint        = 0;
   eyeOffset         = "0.01 0.25 -0.42"; //(xyz)

   // Added for adjustable eyeOffset
   adjustEyeOffset   = true; 
   normalEyeOffset   = "0.01 0.25 -0.42"; //This should always match eyeOffset
                                          //value listed above
   adjustedEyeOffset = "-0.0840 0.175 -0.355"; //This is the eyeOffet value we
                                               //switch to on zoom
   // Added for adjustable eyeOffset

Voila, adjustable eyeOffsets across a network!

[edit]Tidied up some code[/edit]
#11
07/25/2006 (1:06 am)
Since the eyeOffset isn't used in the muzzlePoint/Vector calcs (or anywhere by server code I think) I guess you can get away with that. It's still a bad idea, coz' for _most_ values that won't at all. Though actually I'm a little surprised it's working, since I'd have thought the ghosted datablock would have a different ID on a client. Or are datablocks not ghosted, but replicated I guess? Have to ask Stephen about that tommorow.
#12
07/25/2006 (1:18 am)
It's not pretty but it works flawlessly. Single player, network, dedicated server you name it, it works.

Quote:
Though actually I'm a little surprised it's working, since I'd have thought the ghosted datablock would have a different ID on a client.

That has nothing to do with it. Look at weapon.cs and inventory.cs to see my point. If that were the case, it would be impossible to cycle weapons etc.

Edit: Actually a better example is the ammo class!
#13
07/25/2006 (1:44 am)
I don't see what those have to do with anything, they all use server data with generic or named client commands. You're sending an ID not a named reference, but I did a quick test and it seems like it's because the datablocks have the same ID between client and server. That's not the case with objects so I wasn't sure if datablocks were different.
#14
07/25/2006 (2:03 am)
If Tim's solution works, then it's all good and very cool.
Myself, I'd rather keep stuff clean and optimized in source as long as it's easy enough to implement, which this one was.
#15
07/25/2006 (7:44 am)
It works and I agree with what you said Stefan, however I'm not prepared to give away my code for reasons stated earlier. Doesn't look like anyone else is either so I don't feel so bad now.

Now people have an option, learn how to program in C or use my scripted method :)
#16
07/29/2006 (7:39 am)
Tim whats the ETA of your pack,any pic tasters?
#17
07/31/2006 (3:17 pm)
Just in case anyone else is confused on this point, datablocks _are_ handled differently from other game objects. They have the first set (1000?) IDs allocated, and they are same on all clients. Just ran across this factoid myself a little while ago and had to have Ben Garney clear it up for me.
#18
07/31/2006 (3:28 pm)
Actualy, it have nothing related with ID's.
The Datablock is a "common stuff" for an object, created with this datablock.
Let's take into example the Player (assuming the stock SDK):
in file ~/server/scripts/player.cs we can see the description of datablock:
datablock PlayerData(PlayerBody)
{
   renderFirstPerson = false;
   emap = true;
   
   className = Armor;
   shapeFile = "~/data/shapes/player/player.dts";
[i]........ skipping...[/i]
   runForce = 48 * 90;
   runEnergyDrain = 0;
   minRunEnergy = 0;
   maxForwardSpeed = 14;
   maxBackwardSpeed = 13;
   maxSideSpeed = 13;
[i]........ skipping...[/i]
};
here we see, that our "PlayerBody" datablock contains basic information - the DTS (shapeFile) of an object, the max speed, etc.
Then, in ~/server/scripts/game.cs
function GameConnection::createPlayer(%this, %spawnPoint)
we create a player itself:
[i]........ skipping...[/i]
   %player = new Player() {
      dataBlock = PlayerBody;
      client = %this;
   };
[i]........ skipping...[/i]
Now, with %player we have AN OBJECT, that created from DATABLOCK. On object you can set transform, scale it, etc. And if you scale PLAYER, the only single player is affected.
But when you change something in DATABLOCK - all players derivered from this datablock are affected.
When the client joins the game, it first, loads all datablocks about evrything that server knows about. Then, almost before "jumping in" the server starts sending the "items/object" with their own attributes.

Hope this is understandable, as I had a heavy working day today, fixed lots of bugs (YES!) and now it's more then 2am already and I feel too tired (plus, english is not my first language).
Good luck! :)