terom@58: terom@58: #include "Physics.hh" terom@58: #include "Engine.hh" saiam@115: #include "GameState.hh" saiam@150: #include "Terrain.hh" terom@58: saiam@45: #include terom@50: #include nireco@77: #include ekku@104: #include saiam@45: terom@60: PhysicsWorld::PhysicsWorld (Vector gravity, Vector dimensions) saiam@161: : Terrain(1337), tick_timer(PHYSICS_TICK_MS), tick_counter(0), dimensions(dimensions), saiam@150: gravity(gravity) { terom@54: slots.connect(tick_timer.sig_timer(), this, &PhysicsWorld::tick); terom@50: tick_timer.enable(); terom@50: } ekku@44: ekku@44: void PhysicsWorld::addObject (PhysicsObject *object) { saiam@47: objects.push_back(object); ekku@44: } ekku@44: ekku@44: void PhysicsWorld::tick () { saiam@123: // Engine::log(DEBUG, "physics.apply_force") << "*tick*"; terom@58: nireco@76: for (std::vector::iterator i = objects.begin(); i != objects.end(); i++) { nireco@76: (*i)->tick(); nireco@76: } terom@105: terom@105: tick_counter++; terom@105: } terom@105: terom@105: uint32_t PhysicsWorld::getTick (void) { terom@105: return tick_counter; saiam@48: } saiam@48: saiam@128: PhysicsObject::PhysicsObject (PhysicsWorld &world, float mass, saiam@128: Vector position, Vector velocity) saiam@135: : world(world), position(position), velocity(velocity), saiam@135: mass(mass), inAir(true), aim(0), facingRight(true) { saiam@135: // TODO: Is thir the right way to do this? terom@60: world.addObject(this); terom@60: } ekku@44: terom@96: Vector PhysicsObject::walk (bool right) { nireco@168: float deltaX = right ? 1 : -1; nireco@112: Vector reached = this->position; nireco@168: nireco@168: if(!possibleLocation(position+Vector(deltaX, 0))) { nireco@168: for(int i = 1; i < 3; i++) { nireco@168: if(possibleLocation(position+Vector(deltaX, -i))) { nireco@168: reached = position+Vector(deltaX, -i); nireco@168: break; nireco@112: } saiam@115: } saiam@115: } else { ekku@170: for(int i = 0; i < 3; i++) { nireco@168: if(possibleLocation(position+Vector(deltaX, i))) { nireco@168: reached = position+Vector(deltaX, i); nireco@168: } else { nireco@168: break; nireco@112: } saiam@115: } nireco@168: } nireco@112: return reached; nireco@168: ekku@94: } ekku@94: ekku@170: /** ekku@170: * Makes the player jump in the air. ekku@170: * @param direction -1: jump left, 0: jump up, 1: jump right ekku@170: */ ekku@170: void PhysicsObject::jump (int direction) { saiam@114: // Jump only if player is "on the ground" saiam@114: if (!this->inAir) { ekku@159: velocity.y = -100; ekku@170: switch (direction) { ekku@170: case 1: ekku@170: velocity.x += 20; ekku@170: break; ekku@170: case -1: ekku@170: velocity.x -= 20; ekku@170: break; ekku@170: case 0: ekku@170: break; ekku@170: default: ekku@170: throw std::logic_error("Invalid jump direction"); ekku@170: } ekku@159: inAir = true; saiam@114: } ekku@94: } ekku@94: ekku@94: bool PhysicsObject::possibleLocation (Vector loc) { nireco@112: for(unsigned int i = 0; i < this->shape.size(); i++) { saiam@172: if(world.collides(loc+shape[i])) nireco@112: return false; nireco@112: } nireco@112: return true; ekku@94: } ekku@94: nireco@173: void func1() { nireco@173: nireco@173: } nireco@173: ekku@94: /** saiam@83: * Updates object speed and position. This function organises force saiam@83: * integration and collision detection. saiam@83: */ saiam@83: void PhysicsObject::updatePosition () { ekku@94: saiam@83: // Add gravity to the force queue saiam@85: forceq.push(world.gravity); saiam@83: saiam@83: // Go trough every force in the queue saiam@84: Force total; saiam@83: while (!forceq.empty()) { saiam@85: total += forceq.front(); saiam@83: forceq.pop(); saiam@83: } saiam@114: saiam@114: // TODO: This is _ugly_ (but not so ugly as the last one I think) saiam@114: // hack. I think we should handle walking from the collision saiam@114: // detection code. saiam@114: nireco@167: if (this->velocity.sqrLength() < PLAYER_MIN_SPEED * PLAYER_MIN_SPEED) { nireco@167: this->inAir = !world.collides(this->position+shape[2]+Vector(0, 1)); nireco@167: } nireco@167: nireco@171: if(!possibleLocation(position)) { nireco@171: //if we are trapped in ground form dirtball or something nireco@171: //we might want to just return and stuff velocity to some value nireco@171: // return; nireco@171: } nireco@171: nireco@173: if(!possibleLocation(position)) { nireco@173: Engine::log(DEBUG, "great failure") << "great failure"; nireco@173: func1(); nireco@173: } saiam@114: if (!this->inAir) { saiam@114: if (total.x != 0) saiam@114: this->position = walk(total.x > 0); saiam@154: return; saiam@154: //total.x = 0; saiam@114: } saiam@114: nireco@173: if(!possibleLocation(position)) { nireco@173: Engine::log(DEBUG, "great failure") << "great failure"; nireco@173: func1(); nireco@173: } saiam@169: Vector newPosition; saiam@169: Vector velAfterTick; saiam@169: integrate(total, PHYSICS_TICK_MS, newPosition, velAfterTick); saiam@83: this->velocity = velAfterTick; saiam@83: saiam@123: saiam@114: // Collision detection saiam@115: bool collided = false; saiam@115: saiam@115: const Vector diffVec = newPosition-position; saiam@115: const Vector unitVector = diffVec / diffVec.length(); saiam@115: Vector reached = position; saiam@115: nireco@168: while ((position-reached).sqrLength() < diffVec.sqrLength()) { nireco@173: reached += unitVector; saiam@115: // Check if any of the shapes points collide saiam@154: for (uint64_t i = 0; i < shape.size(); i++) { saiam@115: if (world.getType(reached+shape[i]) != EMPTY) { // Collision saiam@154: if (inAir) { saiam@154: // Engine::log(DEBUG, "Here"); saiam@154: this->bounce(world.getNormal(reached+shape[i], saiam@154: reached-unitVector+shape[i])); saiam@154: //this->velocity *= COLLISION_ELASTICITY; saiam@154: } saiam@115: reached = reached - unitVector; // Return to last point saiam@115: collided = true; nireco@167: if (this->velocity.sqrLength() < PLAYER_MIN_SPEED * PLAYER_MIN_SPEED) { saiam@115: this->inAir = false; saiam@115: this->velocity = Vector(0,0); saiam@115: } saiam@115: break; saiam@115: } saiam@115: } saiam@115: if (collided) saiam@115: break; nireco@173: // reached += unitVector; saiam@115: } saiam@123: ekku@95: nireco@173: if(!possibleLocation(reached)) { nireco@173: Engine::log(DEBUG, "PhysicsObject.updatePosition") << "logic error reached should not be possible to be impossible.. diffVec: " << diffVec; nireco@173: func1(); nireco@173: } saiam@123: ekku@92: // In case of some float error check the final coordinate nireco@77: if(!collided) { nireco@171: if(!possibleLocation(newPosition)) { nireco@171: newPosition = reached; nireco@77: } else { nireco@77: // This means everything was ok, so no need to do anything nireco@77: } nireco@77: } else { nireco@77: newPosition = reached; nireco@116: onCollision(); ekku@92: //this->velocity = Vector(0, 0); nireco@77: //TODO: it shouldn't just stop on collision nireco@77: } nireco@173: if(!possibleLocation(newPosition)) { nireco@173: Engine::log(DEBUG, "great failure") << "great failure"; nireco@173: func1(); nireco@173: } nireco@77: this->position = newPosition; nireco@173: if(!possibleLocation(position)) { nireco@173: Engine::log(DEBUG, "great failure") << "great failure"; nireco@173: func1(); nireco@173: } nireco@158: // Engine::log(DEBUG, "PhysicsObject.updatePosition") << "Pos: " << this->position; ekku@44: } ekku@97: ekku@97: /** ekku@97: * Bounces from straight wall in any direction. ekku@97: * Direction given as normal of that wall ekku@97: */ ekku@97: void PhysicsObject::bounce (Vector normal) { saiam@150: if (normal.length() != 0) { saiam@154: // Engine::log(DEBUG, "PhysicsObject.bounce") << "Velocity: " << velocity; saiam@153: Vector nvel = velocity; saiam@153: // Engine::log(DEBUG, "PhysicsObject.bounce") << "New Velocity: " << nvel; nireco@160: // nvel = nvel - ((1+COLLISION_ELASTICITY)*((nvel*normal)/(normal*normal))*normal); nireco@160: nireco@160: nvel = nvel - ((2)*((nvel*normal)/(normal*normal))*normal); nireco@160: // Engine::log(DEBUG, "PhysicsObject.bounce") << "Projection: " << nvel; saiam@153: velocity = nvel; saiam@154: this->velocity *= COLLISION_ELASTICITY; saiam@154: } ekku@97: } ekku@44: saiam@83: /** saiam@83: * Integrates given force over time and stores new position to saiam@83: * posAfterTick and new velocity to velAfterTick. saiam@83: * @param force Force vector. saiam@83: * @param dt The time the force is applied (<=PHYSICS_TICK_MS) saiam@83: */ saiam@169: void PhysicsObject::integrate(Force force, TimeMS dt, Vector &posAfterTick, Vector &velAfterTick) { saiam@169: posAfterTick = position; saiam@169: velAfterTick = velocity; saiam@83: Derivative tmpd; saiam@169: Derivative k1 = evaluate(force, 0, tmpd, posAfterTick, velAfterTick); saiam@169: Derivative k2 = evaluate(force, 0.5f*dt, k1, posAfterTick, velAfterTick); saiam@169: Derivative k3 = evaluate(force, 0.5f*dt, k2, posAfterTick, velAfterTick); saiam@169: Derivative k4 = evaluate(force, dt, k3, posAfterTick, velAfterTick); saiam@83: saiam@83: saiam@83: const Vector dxdt = (k1.dx + (k2.dx + k3.dx) * 2.0f + k4.dx) * 1.0f/6.0f; saiam@83: const Vector dvdt = (k1.dv + (k2.dv + k3.dv) * 2.0f + k4.dv) * 1.0f/6.0f; saiam@83: saiam@83: // Engine::log(DEBUG, "PhysicsObject.integrate") << "Changes: "<< dxdt << " " << dvdt << " Time: " <aim += da; saiam@108: saiam@108: if (this->aim > PLAYER_AIM_MAX) this->aim = PLAYER_AIM_MAX; saiam@108: if (this->aim < PLAYER_AIM_MIN) this->aim = PLAYER_AIM_MIN; ekku@109: //Engine::log(DEBUG, "PhysicsObject.changeAim") << "Player aim: " << this->aim; saiam@108: } saiam@108: saiam@128: void PhysicsObject::setFacing(bool facingRight) { ekku@109: //Engine::log(DEBUG, "PhysicsObject.setFacing") << "Facing: " << right; saiam@128: this->facingRight = facingRight; saiam@108: } saiam@108: terom@107: void PhysicsObject::updatePhysics (Vector position, Vector velocity, bool inAir) { saiam@47: this->position = position; saiam@47: this->velocity = velocity; terom@107: this->inAir = inAir; ekku@44: } ekku@44: ekku@44: Vector PhysicsObject::getPosition () { saiam@47: return this->position; ekku@44: } ekku@44: saiam@108: bool PhysicsObject::getFacing() { saiam@128: return this->facingRight; saiam@108: } saiam@108: saiam@108: float PhysicsObject::getAim() { saiam@108: return this->aim; saiam@108: } saiam@108: ekku@91: std::vector& PhysicsObject::getShape () { ekku@91: return this->shape; ekku@91: } ekku@91: ekku@91: void PhysicsObject::setShape (std::vector shape) { saiam@123: this->shape = shape; ekku@91: } ekku@91: ekku@44: void PhysicsObject::tick () { saiam@47: this->updatePosition(); ekku@44: } ekku@44: saiam@153: void PhysicsObject::draw(CL_GraphicContext *gc) { saiam@153: CL_Quad player( saiam@153: (position+shape[0]).x, (position+shape[0]).y, saiam@153: (position+shape[1]).x, (position+shape[1]).y, saiam@153: (position+shape[2]).x, (position+shape[2]).y, saiam@153: (position+shape[3]).x, (position+shape[3]).y saiam@153: ); saiam@153: saiam@153: gc->fill_quad(player, CL_Color::green); saiam@153: saiam@153: const uint16_t chlen = 10; saiam@153: uint16_t x = player.center().x; saiam@153: uint16_t y = player.center().y; saiam@153: if (facingRight) { saiam@153: gc->draw_line(x, y, saiam@153: x + std::cos(aim)*chlen, saiam@153: y - std::sin(aim)*chlen, saiam@153: CL_Color::black); saiam@153: } else { saiam@153: gc->draw_line(x, y, saiam@153: x - std::cos(aim)*chlen, saiam@153: y - std::sin(aim)*chlen, saiam@153: CL_Color::black); saiam@153: } saiam@153: } saiam@153: