Getting an angle between locations
#1
Posté 17 juin 2012 - 11:15
I found a script Lightfoot8 had written here, that's in essence the opposite of what I need, getting a location in a certain arc, instead of checking if a location falls in that arc.
Essentially I just need it to check if location 2 is within +/- 90 degrees of the facing of location 1.
I assume between VectorNormalize, VectorToAngle, and AngleToVector it must be possible to do, but I'm drawing a blank in how to go about it. I can probably hammer something out based on trig comparisons, but that's not usually very efficient.
Any ideas how to go about this?
#2
Posté 17 juin 2012 - 11:31
I would GetAheadLocation and then GetAngleBetweenLocations and see if it's >90.
<...sextant>
#3
Posté 17 juin 2012 - 11:56
#4
Posté 18 juin 2012 - 12:08
Only Percieve Player if Seen/ Deaf NPCs also covers the same topic and function.
As discused in the Function Request thread GetAngleBetweenLocations Is somewhat flawed.
Modifié par Lightfoot8, 18 juin 2012 - 12:10 .
#5
Posté 18 juin 2012 - 12:14
The biggest problem is that nwn engine calls OnPerceived only in certain cases. If you block the attack from OnPerceived because guard in fact shouldn't see the thief behind him, its a bit problematic to get him attack when he changes position. Heartbeat is each 6seconds which gives PC plenty of time to hide/overhaul him.
Probably some code could be added into the WalkWaypoints function to re-initiate sight of view everytime NPC reach new WP, but still far from perfection.
Anyway, searching in old modules, I found this piece of code in OnPerceived:
if(!GetIsInShape(oPercept,SHAPE_SPELLCONE,35.0,GetLocation(OBJECT_SELF),TRUE)) return;
Might help you as you don't need to do anything with angles with this approach. If you finish it anytime, I would be very interested in such code.
#6
Posté 18 juin 2012 - 12:22
The spellcone idea is a good one though, I think those are 60 degree wedges, so I could check the front wedge, then the outer two, and apply different modifiers based on how directly in the line of sight the target is.
I should have an idea if that's workable or not in another 15 or 20 minutes.
#7
Posté 18 juin 2012 - 12:22
Here is six's version.
// Determine if oSource is facing oTarget using tolerance of angle fRange
// * returns TRUE if facing, else FALSE
// _six
int GetIsFacingObject(object oSource, object oTarget, float fRange);
int GetIsFacingObject(object oSource, object oTarget, float fRange)
{
// Create directional vector
vector vDir = GetPosition(oTarget) - GetPosition(oSource);
float fVLen = GetDistanceBetween(oTarget, oSource);
vDir /= fVLen;
// Calculate angle between two objects, and facing of source
float angle = acos(vDir.y);
float facing = GetFacing(oSource);
// Make sure angle is signed equivalently to the dir vector
if(vDir.x < 0.0f) angle = 0.0f - angle;
// Deal with NWN's rotation offset - due north is actually 90 degrees
if(angle > 90.0f) angle -= 360.0f;
// If angle falls within range given, must be facing
if(facing < 90.0f - angle + fRange/2.0f
&& facing > 90.0f - angle - fRange/2.0f)
{
return TRUE;
}
// ...otherwise can't be, return FALSE
return FALSE;
}
And here is My version.
int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc);
int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc)
{
location lSelf = GetLocation(oSelf);
location lTarget = GetLocation(oTarget);
float AngleOffset = VectorToAngle( GetPosition(oTarget) - GetPosition(oSelf)) - GetFacing(oSelf) ;
//float fViewArc = 180.0;
return (abs(FloatToInt(AngleOffset)) < nViewArc/2);
}
Modifié par Lightfoot8, 18 juin 2012 - 12:26 .
#8
Posté 18 juin 2012 - 12:37
Here's what I've got at the moment for my reaction time finding script (with Lightfoots above it):
Admittedly, it's untested still, so the values might need tweaking. I'm only using it in the events when the NPC is out of combat, to simulate surprise. I even threw in some use for those seldom used initiative feats in it.int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc)
{
location lSelf = GetLocation(oSelf);
location lTarget = GetLocation(oTarget);
float AngleOffset = VectorToAngle( GetPosition(oTarget) - GetPosition(oSelf)) - GetFacing(oSelf) ;
//float fViewArc = 180.0;
return (abs(FloatToInt(AngleOffset)) < nViewArc/2);
}
//// Gets the reaction time of oSource to the perceived threat (oTarget).
//// Returned time is 0.1 to 6.0, allowing for one full surprise round in certain conditions.
float GetReactionTime (object oTarget, object oSource = OBJECT_SELF)
{
float fTime;
location lTarget = GetLocation (oTarget);
location lSource = GetLocation (oSource);
float fDistance = GetDistanceBetweenLocations(lTarget, lSource);
//// Is lTarget seen?
if (GetIsFacingTarget (oSource, oTarget, 180) && GetObjectSeen (oTarget, oSource))
{
fDistance -= IntToFloat (GetSkillRank (SKILL_SPOT, oSource));
}
//// or heard?
else if (GetObjectHeard (oTarget, oSource))
{
fDistance -= IntToFloat (GetSkillRank (SKILL_LISTEN, oSource));
}
//// Distance, modified potentially by spot/listen, +/- 0.1 per meter.
if (fDistance > 0.0) fTime += (fDistance / 10.0);
//// Dexterity modifies 0.1 per point +/-
fTime -= (IntToFloat (GetAbilityModifier (ABILITY_DEXTERITY, oSource)) / 10.0);
if (GetHasFeat (FEAT_IMPROVED_INITIATIVE, oSource)) fTime -= 0.4;
if (GetHasFeat (FEAT_EPIC_SUPERIOR_INITIATIVE, oSource)) fTime -= 0.4;
if (fTime < 0.1) fTime = 0.1;
else if (fTime > 6.0) fTime = 6.0;
return fTime;
}
Edit: I'm not sure why that even compiled, since I was subtracting an int from a float. My version of the GetSkillRank checks for skill synergies as well, but I editted that out in case anyone wanted to try this script.
Modifié par Failed.Bard, 18 juin 2012 - 04:17 .
#9
Posté 18 juin 2012 - 03:33
Failed.Bard wrote...
I tried Lightfoot8's code first, works like a charm, though admittedly I have no idea why. Vector manipulation still throws me for a loop.
I will try and give an explanation. In truth once you get use to vectors they are easy to work with and open up a lot of things you can do in your code.
A vector is nothing more then a set of coordinates that represent where one object is from another. where a position is coordinates that represent where a point is from the (0,0) point on a graph. The vector defines both a Magnitude ( length of the vector ) and an angle.
As an example I will place two objects on a grid the source PC at (4,4) facing off at a 45 degree angle to the left of due north( up). and the target NPC at (13,13) .

To get the vector that the NPC is from the PC you just subtract the cords of the source from the cords of the target.
in this case that will give us a vector of (9,9). Our magnitude of the vector will be 12.7 the distance between the two points. and the angle will be 45 degrees.
An important note here is that a lot of people make a big fuss about what angle north is at in the game and the effects it has on there calulations. In some cases it might. but it really has no effect when working with vectors. Since the vector is reltive to the two objects it really maks no differance where the 0 angle points is , As long as both angles are calculated from the same refferance, the answer will be correct.
in the Pic above my 0 degree refferance is the x axis giving me a 45 deg angle for my vector. That would give the PC a facing of 135 deg.
If the refferance is the y axis, for our 0 deg, That would make the vector a -45 deg angle and the PC's facing a 45 deg angle.
it just does not really matter what system the engine uses to calculate the angles, the differance between them will be the same. 90 degree result.
I believe it is easy to see from the pic above. how to find the angle that oNPC is from the facing of oPC. broken down it is:
1) Get the position of oPC.
2 ) Get the position of oNPC.
3 ) Get the Vector between the two.
4 ) convert the vector to an angle.
5 ) Get the facing of oPC.
6 ) subtract the facing of the PC from the vector.
Postive angles will be to left of his facing, negtive angles to the right.
//Get the position of oPC.
vector posPC = GetPosition( oPC);
//Get the position of oNPC.
vector posNPC = GetPosition( oNPC);
//Get the Vector between the two.
vector vPCtoNPC = posNPC - posPC ;
//convert the vector to an angle.
float AngleOfVector = VectorToAngle(vPCtoNPC);
//Get the facing of oPC.
float PCFacing = GetFacing(oPC);
//subtract the facing of the PC from the vector.
float AngleOfSight = AngleOfVector - PCFacing;
Modifié par Lightfoot8, 18 juin 2012 - 03:47 .
#10
Posté 18 juin 2012 - 04:34
#11
Posté 18 juin 2012 - 04:57
A Normalized vector is any vector with a magnitude (length) of 1.
It is simple to normlize a vector in NWN using the VectorNormalize(vVector) function.
It is also simple to get the magnitude of a vector using the VectorMagnitude( vVector) function.
multiplying a Vector by a float changes its magnitude, but leaves the angle the same. . this make it simple to get any location between objects.
If you wanted to get the position half way between two objects. you simply find the vector between the two objects, cut it in half and added it to your source object.
vector vVec = posTarget - posSource;
vector vBetween = ( vVec/2 ) + posSource;
if you want the distance 5m in front of oTarget simply get his facing, turn it into a vector ( Already normlized), multiply it by 5 and add it back to your target.
vector posTarget = GetPosition (oTarget);
vector vFacing = AngleToVector(GetFacing(oTarget));
vector posForwardFive = posTarget + vFacing * 5;
Once you get the hang of it, it becomes really simple, even though the code can end up looking complex.
Modifié par Lightfoot8, 18 juin 2012 - 04:58 .
#12
Posté 18 juin 2012 - 06:54
int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc)
{
float AngleOffset = VectorToAngle( GetPosition(oTarget) - GetPosition(oSelf)) - GetFacing(oSelf) ;
//float fViewArc = 180.0;
return (abs(FloatToInt(AngleOffset)) < nViewArc/2);
}
//// Gets the reaction time of oSource to the perceived threat (oTarget).
//// Returned time is 0.1 to 6.0, allowing for one full surprise round in certain conditions.
float GetReactionTime (object oTarget, object oSource = OBJECT_SELF)
{
float fTime;
location lTarget = GetLocation (oTarget);
location lSource = GetLocation (oSource);
float fDistance = GetDistanceBetweenLocations(lTarget, lSource);
//// Is lTarget seen?
if (GetIsFacingTarget (oSource, oTarget, 180) && GetObjectSeen (oTarget, oSource))
{
fDistance -= GetSkillRank (SKILL_SPOT, oSource);
}
//// or heard?
else if (GetObjectHeard (oTarget, oSource))
{
fDistance -= GetSkillRank (SKILL_LISTEN, oSource);
}
//// Distance, modified potentially by spot/listen, +/- 0.1 per meter.
if (fDistance > 0.0) fTime += (fDistance / 10.0);
//// Dexterity modifies 0.1 per point +/-
fTime -= GetAbilityModifier (ABILITY_DEXTERITY, oSource) / 10.0;
if (GetHasFeat (FEAT_IMPROVED_INITIATIVE, oSource)) fTime -= 0.4;
if (GetHasFeat (FEAT_EPIC_SUPERIOR_INITIATIVE, oSource)) fTime -= 0.4;
if (fTime < 0.1) fTime = 0.1;
else if (fTime > 6.0) fTime = 6.0;
return fTime;
}
#13
Posté 18 juin 2012 - 07:24
Lightfoot8 wrote...
Cleaned the code just a tad. I have no Idea why I had the two location variables in my function, So I removed them. I also removed the IntToFloat functions in your main body, They are not needed.
int GetIsFacingTarget(object oSelf, object oTarget, int nViewArc)
{
float AngleOffset = VectorToAngle( GetPosition(oTarget) - GetPosition(oSelf)) - GetFacing(oSelf) ;
//float fViewArc = 180.0;
return (abs(FloatToInt(AngleOffset)) < nViewArc/2);
}
//// Gets the reaction time of oSource to the perceived threat (oTarget).
//// Returned time is 0.1 to 6.0, allowing for one full surprise round in certain conditions.
float GetReactionTime (object oTarget, object oSource = OBJECT_SELF)
{
float fTime;
float fDistance = GetDistanceBetween(oTarget, oSource);
//// Is lTarget seen?
if (GetIsFacingTarget (oSource, oTarget, 180) && GetObjectSeen (oTarget, oSource))
{
fDistance -= GetSkillRank (SKILL_SPOT, oSource);
}
//// or heard?
else if (GetObjectHeard (oTarget, oSource))
{
fDistance -= GetSkillRank (SKILL_LISTEN, oSource);
}
//// Distance, modified potentially by spot/listen, +/- 0.1 per meter.
if (fDistance > 0.0) fTime += (fDistance / 10.0);
//// Dexterity modifies 0.1 per point +/-
fTime -= GetAbilityModifier (ABILITY_DEXTERITY, oSource) / 10.0;
if (GetHasFeat (FEAT_IMPROVED_INITIATIVE, oSource)) fTime -= 0.4;
if (GetHasFeat (FEAT_EPIC_SUPERIOR_INITIATIVE, oSource)) fTime -= 0.4;
if (fTime < 0.1) fTime = 0.1;
else if (fTime > 6.0) fTime = 6.0;
return fTime;
}
I didn't really need the location checks in mine either anymore. They'd been in there for trying to make the bioware script work.
Edit: I should add, without using a wrapper for DetermineCombatRound, you have to add a GetObjectSeen check into the start of that to make sure someone didn't go invisible or use HiPS during the delay.
Modifié par Failed.Bard, 18 juin 2012 - 07:26 .
#14
Posté 18 juin 2012 - 05:47
Lightfoot8 wrote...
If you wanted to get the position half way between two objects. you simply find the vector between the two objects, cut it in half and added it to your source object.
vector vVec = posTarget - posSource;
vector vBetween = ( vVec/2 ) + posSource;
if you want the distance 5m in front of oTarget simply get his facing, turn it into a vector ( Already normlized), multiply it by 5 and add it back to your target.
vector posTarget = GetPosition (oTarget);
vector vFacing = AngleToVector(GetFacing(oTarget));
vector posForwardFive = posTarget + vFacing * 5;
Once you get the hang of it, it becomes really simple, even though the code can end up looking complex.
Make sure multiplication/division is done by floats not integers. (e.g. vVec/2 should be vVec/2.0, and vFacing*5 should be vFacing*5.0).
Modifié par WhiZard, 18 juin 2012 - 05:47 .
#15
Posté 18 juin 2012 - 08:11





Retour en haut






