Permalink
Browse files

movement, gravity, jumps, block collision

  • Loading branch information...
1 parent ef928a2 commit 6eeddae521279ba764a860441dd44b6733256c38 Dmytro Lytovchenko committed Sep 30, 2011
Showing with 233 additions and 28 deletions.
  1. +2 −0 src/game_state.cpp
  2. +156 −14 src/player.cpp
  3. +36 −7 src/player.h
  4. +22 −2 src/world.cpp
  5. +17 −5 src/world.h
View
@@ -488,6 +488,8 @@ bool GameState_Play::Think( MyGame * game )
{
// give the World a chance to play its internal logic, like move monsters and animate things
m_world->Think();
+ // give the Player a chance to check keys, test powerup collisions etc
+ if( ! m_world->m_pause_flag ) m_player->Think();
// TODO: Design a way to return events from the World::Think, like inform about player taking damage/dying
View
@@ -3,45 +3,61 @@
#include "player.h"
#include "world.h"
+#undef min
+#include <algorithm>
+
Player::Player()
: m_lives(INITIAL_LIVES_COUNT)
- , m_position(0, 0, World::CELL_BOX_SIZE, World::CELL_BOX_SIZE)
+ , m_position(0, 0, World::CELL_BOX_SIZE-1, World::CELL_BOX_SIZE-1), m_speed(0,0)
+ , m_last_facing(FACING_RIGHT)
{
- m_character_right[0] = m_sprite_manager.GetSprite( "textures/mario_r1.png" );
- m_character_right[1] = m_sprite_manager.GetSprite( "textures/mario_r2.png" );
+ m_character_right[0][0] = m_sprite_manager.GetSprite( "textures/mario_r1.png" );
+ m_character_right[0][1] = m_sprite_manager.GetSprite( "textures/mario_r2.png" );
+ m_character_right[1][0] = m_sprite_manager.GetSprite( "textures/mario_l1.png" );
+ m_character_right[1][1] = m_sprite_manager.GetSprite( "textures/mario_l2.png" );
+
+ // this will not create another HGE, instead we will get the global
+ // unique HGE object which is already started
+ m_hge = hgeCreate( HGE_VERSION );
}
Player::~Player()
{
- delete m_character_right[0];
- delete m_character_right[1];
+ delete m_character_right[0][0];
+ delete m_character_right[0][1];
+ delete m_character_right[1][0];
+ delete m_character_right[1][1];
}
void Player::EnterWorld( World * w )
{
+ m_world = w;
+
// we need a simple way to determine player starting position
// we scan the world for '@' and use that as start
for( uint32_t r = 0; r < w->m_world_height; ++r ) {
for( uint32_t c = 0; c < w->m_world_width; ++c ) {
if( w->At(r,c) == World::WORLD_CELL_PLAYER_START )
{
// found the @ character, set player position
- m_position.Set(
- c * World::CELL_BOX_SIZE, r * World::CELL_BOX_SIZE,
- (c+1) * World::CELL_BOX_SIZE - 1, (r+1) * World::CELL_BOX_SIZE - 1
- );
+ MoveTo( c * World::CELL_BOX_SIZE, r * World::CELL_BOX_SIZE );
}
}
}
}
-hgeVector Player::GetPos()
+hgeRect Player::GetScreenPosition()
{
- return hgeVector(m_position.x1, m_position.y1);
+ hgeRect spos = m_position;
+ spos.x1 -= m_world->m_camera_pos.x;
+ spos.x2 -= m_world->m_camera_pos.x;
+ spos.y1 -= m_world->m_camera_pos.y;
+ spos.y2 -= m_world->m_camera_pos.y;
+ return spos;
}
@@ -61,7 +77,7 @@ hgeSprite * Player::GetSprite()
// total of 2 frames, hence the modulo of 2
uint32_t f = (milliseconds / 333) % 2;
- return m_character_right[f];
+ return m_character_right[m_last_facing][f];
}
@@ -70,7 +86,133 @@ void Player::Render( World * world )
hgeSprite * spr = GetSprite();
if( ! spr ) return;
- hgeVector pos = GetPos();
+ hgeRect pos = GetScreenPosition();
- spr->Render( pos.x - world->m_camera_pos.x, pos.y - world->m_camera_pos.y );
+ spr->Render( pos.x1, pos.y1 );
+
+ m_hge->Gfx_RenderLine( pos.x1, pos.y1, pos.x2, pos.y1 );
+ m_hge->Gfx_RenderLine( pos.x2, pos.y1, pos.x2, pos.y2 );
+ m_hge->Gfx_RenderLine( pos.x2, pos.y2, pos.x1, pos.y2 );
+ m_hge->Gfx_RenderLine( pos.x1, pos.y1, pos.x1, pos.y2 );
}
+
+
+void Player::Think()
+{
+ float delta = m_hge->Timer_GetDelta();
+
+ if( m_hge->Input_GetKeyState( HGEK_RIGHT )
+ || m_hge->Input_GetKeyState( HGEK_D ) )
+ {
+ m_last_facing = FACING_RIGHT;
+ m_speed.x = BASE_MOVING_SPEED;
+ }
+ else if( m_hge->Input_GetKeyState( HGEK_LEFT )
+ || m_hge->Input_GetKeyState( HGEK_A ) )
+ {
+ m_last_facing = FACING_LEFT;
+ m_speed.x = -BASE_MOVING_SPEED;
+ }
+ else {
+ // stop moving if neither right nor left pressed, but preserve facing
+ m_speed.x = 0;
+ }
+
+ float dy = m_speed.y * delta;
+ float dx = m_speed.x * delta;
+
+ bool solid_under_bottom_left = m_world->IsSolidAt( m_position.x1, m_position.y2+dy );
+ bool solid_under_bottom_right = m_world->IsSolidAt( m_position.x2, m_position.y2+dy );
+ bool standing_flag = solid_under_bottom_left || solid_under_bottom_right;
+
+ if( standing_flag )
+ {
+ if (m_hge->Input_GetKeyState( HGEK_W )
+ || m_hge->Input_GetKeyState( HGEK_SPACE )
+ || m_hge->Input_GetKeyState( HGEK_UP ))
+ {
+ m_speed.y = -JumpSpeed();
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // Get speed affected by gravity if our feet are not standing firm
+ //-----------------------------------------------------------------
+
+ // gravity drags us down, man (to the limit of MAX_FALL_SPEED)
+ m_speed.y = std::min( m_speed.y + m_world->GravityAccel(), (float)MAX_FALL_SPEED );
+ dy = m_speed.y * delta;
+
+ // if we are standing firmly on feet
+ if (m_speed.y > 0 )
+ {
+ // stop falling
+ if( standing_flag )
+ {
+ m_speed.y = 0;
+ }
+ else {
+ hgeRect future_pos = m_position;
+ future_pos.y1 += m_speed.y * delta;
+ future_pos.y2 += m_speed.y * delta;
+ if( m_world->TestBlockCollisionAt( future_pos ) ) m_position = future_pos;
+ }
+ }
+ else
+ // jumping/flying up
+ if( m_speed.y < 0) {
+ // 2 is a magic number to allow sliding down/jumping through tight 1-block wide holes
+ bool solid_above_top_left = m_world->IsSolidAt( m_position.x1+2, m_position.y1-dy );
+ bool solid_above_top_right = m_world->IsSolidAt( m_position.x2-2, m_position.y1-dy );
+ bool hitting_the_ceiling = solid_above_top_left || solid_above_top_right;
+ if( hitting_the_ceiling ) {
+ // stop flying, hit the ceiling
+ m_speed.y = 0;
+ } else {
+ hgeRect future_pos = m_position;
+ future_pos.y1 += m_speed.y * delta;
+ future_pos.y2 += m_speed.y * delta;
+ if( m_world->TestBlockCollisionAt( future_pos ) ) m_position = future_pos;
+ }
+ }
+
+ // moving left, test if we hit the wall
+ if( m_speed.x < 0) {
+ bool solid_top_left = m_world->IsSolidAt( m_position.x1-dx, m_position.y1 );
+ bool solid_bottom_left = m_world->IsSolidAt( m_position.x1-dx, m_position.y2 );
+ bool hitting_left_wall = solid_top_left || solid_bottom_left;
+ if( hitting_left_wall ) {
+ // stop moving, hit the wall
+ m_speed.x = 0;
+ } else {
+ hgeRect future_pos = m_position;
+ future_pos.x1 += m_speed.x * delta;
+ future_pos.x2 += m_speed.x * delta;
+ if( m_world->TestBlockCollisionAt( future_pos ) ) m_position = future_pos;
+ }
+ }
+ else
+ // moving right, test if we hit the wall
+ if( m_speed.x > 0) {
+ bool solid_top_right = m_world->IsSolidAt( m_position.x2+dx, m_position.y1 );
+ bool solid_bottom_right = m_world->IsSolidAt( m_position.x2+dx, m_position.y2 );
+ bool hitting_right_wall = solid_top_right || solid_bottom_right;
+ if( hitting_right_wall ) {
+ // stop moving, hit the wall
+ m_speed.x = 0;
+ } else {
+ hgeRect future_pos = m_position;
+ future_pos.x1 += m_speed.x * delta;
+ future_pos.x2 += m_speed.x * delta;
+ if( m_world->TestBlockCollisionAt( future_pos ) ) m_position = future_pos;
+ }
+ }
+}
+
+void Player::MoveTo( float x, float y )
+{
+ m_position.Set(
+ x, y,
+ x + World::CELL_BOX_SIZE - 1.0f,
+ y + World::CELL_BOX_SIZE - 1.0f );
+}
View
@@ -20,14 +20,36 @@ class Player
// You can use acceleration to affect this value when player presses buttons
// to simulate inertia. Or you can write exact speeds when player presses buttons
// to do instant turns and direction changes.
- hgeVertex m_speed;
+ hgeVector m_speed;
+
+ // pixels per second when player is moving (remember 64 is cell size)
+ static const int BASE_MOVING_SPEED = 120;
// Hack this value for infinite lives
- int m_lives;
+ int m_lives;
+
+ // world we have entered, must not be NULL when game is active
+ World * m_world;
+
+ enum {
+ FACING_RIGHT = 0,
+ FACING_LEFT = 1
+ };
+
+ // make box slightly smaller for the player
+ //static const int PADDING_LEFT = 12;
+ //static const int PADDING_RIGHT = 12;
+ static const int MAX_FALL_SPEED = 300;
+
+ // set this to 0 to render sprite facing right, 1 to render sprite facing left
+ // used in GetSprite to return the correct facing
+ int m_last_facing;
SpriteManager m_sprite_manager;
- hgeSprite * m_character_right[2];
+ hgeSprite * m_character_right[2][2];
+
+ HGE * m_hge;
public:
// A bounding box for character sprite used for rendering and collisions
@@ -45,11 +67,13 @@ class Player
// jumping.
// This can be modified to return different values depending if player picks up
// some bonus for higher jumps or eats some weakening mushroom
- float JumpAccel() {
- return 50.0f;
+ float JumpSpeed() {
+ return 420.0f;
}
- hgeVector GetPos();
+ //hgeVector GetPos();
+ // returns position projected from world to screen coordinates
+ hgeRect GetScreenPosition();
// this one should reduce lives count
virtual void Die();
@@ -61,5 +85,10 @@ class Player
virtual void Render( World * world );
// called by gamestate when player enters the world and game begins
- void EnterWorld( World * w );
+ virtual void EnterWorld( World * w );
+
+ // Here check keyboard
+ virtual void Think();
+
+ void MoveTo( float x, float y );
};
View
@@ -107,7 +107,8 @@ void World::Think()
m_camera_pos.x += d * 16;
// test if player was pushed out of screen fully (to be pushed out partially is allowed)
- if (m_player->GetPos().x < CELL_BOX_SIZE) {
+ if ((m_player->m_position.x2 - m_camera_pos.x) < CELL_BOX_SIZE)
+ {
m_player->Die();
m_pause_flag = true;
}
@@ -148,7 +149,7 @@ void World::Render()
CellType cell_contents = this->At( r, c );
switch( cell_contents )
{
- case WORLD_CELL_SOLID:
+ case WORLD_CELL_WALL1:
// find position in world and render it
m_sprite_brick1->Render(
c * CELL_BOX_SIZE - m_camera_pos.x,
@@ -166,3 +167,22 @@ void World::Render()
(*i)->Render();
}
}
+
+
+bool World::TestBlockCollisionAt( const hgeRect & rc )
+{
+ // we simplify calculation by only testing 4 corners
+ // using as advantage the fact, that player has same size as world blocks
+ if( IsSolidAt(rc.x1, rc.y1) ) return false;
+ if( IsSolidAt(rc.x1, rc.y2) ) return false;
+ if( IsSolidAt(rc.x2, rc.y1) ) return false;
+ if( IsSolidAt(rc.x2, rc.y2) ) return false;
+
+ return true;
+}
+
+
+bool World::IsSolid( CellType contents )
+{
+ return contents == WORLD_CELL_WALL1;
+}
View
@@ -6,6 +6,7 @@
#include <list>
#include <hge.h>
+#include <hgerect.h>
#include <hgevector.h>
// this is for standard integer types like uint32_t (very useful)
@@ -51,9 +52,6 @@ class World
HGE * m_hge;
- // is the game running or paused (pause to animate player death for example)
- bool m_pause_flag;
-
// this also delimits max world width in cells, increase if your world grows wider
// actual world will be as wide as the widest line in your level file
// -- this const contains no secret, moving it to public section
@@ -64,6 +62,9 @@ class World
object_list_t m_objects;
public:
+ // is the game running or paused (pause to animate player death for example)
+ bool m_pause_flag;
+
// This stores all loaded textures for the world, including blocks and creatures, but not player
SpriteManager m_sprite_manager;
@@ -92,7 +93,7 @@ class World
enum {
WORLD_CELL_EMPTY = ' ',
WORLD_CELL_PLAYER_START = '@',
- WORLD_CELL_SOLID = '#',
+ WORLD_CELL_WALL1 = '#',
WORLD_CELL_MONEY = '$'
};
@@ -118,11 +119,22 @@ class World
// fall harder or slower, make it negative to change gravity vector up
// This can be modified to return different values depending if player picks up something
// or flips a gravity switch (if you wish)
- virtual float GravityAccel() { return 20.0f; }
+ virtual float GravityAccel() { return 10.0f; }
// Draws the world in its current state. This function must not call or perform
// any other game logic, only drawing
virtual void Render();
+
+ // tests if rect rc is allowed to be in the world and does not collide a solid block
+ virtual bool TestBlockCollisionAt( const hgeRect & rc );
+
+ // tests if cell type is solid or pass-through
+ inline bool IsSolidAt( float x, float y ) {
+ return IsSolid(
+ At( (uint32_t)(y / CELL_BOX_SIZE), (uint32_t)(x / CELL_BOX_SIZE) )
+ );
+ }
+ virtual bool IsSolid( CellType contents );
};

0 comments on commit 6eeddae

Please sign in to comment.