1
0
forked from 0ad/0ad

Early form of collision detection/avoidance added.

This was SVN commit r272.
This commit is contained in:
MarkT 2004-05-26 20:57:25 +00:00
parent e326147f9e
commit 089472eb24
13 changed files with 413 additions and 167 deletions

View File

@ -269,8 +269,16 @@ static void Render()
RenderTerrain();
RenderModels();
g_Renderer.FlushFrame();
/*
glPushAttrib( GL_ENABLE_BIT );
glDisable( GL_LIGHTING );
glDisable( GL_TEXTURE_2D );
glColor3f( 1.0f, 0.0f, 1.0f );
glColor3f(1.0f, 1.0f, 1.0f);
// g_EntityManager.renderAll(); // <-- collision outlines...
glPopAttrib();
*/
// overlay mode
glPushAttrib(GL_ENABLE_BIT);
@ -310,6 +318,8 @@ static void Render()
g_GUI.Draw();
#endif
// restore
glMatrixMode(GL_PROJECTION);
glPopMatrix();
@ -471,7 +481,7 @@ int main(int argc, char* argv[])
#endif
font = font_load("fonts/verdana.fnt");
font = font_load("fonts/verdana18.fnt");
// set renderer options from command line options - NOVBO must be set before opening the renderer
g_Renderer.SetOption(CRenderer::OPT_NOVBO,g_NoGLVBO);

View File

@ -17,8 +17,6 @@ XERCES_CPP_NAMESPACE_USE
bool CBaseEntity::loadXML( CStr filename )
{
// I can only assume Rich knows what this does ;)
bool parseOK = false;
// Initialize XML library
@ -71,12 +69,52 @@ bool CBaseEntity::loadXML( CStr filename )
DOMNode *value_node= child_element->getChildNodes()->item(0);
CStr element_value=value_node ? XMLString::transcode(value_node->getNodeValue()) : "";
if (element_name==CStr("Name")) {
if( element_name == CStr( "Name" ) )
{
m_name = element_value;
} else if (element_name==CStr("Actor")) {
}
else if( element_name == CStr( "Actor" ) )
{
m_actorObject = g_ObjMan.FindObject( element_value );
} else if (element_name==CStr("Speed")) {
}
else if( element_name == CStr( "Speed" ) )
{
m_speed = element_value.ToFloat();
}
else if( element_name == CStr( "Size" ) )
{
if( !m_bound_circle )
m_bound_circle = new CBoundingCircle();
CStr radius = XMLString::transcode( child_element->getAttribute( XMLString::transcode( "Radius" ) ) );
m_bound_circle->setRadius( radius.ToFloat() );
m_bound_type = CBoundingObject::BOUND_CIRCLE;
}
else if( element_name == CStr( "Footprint" ) )
{
if( !m_bound_box )
m_bound_box = new CBoundingBox();
CStr width = XMLString::transcode( child_element->getAttribute( XMLString::transcode( "Width" ) ) );
CStr height = XMLString::transcode( child_element->getAttribute( XMLString::transcode( "Height" ) ) );
m_bound_box->setDimensions( width.ToFloat(), height.ToFloat() );
m_bound_type = CBoundingObject::BOUND_OABB;
}
else if( element_name == CStr( "BoundsOffset" ) )
{
CStr x = XMLString::transcode( child_element->getAttribute( XMLString::transcode( "x" ) ) );
CStr y = XMLString::transcode( child_element->getAttribute( XMLString::transcode( "y" ) ) );
if( !m_bound_circle )
m_bound_circle = new CBoundingCircle();
if( !m_bound_box )
m_bound_box = new CBoundingBox();
m_bound_circle->m_offset.x = x.ToFloat();
m_bound_circle->m_offset.y = y.ToFloat();
m_bound_box->m_offset.x = x.ToFloat();
m_bound_box->m_offset.y = y.ToFloat();
}
}
}

View File

@ -22,10 +22,12 @@
#include "ObjectEntry.h"
#include "EntityProperties.h"
#include "BoundingObjects.h"
class CBaseEntity
{
public:
CBaseEntity() { m_bound_circle = NULL; m_bound_box = NULL; }
// Load from XML
bool loadXML( CStr filename );
@ -34,6 +36,10 @@ public:
CObjectEntry* m_actorObject;
CStr m_name;
CBoundingCircle* m_bound_circle;
CBoundingBox* m_bound_box;
CBoundingObject::EBoundingType m_bound_type;
float m_speed;
// Extended properties table

View File

@ -1,12 +1,17 @@
#include "BoundingObjects.h"
#include "ogl.h"
#include "terrain/MathUtil.h"
#include "stdio.h"
#include "assert.h"
bool CBoundingObject::intersects( CBoundingObject* obj )
{
CVector2D delta = pos - obj->pos;
if( !delta.within( trivialRejectionRadius + obj->trivialRejectionRadius ) )
CVector2D delta = m_pos - obj->m_pos;
if( !delta.within( m_radius + obj->m_radius ) )
return( false );
if( obj->type > type )
if( obj->m_type > m_type ) // More complex types get the burden of processing.
{
return( obj->_intersects( this, &delta ) );
}
@ -14,68 +19,112 @@ bool CBoundingObject::intersects( CBoundingObject* obj )
return( _intersects( obj, &delta ) );
}
void CBoundingCircle::setPosition( float _x, float _y )
CBoundingCircle::CBoundingCircle( float x, float y, float radius )
{
pos.x = _x; pos.y = _y;
m_type = BOUND_CIRCLE;
setPosition( x, y );
setRadius( radius );
}
void CBoundingCircle::setRadius( float _radius )
CBoundingCircle::CBoundingCircle( float x, float y, CBoundingCircle* copy )
{
r = _radius;
trivialRejectionRadius = _radius * _radius;
m_type = BOUND_CIRCLE;
m_offset = copy->m_offset;
setPosition( x, y );
setRadius( copy->m_radius );
}
void CBoundingObject::setPosition( float x, float y )
{
m_pos.x = x; m_pos.y = y;
m_pos += m_offset;
}
void CBoundingCircle::setRadius( float radius )
{
m_radius = radius;
}
bool CBoundingCircle::_intersects( CBoundingObject* obj, CVector2D* delta )
{
// Easy enough. Trivial rejection is sufficient.
assert( obj->m_type == BOUND_CIRCLE );
// Easy enough. The only time this gets called is a circle-circle collision,
// but we know the circles collide (they passed the trivial rejection step)
return( true );
}
CBoundingBox::CBoundingBox( float _x, float _y, float _orientation, float _width, float _height )
void CBoundingCircle::render( float height )
{
setPosition( _x, _y );
setDimensions( _width, _height );
setOrientation( _orientation );
glBegin( GL_LINE_LOOP );
for( int i = 0; i < 10; i++ )
{
float ang = i * 2 * PI / 10.0f;
float x = m_pos.x + m_radius * sin( ang );
float y = m_pos.y + m_radius * cos( ang );
glVertex3f( x, height, y );
}
glEnd();
}
void CBoundingBox::setPosition( float _x, float _y )
CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, float width, float height )
{
pos.x = _x; pos.y = _y;
m_type = BOUND_OABB;
setPosition( x, y );
setDimensions( width, height );
setOrientation( u );
}
void CBoundingBox::setDimensions( float _width, float _height )
CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, CBoundingBox* copy )
{
w = _width / 2.0f;
h = _height / 2.0f;
trivialRejectionRadius = ( w * w ) + ( h * h );
m_type = BOUND_OABB;
m_offset = copy->m_offset;
setPosition( x, y );
setDimensions( copy->getWidth(), copy->getHeight() );
setOrientation( u );
}
void CBoundingBox::setOrientation( float _orientation )
void CBoundingBox::setDimensions( float width, float height )
{
u.x = sin( _orientation );
u.y = cos( _orientation );
v.x = u.y;
v.y = -u.x;
m_w = width / 2.0f;
m_h = height / 2.0f;
m_radius = sqrt( ( m_w * m_w ) + ( m_h * m_h ) );
}
void CBoundingBox::setOrientation( float orientation )
{
m_u.x = sin( orientation );
m_u.y = cos( orientation );
m_v.x = m_u.y;
m_v.y = -m_u.x;
}
void CBoundingBox::setOrientation( const CVector2D& u )
{
m_u = u;
m_v.x = m_u.y;
m_v.y = -m_u.x;
}
bool CBoundingBox::_intersects( CBoundingObject* obj, CVector2D* delta )
{
if( obj->type == BOUND_CIRCLE )
if( obj->m_type == BOUND_CIRCLE )
{
// Imperfect but quick...
CBoundingCircle* c = (CBoundingCircle*)obj;
float deltah = fabs( delta->dot( u ) );
if( deltah > ( h + c->r ) ) return( false );
float deltaw = fabs( delta->dot( v ) );
if( deltaw > ( w + c->r ) ) return( false );
float deltah = fabs( delta->dot( m_u ) );
if( deltah > ( m_h + c->m_radius ) ) return( false );
float deltaw = fabs( delta->dot( m_v ) );
if( deltaw > ( m_w + c->m_radius ) ) return( false );
return( true );
}
else
{
// Another OABB:
// Seperable axis theorem. (Optimizations: Algorithmic done, really low-level stuff not.)
// Seperable axis theorem. (Optimizations: Algorithmic done, low-level stuff not.)
// Debugging this can often be quite entertaining.
CBoundingBox* b = (CBoundingBox*)obj;
@ -95,67 +144,86 @@ bool CBoundingBox::_intersects( CBoundingObject* obj, CVector2D* delta )
// Note that a trivial-rejection test has already taken place by this point.
float uv, vu, uu, vv, prj1, prj2, dm;
uv = u.dot( b->v );
vu = v.dot( b->u );
uu = u.dot( b->u );
vv = v.dot( b->v );
uv = m_u.dot( b->m_v );
vu = m_v.dot( b->m_u );
uu = m_u.dot( b->m_u );
vv = m_v.dot( b->m_v );
// Project box 2 onto v-axis of box 1
prj1 = fabs( vu * b->h + vv * b->w );
prj2 = fabs( vu * b->h - vv * b->h );
dm = delta->dot( v );
prj1 = fabs( vu * b->m_h + vv * b->m_w );
prj2 = fabs( vu * b->m_h - vv * b->m_h );
dm = delta->dot( m_v );
if( prj1 > prj2 )
{
if( ( dm - prj1 ) > w ) return( false );
if( ( dm - prj1 ) > m_w ) return( false );
}
else
if( ( dm - prj2 ) > w ) return( false );
if( ( dm - prj2 ) > m_w ) return( false );
// Project box 2 onto u-axis of box 1
prj1 = fabs( uu * b->h + uv * b->w );
prj2 = fabs( uu * b->h - uv * b->w );
dm = delta->dot( u );
prj1 = fabs( uu * b->m_h + uv * b->m_w );
prj2 = fabs( uu * b->m_h - uv * b->m_w );
dm = delta->dot( m_u );
if( prj1 > prj2 )
{
if( ( dm - prj1 ) > h ) return( false );
if( ( dm - prj1 ) > m_h ) return( false );
}
else
if( ( dm - prj2 ) > h ) return( false );
if( ( dm - prj2 ) > m_h ) return( false );
// Project box 1 onto v-axis of box 2
prj1 = fabs( uv * h + vv * w );
prj2 = fabs( uv * h - vv * w );
prj1 = fabs( uv * m_h + vv * m_w );
prj2 = fabs( uv * m_h - vv * m_w );
if( prj1 > prj2 )
{
if( ( dm - prj1 ) > b->w ) return( false );
if( ( dm - prj1 ) > b->m_w ) return( false );
}
else
if( ( dm - prj2 ) > b->w ) return( false );
if( ( dm - prj2 ) > b->m_w ) return( false );
// Project box 1 onto u-axis of box 2
prj1 = fabs( uu * h + vu * w );
prj2 = fabs( uu * h - vu * w );
prj1 = fabs( uu * m_h + vu * m_w );
prj2 = fabs( uu * m_h - vu * m_w );
if( prj1 > prj2 )
{
if( ( dm - prj1 ) > b->h ) return( false );
if( ( dm - prj1 ) > b->m_h ) return( false );
}
else
if( ( dm - prj2 ) > b->h ) return( false );
if( ( dm - prj2 ) > b->m_h ) return( false );
// And a partridge in a pear tree...
return( true );
}
}
void CBoundingBox::render( float height )
{
glBegin( GL_LINE_LOOP );
CVector2D p;
p = m_pos + m_u * m_h + m_v * m_w;
glVertex3f( p.x, height, p.y );
p = m_pos + m_u * m_h - m_v * m_w;
glVertex3f( p.x, height, p.y );
p = m_pos - m_u * m_h - m_v * m_w;
glVertex3f( p.x, height, p.y );
p = m_pos - m_u * m_h + m_v * m_w;
glVertex3f( p.x, height, p.y );
glEnd();
}

View File

@ -10,35 +10,7 @@
#ifndef BOUNDING_OBJECTS_INCLUDED
#define BOUNDING_OBJECTS_INCLUDED
#include "Prometheus.h"
class CVector2D
{
public:
float x;
float y;
CVector2D() {}
CVector2D( float _x, float _y )
{
x = _x; y = _y;
}
static inline float dot( CVector2D& u, CVector2D& v )
{
return( u.x * v.x + u.y * v.y );
}
inline float dot( CVector2D& u )
{
return( dot( *this, u ) );
}
inline CVector2D operator-( CVector2D& u )
{
return( CVector2D( x - u.x, y - u.y ) );
}
inline bool within( float dist )
{
return( ( x * x + y * y ) <= ( dist * dist ) );
}
};
#include "Vector2D.h"
class CBoundingBox;
class CBoundingCircle;
@ -46,42 +18,58 @@ class CBoundingCircle;
class CBoundingObject
{
public:
CBoundingObject() {}
enum CBoundingType
CBoundingObject() { m_offset.x = 0; m_offset.y = 0; }
enum EBoundingType
{
BOUND_CIRCLE,
BOUND_OABB
};
CBoundingType type;
CVector2D pos;
float trivialRejectionRadius;
EBoundingType m_type;
CVector2D m_pos;
CVector2D m_offset;
float m_radius;
void setPosition( float x, float y );
bool intersects( CBoundingObject* obj );
virtual bool _intersects( CBoundingObject* obj, CVector2D* delta ) = 0;
virtual void render( float height ) = 0; // Temporary
};
class CBoundingCircle : public CBoundingObject
{
public:
float r;
CBoundingCircle( float _x, float _y, float _radius );
void setPosition( float _x, float _y );
void setRadius( float _radius );
CBoundingCircle() { m_type = BOUND_OABB; }
CBoundingCircle( float x, float y, float radius );
CBoundingCircle( float x, float y, CBoundingCircle* copy );
void setRadius( float radius );
bool _intersects( CBoundingObject* obj, CVector2D* delta );
void render( float height ); // Temporary
};
class CBoundingBox : public CBoundingObject
{
public:
CVector2D u; // Unit vector along the direction of this box's height.
CVector2D v; // Unit vector along the direction of this box's width.
float h; // Half this box's height.
float w; // Half this box's width.
CBoundingBox( float _x, float _y, float _orientation, float _width, float _height );
void setPosition( float _x, float _y );
void setDimensions( float _width, float _height );
void setOrientation( float _orientation );
CBoundingBox() { m_type = BOUND_OABB; }
CVector2D m_u; // Unit vector along the direction of this box's height.
CVector2D m_v; // Unit vector along the direction of this box's width.
float m_h; // Half this box's height.
float m_w; // Half this box's width.
CBoundingBox( float x, float y, float orientation, float width, float height )
{
CBoundingBox( x, y, CVector2D( sin( orientation ), cos( orientation ) ), width, height );
}
CBoundingBox( float x, float y, const CVector2D& orientation, float width, float height );
CBoundingBox( float x, float y, float orientation, CBoundingBox* copy )
{
CBoundingBox( x, y, CVector2D( sin( orientation ), cos( orientation ) ), copy );
}
CBoundingBox( float x, float y, const CVector2D& orientation, CBoundingBox* copy );
void setDimensions( float width, float height );
void setOrientation( float orientation );
void setOrientation( const CVector2D& orientation );
float getWidth() const { return( 2.0f * m_w ); };
float getHeight() const { return( 2.0f * m_h ); };
bool _intersects( CBoundingObject* obj, CVector2D* delta );
void render( float height ); // Temporary
};
#endif

View File

@ -1,11 +1,12 @@
// Last modified: May 15 2004, Mark Thompson (mark@wildfiregames.com)
#include "Entity.h"
#include "Model.h"
#include "Terrain.h"
#include "EntityManager.h"
#include "BaseEntityCollection.h"
#include "Renderer.h"
#include "Model.h"
#include "Terrain.h"
CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
{
@ -27,6 +28,18 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
m_position = position;
m_orientation = orientation;
m_ahead.x = sin( orientation );
m_ahead.y = cos( orientation );
if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE )
{
m_bounds = new CBoundingCircle( m_position.X, m_position.Z, m_base->m_bound_circle );
}
else if( m_base->m_bound_type == CBoundingObject::BOUND_OABB )
{
m_bounds = new CBoundingBox( m_position.X, m_position.Z, m_ahead, m_base->m_bound_box );
}
snapToGround();
updateActorTransforms();
@ -44,11 +57,22 @@ bool isWaypoint( CEntity* e )
void CEntity::updateActorTransforms()
{
CMatrix3D m;
m._11 = -m_ahead.y; m._12 = 0.0f; m._13 = -m_ahead.x; m._14 = m_position.X;
m._21 = 0.0f; m._22 = 1.0f; m._23 = 0.0f; m._24 = m_position.Y;
m._31 = m_ahead.x; m._32 = 0.0f; m._33 = -m_ahead.y; m._34 = m_position.Z;
m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f;
/* Equivalent to:
m.SetYRotation( m_orientation );
m.Translate( m_position );
But the matrix multiplication seemed such a waste when we already have a forward vector
*/
m_actor->m_Model->SetTransform( m );
}
@ -108,34 +132,97 @@ void CEntity::update( float timestep )
switch( current->m_type )
{
case CEntityOrder::ORDER_GOTO_NOPATHING:
case CEntityOrder::ORDER_GOTO_COLLISION:
{
float deltax = (float)current->m_data[0].location.x - m_position.X;
float deltay = (float)current->m_data[0].location.y - m_position.Z;
CVector2D delta;
delta.x = (float)current->m_data[0].location.x - m_position.X;
delta.y = (float)current->m_data[0].location.y - m_position.Z;
m_targetorientation = atan2( -deltax, -deltay );
float deltatheta = m_targetorientation - m_orientation;
if( deltatheta > PI )
deltatheta -= 2 * PI;
if( deltatheta < -PI )
deltatheta += 2 * PI;
m_ahead = delta.normalize();
/*
if( deltatheta > 1.0f * timestep )
deltatheta = 1.0f * timestep;
if( deltatheta < -1.0f * timestep )
deltatheta = -1.0f * timestep;
*/
if( m_bounds->m_type == CBoundingObject::BOUND_OABB )
((CBoundingBox*)m_bounds)->setOrientation( m_ahead );
m_orientation += deltatheta;
float len = delta.length();
float len = sqrt( deltax * deltax + deltay * deltay );
float scale = timestep * m_speed;
if( scale > len )
scale = len;
deltax = -sinf( m_orientation ) * scale; deltay = -cosf( m_orientation ) * scale;
m_position.X += deltax;
m_position.Z += deltay;
delta = m_ahead * scale;
m_position.X += delta.x;
m_position.Z += delta.y;
m_bounds->setPosition( m_position.X, m_position.Z );
HEntity collide = getCollisionObject();
if( collide )
{
// Hit something. Take a step back.
m_position.X -= delta.x;
m_position.Z -= delta.y;
m_bounds->setPosition( m_position.X, m_position.Z );
// Are we still hitting it?
if( collide->m_bounds->intersects( m_bounds ) )
{
// Oh dear. Most likely explanation is that this unit was created
// within the bounding area of another entity.
// Try a little boost of speed, to help resolve the situation more quickly.
m_position.X += delta.x * 2.0f;
m_position.Z += delta.y * 2.0f;
m_bounds->setPosition( m_position.X, m_position.Z );
return;
}
if( collide->m_bounds->m_type == CBoundingObject::BOUND_OABB )
{
// And it's square.
// TODO: Implement this case properly.
// HACK: See if this thing we've hit is likely to be our destination. If so, just skip to our next waypoint.
// Otherwise, turn right (as with circle collisions)
if( len < collide->m_bounds->m_radius * 2.0f )
{
m_orderQueue.pop_front();
return;
}
else
{
CEntityOrder avoidance;
avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION;
CVector2D right;
right.x = m_ahead.y; right.y = -m_ahead.x;
CVector2D avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius * 2.5f );
avoidance.m_data[0].location = avoidancePosition;
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
m_orderQueue.pop_front();
m_orderQueue.push_front( avoidance );
return;
}
}
else
{
// A circle.
// TODO: Implement this properly.
// Try turning right.
CEntityOrder avoidance;
avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION;
CVector2D right;
right.x = m_ahead.y; right.y = -m_ahead.x;
CVector2D avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius * 2.5f );
avoidance.m_data[0].location = avoidancePosition;
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
m_orderQueue.pop_front();
m_orderQueue.push_front( avoidance );
return;
}
}
snapToGround();
updateActorTransforms();
@ -199,6 +286,7 @@ void CEntity::dispatch( CMessage* msg )
pushOrder( patrol );
waypoints->erase( it );
}
delete( waypoints );
}
break;
}
@ -209,34 +297,49 @@ void CEntity::pushOrder( CEntityOrder& order )
m_orderQueue.push_back( order );
}
void PASAPScenario()
void CEntity::render()
{
// Entity demo. I don't know where this information is going to come from for pre-PASAP.
// So, it's hardcoded here. *shrug*
// Rich! Help! ;)
// We can loose this later on, I just need a way to see collision boxes temporarily
/*
float waypoints[4][2] = { { 12.0f, 64.0f },
{ 28.0f, 64.0f },
{ 56.0f, 52.0f },
{ 24.0f, 28.0f } };
*/
glColor3f( 1.0f, 1.0f, 1.0f );
if( getCollisionObject() ) glColor3f( 0.5f, 0.5f, 1.0f );
m_bounds->render( m_position.Y + 0.25f );
}
/*
HEntity CEntity::getCollisionObject()
{
if( !m_bounds ) return( HEntity() );
std::vector<HEntity>* entities = g_EntityManager.getActive();
std::vector<HEntity>::iterator it;
for( int i = 0; i < 4; i++ )
for( it = entities->begin(); it != entities->end(); it++ )
{
HEntity bob = g_EntityManager.create( "Prometheus Dude", CVector3D( waypoints[i][0], 0.0f, waypoints[i][1] ), 0.0f );
for( int t = 0; t < 4; t++ )
{
CEntityOrder march_of_bob;
march_of_bob.m_type = CEntityOrder::ORDER_PATROL ;
march_of_bob.m_data[0].location.x = waypoints[(i+t+1)%4][0];
march_of_bob.m_data[0].location.y = waypoints[(i+t+1)%4][1];
bob->pushOrder( march_of_bob );
}
if( *it == me ) continue;
if( (*it)->m_bounds )
if( m_bounds->intersects( (*it)->m_bounds ) )
{
HEntity collisionObject = *it;
delete( entities );
return( collisionObject );
}
}
*/
delete( entities );
return( HEntity() );
}
HEntity CEntity::getCollisionObject( float x, float y )
{
float _x = m_bounds->m_pos.x;
float _y = m_bounds->m_pos.y;
m_bounds->setPosition( x, y );
HEntity _e = getCollisionObject();
m_bounds->setPosition( _x, _y );
return( _e );
}
void PASAPScenario()
{
// Got rid of all the hardcoding that was here.
}

View File

@ -1,6 +1,6 @@
// Entity.h
//
// Last modified: 22 May 04, Mark Thompson mot20@cam.ac.uk / mark@wildfiregames.com
// Last modified: 26 May 04, Mark Thompson mot20@cam.ac.uk / mark@wildfiregames.com
//
// Entity class.
//
@ -15,10 +15,12 @@
//
// snapToGround(): Called every frame, this will ensure the entity never takes flight.
// updateActorTransforms(): Must be called every time the position of this entity changes.
// Also remember to update the collision object if you alter the position directly.
//
// Some notes: update() and dispatch() /can/ be called directly without ill effects,
// but it's preferable to go through the Entity manager and the Scheduler, respectively.
//
// Collision detection/avoidance is now present in some form; this is a work in progress.
#ifndef ENTITY_INCLUDED
#define ENTITY_INCLUDED
@ -27,6 +29,8 @@
#include "EntityProperties.h"
#include "BaseEntity.h"
#include "Vector2D.h"
#include "BoundingObjects.h"
#include "Vector3D.h"
#include "Unit.h"
#include "UnitManager.h"
@ -45,7 +49,9 @@ public:
CStr m_name;
float m_speed;
CVector3D m_position;
CBoundingObject* m_bounds;
float m_targetorientation;
CVector2D m_ahead;
float m_orientation;
CBaseEntity* m_base;
CUnit* m_actor;
@ -65,9 +71,12 @@ public:
void dispatch( CMessage* msg );
void update( float timestep );
void updateActorTransforms();
void render();
float getExactGroundLevel( float x, float y );
void snapToGround();
void pushOrder( CEntityOrder& order );
HEntity getCollisionObject();
HEntity getCollisionObject( float x, float y );
};
#endif

View File

@ -39,6 +39,11 @@ void HEntity::operator=( const HEntity& copy )
addRef();
}
bool HEntity::operator ==( const HEntity& test ) const
{
return( m_handle == test.m_handle );
}
void HEntity::addRef()
{
if( m_handle != INVALID_HANDLE )
@ -57,14 +62,14 @@ void HEntity::decRef()
}
}
CEntity* HEntity::operator->()
CEntity* HEntity::operator->() const
{
assert( m_handle != INVALID_HANDLE );
assert( g_EntityManager.m_entities[m_handle].m_refcount != 0 );
return( g_EntityManager.m_entities[m_handle].m_entity );
}
CEntity& HEntity::operator*()
CEntity& HEntity::operator*() const
{
assert( m_handle != INVALID_HANDLE );
assert( g_EntityManager.m_entities[m_handle].m_refcount != 0 );

View File

@ -43,11 +43,13 @@ class HEntity
void decRef();
HEntity( u16 index );
public:
CEntity& operator*();
CEntity* operator->();
CEntity& operator*() const;
CEntity* operator->() const;
HEntity();
HEntity( const HEntity& copy );
void operator=( const HEntity& copy );
bool operator==( const HEntity& test ) const;
operator bool() const { return( m_handle != INVALID_HANDLE ); }
~HEntity();
};

View File

@ -39,6 +39,15 @@ std::vector<HEntity>* CEntityManager::matches( EntityPredicate predicate )
return( matchlist );
}
std::vector<HEntity>* CEntityManager::getActive()
{
std::vector<HEntity>* activelist = new std::vector<HEntity>;
for( int i = 0; i < MAX_HANDLES; i++ )
if( m_entities[i].m_refcount )
activelist->push_back( HEntity( i ) );
return( activelist );
}
void CEntityManager::dispatchAll( CMessage* msg )
{
for( int i = 0; i < MAX_HANDLES; i++ )
@ -53,4 +62,11 @@ void CEntityManager::updateAll( float timestep )
m_entities[i].m_entity->update( timestep );
}
void CEntityManager::renderAll()
{
for( int i = 0; i < MAX_HANDLES; i++ )
if( m_entities[i].m_refcount )
m_entities[i].m_entity->render();
}
bool CEntityManager::m_extant = false;

View File

@ -39,7 +39,9 @@ public:
HEntity create( CStr templatename, CVector3D position, float orientation );
void updateAll( float timestep );
void dispatchAll( CMessage* msg );
void renderAll(); // TODO MT: What's the correct way to hook this up to the renderer?
std::vector<HEntity>* matches( EntityPredicate predicate );
std::vector<HEntity>* getActive();
static inline bool extant() // True if the singleton is actively maintaining handles. When false, system is shutting down, handles are quietly dumped.
{
return( m_extant );

View File

@ -8,6 +8,10 @@
// Orders are: ORDER_GOTO_NOPATHING: Attempts to reach the given destination via a line-of-sight
// system. Do not create an order of this type directly; it is
// used to return a path of line segments from the pathfinder.
// ORDER_GOTO_COLLISION: When the coldet system is trying to get us out of a collision,
// it generates these intermediate waypoints. We don't really have
// any reason to go to this specific point, so if a better way
// comes along, this order can be deleted.
// ORDER_GOTO: Attempts to reach the given destination. Uses the pathfinder
// to... er... find the path.
// Create this order when a standard movement or movement waypoint
@ -26,18 +30,12 @@
#define ORDER_MAX_DATA 1
#include "EntityHandles.h"
#include "Vector2D.h"
struct SOrderData
{
union
{
struct
{
float x;
float y;
} location;
u64 data; // miscellaneous
};
CVector2D location;
u64 data; // miscellaneous
HEntity entity;
};
@ -47,6 +45,7 @@ public:
enum
{
ORDER_GOTO_NOPATHING,
ORDER_GOTO_COLLISION,
ORDER_GOTO,
ORDER_PATROL
} m_type;

View File

@ -164,7 +164,7 @@ void CMapReader::ApplyData(CFileUnpacker& unpacker)
CVector3D orient = ((CMatrix3D*)m_Objects[i].m_Transform)->GetIn();
CVector3D position = ((CMatrix3D*)m_Objects[i].m_Transform)->GetTranslation();
g_EntityManager.create( templateObject, position, atan2( orient.X, orient.Z ) );
g_EntityManager.create( templateObject, position, atan2( -orient.X, -orient.Z ) );
}
else
{