--Collision Detection Rewrite (all [H])--
* make blocks bounce again - ok
* bonusblocks don't always bounce back to their original position (but stay a
- few pixels higher)
+ few pixels higher) - hopefully ok
* it's impossible to go into passages that have exactly the size of tux,
either reduce collision rectangle by DELTA or round collision coordinates to
integers...
* rework collision detection to take movement into account - this should fix
the egg suddenly turning directions and the somtimes strange behaviour
when hitting a block from the side when falling.
+ - done for rectangles, fixes the issues with blocks and enemies hitting
+ when they should get squished, still it's not optimal as when hitting 2
+ blocks now only 1 gets cleared...
* rethink slopes collision feedback... tux becomes too slow when walking up
and starts jumping when walking down
* think about an attachement mechanism for moving platforms
Menu::clear()
{
item.clear();
+ active_item = 0;
}
/* Process actions done on the menu */
namespace SuperTux
{
+static const float DELTA = .0001;
+
bool
Collision::rectangle_rectangle(CollisionHit& hit, const Rectangle& r1,
- const Rectangle& r2)
+ const Vector& movement, const Rectangle& r2)
{
if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
return false;
if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
return false;
-
- hit.depth = r1.p2.x - r2.p1.x;
- hit.normal.x = -1;
- hit.normal.y = 0;
- float px2 = r2.p2.x - r1.p1.x;
- if(px2 < hit.depth) {
- hit.depth = px2;
+ float xr;
+ if(movement.x > DELTA) {
+ hit.depth = r1.p2.x - r2.p1.x;
+ hit.normal.x = -1;
+ hit.normal.y = 0;
+ xr = hit.depth / movement.x;
+ } else if(movement.x < -DELTA) {
+ hit.depth = r2.p2.x - r1.p1.x;
hit.normal.x = 1;
hit.normal.y = 0;
+ xr = hit.depth / -movement.x;
+ } else {
+ xr = FLT_MAX;
+ if(movement.y > -DELTA && movement.y < DELTA) {
+ return false;
+ }
}
- float py1 = r1.p2.y - r2.p1.y;
- if(py1 < hit.depth) {
- hit.depth = py1;
- hit.normal.x = 0;
- hit.normal.y = -1;
- }
-
- float py2 = r2.p2.y - r1.p1.y;
- if(py2 < hit.depth) {
- hit.depth = py2;
- hit.normal.x = 0;
- hit.normal.y = 1;
+ if(movement.y > DELTA) {
+ float ydepth = r1.p2.y - r2.p1.y;
+ float yr = ydepth / movement.y;
+ if(yr < xr) {
+ hit.depth = ydepth;
+ hit.normal.x = 0;
+ hit.normal.y = -1;
+ }
+ } else if(movement.y < -DELTA) {
+ float ydepth = r2.p2.y - r1.p1.y;
+ float yr = ydepth / -movement.y;
+ if(yr < xr) {
+ hit.depth = ydepth;
+ hit.normal.x = 0;
+ hit.normal.y = 1;
+ }
}
return true;
bool
Collision::rectangle_aatriangle(CollisionHit& hit, const Rectangle& rect,
- const AATriangle& triangle)
+ const Vector& movement, const AATriangle& triangle)
{
- if(!rectangle_rectangle(hit, rect, (const Rectangle&) triangle))
+ if(!rectangle_rectangle(hit, rect, movement, (const Rectangle&) triangle))
return false;
Vector normal;
namespace SuperTux
{
+class Vector;
class Rectangle;
class CollisionHit;
class AATriangle;
* collision and fills in the hit structure then.
*/
static bool rectangle_rectangle(CollisionHit& hit, const Rectangle& r1,
- const Rectangle& r2);
+ const Vector& movement, const Rectangle& r2);
/** does collision detection between a rectangle and an axis aligned triangle
* Returns true in case of a collision and fills in the hit structure then.
*/
static bool rectangle_aatriangle(CollisionHit& hit, const Rectangle& rect,
- const AATriangle& triangle);
+ const Vector& movement, const AATriangle& triangle);
};
}
global_time += elapsed_time;
lastticks = ticks;
// 40fps is minimum
- if(elapsed_time > .05)
- elapsed_time = .05;
+ if(elapsed_time > .025)
+ elapsed_time = .025;
/* Handle events: */
currentsector->player->input.old_fire = currentsector->player->input.fire;
HitResponse
Block::collision(GameObject& other, const CollisionHit& hitdata)
{
- if(bouncing)
- return FORCE_MOVE;
-
// TODO kill badguys when bumping them...
Player* player = dynamic_cast<Player*> (&other);
- if(!player)
- return ABORT_MOVE;
-
- // collided from below?
- if(hitdata.normal.x == 0 && hitdata.normal.y < 0
- && player->get_movement().y < 0) {
- hit(*player);
+ if(player) {
+ // collided from below?
+ if(hitdata.normal.x == 0 && hitdata.normal.y < 0
+ && player->get_movement().y < 0) {
+ hit(*player);
+ }
}
- return ABORT_MOVE;
+ return FORCE_MOVE;
}
void
float offset = original_y - get_pos().y;
if(offset > BOUNCY_BRICK_MAX_OFFSET) {
- bounce_dir *= -1;
+ bounce_dir = BOUNCY_BRICK_SPEED;
movement = Vector(0, bounce_dir * elapsed_time);
- } else if(offset < -EPSILON) {
+ } else if(offset < BOUNCY_BRICK_SPEED * elapsed_time && bounce_dir > 0) {
movement = Vector(0, offset);
bounce_dir = 0;
bouncing = false;
else
tux_body = big_tux;
- int layer = LAYER_OBJECTS - 1;
+ int layer = LAYER_OBJECTS + 10;
/* Set Tux sprite action */
if (duck && size == BIG)
&& !dying)
{
if (size == SMALL || duck)
- smalltux_star->draw(context, get_pos(), LAYER_OBJECTS + 2);
+ smalltux_star->draw(context, get_pos(), layer + 5);
else
- bigtux_star->draw(context, get_pos(), LAYER_OBJECTS + 2);
+ bigtux_star->draw(context, get_pos(), layer + 5);
}
if (debug_mode)
context.draw_filled_rect(get_pos(),
Vector(bbox.get_width(), bbox.get_height()),
- Color(75,75,75, 150), LAYER_OBJECTS+1);
+ Color(75,75,75, 150), LAYER_OBJECTS+20);
}
HitResponse
physic.set_velocity_y(0);
on_ground_flag = true;
} else if(hit.normal.y > 0) { // bumped against the roof
- physic.set_velocity_y(0);
+ physic.set_velocity_y(.1);
}
if(hit.normal.x != 0) { // hit on the side?
break;
}
- if(Collision::rectangle_aatriangle(temphit, dest, triangle)) {
+ if(Collision::rectangle_aatriangle(temphit, dest, object->movement,
+ triangle)) {
if(temphit.depth > hit.depth)
hit = temphit;
}
} else { // normal rectangular tile
Rectangle rect(x*32, y*32, (x+1)*32, (y+1)*32);
- if(Collision::rectangle_rectangle(temphit, dest, rect)) {
+ if(Collision::rectangle_rectangle(temphit, dest,
+ object->movement, rect)) {
if(temphit.depth > hit.depth)
hit = temphit;
}
}
// did we collide at all?
- if(hit.depth == -1)
+ if(hit.depth < 0)
return;
// call collision function
dest1.move(object1->get_movement());
Rectangle dest2 = object2->get_bbox();
dest2.move(object2->get_movement());
- if(Collision::rectangle_rectangle(hit, dest1, dest2)) {
- HitResponse response = object1->collision(*object2, hit);
- if(response == ABORT_MOVE) {
- object1->movement = Vector(0, 0);
- } else if(response == CONTINUE) {
- object1->movement += hit.normal * (hit.depth/2 + .001);
- }
+
+ Vector movement = object1->get_movement() - object2->get_movement();
+ if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) {
+ HitResponse response1 = object1->collision(*object2, hit);
+ Vector hitnormal1 = hit.normal;
hit.normal *= -1;
- response = object2->collision(*object1, hit);
- if(response == ABORT_MOVE) {
- object2->movement = Vector(0, 0);
- } else if(response == CONTINUE) {
- object2->movement += hit.normal * (hit.depth/2 + .001);
+ HitResponse response2 = object2->collision(*object1, hit);
+
+ if(response1 != CONTINUE) {
+ if(response1 == ABORT_MOVE)
+ object1->movement = Vector(0, 0);
+ if(response2 == CONTINUE)
+ object2->movement += hit.normal * (hit.depth + .1);
+ } else if(response2 != CONTINUE) {
+ if(response2 == ABORT_MOVE)
+ object2->movement = Vector(0, 0);
+ if(response1 == CONTINUE)
+ object1->movement += hitnormal1 * (hit.depth + .1);
+ } else {
+ object1->movement += hitnormal1 * (hit.depth/2 + 1);
+ object2->movement += hit.normal * (hit.depth/2 + 1);
}
}
}
MovingObject* movingobject = dynamic_cast<MovingObject*> (gameobject);
if(!movingobject)
continue;
-
+
// collision with tilemap
if(! (movingobject->movement == Vector(0, 0)))
collision_tilemap(movingobject, 0);
collision_object(movingobject, movingobject2);
}
-
+
movingobject->bbox.move(movingobject->get_movement());
movingobject->movement = Vector(0, 0);
}
reader.read_string("message", message);
}
-SecretAreaTrigger::SecretAreaTrigger(const Vector& pos,
- const std::string& secretarea)
+SecretAreaTrigger::SecretAreaTrigger(const Vector& pos)
{
bbox.set_pos(pos);
bbox.set_size(32, 32);
{
public:
SecretAreaTrigger(LispReader& reader);
- SecretAreaTrigger(const Vector& pos, const std::string& sequence);
+ SecretAreaTrigger(const Vector& pos);
~SecretAreaTrigger();
void write(LispWriter& writer);