Solved
Targetting algorithm... I AM REALLY STUCK!
Posted on 1999-01-16
Hi
I am using this code in a game I am creating (asteroids clone). The computer opponent uses it to target an enemy, usually the player. The intent of the code is to predict where the player will be. It should also compensate for the computer oppoenent's speed to make sure it hits the target every time.
For some reason, however, the computer occasionally enters some sort of "berserk" mode in which it rapidly targets one way and then flips 180 degrees the other way. This results in a rather funny spectacle, but at the same time the computer opponent is basically an easy target. Can anyone suggest ways to improve the algorithm and/or fix the flipping-back-and-forth problem?
Also note, in the code I placed a command to draw small rectangle at the point the computer is aiming for. When the computer enters this "berserk mode," the point is always right near the computer opponent. It then rapidily flickers from one side of the computer to the other, resulting in the 180 degree change.
I know the code is slow because it uses floating point math. I don't particularily care about speed at the time being however, all I want is a function that works.
To sumarize, the problem is basically:
- Determine a point on the screen, where, if fired at, the shot would hit the opponenet.
- The speed of the shot is always 6.5 pixels/frame. Of course, the shot's speed in the x and y planes will vary, but the total speed is 6.5/frame.
- The shot's speed is affected by the player shooting it, thus if the player shooting it is moving at 10 pixels/frame to the left, the shots fired by that player would have their speed adjusted accordingly. I suspect this means that the player firing the shot's speed must be taken into account as well as the target's speed.
- You know the opponent's position and speed in the x and y planes.
- You know your (the player firing's) speed in the x and y planes.
Basically, all information that could possibly be relavent can be obtained about where to aim.
I am really stuck on this problem, and have asked several questions in the past regarding it, none of which recieved a good, solid, algorithm (scrapdog's was a great help though). So, for this problem, I am offering 200 points. If anyone can provide any insight on how to do this, please contribute. I really would like to get this thing figured out once and for all. Thank you!
Here is the targetting function:
void CPlayer::Target (CPlayer *players, CRock * /*asteroids*/, CDC *dc)
{ double xdist, ydist;
//double xdist2, ydist2;
double numframes;
double targetx, targety;
double dist1 = 9999.0, curdist;
int i;
double angle;
double playerx, playery;
m_ShouldAccel = false;
m_ShouldFire = false;
m_ShouldDecel = false;
// Determine who to shoot at:
if (m_Target == -1 || (m_Target != -1 ? players[m_Target].IsDead () == true : true))
{ for (i = 0; i < 34; i++)
{ if (players[i].IsDead () == true)
{ continue; }
if (i == m_PlayerNumber)
{ continue; }
curdist = Distance ((int) players[i].GetX (), (int) players[i].GetY ());
if (dist1 > curdist)
{ dist1 = curdist;
m_Target = i;
}
}
}
// This is the actual targetting code.
playerx = players[m_Target].GetX ();
playery = players[m_Target].GetY ();
xdist = playerx - m_PosX;
ydist = playery - m_PosY;
numframes = hypot (xdist, ydist) / 6.5;
// None of these three algorithms works very well.
// The last two result in the "berserck" mode problem,
// and the first one is not very accurate.
// I am confident the problem lies in these lines...
//targetx = players[m_Target].GetSpeedX () * numframes + players[m_Target].GetX ();
//targety = players[m_Target].GetSpeedY () * numframes + players[m_Target].GetY ();
targetx = (players[m_Target].GetSpeedX () - m_SpeedX) * numframes + players[m_Target].GetX ();
targety = (players[m_Target].GetSpeedY () - m_SpeedY) * numframes + players[m_Target].GetY ();
//targetx = (playerx + players[m_Target].GetSpeedX () * numframes)/* - (m_SpeedX * numframes)*/;
//targety = (playery + players[m_Target].GetSpeedY () * numframes)/* - (m_SpeedY * numframes)*/;
//BoundCoords (targetx, targety);
// This was added in the hope that I could figure out where the problem lied. It failed.
// CString temp;
// dc->SetTextColor (RGB (255, 0, 0));
// temp.Format ("Distance X: %.1f, Distance Y: %.1f", xdist, ydist);
// dc->TextOut (50, 50, temp);
// temp.Format ("Number of Frames: %.1f", numframes);
// dc->TextOut (50, 75, temp);
//
// temp.Format ("SpeedX of comp: %.1f, SpeedY of comp: %.1f", m_SpeedX, m_SpeedY);
// dc->TextOut (50, 100, temp);
// temp.Format ("SpeedX of player: %.1f, SpeedY of player: %.1f", players[m_Target].GetSpeedX (), players[m_Target].GetSpeedY ());
// dc->TextOut (50, 125, temp);
if (targetx > m_PosX)
{ if (targety > m_PosY) // #4
{ angle = atan2 ((targety - m_PosY), (targetx - m_PosX));
angle /= DEG2RAD;
angle = 360.0 - angle;
}
else // #1
{ angle = atan2 ((m_PosY - targety), (targetx - m_PosX));
angle /= DEG2RAD;
}
}
else
{ if (targety > m_PosY) // #3
{ angle = atan2 ((targety - m_PosY), (m_PosX - targetx));
angle /= DEG2RAD;
angle += 180.0;
}
else // #2
{ angle = atan2 ((m_PosY - targety), (m_PosX - targetx));
angle /= DEG2RAD;
angle = 180.0 - angle;
}
}
BoundDegrees (angle);
// Show target:
dc->FillSolidRect ((int) targetx - 2, (int) targety - 2, 4, 4, RGB (255, 255, 0));
m_Degrees = angle;
// if (angle > m_Degrees)
// { m_SpeedDegrees++;
// if (m_SpeedDegrees > 5)
// { m_SpeedDegrees = 5; }
// }
// else
// { m_SpeedDegrees--;
// if (m_SpeedDegrees < -5)
// { m_SpeedDegrees = -5; }
// }
m_PrevDist = m_CurDist;
m_CurDist = hypot (m_PosX - players[m_Target].GetX (), m_PosY - players[m_Target].GetY ());
if (m_PrevDist <= m_CurDist)
{ m_ShouldAccel = true; }
if (m_CurDist > m_MinDist)
{ m_ShouldAccel = true; }
if (m_MinDist < 150)
{ m_MinDist = 150; }
if (m_CurDist < m_MinDist) // Glom on mode.
{ m_MinDist = m_CurDist; }
if (m_CurDist < 250.0)
{ m_ShouldFire = true; }
}
Thank you very much!