Rubber balls should now target the first kart again; improved handling
of the situation that the target kart is lost (e.g. it might have finished the race), so that the ball smoothly aims at the new target now. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@9547 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
2824225df2
commit
a6414b08b4
@ -43,40 +43,51 @@ RubberBall::RubberBall(Kart *kart)
|
|||||||
setAdjustUpVelocity(false);
|
setAdjustUpVelocity(false);
|
||||||
m_max_lifespan = 9999;
|
m_max_lifespan = 9999;
|
||||||
m_target = NULL;
|
m_target = NULL;
|
||||||
|
// Just init the previoux coordinates with some value that's not getXYZ()
|
||||||
|
m_previous_xyz = m_owner->getXYZ();
|
||||||
|
|
||||||
computeTarget();
|
computeTarget();
|
||||||
|
|
||||||
// Get 4 points for the interpolation
|
// Initialises the current graph node
|
||||||
// Determine distance along track
|
|
||||||
TrackSector::update(getXYZ());
|
TrackSector::update(getXYZ());
|
||||||
m_control_points[0] = m_owner->getXYZ();
|
initializeControlPoints(m_owner->getXYZ());
|
||||||
|
|
||||||
|
// At the start the ball aims at quads till it gets close enough to the
|
||||||
|
// target:
|
||||||
|
m_aiming_at_target = false;
|
||||||
|
m_timer = 0.0f;
|
||||||
|
m_interval = m_st_interval;
|
||||||
|
m_current_max_height = m_max_height;
|
||||||
|
} // RubberBall
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
/** Sets up the control points for the interpolation. The parameter contains
|
||||||
|
* the coordinates of the first control points (i.e. a control point that
|
||||||
|
* influences the direction the ball is flying only, not the actual
|
||||||
|
* coordinates - see details about Catmull-Rom splines). This function will
|
||||||
|
* then set the 2nd control point to be the current coordinates of the ball,
|
||||||
|
* and find two more appropriate control points for a smooth movement.
|
||||||
|
* \param xyz Coordinates of the first control points.
|
||||||
|
*/
|
||||||
|
void RubberBall::initializeControlPoints(const Vec3 &xyz)
|
||||||
|
{
|
||||||
|
m_control_points[0] = xyz;
|
||||||
m_control_points[1] = getXYZ();
|
m_control_points[1] = getXYZ();
|
||||||
m_last_aimed_graph_node = getSuccessorToHitTarget(getCurrentGraphNode());
|
m_last_aimed_graph_node = getSuccessorToHitTarget(getCurrentGraphNode());
|
||||||
// This call defined m_control_points[3], but also sets a new
|
// This call defined m_control_points[3], but also sets a new
|
||||||
// m_last_aimed_graph_node, which is further away from the current point,
|
// m_last_aimed_graph_node, which is further away from the current point,
|
||||||
// which avoids the problem that the ball might go too quickly to the
|
// which avoids the problem that the ball might go too quickly to the
|
||||||
// left or right when firing the ball off track.
|
// left or right when firing the ball off track.
|
||||||
static int xx=0;
|
getNextControlPoint();
|
||||||
xx++; if(xx>1) xx=0;
|
|
||||||
if(xx) getNextControlPoint();
|
|
||||||
m_control_points[2] =
|
m_control_points[2] =
|
||||||
QuadGraph::get()->getQuadOfNode(m_last_aimed_graph_node).getCenter();
|
QuadGraph::get()->getQuadOfNode(m_last_aimed_graph_node).getCenter();
|
||||||
|
|
||||||
// This updates m_last_aimed_graph_node, and sets m_control_points[3]
|
// This updates m_last_aimed_graph_node, and sets m_control_points[3]
|
||||||
getNextControlPoint();
|
getNextControlPoint();
|
||||||
m_length_cp_1_2 = (m_control_points[2]-m_control_points[1]).length();
|
m_length_cp_1_2 = (m_control_points[2]-m_control_points[1]).length();
|
||||||
|
m_t = 0;
|
||||||
m_t = 0;
|
m_t_increase = m_speed/m_length_cp_1_2;
|
||||||
m_t_increase = m_speed/m_length_cp_1_2;
|
} // initialiseControlPoints
|
||||||
|
|
||||||
// At the start the ball aims at quads till it gets close enough to the
|
|
||||||
// target:
|
|
||||||
m_aiming_at_target = false;
|
|
||||||
m_wrapped_around = false;
|
|
||||||
m_timer = 0.0f;
|
|
||||||
m_interval = m_st_interval;
|
|
||||||
m_current_max_height = m_max_height;
|
|
||||||
} // RubberBall
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
/** Determines the first kart. If a target has already been identified in an
|
/** Determines the first kart. If a target has already been identified in an
|
||||||
@ -193,47 +204,32 @@ void RubberBall::update(float dt)
|
|||||||
hit(NULL);
|
hit(NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkDistanceToTarget();
|
||||||
|
|
||||||
// FIXME: do we want to test if we have overtaken the target kart?
|
// FIXME: do we want to test if we have overtaken the target kart?
|
||||||
|
m_previous_xyz = getXYZ();
|
||||||
|
|
||||||
// If we have reached or overshot the next control point, move to the
|
Vec3 next_xyz;
|
||||||
// the next section of the spline
|
if(m_aiming_at_target)
|
||||||
m_t += m_t_increase * dt;
|
|
||||||
if(m_t > 1.0f)
|
|
||||||
{
|
{
|
||||||
// Move the control points and estimated distance forward.
|
// If the rubber ball is already close to a target, i.e. aiming
|
||||||
for(unsigned int i=1; i<4; i++)
|
// at it directly, stop interpolating, instead fly straight
|
||||||
m_control_points[i-1] = m_control_points[i];
|
// towards it.
|
||||||
m_length_cp_1_2 = m_length_cp_2_3;
|
Vec3 diff = m_target->getXYZ()-getXYZ();
|
||||||
int old = m_last_aimed_graph_node;
|
next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff;
|
||||||
//
|
|
||||||
// This automatically sets m_control_points[3]
|
|
||||||
getNextControlPoint();
|
|
||||||
//printf("1_2 %f length %f\n", m_length_cp_1_2,
|
|
||||||
// (m_control_points[2]-m_control_points[1]).length());
|
|
||||||
m_t_increase = m_speed/m_length_cp_1_2;
|
|
||||||
m_t -= 1.0f;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
Vec3 next_xyz = 0.5f * ((-m_control_points[0] + 3*m_control_points[1] -3*m_control_points[2] + m_control_points[3])*m_t*m_t*m_t
|
{
|
||||||
+ (2*m_control_points[0] -5*m_control_points[1] + 4*m_control_points[2] - m_control_points[3])*m_t*m_t
|
interpolate(&next_xyz, dt);
|
||||||
+ (-m_control_points[0]+m_control_points[2])*m_t
|
}
|
||||||
+ 2*m_control_points[1]);
|
m_timer += dt;
|
||||||
|
float height = updateHeight();
|
||||||
float old_distance = getDistanceFromStart();
|
next_xyz.setY(getHoT()+height);
|
||||||
|
|
||||||
// Determine new distance along track
|
// Determine new distance along track
|
||||||
TrackSector::update(next_xyz);
|
TrackSector::update(next_xyz);
|
||||||
|
|
||||||
float track_length = World::getWorld()->getTrack()->getTrackLength();
|
|
||||||
|
|
||||||
// Detect if the ball crossed the start line
|
|
||||||
m_wrapped_around = old_distance > 0.9f * track_length &&
|
|
||||||
getDistanceFromStart()< 10.0f;
|
|
||||||
|
|
||||||
m_timer += dt;
|
|
||||||
float height = updateHeight();
|
|
||||||
next_xyz.setY(getHoT()+height);
|
|
||||||
|
|
||||||
// Ball squashing:
|
// Ball squashing:
|
||||||
// ===============
|
// ===============
|
||||||
// If we start squashing the ball as soon as the height is smaller than
|
// If we start squashing the ball as soon as the height is smaller than
|
||||||
@ -250,6 +246,38 @@ void RubberBall::update(float dt)
|
|||||||
setXYZ(next_xyz);
|
setXYZ(next_xyz);
|
||||||
} // update
|
} // update
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
/** Uses Hermite splines (Catmull-Rom) to interpolate the position of the
|
||||||
|
* ball between the control points. If the next point would be outside of
|
||||||
|
* the spline between control_points[1] and [2], a new control point is
|
||||||
|
* added.
|
||||||
|
* \param next_xyz Returns the new position.
|
||||||
|
* \param The time step size.
|
||||||
|
*/
|
||||||
|
void RubberBall::interpolate(Vec3 *next_xyz, float dt)
|
||||||
|
{
|
||||||
|
// If we have reached or overshot the next control point, move to the
|
||||||
|
// the next section of the spline
|
||||||
|
m_t += m_t_increase * dt;
|
||||||
|
if(m_t > 1.0f)
|
||||||
|
{
|
||||||
|
// Move the control points and estimated distance forward.
|
||||||
|
for(unsigned int i=1; i<4; i++)
|
||||||
|
m_control_points[i-1] = m_control_points[i];
|
||||||
|
m_length_cp_1_2 = m_length_cp_2_3;
|
||||||
|
|
||||||
|
// This automatically sets m_control_points[3]
|
||||||
|
getNextControlPoint();
|
||||||
|
m_t_increase = m_speed/m_length_cp_1_2;
|
||||||
|
m_t -= 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
*next_xyz = 0.5f * ((-m_control_points[0] + 3*m_control_points[1] -3*m_control_points[2] + m_control_points[3])*m_t*m_t*m_t
|
||||||
|
+ (2*m_control_points[0] -5*m_control_points[1] + 4*m_control_points[2] - m_control_points[3])*m_t*m_t
|
||||||
|
+ (-m_control_points[0]+m_control_points[2])*m_t
|
||||||
|
+ 2*m_control_points[1]);
|
||||||
|
} // interpolate
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Updates the height of the rubber ball, and if necessary also adjusts the
|
/** Updates the height of the rubber ball, and if necessary also adjusts the
|
||||||
* maximum height of the ball depending on distance from the target. The
|
* maximum height of the ball depending on distance from the target. The
|
||||||
@ -306,106 +334,49 @@ float RubberBall::updateHeight()
|
|||||||
} // updateHeight
|
} // updateHeight
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Determines which coordinates the ball should aim at next. If the ball is
|
void RubberBall::checkDistanceToTarget()
|
||||||
* still 'far' away from the target (>20), then it will aim at the next
|
|
||||||
* graph node. If it's closer, the ball will aim directly at the kart and
|
|
||||||
* keep on aiming at the kart, it will not follow the drivelines anymore.
|
|
||||||
* \param aim_xyz On return contains the xyz coordinates to aim at.
|
|
||||||
*/
|
|
||||||
void RubberBall::determineTargetCoordinates(float dt, Vec3 *aim_xyz)
|
|
||||||
{
|
{
|
||||||
// If aiming at target phase, keep on aiming at target.
|
// If aiming at target phase, keep on aiming at target.
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
if(m_aiming_at_target)
|
if(m_aiming_at_target) return;
|
||||||
{
|
|
||||||
*aim_xyz = m_target->getXYZ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aiming at a graph node
|
|
||||||
// ----------------------
|
|
||||||
GraphNode *gn = &(QuadGraph::get()->getNode(m_last_aimed_graph_node));
|
|
||||||
|
|
||||||
// At this stage getDistanceFromStart() is already the new distance (set
|
|
||||||
// in the previous time step when aiming). It has to be detected if the
|
|
||||||
// ball is now ahead of the graph node, and if so, the graph node has to
|
|
||||||
// be updated till it is again ahead of the ball. Three distinct cases
|
|
||||||
// have to be considered:
|
|
||||||
// 1) The ball just crossed the start line (-> distance close to 0),
|
|
||||||
// but the graph node is still before the start line, in which case
|
|
||||||
// a new graph node has to be determined.
|
|
||||||
// 2) The ball hasn't crossed the start line, but the graph node has
|
|
||||||
// (i.e. graph node is 0), in which case the graph node is correct.
|
|
||||||
// This happens after the first iteration, i.e. graph node initially
|
|
||||||
// is the last one (distance close to track length), but if the ball
|
|
||||||
// is ahead (distance of a graph node is measured to the beginning of
|
|
||||||
// the quad, so if the ball is in the middle of the last quad it will
|
|
||||||
// be ahead of the graph node!) the graph node will be set to the
|
|
||||||
// first graph node (distance close to 0). In this case the graph node
|
|
||||||
// should not be changed anymore.
|
|
||||||
// 3) All other cases that do not involve the start line at all
|
|
||||||
// (including e.g. ball and graph node crossed start line, neither
|
|
||||||
// ball nor graph node crossed start line), which means that a new
|
|
||||||
// graph node need to be determined only if the distance along track
|
|
||||||
// of the ball is greater than the distance for
|
|
||||||
float gn_distance = gn->getDistanceFromStart();
|
|
||||||
float track_length = World::getWorld()->getTrack()->getTrackLength();
|
|
||||||
|
|
||||||
// Test 1: ball wrapped around, and graph node is close to end of track
|
|
||||||
bool ball_ahead = m_wrapped_around && gn_distance >0.9f*track_length;
|
|
||||||
|
|
||||||
// Test 3: distance of ball greater than distance of graph node
|
|
||||||
if(!ball_ahead && gn_distance < getDistanceFromStart())
|
|
||||||
// The distance test only applies if the graph node hasn't wrapped
|
|
||||||
// around
|
|
||||||
ball_ahead = true;
|
|
||||||
|
|
||||||
while(ball_ahead)
|
|
||||||
{
|
|
||||||
// FIXME: aim better if necessary!
|
|
||||||
m_last_aimed_graph_node = getSuccessorToHitTarget(m_last_aimed_graph_node);
|
|
||||||
gn = &(QuadGraph::get()->getNode(m_last_aimed_graph_node));
|
|
||||||
|
|
||||||
// Detect a wrap around of the graph node. We could just test if
|
|
||||||
// the index of the new graph node is 0, but since it's possible
|
|
||||||
// that we might have tracks with a more complicated structure, e.g
|
|
||||||
// with two different start lines, we use this more general test:
|
|
||||||
// If the previous distance was close to the end of the track, and
|
|
||||||
// the new distance is close to 0, the graph node wrapped around.
|
|
||||||
// This test prevents an infinite loop if the ball is on the last
|
|
||||||
// quad, in which case no graph node would fulfill the distance test.
|
|
||||||
if(gn_distance > 0.9f*track_length &&
|
|
||||||
gn->getDistanceFromStart()<10.0f)
|
|
||||||
break;
|
|
||||||
gn_distance = gn->getDistanceFromStart();
|
|
||||||
ball_ahead = m_wrapped_around && gn_distance >0.9f*track_length;
|
|
||||||
ball_ahead = !ball_ahead &&
|
|
||||||
gn->getDistanceFromStart() < getDistanceFromStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
const LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld());
|
const LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld());
|
||||||
if(!world) return; // FIXME battle mode
|
if(!world) return; // FIXME battle mode
|
||||||
|
|
||||||
float target_distance =
|
float target_distance =
|
||||||
world->getDistanceDownTrackForKart(m_target->getWorldKartId());
|
world->getDistanceDownTrackForKart(m_target->getWorldKartId());
|
||||||
|
float ball_distance = getDistanceFromStart();
|
||||||
|
|
||||||
// Handle wrap around of distance if target crosses finishing line
|
float diff = target_distance - ball_distance;
|
||||||
if(getDistanceFromStart() > target_distance)
|
if(diff < 0)
|
||||||
target_distance += world->getTrack()->getTrackLength();
|
|
||||||
|
|
||||||
// If the ball is close enough, start aiming directly at the target kart
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
if(target_distance-getDistanceFromStart()< 20)
|
|
||||||
{
|
{
|
||||||
m_aiming_at_target = true;
|
diff += world->getTrack()->getTrackLength();
|
||||||
*aim_xyz = m_target->getXYZ();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------
|
if(diff < 50)
|
||||||
*aim_xyz = gn->getQuad().getCenter();
|
{
|
||||||
|
m_aiming_at_target = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(m_aiming_at_target)
|
||||||
|
{
|
||||||
|
// It appears that we have lost the target. It was within
|
||||||
|
// the target distance, and now it isn't. That means either
|
||||||
|
// the original target escaped, or perhaps that there is a
|
||||||
|
// new target. In this case we have to reset the control
|
||||||
|
// points, since it's likely that the ball is (after some time
|
||||||
|
// going directly towards the target) far outside of the
|
||||||
|
// old control points.
|
||||||
|
|
||||||
} // determineTargetCoordinates
|
// We use the previous XYZ point to define the first control
|
||||||
|
// point, which results in a smooth transition from aiming
|
||||||
|
// directly at the target back to interpolating again.
|
||||||
|
initializeControlPoints(m_previous_xyz);
|
||||||
|
m_aiming_at_target = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} // checkDistanceToTarget
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Callback from the physics in case that a kart or object is hit. The rubber
|
/** Callback from the physics in case that a kart or object is hit. The rubber
|
||||||
|
@ -49,9 +49,14 @@ private:
|
|||||||
int m_last_aimed_graph_node;
|
int m_last_aimed_graph_node;
|
||||||
|
|
||||||
/** Keep the last two, current, and next aiming points
|
/** Keep the last two, current, and next aiming points
|
||||||
* for interpolation. */
|
* for interpolation. */
|
||||||
Vec3 m_control_points[4];
|
Vec3 m_control_points[4];
|
||||||
|
|
||||||
|
/** Saves the previous location of the ball. This is needed if a ball
|
||||||
|
* should lose it target, and has to reinitialise the control points
|
||||||
|
* for the interpolation. */
|
||||||
|
Vec3 m_previous_xyz;
|
||||||
|
|
||||||
/** Estimated length of the spline between the control points
|
/** Estimated length of the spline between the control points
|
||||||
* 1 and 2. */
|
* 1 and 2. */
|
||||||
float m_length_cp_1_2;
|
float m_length_cp_1_2;
|
||||||
@ -86,11 +91,6 @@ private:
|
|||||||
* reduced if the ball gets closer to the target. */
|
* reduced if the ball gets closer to the target. */
|
||||||
float m_current_max_height;
|
float m_current_max_height;
|
||||||
|
|
||||||
/** True if the ball just crossed the start line, i.e. its
|
|
||||||
* distance changed from close to length of track in the
|
|
||||||
* previous time step to a bit over zero now. */
|
|
||||||
bool m_wrapped_around;
|
|
||||||
|
|
||||||
/** Once the ball is close enough, it will aim for the kart. If the
|
/** Once the ball is close enough, it will aim for the kart. If the
|
||||||
* kart should be able to then increase the distance to the ball,
|
* kart should be able to then increase the distance to the ball,
|
||||||
* the ball will be removed and the kart escapes. This boolean is
|
* the ball will be removed and the kart escapes. This boolean is
|
||||||
@ -98,11 +98,13 @@ private:
|
|||||||
bool m_aiming_at_target;
|
bool m_aiming_at_target;
|
||||||
|
|
||||||
void computeTarget();
|
void computeTarget();
|
||||||
void determineTargetCoordinates(float dt, Vec3 *aim_xyz);
|
void checkDistanceToTarget();
|
||||||
unsigned int getSuccessorToHitTarget(unsigned int node_index,
|
unsigned int getSuccessorToHitTarget(unsigned int node_index,
|
||||||
float *f=NULL);
|
float *f=NULL);
|
||||||
void getNextControlPoint();
|
void getNextControlPoint();
|
||||||
float updateHeight();
|
float updateHeight();
|
||||||
|
void interpolate(Vec3 *next_xyz, float dt);
|
||||||
|
void initializeControlPoints(const Vec3 &xyz);
|
||||||
public:
|
public:
|
||||||
RubberBall (Kart* kart);
|
RubberBall (Kart* kart);
|
||||||
static void init(const XMLNode &node, scene::IMesh *bowling);
|
static void init(const XMLNode &node, scene::IMesh *bowling);
|
||||||
|
Loading…
Reference in New Issue
Block a user