• Hey! Guest! The 40th (!!!) GMC Jam will take place between February 25th, 12:00 UTC to March 1st 12:00 UTC. Why not join in this very special anniversary jam! Click here to find out more!

3D Need help with physics engine

Garström

Member
Hello, I took upon myself to create a full-rigid physics engine for GMS 2. For the most part, it goes well except for hard constraints where the number of calls increases the vibrations within the instance.

Basically it works like this:
Forces are calculated and distributed to the instances. the parameters for forces are stored in a ds_grid. This is done in the event begin_step. this workes really well even with hundreds of instances.
The instances are then moving according to factors like velocity, acceleration ect. this is placed if the instances step event
The hard constraints adjust the instances based on parameters in a second ds_grid. This is done in the event end_step.

There are two hard constraints:
The first one is Rod constraint which forces the instance to be at a fixed distance from a second instance at all times.
The second is Rope constraint which forces the instance to move closer if the distance apart is to far.

To many instances makes the system go haywire.

So the questions are if this approach is bad?
Is there something wrong with the code? (Most likely)

Small code dictionary
E_Ins, E_Arg etc ar macros that represent integer values (1 2 3 ect)
Contact_Normal= Instance_1.position - instance_2.position
Penetration= Set distance between instances - the actual distance between instances
Restitution= Bounce back once the script activates
finitemass= 1/ instance mass (When zero the instance is unmoveable)

GML:
for(i=0;i<ds_grid_height(ContactGrid);i++)
{
    switch(ContactGrid[# 0, i])
    {
        case(1):
            if E_Scr_CheckExists(ContactGrid,i,true){
            if E_Scr_Rope(ContactGrid[# E_Ins, i],ContactGrid[# E_Arg_3, i],ContactGrid[# E_Arg_2, i],ContactGrid[# E_Arg_1, i])
            {
                E_Scr_Resolve(ContactGrid[# E_Ins, i],ContactGrid[# E_Arg_3, i],Restitution)
                i-=E_Scr_DecreaseGridTime(ContactGrid,i)
            }
        }
        break;
       
        case(2):
            if E_Scr_CheckExists(ContactGrid,i,true){
            if E_Scr_Rod(ContactGrid[# E_Ins, i],ContactGrid[# E_Arg_3, i],ContactGrid[# E_Arg_2, i])
            {
                E_Scr_Resolve(ContactGrid[# E_Ins, i],ContactGrid[# E_Arg_3, i],Restitution)
                i-=E_Scr_DecreaseGridTime(ContactGrid,i)
            }
        }
        break;
    }
}

GML:
///@function E_Scr_Rod(Instance 1 ID, Instance 2 ID, Length)
///@desc Create a Rod between Instance 1 and Instance 2
///@param {integer} Instance_1_ID
///@param {integer} Instance_2_ID
///@param {real} Length

var E_Inst_1,E_Inst_2,E_Length,E_Current_Length;

E_Inst_1=argument0
E_Inst_2=argument1
E_Length=argument2

E_Current_Length=E_Scr_CurrentLength(E_Inst_1,E_Inst_2)

if E_Current_Length==E_Length
{
    return false exit;
}

var E_Normal;

E_Normal[E_X]=E_Inst_1.position[E_X]
E_Normal[E_Y]=E_Inst_1.position[E_Y]
E_Normal[E_Z]=E_Inst_1.position[E_Z]

if instance_exists(E_Inst_2)
{
E_Normal[E_X]-=E_Inst_2.position[E_X]
E_Normal[E_Y]-=E_Inst_2.position[E_Y]
E_Normal[E_Z]-=E_Inst_2.position[E_Z]
}

E_Normal=vector_normalize(E_Normal)

if E_Current_Length>E_Length
{
    Contact_Normal=E_Normal
    Penetration=E_Current_Length-E_Length
}

if E_Current_Length<E_Length
{
    Contact_Normal[E_X]=-E_Normal[E_X]
    Contact_Normal[E_Y]=-E_Normal[E_Y]
    Contact_Normal[E_Z]=-E_Normal[E_Z]
    Penetration=E_Length-E_Current_Length
}

Restitution=0

return true;

Code:
=gml]///@function E_Scr_Resolve(Instance 1, Instance 2, Contact Normal, Restitution, Penetration)
///@description Resolve collision
///@param {integer} Instance_1_ID
///@param {integer} Instance_2_ID (If any)

E_Scr_ResolveVelocity(argument0,argument1,argument2)
E_Scr_ResolveInterpenetration(argument0,argument1)

GML:
///@function E_Scr_ResolveVelocity(Instance 1 ID, Instance 2 ID, Restitution)
///@desc Resolve Velocity in contact
///@param {integer} Instance_1_ID
///@param {integer} Instance_2_ID (If any)
///@param {real} Restitution

var E_Inst_1,E_Inst_2,E_Sep_Vel;
E_Inst_1=argument0
E_Inst_2=argument1

E_Sep_Vel=E_Scr_Cal_SepVel(E_Inst_1,E_Inst_2)

if E_Sep_Vel[E_X]>0||E_Sep_Vel[E_Y]>0||E_Sep_Vel[E_Z]>0{exit;}

var E_NewSepVel,E_Restitution,E_DeltaVelocity,E_TotalMass,E_Impulse,E_ImpulsePerMass
E_Restitution=argument2

E_NewSepVel[E_X]=-E_Sep_Vel[E_X]*E_Restitution
E_NewSepVel[E_Y]=-E_Sep_Vel[E_Y]*E_Restitution
E_NewSepVel[E_Z]=-E_Sep_Vel[E_Z]*E_Restitution

E_DeltaVelocity[E_X]=E_NewSepVel[E_X]-E_Sep_Vel[E_X]
E_DeltaVelocity[E_Y]=E_NewSepVel[E_Y]-E_Sep_Vel[E_Y]
E_DeltaVelocity[E_Z]=E_NewSepVel[E_Z]-E_Sep_Vel[E_Z]

E_TotalMass=E_Inst_1.finitemass
if instance_exists(E_Inst_2){ E_TotalMass+=E_Inst_2.finitemass}

E_Impulse[E_X]=E_DeltaVelocity[E_X]/E_TotalMass
E_Impulse[E_Y]=E_DeltaVelocity[E_Y]/E_TotalMass
E_Impulse[E_Z]=E_DeltaVelocity[E_Z]/E_TotalMass

E_ImpulsePerMass[E_X]=Contact_Normal[E_X]*E_Impulse[E_X]
E_ImpulsePerMass[E_Y]=Contact_Normal[E_Y]*E_Impulse[E_Y]
E_ImpulsePerMass[E_Z]=Contact_Normal[E_Z]*E_Impulse[E_Z]

E_Inst_1.velocity[E_X]+=E_ImpulsePerMass[E_X]*E_Inst_1.finitemass
E_Inst_1.velocity[E_Y]+=E_ImpulsePerMass[E_Y]*E_Inst_1.finitemass
E_Inst_1.velocity[E_Z]+=E_ImpulsePerMass[E_Z]*E_Inst_1.finitemass

if instance_exists(E_Inst_2){
    E_Inst_1.velocity[E_X]+=E_ImpulsePerMass[E_X]*-E_Inst_2.finitemass
    E_Inst_2.velocity[E_Y]+=E_ImpulsePerMass[E_Y]*-E_Inst_2.finitemass
    E_Inst_2.velocity[E_Z]+=E_ImpulsePerMass[E_Z]*-E_Inst_2.finitemass
}

GML:
///@function E_Scr_Cal_SepVel(Particle 1, Particle 2, Contact Normal)
///@desciption Calculate seperating velocity between two particle
///@param {integer} Particle_1_ID
///@param {integer} Particle_2_ID (If any)
///@param {real} Contact_Normal
var E_Inst_1,E_Inst_2,E_If_Ob,E_Rev_Vel;
E_Inst_1=argument0
E_Inst_2=argument1

if instance_exists(E_Inst_2)
{
    E_If_Ob=true
}else E_If_Ob=false

E_Rev_Vel=E_Inst_1.velocity
if E_If_Ob
{
    
    E_Rev_Vel[E_X]-=E_Inst_2.velocity[E_X]
    E_Rev_Vel[E_Y]-=E_Inst_2.velocity[E_Y]
    E_Rev_Vel[E_Z]-=E_Inst_2.velocity[E_Z]
}
E_Rev_Vel[E_X]*=Contact_Normal[E_X]
E_Rev_Vel[E_Y]*=Contact_Normal[E_Y]
E_Rev_Vel[E_Z]*=Contact_Normal[E_Z]

return E_Rev_Vel

GML:
///@function E_Scr_ResolveInterpenetration( Instance 1 ID, Instance 2 ID)
///@desc Resolve penetration when two particle collides
///@param {integer} Instance_1_ID
///@param {integer} Instance_2_ID (if any)

var E_Inst_1,E_Inst_2;

E_Inst_1=argument0
E_Inst_2=argument1

if Penetration<=0 exit;

var Total_Finite_Mass;

Total_Finite_Mass=E_Inst_1.finitemass
if instance_exists(E_Inst_2){Total_Finite_Mass+=E_Inst_2.finitemass}

if Total_Finite_Mass<=0 exit;

var MovePerFMass;

MovePerFMass[E_X]=Contact_Normal[E_X]*(-Penetration/Total_Finite_Mass)
MovePerFMass[E_Y]=Contact_Normal[E_Y]*(-Penetration/Total_Finite_Mass)
MovePerFMass[E_Z]=Contact_Normal[E_Z]*(-Penetration/Total_Finite_Mass)

E_Inst_1.position[E_X]+=MovePerFMass[E_X]*E_Inst_1.finitemass
E_Inst_1.position[E_Y]+=MovePerFMass[E_Y]*E_Inst_1.finitemass
E_Inst_1.position[E_Z]+=MovePerFMass[E_Z]*E_Inst_1.finitemass

if instance_exists(E_Inst_2)
{
    E_Inst_2.position[E_X]+=MovePerFMass[E_X]*E_Inst_2.finitemass
    E_Inst_2.position[E_Y]+=MovePerFMass[E_Y]*E_Inst_2.finitemass
    E_Inst_2.position[E_Z]+=MovePerFMass[E_Z]*E_Inst_2.finitemass
}
 
Last edited:
Top