1) Added a delete timer: if the ball has no target (e.g. fired by

player currently at front) it will disappear after a given
   time (atm 5 seconds).
2) If the ball overtakes the target it will now be deleted.
3) Fixed ball sometimes turning in the wrong direcftion when
   trying to hit a kart.
4) Some refactoring to make functions smaller.
5) Removed left-over debug code. 


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@9945 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2011-10-05 21:27:21 +00:00
parent b696dfad65
commit 3c8d1e896a
3 changed files with 116 additions and 97 deletions

View File

@ -41,13 +41,17 @@
squash-slowdown: How much karts that are squash-slowdown: How much karts that are
squashed are slowed down. squashed are slowed down.
squash-duration: How long karts stay squashed. squash-duration: How long karts stay squashed.
delete-timer: How long before the ball is removed
if no suitable target is found.
--> -->
<item name="rubber-ball" icon="rubber_ball-icon.png" <item name="rubber-ball" icon="rubber_ball-icon.png"
model="rubber_ball.b3d" speed="35.0" scale="1 1 1" model="rubber_ball.b3d" speed="35.0"
interval="1" max-height="4.0" min-height="0" scale="1 1 1" interval="1"
max-height="4.0" min-height="0"
target-distance="50" target-max-angle = "90" target-distance="50" target-max-angle = "90"
min-interpolation-distance="10" min-interpolation-distance="10"
squash-slowdown="0.5" squash-duration="3" /> squash-slowdown="0.5" squash-duration="3"
delete-time="5.0" />
<item name="parachute" icon="parachute-icon.png" <item name="parachute" icon="parachute-icon.png"
model="parachute.b3d" /> model="parachute.b3d" />
<item name="plunger" icon="plunger-icon.png" <item name="plunger" icon="plunger-icon.png"

View File

@ -34,6 +34,7 @@ float RubberBall::m_st_squash_duration;
float RubberBall::m_st_squash_slowdown; float RubberBall::m_st_squash_slowdown;
float RubberBall::m_st_target_distance; float RubberBall::m_st_target_distance;
float RubberBall::m_st_target_max_angle; float RubberBall::m_st_target_max_angle;
float RubberBall::m_st_delete_time;
int RubberBall::m_next_id = 0; int RubberBall::m_next_id = 0;
RubberBall::RubberBall(Kart *kart, Track* track) RubberBall::RubberBall(Kart *kart, Track* track)
@ -68,13 +69,10 @@ RubberBall::RubberBall(Kart *kart, Track* track)
m_previous_xyz = m_owner->getXYZ(); m_previous_xyz = m_owner->getXYZ();
computeTarget(); computeTarget();
if(!m_target)
{ // If there is no target (i.e. the firing kart is first kart that is
// This happens if the kart firing the rubber ball is the leader // still racing, start the delete timer.
// of all karts left in the race. m_delete_timer = m_target!=m_owner ? -1.0f : 10.0f;
hit(NULL);
return;
}
// initialises the current graph node // initialises the current graph node
TrackSector::update(getXYZ(), kart, track); TrackSector::update(getXYZ(), kart, track);
@ -83,7 +81,7 @@ RubberBall::RubberBall(Kart *kart, Track* track)
// At the start the ball aims at quads till it gets close enough to the // At the start the ball aims at quads till it gets close enough to the
// target: // target:
m_aiming_at_target = false; m_aiming_at_target = false;
m_timer = 0.0f; m_height_timer = 0.0f;
m_interval = m_st_interval; m_interval = m_st_interval;
m_current_max_height = m_max_height; m_current_max_height = m_max_height;
} // RubberBall } // RubberBall
@ -123,11 +121,10 @@ void RubberBall::initializeControlPoints(const Vec3 &xyz)
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 } // initializeControlPoints
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Determines the first kart. If a target has already been identified in an /** Determines the first kart that is still in the race.
* earlier call, it is tested first, avoiding a loop over all karts.
*/ */
void RubberBall::computeTarget() void RubberBall::computeTarget()
{ {
@ -141,11 +138,19 @@ void RubberBall::computeTarget()
m_target = world->getKartAtPosition(p); m_target = world->getKartAtPosition(p);
if(!m_target->isEliminated() && !m_target->hasFinishedRace()) if(!m_target->isEliminated() && !m_target->hasFinishedRace())
{ {
// Don't aim at yourself // If the firing kart itself is the first kart (that is
if(m_target == m_owner) m_target = NULL; // still driving), prepare to remove the rubber ball
if(m_target==m_owner)
m_delete_timer = m_st_delete_time;
return; return;
} }
} } // for p > num_karts
// This means it must be the end-animation phase. Now just
// aim at the owner (the ball is unlikely to hit it), and
// this will trigger the usage of the delete time in updateAndDelete
m_delete_timer = m_st_delete_time;
m_target = m_owner;
} // computeTarget } // computeTarget
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -221,6 +226,8 @@ void RubberBall::init(const XMLNode &node, scene::IMesh *bowling)
m_st_min_interpolation_distance = 30.0f; m_st_min_interpolation_distance = 30.0f;
m_st_target_distance = 50.0f; m_st_target_distance = 50.0f;
m_st_target_max_angle = 25.0f; m_st_target_max_angle = 25.0f;
m_st_delete_time = 10.0f;
if(!node.get("interval", &m_st_interval)) if(!node.get("interval", &m_st_interval))
printf("[powerup] Warning: no interval specified for rubber ball.\n"); printf("[powerup] Warning: no interval specified for rubber ball.\n");
if(!node.get("squash-duration", &m_st_squash_duration)) if(!node.get("squash-duration", &m_st_squash_duration))
@ -237,12 +244,16 @@ void RubberBall::init(const XMLNode &node, scene::IMesh *bowling)
if(!node.get("target-distance", &m_st_target_distance)) if(!node.get("target-distance", &m_st_target_distance))
printf( printf(
"[powerup] Warning: no target-distance specified for rubber ball.\n"); "[powerup] Warning: no target-distance specified for rubber ball.\n");
if(!node.get("delete-time", &m_st_delete_time))
printf(
"[powerup] Warning: no delete-time specified for rubber ball.\n");
if(!node.get("target-max-angle", &m_st_target_max_angle)) if(!node.get("target-max-angle", &m_st_target_max_angle))
printf( printf(
"[powerup] Warning: no target-max-angle specified for rubber ball.\n"); "[powerup] Warning: no target-max-angle specified for rubber ball.\n");
m_st_target_max_angle *= DEGREE_TO_RAD; m_st_target_max_angle *= DEGREE_TO_RAD;
Flyable::init(node, bowling, PowerupManager::POWERUP_RUBBERBALL); Flyable::init(node, bowling, PowerupManager::POWERUP_RUBBERBALL);
} // init } // init
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Picks a random message to be displayed when a kart is hit by the /** Picks a random message to be displayed when a kart is hit by the
* rubber ball. * rubber ball.
@ -289,80 +300,24 @@ bool RubberBall::updateAndDelete(float dt)
// finished the race). // finished the race).
computeTarget(); computeTarget();
if(!m_target) // Remove this item from the game if(m_delete_timer>0)
{
m_delete_timer -= dt;
if(m_delete_timer<0)
{ {
hit(NULL); hit(NULL);
return true; return true;
} }
}
checkDistanceToTarget(); checkDistanceToTarget();
Vec3 next_xyz; Vec3 next_xyz;
if(m_aiming_at_target) if(m_aiming_at_target)
{ moveTowardsTarget(&next_xyz, dt);
// If the rubber ball is already close to a target, i.e. aiming
// at it directly, stop interpolating, instead fly straight
// towards it.
Vec3 diff = m_target->getXYZ()-getXYZ();
next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff;
Vec3 old_vec = getXYZ()-m_previous_xyz;
Vec3 new_vec = next_xyz - getXYZ();
float angle = atan2(new_vec.getZ(), new_vec.getX())
- atan2(old_vec.getZ(), old_vec.getX());
// Adjust angle to be between -180 and 180 degrees
if(angle < -M_PI)
angle += M_PI;
else if(angle > M_PI)
angle -= M_PI;
// If the angle is too much, adjust next xyz
if(fabsf(angle)>m_st_target_max_angle*dt)
{
core::vector2df old_2d(old_vec.getX(), old_vec.getZ());
if(old_2d.getLengthSQ()==0.0f)
old_2d.Y = 1.0f;
old_2d.normalize();
old_2d.rotateBy( RAD_TO_DEGREE * dt
* (angle > 0 ? m_st_target_max_angle
: -m_st_target_max_angle));
next_xyz.setX(getXYZ().getX() + old_2d.X*dt*m_speed);
next_xyz.setZ(getXYZ().getZ() + old_2d.Y*dt*m_speed);
Vec3 old_vec = getXYZ()-m_previous_xyz;
Vec3 new_vec = next_xyz - getXYZ();
angle = atan2(new_vec.getZ(), new_vec.getX())
- atan2(old_vec.getZ(), old_vec.getX());
// Adjust angle to be between -180 and 180 degrees
if(angle < -M_PI)
angle += M_PI;
else if(angle > M_PI)
angle -= M_PI;
}
// To see if we have overtaken the target, construct a line through
// the rear axles of the kart, and see if the current and the new
// position of the ball are on different sides of the line.
const btVector3 &w1 = m_target->getVehicle()
->getWheelInfo(2).m_raycastInfo.m_contactPointWS;
const btVector3 &w2 = m_target->getVehicle()
->getWheelInfo(3).m_raycastInfo.m_contactPointWS;
core::line2df axle(w1.getX(), w1.getZ(), w2.getX(), w2.getZ());
// This is basically impossible to fulfill. I've only managed to do
// this by driving in a very tight circle, then in about 1 out of 10
// balls I avoided the ball.
if( axle.getPointOrientation(getXYZ().toIrrVector2d()) *
axle.getPointOrientation(next_xyz.toIrrVector2d())<0)
{
printf("Congrats, rubber ball removed.\n");
return true;
}
}
else else
{
interpolate(&next_xyz, dt); interpolate(&next_xyz, dt);
}
m_timer += dt; m_height_timer += dt;
float height = updateHeight(); float height = updateHeight();
float new_y = getHoT()+height; float new_y = getHoT()+height;
@ -394,8 +349,7 @@ bool RubberBall::updateAndDelete(float dt)
// tweak the look a bit. // tweak the look a bit.
float r = 2.0f; float r = 2.0f;
if(r*height<m_extend.getY()) if(r*height<m_extend.getY())
m_node->setScale(core::vector3df(1.0f, r*height/m_extend.getY(), m_node->setScale(core::vector3df(1.0f, r*height/m_extend.getY(),1.0f));
1.0f));
else else
m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f)); m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
@ -405,6 +359,46 @@ bool RubberBall::updateAndDelete(float dt)
return false; return false;
} // updateAndDelete } // updateAndDelete
// ----------------------------------------------------------------------------
/** Moves the rubber ball in a straight line towards the target. This is used
* once the rubber ball is close to its target. It restricts the angle by
* which the rubber ball can change its direction per frame.
* \param next_xyz The position the ball should move to.
* \param dt Time step size.
*/
void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt)
{
// If the rubber ball is already close to a target, i.e. aiming
// at it directly, stop interpolating, instead fly straight
// towards it.
Vec3 diff = m_target->getXYZ()-getXYZ();
*next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff;
Vec3 old_vec = getXYZ()-m_previous_xyz;
Vec3 new_vec = *next_xyz - getXYZ();
float angle = atan2(new_vec.getZ(), new_vec.getX())
- atan2(old_vec.getZ(), old_vec.getX());
// Adjust angle to be between -180 and 180 degrees
if(angle < -M_PI)
angle += 2*M_PI;
else if(angle > M_PI)
angle -= 2*M_PI;
// If the angle is too large, adjust next xyz
if(fabsf(angle)>m_st_target_max_angle*dt)
{
core::vector2df old_2d(old_vec.getX(), old_vec.getZ());
if(old_2d.getLengthSQ()==0.0f) old_2d.Y = 1.0f;
old_2d.normalize();
old_2d.rotateBy( RAD_TO_DEGREE * dt
* (angle > 0 ? m_st_target_max_angle
: -m_st_target_max_angle));
next_xyz->setX(getXYZ().getX() + old_2d.X*dt*m_speed);
next_xyz->setZ(getXYZ().getZ() + old_2d.Y*dt*m_speed);
} // if fabsf(angle) > m_st_target_angle_max*dt
} // moveTowardsTarget
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Uses Hermite splines (Catmull-Rom) to interpolate the position of the /** Uses Hermite splines (Catmull-Rom) to interpolate the position of the
* ball between the control points. If the next point would be outside of * ball between the control points. If the next point would be outside of
@ -452,9 +446,9 @@ float RubberBall::updateHeight()
// When the ball hits the floor, we adjust maximum height and // When the ball hits the floor, we adjust maximum height and
// interval so that the ball bounces faster when it is getting // interval so that the ball bounces faster when it is getting
// closer to the target. // closer to the target.
if(m_timer>m_interval) if(m_height_timer>m_interval)
{ {
m_timer -= m_interval; m_height_timer -= m_interval;
if(m_ping_sfx->getStatus()!=SFXManager::SFX_PLAYING) if(m_ping_sfx->getStatus()!=SFXManager::SFX_PLAYING)
{ {
m_ping_sfx->position(getXYZ()); m_ping_sfx->position(getXYZ());
@ -486,7 +480,7 @@ float RubberBall::updateHeight()
m_interval = m_st_interval; m_interval = m_st_interval;
m_current_max_height = m_max_height; m_current_max_height = m_max_height;
} }
} // if m_timer > m_interval } // if m_height_timer > m_interval
// Determine the height of the ball // Determine the height of the ball
@ -497,7 +491,7 @@ float RubberBall::updateHeight()
// f(m_interval/2) = s*(-m_interval^2)/4 = max_height // f(m_interval/2) = s*(-m_interval^2)/4 = max_height
// --> s = 4*max_height / -m_interval^2 // --> s = 4*max_height / -m_interval^2
float s = 4.0f * m_current_max_height / (-m_interval*m_interval); float s = 4.0f * m_current_max_height / (-m_interval*m_interval);
return m_timer * (m_timer-m_interval) * s; return m_height_timer * (m_height_timer-m_interval) * s;
} // updateHeight } // updateHeight
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -550,17 +544,26 @@ void RubberBall::checkDistanceToTarget()
// It appears that we have lost the target. It was within // It appears that we have lost the target. It was within
// the target distance, and now it isn't. That means either // the target distance, and now it isn't. That means either
// the original target escaped, or perhaps that there is a // the original target escaped, or perhaps that there is a
// new target. In this case we have to reset the control // new target. If the new distance is nearly the full track
// points, since it's likely that the ball is (after some time // length, assume that the rubber ball has overtaken the
// going directly towards the target) far outside of the // original target, and start deleting it.
// old control points. if(diff > 0.9f * world->getTrack()->getTrackLength())
{
m_delete_timer = m_st_delete_time;
}
// Otherwise (target disappeared, e.g. has finished the race or
// was eliminated) 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.
// We use the previous XYZ point to define the first control // We use the previous XYZ point to define the first control
// point, which results in a smooth transition from aiming // point, which results in a smooth transition from aiming
// directly at the target back to interpolating again. // directly at the target back to interpolating again.
initializeControlPoints(m_previous_xyz); initializeControlPoints(m_previous_xyz);
m_aiming_at_target = false; m_aiming_at_target = false;
printf("Target lost\n");
printf("Target lost, diff %f time %f\n", diff, m_delete_timer);
} }
return; return;

View File

@ -65,6 +65,11 @@ private:
* even if the quads should be close to each other. */ * even if the quads should be close to each other. */
static float m_st_min_interpolation_distance; static float m_st_min_interpolation_distance;
/** If the ball overtakes its target or starts to aim at the kart which
* originally shot the rubber ball, after this amount of time the
* ball will be deleted. */
static float m_st_delete_time;
/** A pointer to the target kart. */ /** A pointer to the target kart. */
const Kart *m_target; const Kart *m_target;
@ -109,7 +114,13 @@ private:
/** This timer is used to determine the height depending on the time. /** This timer is used to determine the height depending on the time.
* It is always between 0 and m_interval. */ * It is always between 0 and m_interval. */
float m_timer; float m_height_timer;
/** If the ball overtakes its target or starts to aim at the kart which
* originally shot the rubber ball, after a certain amount of time the
* ball will be deleted. This timer tracks this time. If it is < 0
* it indicates that the ball is targeting another kart atm. */
float m_delete_timer;
/** The current maximum height of the ball. This value will be /** The current maximum height of the ball. This value will be
* reduced if the ball gets closer to the target. */ * reduced if the ball gets closer to the target. */
@ -136,6 +147,7 @@ private:
void getNextControlPoint(); void getNextControlPoint();
float updateHeight(); float updateHeight();
void interpolate(Vec3 *next_xyz, float dt); void interpolate(Vec3 *next_xyz, float dt);
void moveTowardsTarget(Vec3 *next_xyz, float dt);
void initializeControlPoints(const Vec3 &xyz); void initializeControlPoints(const Vec3 &xyz);
float getMaxTerrainHeight() const; float getMaxTerrainHeight() const;
public: public: