Game Development Community

Orbit Cam and Targeting (EyeOffset) [Finally Solved!!]

by Jesse Allen · in Torque 3D Professional · 06/06/2014 (1:50 pm) · 14 replies

Fair warning, the majority of this thread is speculation and guesswork. If you really want to read about the progress around this topic, I'd recommend you read Post #14. Cheers!

Guys I need a little help figuring out how to configure the aiming of my AIPlayers. First, let me give a rundown of what I've got so far:

1 - I've setup an Orbit Cam so that all of this is taking place from an isometric view. The player uses the mouse to move and attack (left and right click respectively).

2 - I've installed Walkabout, and I'm testing all of this on the basic test map that comes with Walkabout. Basically it's just a small area with some geometry to run on with a NavMesh applied to it.

3 - I've setup the Aliens from the GarageGames Alien Combo Pack as AIPlayers who will spawn at a random waypoint, run to another depending on level of aggro, shoot at client players, etc. This is all (the basic AI) a port of Steve's ChinaTown AI Deathmatch to work with Walkabout post-T3D 3.5.

4 - I made a new version of Steve's bot AI for the client's AIPlayer to use, so that when a player attacks an enemy alien it will continue to auto fire until the alien's dead or out of sight. This wasn't too hard just passing a %target variable to the modified 'canWeMakebangBang' function of Steve's.

Alright, so that's the basic setup. All of the basic scripting of that works as intended. But there is one major problem: The AIPlayer bots can't aim for crap.

In an effort to get my AIPlayer bots to actually hit their targets, I've ran through any and every forum post/resource/tutorial I could find pertaining to AIPlayer aiming. I've tried many, many ways to obtain the target, aim at it, and shoot. Ultimately, the AIPlayers do aim and shoot at the proper target but the shots (by default) go to the ground before reaching the target.

Solutions?
The only adjustment whatsoever that has affected the aiming of my AIPlayers from this isometric Orbit Cam view is the eyeOffset setting in the weapon datablock.

I followed Richard's advice in the RTS tutorial and tried this in the player's weapon datablock:
firstPerson = false;
   useEyeNode = false;
   animateOnServer = true;
   
   useEyeOffset = true;
   eyeOffset = "0 0 -0.35"; //works up close

You'll notice I added a comment //works up close. That is because this offset will work okay if my AIPlayer is near the enemy alien target, but if he backs away (around 15 feet away or so) the shots fire high and to the left.

Investigating Farther
So I continued to tinker with the eyeOffset setting in the weapon datablock, and found a new setting:
firstPerson = false;
   useEyeNode = false;
   animateOnServer = true;
   
   useEyeOffset = true;
   eyeOffset = "0.8 0 -1"; //works at mid to high range
This time you'll see I found this to work at mid to high range. In my case, high range is the edge of the screen looking down at this isometric view. At this point, I started to notice something: With this setting, if I was up close (within 15 feet away or so) I'd hit the ground and miss the target altogether. If I was 15 feet or so away or more I'd be really accurate most shots.

So I've noticed that this eyeOffset setting, when used in this scenario, is sort of 'scaling' based on distance from the target. I guess what I'm trying to find out is:

1 - Is there another way to get my AIPlayers to hit more accurately?
2 - If the only way to get this working is by scripting some sort of algorithm that scales the eyeOffset based on distance...could someone please help me with figuring this out?

As usual, thanks in advance guys. I know this is a bit long, but if more clarity is needed I'd be more than happy to make and upload a video. I'm really close to having a comfortable, isometric view, 'auto-attack' based prototype...just need those projectiles to hit! Cheers!

#1
06/06/2014 (4:24 pm)
Just a quick thought. Check the projectileSpread in the weapon's datablock to determine if it is 0. If it is anything else then it will create a random offset to the projectile. You can just comment out projectile spread to make the function ignore any deviation.
#2
06/06/2014 (4:43 pm)
Hi, thanks for the reply my friend. The weapon I'm testing is the Ryder (the default pistol) and it defaults the 'BulletProjectile' to a projectileSpread of "0.0".

I have pondered over trying something like (pseudocode):
%tempdist = vectorDist(%this.getposition(), %target.getposition());
if(%tempdist >= 5)
{
    //change the eyeOffset of the Ryder
}
else if (%tempdist <= 5)
{
    //change the eyeOffset of the Ryder
}

...although this 'solution' presents other problems. Firstly, I wouldn't know the correct syntax to call on the Ryder datablock to change its eyeOffset value from within this function (i.e. Ryder.eyeOffset = "0.8 0 -1"; ??)

Also, as is the case with this problem...this requires every weapon datablock's eyeOffset to be adjusted and that would mean also iterating through each available weapon on fire to be sure the proper eyeOffset is in place for the weapon being used. (take breath, that was rather long-winded wasn't it?) Phew, anyhow, it does seem that there is something largely wrong here if each weapon's datablock is needing to be adjusted/iterated through for precision firing. Obviously there is a better solution, it just has eluded me thus far..
#3
06/06/2014 (6:24 pm)
Try taking out the line projectilespread because the function calls a mod to the projectile if it exists in the datablock. Let me know. I consistently have issues like this because I am making my own weapons in Blender. This causes all kinds of issues that affect my game because of my crappy designs (not a Torque issue) but I will put them out here in case it helps someone else reading this that may be trying to make their own weapons:
1): How the weapon is held alters the ability for there to be an aligned attack including what happens during an animation.
2): The "MUZZLEPOINT" node not being placed correctly within the model can create issues as well.

Aside from this, see if you can alter the "think speed" of the AI to see if this is causing an issue because they might not calculate fast enough or they calculate an attack too soon. Try replacing the alien model with the normal character model to see if the model has an issue -- make sure to test out the parameters you have been testing on the alien model on the normal model as well.

Let me know what you find, I will try to think of what happened years ago when I still had projectile only weapons (I am making an RPG so I have few projectiles right now).
#4
06/06/2014 (6:48 pm)
Thanks for all the suggestions, I'll take a look at all these factors in the morning. I did have time to comment out the projectileSpread line although the shots are still inaccurate, depending on range and eyeOffset of the weapon.

I do believe that this issue deals precisely with your point 1 and 2 above: "How the weapon is held." I think the actual weapon in the hands of the Soldier model is aiming off from 'center' of what a ray cast from the AIPlayer (client.player) to the alien (AIPlayer bot) would be.

Also "The Muzzlepoint node being placed correctly" - for some reason these seem very likely culprits although I am not experienced or knowledgeable with altering these nodes. I attempted to alter the cam node on the Soldier model once while testing a 3rd person view, although when I reloaded the level I had a 'bad box' error as a result...so needless to say, perhaps if you are familiar with mounting weapons to the models you could have a peek at the muzzlepoint/weapon hold position on the stock T3D weapons?

Thanks again, I'll do my best to follow up on these leads in the morning. It really does seem like some sort of 'eye' or 'muzzlepoint' node is out of line and it's knocking the entire shooting match out of whack. I am pretty sure that no amount of tinkering with the eyeOffset of weapon datablocks will 'truly' fix this problem.
#5
06/06/2014 (7:11 pm)
Now to think of it the simple spells I have in the game rely on projectiles, but I relied so heavily on the explosion damage that I put the issue of the creatures not hitting me directly off to the side as a result. I will see what I can come up with tomorrow as a result from messing around with this issue. Please let me know what you come up with as well. Take care
#6
06/07/2014 (6:27 am)
Good morning, and thanks for any help with this DreamPharaoh. I'm not the sharpest tool in the shed, and I know 'just enough to be dangerous' I always say. Anyhow, I've done some investigative work with the Ryder weapon model and have some screen captures to share. Opening up the Ryder model in the Shape Editor, there are 2 models: TP_Ryder and FP_Ryder (for 3rd and 1st person respectively). Taking a look at the 3rd person model, things look pretty much perfect; things line up, the MUZZLEPOINT node appears to be in a good place, etc. Here's the 3rd person model, looking good (I've labeled the basic nodes in the first shot):

Third Person Ryder Model
i1213.photobucket.com/albums/cc466/rockoutsolid/Development/ryder_top_TP.png
i1213.photobucket.com/albums/cc466/rockoutsolid/Development/ryder_side_TP.png
Now, when I took a look at the FP_Ryder model a different picture was painted entirely. The gun model, in the hands of the 'animation skeleton' does appear to be facing up and to the left very slightly. Here are the screen captures:

First Person Ryder Model

i1213.photobucket.com/albums/cc466/rockoutsolid/Development/ryder_top_FP.png
i1213.photobucket.com/albums/cc466/rockoutsolid/Development/ryder_side_FP.png
This is all very interesting, but it raises more questions than answers for me. Firstly, why would the FP_Ryder model have a slightly offset angle and effect my game's aiming with a top-down view (isometric view).

Additionally, by default (before making any sort of eyeOffset or aimObject offset changes) the AIPlayers will shoot to the ground before hitting the target, not up and to the left. If an adjustment is made to the weapon databock (i.e. eyeOffset = "0 0 -0.35"), only then will you notice the AIPlayer aiming up and to the left from a distance.

Needless to say, this is all very confusing, and it seems like this should 'just work' regardless of the model. Here's why:

Before my AIPlayers take a shot, I call this:

%this.setAimObject(%target, "0 0 0");

Now, this particular line sets the aim to the object. With a default setting (as above) the AIPlayer will shoot the ground before the shot reaches the target. In Richard's RTS Prototype tutorial, it is advised that the offset there be changed to "0 0 1" to account for this...although after rigorous testing I've found this offset to not affect the aiming whatsoever. I've set it to outlandish values such as "0 0 10" to test it and it doesn't move.

So, leaving this setting alone, we then take a look at the weapon datablock. The only thing that's affected the aim whatsoever thus far has been the eyeOffset setting here...although the //comments in the stock T3D code seem to imply that the eyeOffset fields are designed to be altered in a First Person view. I'm fine with making adjustments that would 'normally' be made in a First Person environment if it fixes my problem (and based on the investigation into the 1st person model above it may).

Still Searching
As of this posting, I'm still not entirely sure what's not lining up exactly. I'll continue to explore the other possible solutions suggested by DreamPharaoh (changing alien model with stock, test different weapons, alter the timings on the attack schedules)and report back with my findings.
#7
06/07/2014 (7:44 am)
Alright, I cleared up some of the confusion around this. First off, by 'default' (before making any eyeOffset change to weapon datablock) the AIPlayer does NOT shoot the ground. As a matter of fact, by default the AIPlayer shoots well up close and misses (shooting high and to the left) a bit farther away. Here's the default Ryder weapon datablock:

mountPoint = 0;
   firstPerson = true;
   useEyeNode = true;
   animateOnServer = true;

Very important to note here is that by default the weapon has firstPerson set to 'true'. So even if I've positioned my camera in an isometric view, the weapon datablock is wanting to aim and fire in firstPerson.

Now, I initially followed the RTS Prototype to change the weapon datablock as follows:

mountPoint = 0;
   firstPerson = false;//turn off the firstPerson 'mode'
   useEyeNode = false;//don't use the eye node of the player model?
   animateOnServer = true;

   //all new below...
   useEyeOffset = true;
   eyeOffset = "0 0 -0.35";

Using this, here's what I think is taking place: We're turning off firstPerson 'mode' and thus allowing the use of the eyeOffset fields. But the end result is, well, the same as if we just left it alone in the first place...Up close we hit fine, but if we back away (15 feet or so) we hit high and to the left. The same EXACT results as if we had left it all alone and not touched the weapon datablock at all.

So at this point, I have reverted the weapon to default so that it looks like so:

mountPoint = 0;
   firstPerson = true;
   useEyeNode = true;
   animateOnServer = true;

My AIPlayer is hitting just as accurately and...the mystery as to why I am hitting high and to the left seems to point to the FP_Ryder model as I investigated earlier. Additionally, by default we can see that the weapon datablock uses firstPerson = true; so any changes made to the FP_Ryder model will probably affect us here...

I will try to alter the aiming of the FP_Ryder model and report.

Edit:
After a quick lunch I fired up the FP_Ryder model in the Shape Editor. I didn't see any way to rotate the actual model within the Shape Editor. I could select/move any of the nodes but the actual model rotation eluded me. So I imported the FP_Ryder model into Blender to have a look...I must say the layout of the model in Blender has got to be a bit more confusing than it should be...Lines all over the place, and the actual MUZZLEPOINT node is way offset strangely in the .DAE file:

i1213.photobucket.com/albums/cc466/rockoutsolid/Development/Ryder_blender.png
Perhaps someone more knowledgeable with stock T3D weapon models can chime in?

P.S. Also wanted to add that I did investigate using stock player models and alien models. The results are the same.
#8
06/07/2014 (10:00 am)
Okay, I discovered why in some cases the %this.setAimObject() setting did not seem to work. As I reported in the post above, it is not necessary to use the eyeOffset values at all. Following this way of thinking, we opted to leave the weapon datablcock at the default values:

firstPerson = true;
   useEyeNode = true;

The setting here useEyeNode, when set to false, will cause any prior offsets not to work. For example, in the script that is actually doing the firing if you had any kind of offset when aiming:

%this.setAimObject(%target, "0 0 1");

...this offset will do nothing and will be overwritten by the eyeOffset values but only if useEyeNode is set to false.

The Conclusion
This being the case, we find that the only thing that is necessary to adjust is the initial offset when declaring:

%this.setAimObject(%target, "0 0 0");

So long as you leave the weapon datablock alone, the default of "0 0 0" will aim the same as if you had set the weapon's eyeOffset to "0 0 -0.35". Aim will be good up close and if you get farther away (15-20 feet or so) the aim will start skewing high and to the left.

I attempted to adjust this offset directly to:

%this.setAimObject(%target, "0.8 0 -0.2");

...to set the aim to the right a bit and down. Works good at a distance (where we were previously missing), but now we have the reverse effect: If we get closer we point at the floor to the right and down, completely missing.

So far all I've managed to do is discover that the eyeOffset values in the weapon datablocks don't need to be changed...which is a good discovery, because it saves having to change every weapon datablock...but ultimately the end result is the same:

No matter how you offset the aiming, it scales based on distance from the target.

#9
06/07/2014 (10:47 am)
Hmm, well after all of that I've reverted to my pseudocode (posted above a few posts or so...). It appears I can solve this just with script. Since I've found that I can just alter the offset value directly without changing the weapon datablock, I'm just using a %tempdist variable to store the distance. Then, based on distance, offset the shot accordingly.

Here's a rough example in case someone else tries to get the aim lined up. In my case, in this isometric view, I will only need to use perhaps 2 or 3 'ranges':

%tempdist = vectorDist(%this.getposition(), %target.getposition());
				echo(%tempdist);
				if(%tempdist >= 15)
				{
					%this.setAimObject(%target, "0 -1 -1");
					
					%this.schedule(600, "canWeShoot", %target);
					if(%attack == 0) 
					{
						%this.attack = 1;
					}
					
					%this.schedule(400, "aiShoot");
					return;
				}
				
				else if(%tempdist < 15 && %tempdist > 10)
				{
					%this.setAimObject(%target, "0 0 -1");
					
					%this.schedule(600, "canWeShoot", %target);
					if(%attack == 0) 
					{
						%this.attack = 1;
					}
					
					%this.schedule(400, "aiShoot");
					return;
				}				
				
				else if(%tempdist <= 10)
				{
					%this.setAimObject(%target, "0 0 0");
					
					%this.schedule(600, "canWeShoot", %target);
					if(%attack == 0) 
					{
						%this.attack = 1;
					}
					
					%this.schedule(400, "aiShoot");
					return;
				}

I am not sure exactly what the offset values should be for each range just yet, but I am happy to report that the aiming offset is indeed changing based on range using the above. It may take some doing, but at least now I feel that I may get this working. I'm considering this solved unless some other huge issue arises; if nothing else, by observing my maddened ramblings someone else may find the solution faster in the future.

P.S. It should be noted that the above will loop the shooting for auto-shooting AIPlayers. You would want to 'zero out' the %tempdist prior to the shot aiming again (%tempdist = "";).

P.P.S. It also should be noted that this will work for me given my viewpoint and AIPlayer setup. I still feel that perhaps none of this scripting offset adjustment would be necessary if the model was aimed correctly. Although this is just speculation. Cheers!
#10
06/07/2014 (12:30 pm)
Thank you for posting the results, this may be useful for when I get back to the spells again.
#11
06/08/2014 (7:08 am)
Digging a little farther, I've noticed that when you adjust the offset using:

%this.setAimObject(%target, "0 0 0");

...if you move your client player around the target, any changes you've made to the x and y can cause the location you are aiming at to change. This actually makes perfect sense because what we are doing is setting our aim position to the target and offsetting that. Obviously, rotating around the object what used to be right can end up being left and so on.

The Beginnings of a True Solution
Taking all of this into consideration, I realized that there are 3 variables that we will need to make this all work just right:
  • Distance
  • Rotation
  • Elevation

No matter how you approach this problem, unless you can find where the error in aiming at a distance is originating (the model or perhaps a wrong integer in an algorithm within the source code) it's going to be necessary to calculate all of this position data prior to aiming. The first thing I did was set up the %tempdist variable as described above to store the distance between the player and the target. Using echos, I'm able to see the distance each time a shot is fired. Okay, check.

So now, using this as a guide, I found that if I leave the offset alone ("0 0 0") it will accurately hit the target until I get 10 units away (as reported by the %tempdist echo). After that, shots are going high and to the left. Now, in my case, since the view is isometric that leaves a range from 11 - 25 before the enemies start going off the edge of the screen. I quickly realized that if I were to 'scale' the z offset based on distance this could all work more smoothly.

As of this posting (with some help from my loving wife with the unpracticed math), we've managed to figure out the formula needed for the %offsetz variable so that it will scale based on distance (in my case the range is from 11-25). I wanted to share this with the community, although there is still much more to be calculated, because I believe once we figure out all of the calculations (elevation, rotation, %offsetx, %offsety) this should work as a precise aiming solution:

Getting the %offsetz Based on Distance
First, here's the basic formula (bear in mind this number works within a range of 11 to 25 units):

Z = D/-15.6
Z equals Distance divided by -15.6.

The code:
%offsetz = %tempdist/-15.6;

Then, all you have to do is be sure that you plug in the %offsetz when declaring the offset like so:

%this.setAimObject(%target, "0 0"SPC%offsetz);

Also keep in mind I call on this offset if the distance is greater than 10 or less than 25. Otherwise, it's just 'zero'd out' at "0 0 0" (within 10 units). I'm happy to report this works flawlessly within my distance ranges, but as of now this is only the solution for:

%offsetz based on Distance

I'm still working on:
  • %offsetx, %offsety based on Distance
  • %offsetx, %offsety, %offsetz based on Rotation
  • %offsetx, %offsety, %offsetz based on Elevation

As you can see that's quite a bit of formulas that need to be worked out to get this all rolling. Presently I'm more inclined to figure this out in script than fix the actual underlying problem (in Torque source or in the model) simply because I'm not sure where the problem is originating. If anyone has a 'real fix' for the shortcomings of stock T3D aiming from this camera view please share! I'd rather not have to script in all these formula just to 'fix' an aim offset that should be working in the first place.( i.e. Aiming up and to the left at a distance beyond 10 units.)
#12
06/08/2014 (2:54 pm)
Without actually reading through this whole thread ...

Jesse, do a little test.

//comment these 2 out
   //useEyeNode = true;
   //useEyeOffset = true;

//I've always been suspicious of this working correctly, turn to false
   correctMuzzleVector = false;
   eyeOffset = "0.0 0.0 0.1";
   projectileSpread = "0.0";

eyeOffset is supposed to override the firing point at which the projectile is created and thus launched from. So with the above value the bullet will spawn 1cm above the eye node of the player and not from the weapon's muzzlepoint.

setAimObject fires at the object's xyz which is the feet. The "x y z" modifier for it is in world units.
%this.setAimObject(%tgt, "0 0 1");
//this fires 1 metre up from the target's feet, note soldier character is >2m high

That "should" give perfect accuracy to an Ai.

This is all one of the reasons I tend to use %this.setAimLocation(%tgt.getEyePoint()); instead, but make sure the eye node is actually inside the model, I have a feeling soldier's isn't. I always thought that there was a lot of bent nodes on soldier that just don't line up with the real world.
#13
06/08/2014 (4:01 pm)
Firstly, thanks for the response Steve. I'm greatly inspired by your work, and have learned so many things from your resources. Also, congratulations on Airship Dragoon, so cool! In many ways, I'd like to have shooting that works like you have there only in my case I want the camera locked at an isometric (top-down like Diablo) perspective.

I just tested the settings above, and my results are largely different than what's expected. Using an offset of "0 0 1" only causes my AIPlayer to aim much higher than the original up and to the left problem area. I wonder, since obviously you have this working even at high ranges, if my position data of the %target is somehow off. What I'm doing right now is passing a %target variable to the serverCmdattack. I'm gathering this %target variable from right mouse click, perhaps this is incorrect (I admit I am having trouble understanding the exact way a mouse click's position data is translated to the target's position):

In scripts/gui/PlayGui.cs:
function PlayGui::onRightMouseDown(%this, %pos, %start, %ray)
{	
	%ai = %client.player;
	%ray = VectorScale(%ray, 30);
	%end = VectorAdd(%start, %ray);
	
	%searchMasks = $TypeMasks::PlayerObjectType;
	
	%scanTarg = ContainerRayCast (%start, %end, %searchMasks, %ai );
	
	if( %scanTarg )
	{
		%target = firstWord(%scanTarg);
		%xyz = restWords(%scanTarg);	
	
		commandToServer('attack', %target);
	}
}

The only other thing I can think of is my camera is set to 'OrbitMode' and %client.setFirstPerson(false);

Again, thanks, made my day having you reply Steve. A legend, right here in my thread!
#14
06/08/2014 (5:18 pm)
I found the problem. It's pretty ridiculous after all of this trouble I've gone through:

In Ryder.cs:
...
datablock ShapeBaseImageData(RyderWeaponImage)
{
   // Basic Item properties
   shapeFile = "art/shapes/weapons/Ryder/TP_Ryder.DAE";
   
   // COMMENT THIS OUT! IF NOT THE AIM IS OFF!!
   //shapeFileFP = "art/shapes/weapons/Ryder/FP_Ryder.DAE";

For whatever reason, the engine decided it would be a good idea to toy with noobs like me by allowing the FP shapeFile to be loaded and used erroneously with .setFirstPerson set to false...

Nothing to see here, just a totally awesome working prototype!! Thanks for the replies, ended up just stumbling on this silly fix. Cheers!