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
squashed are slowed down.
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"
model="rubber_ball.b3d" speed="35.0" scale="1 1 1"
interval="1" max-height="4.0" min-height="0"
model="rubber_ball.b3d" speed="35.0"
scale="1 1 1" interval="1"
max-height="4.0" min-height="0"
target-distance="50" target-max-angle = "90"
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"
model="parachute.b3d" />
<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_target_distance;
float RubberBall::m_st_target_max_angle;
float RubberBall::m_st_delete_time;
int RubberBall::m_next_id = 0;
RubberBall::RubberBall(Kart *kart, Track* track)
@ -68,13 +69,10 @@ RubberBall::RubberBall(Kart *kart, Track* track)
m_previous_xyz = m_owner->getXYZ();
computeTarget();
if(!m_target)
{
// This happens if the kart firing the rubber ball is the leader
// of all karts left in the race.
hit(NULL);
return;
}
// If there is no target (i.e. the firing kart is first kart that is
// still racing, start the delete timer.
m_delete_timer = m_target!=m_owner ? -1.0f : 10.0f;
// initialises the current graph node
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
// target:
m_aiming_at_target = false;
m_timer = 0.0f;
m_height_timer = 0.0f;
m_interval = m_st_interval;
m_current_max_height = m_max_height;
} // 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_t = 0;
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
* earlier call, it is tested first, avoiding a loop over all karts.
/** Determines the first kart that is still in the race.
*/
void RubberBall::computeTarget()
{
@ -141,11 +138,19 @@ void RubberBall::computeTarget()
m_target = world->getKartAtPosition(p);
if(!m_target->isEliminated() && !m_target->hasFinishedRace())
{
// Don't aim at yourself
if(m_target == m_owner) m_target = NULL;
// If the firing kart itself is the first kart (that is
// still driving), prepare to remove the rubber ball
if(m_target==m_owner)
m_delete_timer = m_st_delete_time;
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
// ----------------------------------------------------------------------------
@ -221,6 +226,8 @@ void RubberBall::init(const XMLNode &node, scene::IMesh *bowling)
m_st_min_interpolation_distance = 30.0f;
m_st_target_distance = 50.0f;
m_st_target_max_angle = 25.0f;
m_st_delete_time = 10.0f;
if(!node.get("interval", &m_st_interval))
printf("[powerup] Warning: no interval specified for rubber ball.\n");
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))
printf(
"[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))
printf(
"[powerup] Warning: no target-max-angle specified for rubber ball.\n");
m_st_target_max_angle *= DEGREE_TO_RAD;
Flyable::init(node, bowling, PowerupManager::POWERUP_RUBBERBALL);
} // init
// ----------------------------------------------------------------------------
/** Picks a random message to be displayed when a kart is hit by the
* rubber ball.
@ -289,80 +300,24 @@ bool RubberBall::updateAndDelete(float dt)
// finished the race).
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);
return true;
}
}
checkDistanceToTarget();
Vec3 next_xyz;
if(m_aiming_at_target)
{
// 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;
}
}
moveTowardsTarget(&next_xyz, dt);
else
{
interpolate(&next_xyz, dt);
}
m_timer += dt;
m_height_timer += dt;
float height = updateHeight();
float new_y = getHoT()+height;
@ -394,8 +349,7 @@ bool RubberBall::updateAndDelete(float dt)
// tweak the look a bit.
float r = 2.0f;
if(r*height<m_extend.getY())
m_node->setScale(core::vector3df(1.0f, r*height/m_extend.getY(),
1.0f));
m_node->setScale(core::vector3df(1.0f, r*height/m_extend.getY(),1.0f));
else
m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
@ -405,6 +359,46 @@ bool RubberBall::updateAndDelete(float dt)
return false;
} // 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
* 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
// interval so that the ball bounces faster when it is getting
// 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)
{
m_ping_sfx->position(getXYZ());
@ -486,7 +480,7 @@ float RubberBall::updateHeight()
m_interval = m_st_interval;
m_current_max_height = m_max_height;
}
} // if m_timer > m_interval
} // if m_height_timer > m_interval
// Determine the height of the ball
@ -497,7 +491,7 @@ float RubberBall::updateHeight()
// f(m_interval/2) = s*(-m_interval^2)/4 = max_height
// --> s = 4*max_height / -m_interval^2
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
// ----------------------------------------------------------------------------
@ -550,17 +544,26 @@ void RubberBall::checkDistanceToTarget()
// 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.
// new target. If the new distance is nearly the full track
// length, assume that the rubber ball has overtaken the
// original target, and start deleting it.
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
// 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;
printf("Target lost\n");
printf("Target lost, diff %f time %f\n", diff, m_delete_timer);
}
return;

View File

@ -65,6 +65,11 @@ private:
* even if the quads should be close to each other. */
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. */
const Kart *m_target;
@ -109,7 +114,13 @@ private:
/** This timer is used to determine the height depending on the time.
* 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
* reduced if the ball gets closer to the target. */
@ -136,6 +147,7 @@ private:
void getNextControlPoint();
float updateHeight();
void interpolate(Vec3 *next_xyz, float dt);
void moveTowardsTarget(Vec3 *next_xyz, float dt);
void initializeControlPoints(const Vec3 &xyz);
float getMaxTerrainHeight() const;
public: