Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Engine design

Started by daniel_i_l Aug 15, 2006 at 5:07 PM 0 replies 500+ views
Original Post
daniel_i_l
daniel_i_l
Hi! Now I just finished the base of my first physics engine (2D). I think that I got the physics right (atleast it looks real) but the design seems pretty bad to me. I'm not that good at it and it'd be great if you guys could look it over and give me some advice. As I said, my MAIN interest here is the OO design but if there're any physical mistakes I'd be happy to know:) Ok, first of all the only thing I've implemented so far are box's connected with springs, the springs apply linear force and torque to the boxs, no collision detection so far but I hope to make the code flexable enough so that it will be easy to implement. Here's a short overview of the classes: -State: hold parameters defining the state of a rigid body, like position, rotation, speed... - RigidBody: holds all parameters of a rigid body, an instance of State, the vertices that make it up (handy for collision detection and rendering), and a function that translates vertices from their local space to world space according to the position of the body, and a vector of Forces that act on the body. -Force: holds all parameters of a spring, hold a pointer to the body it's acting on and of the body on the other end, it also has functions that calculate force and torque. -Simulation: gets a vector of bodies and integrates them one step ahead via RK4. Here they are in code: State.h

#ifndef STATE_H
#define STATE_H

#include "Vector2D.h"
struct State{
	State(){Position = Vector2D(0,0); 
            Velocity = Vector2D(0,0); 
            Mass = 1;
            MomentOfInertia = 1;
            Rotation = 0;
            AngularVelocity = 0;}

	State(Vector2D p, Vector2D v, double m, double i)
		:Position(p), 
         Velocity(v), 
	     Mass(m),
         MomentOfInertia(i),
         Rotation(0),
         AngularVelocity(0)
		 {}

 //a vector to store the position
 Vector2D  Position;
 //a scalar to store rotation
 double    Rotation;

 //a vector to store the velocity
 Vector2D  Velocity;
 //a scalar to store anguler velocity
 double    AngularVelocity;

 //mass
 double Mass;
 double MomentOfInertia;

 //derivaitive of position
 //Vector2D dPosition;
};

#endif 




RigidBody.h/.cpp

#ifndef RIGIDBODY
#define RIGIDBODY

#include <vector>
#include "State.h"
#include "Force.h"
#include "Vector2D.h"

using namespace std;

//-------------------Rigid Body class----------------------
// This class holds all the information needed for a rigid
// body, including it's state and the forces acting on it.
// For now it can handle only squares but I hope to upgrade
// it or inherit it in the future.
//---------------------------------------------------------
class RigidBody{
private:
	//a list of all the coordinates as they're in world space
	vector<Vector2D> m_CornersInWorldSpace;

	//a list of all the coordinates as they're in body space
	vector<Vector2D> m_CornersInBodySpace;

	//the width;
	double           m_Width;
    //the height
	double           m_Height;

	//a vector of all the forces acting on the body;
	vector<Force> Forces;

public:
	//ctor, overloaded ctor, and dtor.
	RigidBody(){}
	RigidBody(Vector2D p, Vector2D v, double m, double w, double h);
	~RigidBody();

	//transforms the corners to world space
	void UpdateGeometry();

	//transforms a vector of point/one point to world space
	void WorldTransform(vector<Vector2D> &points);
	void WorldTransform(Vector2D &point);

	//the state of the body
	State            m_MyState;

	//returns vector of the corners in world space, used for rendering
	vector<Vector2D> GetCorners(){return m_CornersInWorldSpace;} 

	//adds a force to the list of forces
	void AddForce(Force F);
	//clears the list
	void ClearForces(){Forces.clear();}
	//use the forces to get total force and torque
    void ApplyForces();
	Vector2D TotalForce;
	double   TotalTorque;
};
    
#endif

///////////////////////////////.CPP/////////////////////////////////////
RigidBody::RigidBody(Vector2D p, Vector2D v, double m, double w, double h)
{
	double MomentOfInertia = (1.0f/12.0f)*m*(w*w + h*h);
	m_MyState = State(p, v, m, MomentOfInertia);

	m_Width   = w;
	m_Height  = h;

	m_CornersInBodySpace.push_back(Vector2D(-w,-h));
	m_CornersInBodySpace.push_back(Vector2D( w,-h));
	m_CornersInBodySpace.push_back(Vector2D( w, h));
	m_CornersInBodySpace.push_back(Vector2D(-w, h));

	UpdateGeometry();
}

RigidBody::~RigidBody()
{
}

void RigidBody::AddForce(Force F)
{
	F.SetRigidBody(this);
	Forces.push_back(F);
}

void RigidBody::ApplyForces()
{
for(unsigned int i=0; i<Forces.size(); ++i) Forces.TranslatePoints();
	TotalForce.Zero();
	TotalTorque = 0;
	Vector2D    force;
	for(unsigned int i=0;i<Forces.size();++i) 
	{
		force        =  Forces.CalculateForce();
		TotalForce  +=  force;
		TotalTorque +=  Forces.CalculateTorque(force);
	}
	TotalForce  -= m_MyState.Velocity * 2;
	TotalForce  += Vector2D(0,-10) * m_MyState.Mass;
	TotalTorque -= 2*m_MyState.AngularVelocity;
}

void RigidBody::UpdateGeometry()
{
  m_CornersInWorldSpace = m_CornersInBodySpace;
  WorldTransform(m_CornersInWorldSpace);
}

void RigidBody::WorldTransform(vector<Vector2D> &points)
{
	//first calculate world space of point
	C2DMatrix matTransform;
	
	//rotate
	matTransform.Rotate(m_MyState.Rotation);

	//and translate
	matTransform.Translate(m_MyState.Position.x, m_MyState.Position.y);
	
    //now transform the ships vertices
    matTransform.TransformSPoints(points);
}
			

void RigidBody::WorldTransform(Vector2D &point)
{
	//first calculate world space of point
	C2DMatrix matTransform;
	
	//rotate
	matTransform.Rotate(m_MyState.Rotation);

	//and translate
	matTransform.Translate(m_MyState.Position.x, m_MyState.Position.y);
	
    //now transform the ships vertices
	vector<Vector2D> points;
	points.push_back(point);
    matTransform.TransformSPoints(points);
	point = points[0];
}




Force.h/cpp

#ifndef FORCE_H
#define FORCE_H

#include <vector>
#include "State.h"
#include "Vector2D.h"

using namespace std;

class RigidBody;
class Force{
public:
	Force(){}
	Force(Vector2D a, Vector2D b, double k, double d, double l, 
        RigidBody *r);
	Force (const Force &F);      //copy ctor
	virtual ~Force(){}
	void SetRigidBody(RigidBody *R){ OwnerOfPoint=R; }

	//local and world coords in the body on the far end of the spring
	Vector2D AncorInBodySpace;
	Vector2D AncorInWorldSpace;

	//local and world coords in the body that the force is on 
	Vector2D PointInBodySpace;
	Vector2D PointInWorldSpace;

	double   SpringConstant;
	double   Damping;
	double   RestLength;

	//translate all local coords to world coords
	void     TranslatePoints();
	
	Vector2D CalculateForce();
	double   CalculateTorque(const Vector2D &force);

private:
	RigidBody *OwnerOfPoint;
	RigidBody *OwnerOfAncor;
};

#endif

////////////////////////////////CPP//////////////////////////////
Force::Force(Vector2D a, Vector2D b, double k, double d, double l, 
             RigidBody *r)
	   :AncorInBodySpace(a), 
	    PointInBodySpace(b),
		SpringConstant(k),
		Damping(d),
	    RestLength(l),
		OwnerOfAncor(r)
		{}

Force::Force (const Force &F)
{
	AncorInBodySpace  = F.AncorInBodySpace;
	AncorInWorldSpace = F.AncorInWorldSpace;
	Damping    = F.Damping;
	PointInBodySpace  = F.PointInBodySpace;
	PointInWorldSpace = F.PointInWorldSpace;
	RestLength  =F.RestLength;
	SpringConstant = F.SpringConstant;
	
	//RigidBody *B=new RigidBody;
	//B = F.OwnerOfPoint;
	OwnerOfPoint = F.OwnerOfPoint;
	OwnerOfAncor = F.OwnerOfAncor;
}

Vector2D Force::CalculateForce()
{
	const State state = OwnerOfPoint->m_MyState;

	double dis = Vec2DDistance(AncorInWorldSpace, PointInWorldSpace);
	Vector2D ToSource = AncorInWorldSpace - PointInWorldSpace;
	ToSource /= dis;
	
	double   SpringForce  = SpringConstant*(dis - RestLength);
	double   DampingForce = Damping*state.Velocity.Dot(ToSource);
	Vector2D TotalForce =  (SpringForce - DampingForce)*ToSource;

	return   TotalForce;
}


double Force::CalculateTorque(const Vector2D &force)
{
	const State state = OwnerOfPoint->m_MyState;

	Vector2D ToPoint    =  PointInWorldSpace - state.Position;
	Vector2D PerpToPoint = ToPoint.Perp();
	double   DampingTorque = 30*state.AngularVelocity;
	double   Torque      = PerpToPoint.Dot(force) - DampingTorque;

	return   Torque;
}


void   Force::TranslatePoints()
{
	PointInWorldSpace = PointInBodySpace;
	AncorInWorldSpace = AncorInBodySpace;
	OwnerOfPoint->WorldTransform(PointInWorldSpace);
	OwnerOfAncor->WorldTransform(AncorInWorldSpace);
}




Simuation.h/cpp

#ifndef SIMULATION_H
#define SIMULATION_H

#include "Vector2D.h"
#include "State.h"
#include "RigidBody.h"

struct Derivative{
	Derivative(){dPosition = Vector2D(0,0);
	             dRotation = 0;
				 dVelocity = Vector2D(0,0);
				 dAngularVelocity = 0;}
	
	//deravitive of position
	Vector2D dPosition;
	//derivative of rotation
	double   dRotation;

	//deravitive of velocity
	Vector2D dVelocity;
	//derivative of angular velocity
	double   dAngularVelocity;
};





class CSimulation{
private:
    double dt;

	//interpolate between two states according to time
	vector<RigidBody> Interpolate(const vector<RigidBody> &previous, 
       const vector<RigidBody> ¤t, double alpha);
    
	//get acceleration from current state
	Vector2D Acceleration(RigidBody &bodies);
    //get angular acceleration from state
	double   AngularAcceleration(RigidBody &bodies);

	//overload evaluation
	vector<Derivative> Evaluate(vector<RigidBody> &initials);

	//evaluate derivative
	vector<Derivative> Evaluate(vector<RigidBody> &initials, double dt, 
                                    vector<Derivative> &d);

	//integrate with RK4
	void Integrate(vector<RigidBody> &bodies, double dt);

public:
	CSimulation();
	//do one iteration
	void Update(vector<RigidBody> ¤ts, double DeltaTime);
};


#endif
///////////////////////////////CPP/////////////////////////////////////
CSimulation::CSimulation()
{
	dt = 0.1;
}

vector<RigidBody> CSimulation::Interpolate(const vector<RigidBody> &previous,
                                    const vector<RigidBody> ¤t, double alpha)
{
	vector<RigidBody> bodies;
	for(unsigned int i=0; i<current.size(); ++i)
	{
	RigidBody body;
	body = current;
	body.m_MyState.Position = current.m_MyState.Position*alpha + 
previous.m_MyState.Position*(1-alpha);
	body.m_MyState.Velocity = current.m_MyState.Velocity*alpha + previous.m_MyState.Velocity*(1-alpha);
	body.m_MyState.Rotation = current.m_MyState.Rotation*alpha + 
previous.m_MyState.Rotation*(1-alpha);
	body.m_MyState.AngularVelocity = current
.m_MyState.AngularVelocity*alpha + previous.m_MyState.AngularVelocity*
 (1-alpha);
	bodies.push_back(body);
	}
	return bodies;
}


Vector2D CSimulation::Acceleration(RigidBody &body)
{
	Vector2D TotalForce = body.TotalForce;

	return  TotalForce/body.m_MyState.Mass;
}


double   CSimulation::AngularAcceleration(RigidBody &body)
{
    double   Torque       = body.TotalTorque;
	double   AngularAccel = Torque / body.m_MyState.MomentOfInertia;

	return  AngularAccel;
}
vector<Derivative> CSimulation::Evaluate(vector<RigidBody> &initials)
{
	Derivative output;
	vector<Derivative> outputs;
	
   for(unsigned int i=0; i<initials.size(); ++i)
   {
	initials.ApplyForces();
	output.dPosition = initials.m_MyState.Velocity;
	output.dVelocity = Acceleration(initials);
	
	output.dRotation = initials.m_MyState.AngularVelocity;
	output.dAngularVelocity = AngularAcceleration(initials);
	outputs.push_back(output);
   }
	return outputs;
}


vector<Derivative> CSimulation::Evaluate(vector<RigidBody> &initials, 
                                    double dt, vector<Derivative> &d)
{
	RigidBody  B;
	vector<Derivative> outputs;
	Derivative output;
    
   for(unsigned int i=0; i<initials.size(); ++i)
   {
	B = initials;
    
	B.m_MyState.Position = initials.m_MyState.Position
                                + d.dPosition * dt;
	B.m_MyState.Velocity = initials.m_MyState.Velocity
                                + d.dVelocity * dt;

	B.m_MyState.Rotation        = initials.m_MyState.Rotation     
                                        + d.dRotation        * dt;
	B.m_MyState.AngularVelocity = initials.m_MyState.AngularVelocity 
                                        + d.dAngularVelocity * dt;

	B.m_MyState.Mass = initials.m_MyState.Mass;
	B.m_MyState.MomentOfInertia = initials.m_MyState.MomentOfInertia;

	B.ApplyForces();
    
 	output.dPosition = B.m_MyState.Velocity;
	output.dVelocity = Acceleration(B);

	output.dRotation        = B.m_MyState.AngularVelocity;
	output.dAngularVelocity = AngularAcceleration(B);
	outputs.push_back(output);
   }
    
    return outputs;

}

void CSimulation::Integrate(vector<RigidBody> &bodies, double dt)
{
	vector<Derivative> a = Evaluate(bodies);
	vector<Derivative> b = Evaluate(bodies, dt*0.5, a);
	vector<Derivative> c = Evaluate(bodies, dt*0.5, b);
	vector<Derivative> d = Evaluate(bodies, dt,     c);
    //for one body do euler
	//Euler
	//vector<Derivative> b = a;
	//vector<Derivative> c = a;
	//vector<Derivative> d = a;
    for(unsigned int i=0; i<bodies.size(); ++i)
	{
	Vector2D  dPosition = 1.0f/6.0f * (a.dPosition + 2.0f*
              (b.dPosition + c.dPosition) + d.dPosition);
	Vector2D  dVelocity = 1.0f/6.0f * (a.dVelocity + 2.0f*
              (b.dVelocity + c.dVelocity) + d.dVelocity);
	
	double    dRotation = 1.0f/6.0f        * (a.dRotation + 2.0f*
                      (b.dRotation + c.dRotation) + d.dRotation);
	double    dAngularVelocity = 1.0f/6.0f * (a.dAngularVelocity + 
                    2.0f*(b.dAngularVelocity + c.dAngularVelocity)+ 
                          d.dAngularVelocity);
	
	
	bodies.m_MyState.Position = bodies.m_MyState.Position + 
dPosition*dt;
	bodies.m_MyState.Velocity = bodies.m_MyState.Velocity + 
dVelocity*dt;

	bodies.m_MyState.Rotation        = bodies.m_MyState.Rotation + 
dRotation*dt;
	bodies.m_MyState.AngularVelocity = bodies
       .m_MyState.AngularVelocity + dAngularVelocity*dt;
	}
}

void CSimulation::Update(vector<RigidBody> ¤ts, double DeltaTime)
{
  double accumulator = 0.0f;
  vector<RigidBody> previous;
  previous = currents;

  if(DeltaTime > 0.25)
	  DeltaTime = 0.25;
  //if(DeltaTime < dt)
	  //DeltaTime = dt;
  accumulator += DeltaTime;
 if(DeltaTime>dt)
 {
 while(accumulator>=dt)
  {
	  accumulator -= dt;
	  previous = currents;
	  Integrate(currents, dt);
  }
  if(accumulator!=0)
   currents = Interpolate(previous, currents, accumulator/dt);
 }
 else
  Integrate(currents, DeltaTime);

 //RigidBody r;
 //if(accumulator!=0)
   //currents = Interpolate(previous, currents, accumulator/dt);
// current = r;
}




Now to set up a simulation I just have to make a vector of RigidBodies, add forces to them, and then pass them through the Simulation Update. Now in general this design seems weak to me when I try to play around with it and add stuff though I'm not sure exactly why. Here're some things that bother me: 1) the Force class is a little odd, one spring requires two different "Forces" each stored in it's own seperate body, and each with opposite pointers (body A's ancor is body B's main point..). 2) the pointers in general in the Force class are a little annoying to manage, atleast the way that they're implemented now. 3) I don't like how in the Simulation class all the Evauate functions and other functions have to keep on looping through the bodies. It would be better if there was an easy, built-in way to pass bodies that are linked together (such as in rope) together and bodies that are alone, by themselves(and perhaps even euler integrate them instead) 4)I could go on some more about the Force class.... 5)No clear way how to elegantly fit collision detection into all this Well thats all I could come up with now but I'm sure that there's more, as I said, I have a lot to learn about OO design. What would be a better(= more efficiant, managable, stable, elegant..) way to design this? Any way to add to or fix what I've done? Advice for further development? Thanks a lot everyone! [Edited by - daniel_i_l on August 15, 2006 5:30:06 PM]
"We've all heard that a million monkeys banging on a million typewriters will eventually reproduce the entire works of Shakespeare. Now, thanks to the internet, we know this is not true." -- Professor Robert Silensky
daniel_i_l
daniel_i_l
Any ideas?
Thanks.
"We've all heard that a million monkeys banging on a million typewriters will eventually reproduce the entire works of Shakespeare. Now, thanks to the internet, we know this is not true." -- Professor Robert Silensky

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.