C64 "Thrust" style physics
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.
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.
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
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.
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.
you can add loads of different forces, some force fields, some collision impacts, ect...
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 thisvoid 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...
no integer maths there I''m afraid, even some cos/acos functions, but you''ll need to do some clever tricks to get the integer maths working.
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/
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/
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
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]
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]
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
[edited by - oliii on October 5, 2003 8:00:58 PM]
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]
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]
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]
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement