Treads of Glory
|
Treads of Glory is a shared-screen multiplayer tank game built using C++ and OpenGL. Players compete in arena-style combat using bouncing bullets and mines. I worked on this project as a programmer. My responsibilities included the game physics and collision. The game was developed by a team of three programmers over three weeks. Artwork courtesy of Alfonso Callejas |
View Code
#include "EVector2.h"
#include "EUtil.h"
#define NUM_COLLISION_SHAPES 2
namespace Equinox
{
enum CollisionShape
{
SQUARE, CIRCLE
};
class CollisionObject
{
public:
CollisionObject( const EVector2& position) : position( position ) { }
virtual CollisionShape getShape() = 0;
void setPosition( const EVector2& position ) { this->position = position; }
const EVector2& getPosition() { return position; }
virtual const EVector2 getMaxBound() = 0;
protected:
EVector2 position;
};
class CollisionCircle : public CollisionObject
{
public:
CollisionCircle( const EVector2& position, float radius ) :
CollisionObject( position ),
radius( radius ) { }
float getRadius() const { return radius; };
void setRadius( float radius ) { this->radius = radius; }
CollisionShape getShape() { return CIRCLE; }
const EVector2 getMaxBound() { return EVector2( radius, radius); }
private:
float radius;
};
class CollisionSquare : public CollisionObject
{
public:
CollisionSquare( const EVector2& position, float halfWidth) :
CollisionObject( position ),
halfWidth( halfWidth ) { }
float getHalfWidth() const { return halfWidth; }
CollisionShape getShape() { return SQUARE; }
const EVector2 getMaxBound() { return EVector2( halfWidth, halfWidth); }
private:
float halfWidth;
};
struct CollisionInfoBundle
{
float depth;
EVector2 normal;
};
class CollisionManager
{
public:
CollisionManager();
// Tests whether (first) and (second) collide. If they do, depth and normal
// will be saved in (collisionInfo) and returns true. Otherwise returns false.
bool DoesCollide( CollisionObject* first, CollisionObject* second, CollisionInfoBundle& collisionInfo );
private:
typedef bool (*CollisionFunc)( CollisionObject*, CollisionObject*, CollisionInfoBundle& );
CollisionFunc m_TestFunc[ NUM_COLLISION_SHAPES ][ NUM_COLLISION_SHAPES ];
static bool UndefinedCollision( CollisionObject*, CollisionObject *, CollisionInfoBundle& collisionInfo );
static bool DoesCollideSquareSquare( CollisionObject* first, CollisionObject* second, CollisionInfoBundle& collisionInfo );
static bool DoesCollideSquareCircle( CollisionObject* first, CollisionObject* second, CollisionInfoBundle& collisionInfo );
static bool DoesCollideCircleCircle( CollisionObject* first, CollisionObject* second, CollisionInfoBundle& collisionInfo );
};
}
[/code]
#include <cassert>
#include <cmath>
namespace Equinox
{
//================================================================================
//================================================================================
CollisionManager::CollisionManager()
{
for ( unsigned int i = 0; i < NUM_COLLISION_SHAPES; ++i )
for ( unsigned int j = 0; j < NUM_COLLISION_SHAPES; ++j )
m_TestFunc[i][j] = UndefinedCollision;
m_TestFunc[0][0] = DoesCollideSquareSquare;
m_TestFunc[0][1] = DoesCollideSquareCircle;
m_TestFunc[1][1] = DoesCollideCircleCircle;
}
//================================================================================
//================================================================================
bool CollisionManager::DoesCollide( CollisionObject* first, CollisionObject* second, CollisionInfoBundle& collisionInfo )
{
if ( first == nullptr || second == nullptr ) return false;
float reverser = 1.0f;
if ( second->getShape() < first->getShape() )
{
swap( first, second );
reverser = -1.0f;
}
bool result = m_TestFunc[first->getShape()][second->getShape()]( first, second, collisionInfo );
collisionInfo.normal *= reverser;
return result;
}
//================================================================================
//================================================================================
bool CollisionManager::UndefinedCollision( CollisionObject*, CollisionObject *, CollisionInfoBundle& collisionInfo )
{
assert( false );
return false;
}
//================================================================================
//================================================================================
bool CollisionManager::DoesCollideSquareSquare( CollisionObject* first, CollisionObject* second, CollisionInfoBundle& collisionInfo )
{
CollisionSquare* squareA = static_cast< CollisionSquare* >( first );
CollisionSquare* squareB = static_cast< CollisionSquare* >( second );
return false;
}
//================================================================================
//================================================================================
bool CollisionManager::DoesCollideSquareCircle( CollisionObject* first, CollisionObject* second, CollisionInfoBundle& collisionInfo )
{
CollisionSquare* square = static_cast< CollisionSquare* >( first );
CollisionCircle* circle = static_cast< CollisionCircle* >( second );
float dist[2];
for ( int i = 0; i < 2; ++i )
dist[i] = abs( square->getPosition().GetAxis( i ) – circle->getPosition().GetAxis( i ) );
unsigned int axis = ( dist[0] > dist[1] ) ? 0 : 1;
unsigned int auxAxis = !axis;
if ( ( dist[axis] < square->getHalfWidth() + circle->getRadius() ) && ( dist[ auxAxis ] < square->getHalfWidth() ) )
{
// Collision occurred with "line" portion of square
collisionInfo.normal.SetAxis( axis, -1.0f );
collisionInfo.normal.SetAxis( auxAxis, 0.0f );
if ( square->getPosition().GetAxis( axis ) < circle->getPosition().GetAxis( axis ) )
collisionInfo.normal *= -1;
collisionInfo.depth = square->getHalfWidth() + circle->getRadius() – dist[ axis ];
return true;
}
float offset = square->getPosition().GetAxis( axis ) > circle->getPosition().GetAxis( axis ) ? -square->getHalfWidth() : square->getHalfWidth();
float auxOffset = square->getPosition().GetAxis( auxAxis ) > circle->getPosition().GetAxis( auxAxis ) ? -square->getHalfWidth() : square->getHalfWidth();
float d1 = ( square->getPosition().GetAxis( axis ) + offset ) – circle->getPosition().GetAxis( axis );
float d2 = ( square->getPosition().GetAxis( auxAxis ) + auxOffset ) – circle->getPosition().GetAxis( auxAxis );
if ( d1 * d1 + d2 * d2 < circle->getRadius() * circle->getRadius() )
{
// Collided with point
collisionInfo.normal.SetAxis( axis, d1 );
collisionInfo.normal.SetAxis( auxAxis, d2 );
collisionInfo.normal.Normalize();
collisionInfo.depth = sqrt( d1 * d1 + d2 * d2) – circle->getRadius();
return true;
}
return false;
}
//================================================================================
//================================================================================
bool CollisionManager::DoesCollideCircleCircle( CollisionObject* first, CollisionObject* second, CollisionInfoBundle& collisionInfo )
{
CollisionCircle* circleA = static_cast< CollisionCircle* >( first );
CollisionCircle* circleB = static_cast< CollisionCircle* >( second );
const EVector2& posA = circleA->getPosition();
const EVector2& posB = circleB->getPosition();
float distX = posB.X – posA.X;
float distY = posB.Y – posA.Y;
float distSq = distX * distX + distY * distY;
float combinedRadius = circleA->getRadius() + circleB->getRadius();
if ( distSq < combinedRadius * combinedRadius )
{
collisionInfo.depth = combinedRadius – sqrt(distSq);
collisionInfo.normal.X = distX;
collisionInfo.normal.Y = distY;
if ( distX == 0 && distY == 0 )
{
collisionInfo.normal.Y = -1;
}
else
{
collisionInfo.normal.Normalize();
}
return true;
}
else
{
return false;
}
}
}
[/code]