# HG changeset patch # User terom # Date 1227224733 0 # Node ID ca80cd67785d9c1cad7f6caf8dac9d44f5cf9b8a # Parent 4b2867fb5c122083700a24d3a9c70db3938c8a94 merge r64 through r88 from trunk diff -r 4b2867fb5c12 -r ca80cd67785d src/proto2/GameState.hh --- a/src/proto2/GameState.hh Thu Nov 20 23:20:00 2008 +0000 +++ b/src/proto2/GameState.hh Thu Nov 20 23:45:33 2008 +0000 @@ -7,14 +7,14 @@ #include #include -// in meters/kg -const float MAP_WIDTH = 100.0; -const float MAP_HEIGHT = 100.0; -const float MAP_GRAVITY = 9.81; +// in cells/kg +const uint16_t MAP_WIDTH = 800; +const uint16_t MAP_HEIGHT = 600; +const float MAP_GRAVITY = 1200.0; const float PLAYER_MASS = 10.0; -const float PLAYER_MOVE_FORCE = 500.0; -const float PLAYER_INITIAL_X = 50.0; -const float PLAYER_INITIAL_Y = 40.0; +const float PLAYER_MOVE_FORCE = 5000.0; +const float PLAYER_INITIAL_X = 400.0; +const float PLAYER_INITIAL_Y = 300.0; // forward-declare GameState class GameState; diff -r 4b2867fb5c12 -r ca80cd67785d src/proto2/Graphics.cc --- a/src/proto2/Graphics.cc Thu Nov 20 23:20:00 2008 +0000 +++ b/src/proto2/Graphics.cc Thu Nov 20 23:45:33 2008 +0000 @@ -1,5 +1,7 @@ #include "Graphics.hh" +#include "Physics.hh" +#include "GameState.hh" Graphics::Graphics (Engine &engine, GameState &state) : engine(engine), @@ -7,7 +9,30 @@ update_timer(GRAPHICS_UPDATE_INTERVAL_MS), win(GRAPHICS_WINDOW_TITLE, GRAPHICS_RESOLUTION_WIDTH, GRAPHICS_RESOLUTION_HEIGHT), keyboard(win.get_ic()->get_keyboard()) { + + Vector tmp; + CL_Color color; + CL_PixelBuffer terr(MAP_WIDTH, MAP_HEIGHT, 3*MAP_WIDTH, CL_PixelFormat::rgb888); + + for (tmp.x = 0; tmp.x < MAP_WIDTH; tmp.x++) { + for (tmp.y = 0; tmp.y < MAP_HEIGHT; tmp.y++) { + if (state.getType(tmp) == EMPTY) { + color = CL_Color(80, 35, 0); + } else if (state.getType(tmp) == DIRT) { + color = CL_Color(144, 82, 23); + } else if (state.getType(tmp) == ROCK) { + color = CL_Color(132, 136, 135); + } else { + // Fale + } + terr.draw_pixel(tmp.x, tmp.y, color); + } + } + terrain = CL_Surface(terr); + + Engine::log(DEBUG, "Graphics") << "Taalla ollaan."; + // connect timer signal slots.connect(update_timer.sig_timer(), this, &Graphics::on_update); @@ -57,6 +82,9 @@ const float factorX = GRAPHICS_RESOLUTION_WIDTH / MAP_WIDTH; const float factorY = GRAPHICS_RESOLUTION_HEIGHT / MAP_HEIGHT; + // draw terrain + terrain.draw(0,0, gc); + // draw players for (std::list::iterator it = state.player_list.begin(); it != state.player_list.end(); it++) { Player *p = *it; diff -r 4b2867fb5c12 -r ca80cd67785d src/proto2/Graphics.hh --- a/src/proto2/Graphics.hh Thu Nov 20 23:20:00 2008 +0000 +++ b/src/proto2/Graphics.hh Thu Nov 20 23:45:33 2008 +0000 @@ -17,26 +17,28 @@ const uint16_t GRAPHICS_UPDATE_INTERVAL_MS = 20; class Graphics { - private: - Engine &engine; - GameState &state; +private: + Engine &engine; + GameState &state; - CL_SlotContainer slots; - - CL_Timer update_timer; - - CL_DisplayWindow win; - CL_InputDevice &keyboard; + CL_SlotContainer slots; - public: - Graphics (Engine &engine, GameState &state); - - private: - void check_input (void); - void do_redraw (void); - - void on_update (void); - + CL_Timer update_timer; + + CL_DisplayWindow win; + CL_InputDevice &keyboard; + + CL_Surface terrain; + +public: + Graphics (Engine &engine, GameState &state); + +private: + void check_input (void); + void do_redraw (void); + + void on_update (void); + }; #endif /* GRAPHICS_HH */ diff -r 4b2867fb5c12 -r ca80cd67785d src/proto2/Physics.cc --- a/src/proto2/Physics.cc Thu Nov 20 23:20:00 2008 +0000 +++ b/src/proto2/Physics.cc Thu Nov 20 23:45:33 2008 +0000 @@ -4,9 +4,12 @@ #include #include +#include PhysicsWorld::PhysicsWorld (Vector gravity, Vector dimensions) - : tick_timer(PHYSICS_TICK_MS), gravity(gravity), dimensions(dimensions) { + : tick_timer(PHYSICS_TICK_MS), gravity(gravity), dimensions(dimensions), terrain(dimensions.x, std::vector(dimensions.y, EMPTY)) { + + generateTerrain(1337); slots.connect(tick_timer.sig_timer(), this, &PhysicsWorld::tick); tick_timer.enable(); @@ -19,9 +22,9 @@ void PhysicsWorld::tick () { // Engine::log(DEBUG, "physics.apply_force") << "*tick*"; - for (std::vector::iterator i = objects.begin(); i != objects.end(); i++) { - (*i)->tick(); - } + for (std::vector::iterator i = objects.begin(); i != objects.end(); i++) { + (*i)->tick(); + } } PhysicsObject::PhysicsObject (PhysicsWorld &world, float mass, Vector position, Vector velocity) @@ -29,74 +32,142 @@ world.addObject(this); } - -void PhysicsObject::updatePosition () { - // Check if the player is moving on the ground - /*if (this->velocity.y == 0 && (position.y >= world.dimensions.y - 3)) { - position.x += 50 * velocity.x * (PHYSICS_TICK_MS / 1000.0); - velocity.x = 0; - return; - }*/ +/** + * Updates object speed and position. This function organises force + * integration and collision detection. + */ +void PhysicsObject::updatePosition () { + // Add gravity to the force queue + forceq.push(world.gravity); + + // Go trough every force in the queue + // TODO: It might be possible to optimize by adding forces together + Force total; + posAfterTick = position; + velAfterTick = velocity; + while (!forceq.empty()) { + total += forceq.front(); + forceq.pop(); + // Engine::log(DEBUG, "PhysicsObject.updatePosition") << "Current position: " << posAfterTick; + } + integrate(total, PHYSICS_TICK_MS); - // If not moving on the ground, apply normal physics + Vector newPosition = posAfterTick /*+ (velAfterTick * PHYSICS_TICK_MS)/1000*/; + this->velocity = velAfterTick; + //Engine::log(DEBUG, "PhysicsObject.updatePosition") << "Nopeus: "<velocity; + /* + this->velocity += world.gravity * (PHYSICS_TICK_MS / 1000.0); - // Calculate gravity's influence on the velocity vector - this->velocity += world.gravity * (PHYSICS_TICK_MS / 1000.0); - Vector newPosition = position + velocity * (PHYSICS_TICK_MS / 1000.0); + */ //TODO Handle the object as a square or a polygon - -// Engine::log(DEBUG, "physics.update_position") << "position=" << newPosition << ", velocity=" << velocity; bool collided = false; - - if (newPosition.x < 0 || (newPosition.x > world.dimensions.x)) { - // CRASH! - this->velocity.x *= -0.5; - - // If the velocity drops under some fixed constant we decide it is zero. - // This is to prevent the object from bouncing eternally. - if (abs(this->velocity.x) < 0.1) - this->velocity.x = 0; - - collided = true; - } else { - this->position.x = newPosition.x; - } - - if (newPosition.y <= 0 || (newPosition.y >= world.dimensions.y)) { - this->velocity.y *= -0.3; - - - if (abs(this->velocity.y) < 0.1) { - this->velocity.y = 0; - // Friction - this->velocity.x *= 0.95; - } else { - // Bigger friction - this->velocity.x *= 0.75; + //goes 1 unit forward every step and check if has hit anything + Vector unitVector = (newPosition-position) / (newPosition-position).length(); + + Vector tmpVector = position; + Vector reached = position; + int steps = (int) (newPosition-position).length(); + for(int i = 0; i < steps; i++) { + tmpVector += unitVector; + if(world.getType(tmpVector) != EMPTY) { + //Engine::log(DEBUG, "physics.update_position") << "hit something"; + // Then we have hit something + reached = position + unitVector*(i-1); + collided = true; + break; + } else { + //Engine::log(DEBUG, "physics.update_position") << "didnt hit"; } + } - collided = true; - } else { - this->position.y = newPosition.y; - } - + // In case of some float error if(!collided) { - this->position = newPosition; + if(world.getType(newPosition)) { + // There was error, and there is ground + newPosition = tmpVector; + } else { + // This means everything was ok, so no need to do anything + } + } else { + newPosition = reached; + this->velocity = Vector(0, 0); + //TODO: it shouldn't just stop on collision } + this->position = newPosition; + } -void PhysicsObject::applyForce (Vector force, uint16_t dt) { - Vector oldVelocity = velocity; +bool PhysicsWorld::collided (Vector oldPos, Vector newPos) { + int deltaX = oldPos.x - newPos.x; + int deltaY = oldPos.y - newPos.y; + double distance = sqrt(deltaX * deltaX + deltaY * deltaY); + double xInc = deltaX / distance; + double yInc = deltaY / distance; + double currentX = oldPos.x; + double currentY = oldPos.y; - this->velocity += force * dt / 1000 / mass; // The last factor denotes the time. - // It should be scaled somehow. + // This implementation is bit slow since it checks some squares twice. + for(unsigned int i = 1; i < distance; i++) { + currentX += xInc; + currentY += yInc; + if(terrain[(int)currentX][(int)currentY] != EMPTY) + return true; + } + return false; +} + +/** + * Integrates given force over time and stores new position to + * posAfterTick and new velocity to velAfterTick. + * @param force Force vector. + * @param dt The time the force is applied (<=PHYSICS_TICK_MS) + */ +void PhysicsObject::integrate(Force force, TimeMS dt) { + Derivative tmpd; + Derivative k1 = evaluate(force, 0, tmpd); + Derivative k2 = evaluate(force, 0.5f*dt, k1); + Derivative k3 = evaluate(force, 0.5f*dt, k2); + Derivative k4 = evaluate(force, dt, k3); -// Engine::log(DEBUG, "physics.apply_force") << "force=" << force << ", velocity " << oldVelocity << " -> " << velocity; + + const Vector dxdt = (k1.dx + (k2.dx + k3.dx) * 2.0f + k4.dx) * 1.0f/6.0f; + const Vector dvdt = (k1.dv + (k2.dv + k3.dv) * 2.0f + k4.dv) * 1.0f/6.0f; + + // Engine::log(DEBUG, "PhysicsObject.integrate") << "Changes: "<< dxdt << " " << dvdt << " Time: " <updatePosition(); } +/** + * simple random map generation + * first fills whole level with dirt + * then randomizes circles of empty or rock + * @param seed - seed number for random number generator + */ +void PhysicsWorld::generateTerrain(int seed) { + // generating should use own random number generator, but didn't find easily how that is done + srand(seed); + + // some constants to control random generation + const int min_range = 10; + const int max_range = 40; + const int num = 1; + const int rock_rarity = 4; // 1 / rock_rarity will be rock circle + + // loops for amount of circles + for(int i = 0; i < num; i++) { + // information of new circle + int midx = rand()%(int)dimensions.x; + int midy = rand()%(int)dimensions.y; + int range = rand()%(max_range-min_range)+min_range; + + // put first circle in the middle of the cave + // so that we have some area we can certainly spawn into + if(i == 0) { + midx = 60; + midy = 60; + range = 50; + } + + TerrainType type = DIRT; + if(rand()%rock_rarity == 0) { + type = ROCK; + } + // loops for every pixel of circle + for(int x = std::max(0, midx-range); x < std::min((int)dimensions.x, midx+range); x++) { + for(int y = std::max(0, midy-range); y < std::min((int)dimensions.y, midy+range); y++) { + if(x*x+y*y < range*range) { + // and sets it to type + terrain[x][y] = type; + } + } + } + } +} + +/** + * Returns terrainType in given tile. ROCK if tile is out of area + * @param pos - coordinate of tile + */ +TerrainType PhysicsWorld::getType(Vector pos) const { + int x = (int)(pos.x); + int y = (int)(pos.y); + if(x < 0 || y < 0 || x >= dimensions.x || y >= dimensions.y) { + return ROCK; + } + return terrain[x][y]; +} diff -r 4b2867fb5c12 -r ca80cd67785d src/proto2/Physics.hh --- a/src/proto2/Physics.hh Thu Nov 20 23:20:00 2008 +0000 +++ b/src/proto2/Physics.hh Thu Nov 20 23:45:33 2008 +0000 @@ -3,53 +3,97 @@ #include "Vector.hh" +#include +#include #include -const uint16_t PHYSICS_TICK_MS = 10; +typedef uint16_t TimeMS; + +const TimeMS PHYSICS_TICK_MS = 10; + +enum TerrainType {EMPTY, DIRT, ROCK}; // forward-declare class PhysicsObject; +typedef Vector Force; +struct Derivative; class PhysicsWorld { friend class PhysicsObject; - - private: - CL_Timer tick_timer; + +private: + CL_Timer tick_timer; + +protected: + std::vector objects; + Vector gravity; + Vector dimensions; - protected: - std::vector objects; - Vector gravity; - Vector dimensions; - CL_SlotContainer slots; - - PhysicsWorld (Vector gravity, Vector dimensions); + std::vector > terrain; - public: - void addObject (PhysicsObject *object); + CL_SlotContainer slots; + + PhysicsWorld (Vector gravity, Vector dimensions); - void tick (void); + +public: + + void addObject (PhysicsObject *object); + + void tick (void); + void generateTerrain (int seed); + bool collided (Vector oldPos, Vector newPos); + + TerrainType getType(Vector pos) const; }; class PhysicsObject { - protected: - PhysicsWorld &world; - float mass; - Vector position; - Vector velocity; +protected: + PhysicsWorld &world; - PhysicsObject (PhysicsWorld &world, float mass, Vector position, Vector velocity); + float mass; + Vector position; + Vector velocity; + // Whether the object (worms mainly) is in the air + // or firmly on the ground. Affects to physics. + bool inAir; - virtual void applyForce (Vector force, uint16_t dt); - void updatePhysics (Vector position, Vector velocity); + // Shape of the object. + // //TODO + std::vector edges; + + // Force queue that is emptied on every tick + std::queue forceq; + Vector posAfterTick; + Vector velAfterTick; + + PhysicsObject (PhysicsWorld &world, float mass, Vector position, Vector velocity); - private: - void updatePosition (void); + virtual void applyForce (Force force, TimeMS dt); + void updatePhysics (Vector position, Vector velocity); + +private: + void updatePosition (void); - public: - Vector getPosition (void); + /** + * Use RK4 to integrate the effects of force over a time intervall. + */ + void integrate(Force force, TimeMS dt); + Derivative evaluate(Force force, TimeMS dt, Derivative &d); + Vector acceleration(const Force &force); - void tick (void); +public: + Vector getPosition (void); + + void tick (void); }; +struct Derivative { + Vector dx; // Velocity + Vector dv; // Acceleration +}; + + + #endif diff -r 4b2867fb5c12 -r ca80cd67785d src/proto2/Vector.hh --- a/src/proto2/Vector.hh Thu Nov 20 23:20:00 2008 +0000 +++ b/src/proto2/Vector.hh Thu Nov 20 23:45:33 2008 +0000 @@ -2,6 +2,7 @@ #define COOR_H #include +#include /** * 2D Vector class. Implements standard vector operations. @@ -55,6 +56,9 @@ this->x /= d; this->y /= d; } + T length() const { + return sqrt(x*x+y*y); + } }; template