Moogie Physics Engine

Without a Physics engine, this server won’t work. Even though it’s been done before, writing a physics engine in Graal will be rather challenging. Let’s discuss how we’ll make it work.

The existing physics engine uses mysterious gravity system that only obeys vertical gravity and doesn’t have any concept of inertia. What do we want in our physics engine?

Requires gravity and lots of it.

I imagine the final script would end up looking a bit like this but with additional features:

//Made by tricxta
if (playerenters){
  if (!isweapon)toweapons -movement;
  disabledefmovement;
  this.speed = .75;
  timeout=0.05;
}

if (isweapon && timeout){
  if (!movementdisabled){
    if (keydown(1)){
      playerdir=1;
      setani walk,;
      for (this.i=0;this.i<this.speed;this.i+=1/16){
        if (onwall(playerx-(1/16),playery+1)){
          playertouchsme();
          break;
        }
        if (onwall(playerx-(1/16),playery+2) && !this.fall==0)break;
        playerx-=1/16;
      }
    }
    else if (keydown(3)){
      playerdir=3;
      setani walk,;
      for (this.i=0;this.i<this.speed;this.i+=1/16){
        if (onwall(playerx+2.5+(1/16),playery+1)){
          playertouchsme();
          break;
        }
        if (onwall(playerx+2.5+(1/16),playery+2) && !this.fall==0)break;
        playerx+=1/16;
      }
    }
    else {
      snap();
      setani idle,;
    }

    if (keydown(6) && this.jump==0 && onwall(playerx+1.5,playery+3+1/16) && !onwall(playerx+1.5,playery) && this.keydown==0){
      play jump.wav;
      this.keydown=1;
      this.jump=1;
    }
    else if (!keydown(6))this.keydown=0;

    if (this.jump>0 && this.jump<10 && !onwall(playerx+1.5,playery-.5)){this.jump++;playery-=(10-this.jump)*.2;}
    else if (this.jump>0 || onwall(playerx+1.5,playery-.5)){
      this.jump=-1;
      if (onwall(playerx+1.5,playery-.5)){
        for (this.i=0;this.i<npcscount;this.i++){
          if (playerx+1.5 in |npcs[this.i].x,npcs[this.i].x+npcs[this.i].width| && playery in |npcs[this.i].y,npcs[this.i].y+npcs[this.i].height|)callnpc this.i,playerhitbottom,#v(playerdir);
        }
      }
    }

    if (!onwall(playerx+1.5,playery+3-1/16) && this.jump<=0){
      this.i=1/16;
      while (!onwall(playerx+1.5,playery+3+this.i))this.i+=1/16;
      if (this.i<2)playery+=this.i;
      else playery+=1;
    }
    if (onwall(playerx+1.5,playery+3)){
      this.jump=0;
      for (this.i=0;this.i<npcscount;this.i++){
        if (playerx+1.5 in |npcs[this.i].x,npcs[this.i].x+npcs[this.i].width| && playery+3.5 in |npcs[this.i].y,npcs[this.i].y+npcs[this.i].height|)callnpc this.i,playerontop,#v(playerdir);
      }
      while (onwall(playerx+1.5,playery+3))playery-=1/16;
    }
  }
  timeout=0.05;
}

function snap(){
  playerx=int(playerx/0.0625)*0.0625;
  playery=int(playery/0.0625)*0.0625;
}

function playertouchsme(){
  for (this.i=0;this.i<npcscount;this.i++){
    if (playerx+vecx(playerdir)*2 in |npcs[this.i].x,npcs[this.i].x+npcs[this.i].width|)callnpc this.i,playertouchsme,#v(playerdir);
  }
}

This Just some script I did for a side scroller project I never ever finished, so im just throwing it out there lul

We should decide certain factors first.

Gravity : ? tiles/sec
Fall damage?
Instant stop when you run into an object or hit the ground?
Change jump direction in mid-air?
Friction/Inertia : ? tiles/sec

Definately going to need inertia.
Fall damage seems a bit harsh…
Of course you will instantly stop when you run into an object, anything other then that is rather o_0
As for changing direction in air, you should be able to change your direction but not your direction of flight.

Who’s going to script this btw?

I vote Cyrus cuz I dun wanna and Tricxta scripts look icky.

For the instant stop, I meant whether or not you should be able to instantly move after a complete stop. Most 2D games freeze you for a bit when you ram into a wall or drop from tall heights. Kind of a “flinch” kind of thing.

No reason we couldn’t both work on it.

Ok cyrus its up to you, show us what you can do :stuck_out_tongue:

You can’t just discuss this sort of thing in details, all you can do is lay some groundwork and get an idea of what kind of physics you want and then tweak, tweak, and tweak it some more.

For example, do you want “floaty” physics, or “heavy, responsive” physics? Do you want slippery traction or responsive traction? Lay down vague ground rules and tweak the numbers until you find something you like.

Seriously… copying Mario is the only way to go imo!
[php]// NPC made by Dusty
if (created) {
toweapons Mario;
enablefeatures 0x80;
disabledefmovement;
setplayerprop #3,mario_spritesheet0.png;
client.height = 4;
client.dead = 0;

setstring this.ganis,idle,walkslow,walk,run,jump,fall,turn,duck;

timeout = 0.05;
}

if (playerenters && isweapon) {
this.friction = 0.09;
this.stoppower = 0.16;
this.walkspeed = .6;
this.runspeed = 1.2;
this.jumppower = -103/64;
client.gravity = 24/64;
client.maxfall = 160/64;

if (!strequals(#e(0,9,#s(this.oldlevel)),mario_map)) this.newx = 0;
setstring this.oldlevel,#L;

timeout = 0.05;
}

if (timeout && isweapon) {
if (strtofloat(#s(client.dead)) == 0) Movement();
else {
if (this.checkdeath == 0) {
setani mario_dead,;
play2 mario_death.wav,playerx,playery,1;
stopmidi;
}
if (this.deadvel > 5) setlevel mariogo.nw;
if (this.deadvel < 3) playery += this.deadvel;
this.deadvel += 0.1;
this.checkdeath = 1;
}
timeout = 0.05;
} else timeout = 0.05;

function Movement() {
this.checkdeath = 0;
this.deadvel = -1.25;
if (onwall2(playerx+1,playery+(3-client.height),1,1/16) && onwall2(playerx+1,playery+3+1/16,1,1/16)) {
if (this.stuck == 0) {
this.velx = 0;
client.velocity_Y = 0;
playerdir = 3;
for (i=0;i<10;i++) {
if (!onwall(playerx+1.5+i,playery)) {
playerdir = 3;
break;
}
if (!onwall(playerx+1.5-i,playery)) {
playerdir = 1;
break;
}
}
}
if (!onwall(playerx+1.5+vecx(playerdir),playery+2.5)) playerx += vecx(playerdir).2;
else playerx -= vecx(playerdir)
.2;
this.newx = playerx;
this.stuck = 1;
DefineSprites();
return;
} else this.stuck = 0;

if (playerdir in {0,2}) playerdir = (playerdir+1)%4;
if (keydown(2)) {
client.ducking = 1;
client.height = 2;
} else {
client.ducking = 0;
client.height = 3;
}

if (client.ducking == 0 || abs(client.velocity_Y) > 0) {
if (keydown(1)) {
playerdir = 1;
this.velx -= this.velx > 0 ? this.stoppower : this.friction;
} else if (keydown(3)) {
playerdir = 3;
this.velx += this.velx < 0 ? this.stoppower : this.friction;
} else {
if (this.velx < 0) this.velx += 0.035;
else this.velx -= 0.035;
if (abs(this.velx) < 0.1) this.velx = 0;
}
} else if (client.velocity_Y == 0) {
if (this.velx < 0) this.velx += 0.055;
else this.velx -= 0.055;
if (abs(this.velx) < 0.1) this.velx = 0;
}

if (keydown(5)) this.running = 1;
else this.running = 0;

this.maxhorzspeed = this.running == 1 ? this.runspeed : this.walkspeed;
if (this.velx < this.maxhorzspeed*-1) this.velx += abs(this.velx)/10;
else if (this.velx > this.maxhorzspeed) this.velx -= this.velx/10;

if (abs(this.velx) > this.runspeed - (abs(this.velx)/5)) this.hittopspeed = true;
else this.hittopspeed = false;

this.headpos = playery+(3-client.height);

for (m=0;m<abs(this.velx)+((client.velocity_Y > 0 && this.velx == 0) ? 1/16 : 0);m+=1/16) {
this.movedir = this.velx < 0 ? 1 : 3;
this.newx = playerx + vecx(this.movedir)m;
CheckForSlopes();
this.onwall = false;
CheckWall();
//if (onwall(this.newx+1.5+vecx(this.movedir)
.75,this.headpos+client.height-1/16) ||
// onwall(this.newx+1.5+vecx(this.movedir)*.75,this.headpos)) {
if (this.hitwall == 1) {
// MAKE SURE THE PLAYER IS NOT ON A SLOPE NOW OR LAST FRAME
if (this.onslope == 0 && timevar2 > this.lastslope+0.1) {
this.onwall = true;
playerx += vecx(this.movedir)m;
playerx = int(playerx+.5)-vecx(this.movedir)
.25;
this.velx = 0;
break;
}
}
}

if (this.newx != 0 && this.onwall == false) playerx = this.newx;

DoJumps();

if (client.velocity_Y == 0) {
client.onground = 1;
this.hitNPC = 0;
} else client.onground = 0;

DefineSprites();
}

function DefineSprites() {
if (abs(this.velx) > 0) {
if (abs(this.velx) < this.runspeed-.25) {
if (abs(this.velx) > this.walkspeed/2) this.mode = 2;
else this.mode = 1;
} else this.mode = 3;
}

if ((keydown(1) && this.velx > 0) || (keydown(3) && this.velx < 0) && abs(this.velx) > .25) this.mode = 6;

if (client.JumpFrame > 0 && client.onground == 0) this.mode = 4;
else if (abs(client.velocity_Y) > 0) this.mode = 5;
else if (this.velx == 0) this.mode = 0;

if (client.ducking == 1) this.mode = 7;
if (this.stuck == 1) this.mode = 1;

//setplayerprop #c,#I(this.ganis,this.mode);
setstring this.gani,mario_big-#I(this.ganis,this.mode);
if (!strequals(#m,#s(this.gani))) setani #s(this.gani),;
}

function CheckWall() {
this.hitwall = (
onwall(this.newx+1.5+vecx(this.movedir).75,this.headpos+client.height-1/16) &&
(int(tiles[this.newx+1.5+vecx(this.movedir)
.75,this.headpos+client.height-1/16]/512) >= 6 &&
int((tiles[this.newx+1.5+vecx(this.movedir).75,this.headpos+client.height-1/16]%512)/256) == 1) == 0
) || (
onwall(this.newx+1.5+vecx(this.movedir)
.75,this.headpos) &&
(int(tiles[this.newx+1.5+vecx(this.movedir).75,this.headpos]/512) >= 6 &&
int((tiles[this.newx+1.5+vecx(this.movedir)
.75,this.headpos]%512)/256) == 1) == 0
);
}

function CheckForSlopes() {
if (client.velocity_Y < 0) return;
else if (client.velocity_Y > 0) this.sensitivity = client.velocity_Y;
else this.sensitivity = 1.5;
for (i=.5;i<1.5+this.sensitivity;i+=1/16) {
this.checktile = tiles[this.newx+1.5,playery+1.5+i];
if (this.checktile in client.left45) {
this.onslope = 1;
this.slopeangle = -45;
playery = int(playery+i)-((this.newx+1.5)%1);
break;
} else if (this.checktile in client.right45) {
this.onslope = 1;
this.slopeangle = 45;
playery = int(playery-1+i)+((this.newx+1.5)%1);
break;
} else if (this.checktile in client.left225) {
this.onslope = 1;
this.slopeangle = -22.5;
this.slopepos = aindexof(this.checktile,client.left225)%2;
playery = int(playery+i)-(((this.newx+1.5)%1)/2)-(this.slopepos*.5);
break;
} else if (this.checktile in client.right225) {
this.onslope = 1;
this.slopeangle = 22.5;
this.slopepos = aindexof(this.checktile,client.right225)%2;
playery = int(playery-1+i)+(((this.newx+1.5)%1)/2)+(this.slopepos*.5);
break;
}
}
if (this.onslope == 1) {
//if (abs(this.slopeangle) == 45) this.velx += this.slopeangle > 0 ? this.friction : this.friction*-1;
this.slopespeed = abs(this.slopeangle) == 22.5 ? (this.runspeed - this.walkspeed)/2 : (this.runspeed - this.walkspeed)/4;
if (this.velx > 0 && this.slopeangle < 0 && this.velx > this.walkspeed && keydown(3)) this.velx = this.walkspeed + this.slopespeed;
else if (this.velx < 0 && this.slopeangle > 0 && this.velx < this.walkspeed*-1 && keydown(1)) this.velx = (this.walkspeed + this.slopespeed)*-1;
this.lastslope = timevar2;
}
if (i == 3+client.velocity_Y) {
if (this.onslope == 1) {
playery = int(playery);
for (i=0;i<3;i+=1/16) {
this.isslope = tiles[this.newx+1.5,playery+i] in client.left45 || tiles[this.newx,playery+2+i] in client.right45;
if (onwall(this.newx+1.5,playery+2+i) && this.isslope == 0) {
playery = int(playery-1+i);
break;
}
}
playery = int(playery+.5);
//if (i < 3) playery = int(playery);
}
if (this.onslope == 1) playery = int(playery+(this.velx < 0 ? .5 : 0));
this.onslope = 0;
}
}

function DoJumps() {
if (keydown(6)) {
if (this.JumpPressed == 0) {
if (client.velocity_Y == 0) {
client.JumpFrame = 8;
//this.slopeaddition = (((abs(this.velx)/this.runspeed).5)this.onslope);
//if (abs(this.slopeangle) == 22.5) this.slopeaddition = this.slopeaddition/2;
client.velocity_Y = ((7
1.9)/8)
-1; // - this.slopeaddition;
//client.velocity_Y = this.jumppower - ((abs(this.velx) >= this.runspeed-.1).1);
}
this.JumpPressed = 1;
}
// || timevar2 < this.enemyjumptimer+.4
//if (client.JumpFrame > 0 && client.JumpFrame < 8) client.velocity_Y -= (client.JumpFrame
.075 + (timevar2 < this.enemyjumptimer+.4)0.075);
if (client.JumpFrame > 0 && client.JumpFrame < 8) client.velocity_Y -= (client.JumpFrame
.075 + (timevar2 < client.enemyjumptimer)*0.075);
} else {
this.JumpPressed = 0;
client.JumpFrame = 0;
}
client.JumpFrame–;
if (client.JumpFrame < 0) client.JumpFrame = 0;

client.velocity_Y += client.gravity;
if (client.velocity_Y > client.maxfall) client.velocity_Y = client.maxfall;

if (client.velocity_Y >= 0) {
if (this.onslope == 1) {
client.velocity_Y = 0;
return;
}
} else this.onslope = 0;

CheckRoof();

for (i=0;i<abs(client.velocity_Y);i+=1/16) {
this.checkfloor = onwall2(playerx+.75,this.headpos+client.height-i,1.5,1/16) && client.velocity_Y < 0 &&
(int(tiles[playerx+.75,this.headpos+client.height+i]/512) >= 6 || int(tiles[playerx+.75+1.5,this.headpos+client.height+i]/512) >= 6);
if (int(tiles[playerx+.75,this.headpos+client.height+i]/512) >= 6 || int(tiles[playerx+.75+1.5,this.headpos+client.height+i]/512) >= 6) this.hitjumpthrough = 1;
else this.hitjumpthrough = 0;
if (this.checkfloor == 1) break;
}

//if (!onwall2(playerx+.75,this.headpos+client.height-1/16,1.5,client.velocity_Y+1/16)) {
if (!onwall2(playerx+.75,this.headpos+client.height-1/16,1.5,client.velocity_Y+1/16) || this.checkfloor == 1) {
if (this.hitroof == true && client.velocity_Y < 0) {
for (i=0;i<abs(client.velocity_Y);i+=1/16) {
if (onwall2(playerx+.75,this.headpos-i,1.5,1/16)) {
playery -= i;
playery = int(playery+.5);
break;
}
}
client.velocity_Y = abs(client.velocity_Y)/4;
client.JumpFrame = 0;
} else playery += client.velocity_Y;
} else {
if (client.velocity_Y => 0) {
for (i=0;i<client.velocity_Y+1/16;i+=1/16) {
if (onwall2(playerx+.75,this.headpos+client.height+i,1.5,1/16)) {
playery += i;
break;
}
}
if (i < client.velocity_Y) playery = int(playery+.5);
// HACKISH WAY TO AVOID PLAYERS GETTING STUCK HALFWAY THROUGH JUMP-THROUGHS WHEN HITTING FROM THE SIDE
else if (onwall2(playerx+.75+(this.velx/2),this.headpos+client.height-4/16,1.5,1/16) && this.hitjumpthrough == 1 && client.velocity_Y > 0) playery += .25;
client.velocity_Y = 0;
}
}
// HACKISH DETECTION TO ALLOW PLAYERS TO RUN OVER SMALL GAPS, REMOVE IF ABNORMAL FLOOR DETECTION OCCURS
if (onwall2(playerx+.75+(this.velx/2),this.headpos+client.height,1.5,1/16) && client.velocity_Y > 0 && abs(this.velx) => this.runspeed-.1) playery = int(playery);
}

function CheckRoof() {
this.hitroof = onwall2(playerx+.75,this.headpos-abs(client.velocity_Y),1.5,abs(client.velocity_Y));
if (this.hitroof == true) {
for (i=0;i<abs(client.velocity_Y);i+=1/16) {
if (onwall(playerx+.75+1.5,this.headpos-i) && int(tiles[playerx+.75+1.5,this.headpos-i]/512) >= 6 && int((tiles[playerx+.75+1.5,this.headpos-i]%512)/256) == 1) {
this.hitroof = false;
break;
} else if (onwall(playerx+.75+(1/16),this.headpos-i) && int(tiles[playerx+.75+(1/16),this.headpos-i]/512) >= 6 && int((tiles[playerx+.75+(1/16),this.headpos-i]%512)/256) == 1) {
this.hitroof = false;
break;
}
}
if (this.hitroof == true && client.velocity_Y < 0) {
this.checknpc = testnpc(playerx+.75+1.5,this.headpos-i);
if (this.checknpc >= 0) {
client.hitfrom = 3;
callnpc this.checknpc,washeadhit;
}
this.checknpc = testnpc(playerx+.75+(1/16),this.headpos-i);
if (this.checknpc >= 0) {
client.hitfrom = 1;
callnpc this.checknpc,washeadhit;
}
//callnpc testnpc(playerx+.75+1.5-.25,this.headpos-i),washeadhit;
//triggeraction playerx+.75+.25+(1/16),this.headpos-i,HeadBump,;
//triggeraction playerx+.75+1.5-.25,this.headpos-i,HeadBump,;
}
}
}[/php]

Do you want to recycle my Mega Graal movement with slopes?

That’s a really detailed movement system, Shiny. Mind if we use it and make a few adjustments of our own? It would be great if we could get Downsider’s as well.

If he didn’t want us to use it he wouldn’t have posted it.

@shiny
Riley had me try make your script work, everything works fine apart from slopes, how do you set the slopes and are the slope tiles meant to be blocking or non-blocking?
I tried client string arrays as well as making them internal but I had no luck…

You have to set them to one of the 4 arrays that determine their slope angle. Not a string array, since the in operator doesn’t work on string arrays. It doesn’t really matter what tile type they are as long as they are in the array, but you might want to consider what happens if you run into the slope from the OTHER side. Do you want to run through it, or stop?

yer I was declaring the arrays as the wrong type, thanks cyris :slight_smile:

Is this already dead?

Apparently we’re waiting for either scripter to finish the ‘core’ script.
riley might want to empty his inbox.

If this goes through it will be ‘derpin’ awesome!

Riley, I added you to my MSN, but I never see you on in the afternoon or the evening. Also, my midterms are this week so if you have anything, send them to my inbox.

[quote]Errors
The following errors occurred with your submission

RileyFiery has exceeded their stored private messages quota and cannot accept further messages until they clear some space.[/quote]
Yeah…
Pm me when you want me to do something.