Archived

This topic is now archived and is closed to further replies.

_TinOmen_

C64 "Thrust" style physics

Recommended Posts

Hi, I''m working on a clone of the C64 classic game "Thrust". For those who do not know "thrust", the game features a space ship that is controlled just like in "Asteroids". The ship can pick up an orb, which then becomes connected to the ship with a tractor beam (a stick). A bit like the two-player mode in the 80:ies arcade game "Space Duel". Both the ship and the orb are subject to gravity which pulls them down toward the planet. I was wondering if anyone can help me with some pointers how to implement the physics routines for animating the ship and orb. I''ve read articles on how to make it the "correct" way in full-blown physics, but I''m needing a simpler approximation. Preferably it would be simple enough to implement in integer math because I''m targeting an 8-bit classic platform.

Share this post


Link to post
Share on other sites
a simple Euler integration should do. It sounds bad already

what you need for your objects (spaceship or asteroid), is some physics properties.

I use a simple 2D vector class, for the sake of readability.



class CMobile
{
public:
Vector2 m_vForces; // force accumulator

Vector2 m_vAcceleration; // acceleration this frame

Vector2 m_vVelocity;
Vector2 m_vPosition;
float m_fAngle;
float m_fMass;

virtual void Update (float dt);
void AddForce(Vector2 vForce) { m_vForces += vForce; }
void AddAcceleration(Vector2 vAcc) { m_vAcceleration += vAcc; }

};

//you update the position of your physical objects like this

void CMobile::Update (float dt)
{
m_vAcceleration += m_vForces / m_fMass;
m_vPosition += m_vVelocity * dt + m_vAcceleration * (dt*dt);
m_vVelocity += m_vAcceleration * dt;

m_vAcceleration = Vector2::Zero();
m_vForces = Vector2::Zero();
}



The update function will make your spaceship and asteroids turn accelerations and forces into movement.



Now, for your spaceship, you can derive a class like this


class CSpaceShip: public CMobile
{
public:
float m_fEnginePower;
Vector2 m_vDirection; // direction the ship is pointing towards


virtual void Update(float dt);
};

void CSpaceShip::Udpate(float dt)
{
m_fAngle += m_xInputs.KeyRotateLeftValue() * dt;
m_fAngle -= m_xInputs.KeyRotateLeftValue() * dt;

m_vDirection = Vector2(cos(m_fAngle), sin(m_fAngle));

float fThrust = m_xInputs.KeyThrottleValue() * m_fEnginePower;

AddForce(m_vDirection * fThrust);
AddAcceleration(Vector2(0.0f, -9.81f, 0.0f)); // gravity


CMobile::Update(dt);
};



the spaceship will rotate around with keypresses, and its engine will push it towards where the spaceship is pointing. Gravity is also added.

You can also add air friction to your spaceship (something like (-m_vVelocity * fAirFriction) , and all sorts of other forces.

For your asteroids, you need another class, with even simpler physics, since they have no engines.



for your spaceship tracting beam, it''s a bit more complicated.

first, you need to be able to tell if an asteroid is in the beam. The function returns the distance of the point to the beam generator, and the angle between the beam core, the point, and the beam generator.


void PointBeamDeviation(Vector2 vPoint, Vector2 vBeamPos, Vector2 vBeamDir, float& fPointDistance, float &fPointAngularDeviation)
{
fPointDistance = (vPoint - vBeamPos).Magnitude();

fPointAngularDeviation = acos((vPoint - vBeamPos).Dot(vBeamDir) / fPointDistance);
}

Given the distance of the point to the generator, and its angle from the beam, you can apply a force to the asteroid that would push it towards the spaceship.



float fMaxAngleDev = (30.0f / 180.0f * PI);
float fDist;
float fAngularDev;
PointBeamDeviation(Asteroid.m_vPosition, SpaceShip.m_vPosition, SpaceShip.m_vDirection, fDist, fAngularDev);

if (fAngularDev < fMaxAngleDev)
{
Vector vTractingDirection = (SpaceShip.m_vPosition - Asteroid.m_vPosition).UnitVector();

float fDistTractingForce = fMaxDistTractingForce / fDist;
float fAngularTractingForce = cos(fAngularDev);

if (fDistTractingForce > 10.0f)
fDistTractingForce = 10.0f;

float fTractingForce = fAngularTractingForce * fDistTractingForce;

Vector2 vTractingForce = vTractingDirection * fTractingForce;

Asteroid.AddForce(vTractingForce);
}



you can add loads of different forces, some force fields, some collision impacts, ect...

Share this post


Link to post
Share on other sites
Many thanks for the examples Oliii, I will look into them.
I''m still hoping there exists a simpler non-generic technique
I got a grip of all the thrust/velocity part for ship movement, it''s only the link between the ship and pod which is the problem.
The distance between the ship and pod is fixed.
See this link for a screen shot from another thrust-clone:
http://www.lysator.liu.se/~peda/thrust/

Share this post


Link to post
Share on other sites
OK, so the pod swings about like a pendulum?

In this case, you may want to look at a verlet integration. It's quite simple



struct CParticle
{
Vector m_pos;
Vector m_oldpos;
Vector m_vAccel;
float m_invmass; // 1.0f / mass of particle


void Update(void)
{
float dt2 = (1.0f / 60.0f) * (1.0f / 60.0f); // 60 fps

Vector Temp = m_pos;
m_vAccel = Vector(0, -10); // gravity

m_pos += (m_pos - m_oldpos) + m_vAccel * dt2;
m_oldpos = Temp;
}
};

struct CLink_Orb_Ship
{
CParticle* m_pxShip;
CParticle* m_pxOrb;
float m_fRestLength;

void Update(void)
{
m_pxOrb ->Update();
m_pxShip->Update();

UpdateLink();

while (FindAndProcessCollision())
UpdateLink();
}

void UpdateLink(void)
{
float m_l2 = m_fRestLength * m_fRestLength;
Vector delta = m_pxShip->m_pos - m_pxOrb->m_pos;
float delta2 = delta*delta;
float invmass0 = m_pxOrb->m_invmass;
float invmass1 = m_pxShip->m_invmass;
float invmass = (invmass0 + invmass1);
float diff = m_l2 / (delta2 + m_l2) - 0.5f;
diff *= -2.0f / invmass;
delta *= diff;
m_pxOrb->m_pos += delta*invmass0;
m_pxShip->m_pos -= delta*invmass1;
}

bool FindAndProcessCollision(void)
{
.....
.....
.....
}
};




What this do, it will connect the pod to the ship, the pod will swing like a heavy pendulum (if you set its mass to larger than the ship), dragging the ship around, while the ship will be pushed by its thrusters. The UpdateLink() function will enforce the pod and the ship to be at a fixed distance, while the heavier of the two (the pod) will move a little and the lighter one will do most of the movement, thus the pod will drag the ship. The verlet integration has the advantage of being more stable when you constrain the particle like this. The euler will be too unstable. Plus, it's simpler. All you need is to record the previous position of the particle in oldpos, calculate the accelerations, and presto. When you first set the ship position at initialisation, set both pos and oldpos to that position, which basically sets the initial velocity to 0.

You can plug in the collision detection for the orb and the ship routine. All you have to do in there, is move the ship and the orb by the collision response, and re-update the link. Of course, when the ship or the orb collides, the link constrain will be broken (the distance between them will be shorten or increased), so you need to re-update the link constrain, and by doing so, it might introduce more collisions, ect...

[edited by - oliii on October 5, 2003 7:59:06 PM]

Share this post


Link to post
Share on other sites
btw, this system is taken from that article

http://www.gamasutra.com/resource_guide/20030121/jacobson_01.shtml

and that collision loop is not fuly necessary, I got carried away. All you need is


void Update(void)
{
m_pxOrb ->Update();
m_pxShip->Update();
FindAndProcessCollision();
UpdateLink();
}



[edited by - oliii on October 5, 2003 8:00:58 PM]

Share this post


Link to post
Share on other sites
This looks like some very good advice Oliii.

I read that gamasutra article just the other day and thought it
interesting, but I was not sure how I would use that information
in my game.

Now I think you have sent me in a good direction

This is a great and friendly forum.

[edited by - _TinOmen_ on October 6, 2003 7:06:12 AM]

Share this post


Link to post
Share on other sites