Graal Forums  

Go Back   Graal Forums > Development Forums > NPC Scripting > Code Gallery
FAQ Members List Calendar Search Today's Posts Mark Forums Read

Reply
 
Thread Tools Search this Thread Display Modes
  #1  
Old 01-09-2011, 01:29 AM
12171217 12171217 is offline
Banned
Join Date: Jan 2009
Posts: 453
12171217 has a spectacular aura about
Interpolation

Hi. Interpolation is basically the concept of taking known values in a linear path and figuring out the points in between. In games, it generally makes things look a lot smoother. Most all FPS games interpolate other players and stuff, so I figured I'd implement it in Graal for fun. I don't have my desktop PC, as the machine took a kick to the case (literally) and the graphics card got smashed up, so I can't test if this works or what anymore. It worked last time I checked, a few weeks ago on Delteria, but I can't tell for sure if it's been touched since.

Anyway, observe another player walking around, add this script to yourself, and press Z to toggle it on and off and observe the difference. They'll be walking a lot smoother after you activate it, especially if they disable UDP. If the boxes around the player make it difficult to tell the difference, comment out the showpoly calls from the script.

IF YOU WANT TO INTERPOLATE NPCS READ THIS

It can also be used on NPCs so you can move them normally (setting their X/Y coordinates) and not have to worry about the move() command (the client will automatically smooth out the NPC's movement) and how annoying move() is to use. Just increment attr[9] every frame like so: attr[9] = (attr[9]+1)%200; on the level NPC and the system NPC in the player will do the rest. It's useful for baddies that move on a .1 or even a variable timeout (To reduce server stress, on Delteria, I had baddies on a .4 timeout, which got smaller as the player got closer to the baddy, to ensure that it would remain responsive).

If anybody wants to implement this on their server, be warned that the other players appear anywhere from 1 - (their ping/20) frames in the past, since interpolation uses values that are already known and thus "old" by a 20th of a second or so. It won't matter with NPCs, but I'm sure some players would blame losing a spar or event on it.

Any questions can be asked below :3

PHP Code:
//#CLIENTSIDE
function onPlayerEnters() {
  
onTimeOut();
}

function 
onTimeOut() {
  
setTimer(.05);
  
  
hideimgs(300this.img);
  
this.img 400;
  
  if (
this.on) {
    if (
this.oTimerVar == 0this.oTimeVar timevar2-.05;
    
this.frameTime timevar2-this.oTimeVar;
    
this.oTimeVar timevar2;
  
    
checkLevelChange();
  
    
saveOldPos();
    
saveOldPosNPCs();
  
    
lerpPlayers();
    
lerpNPCs();
    
    
this.frames++;
    
this.hAvgReal += this.realUpdates;
    
this.hAvgProjected += this.projectedUpdates;
    if (
this.frames >= 20) {
      
this.avgReal this.hAvgReal/20;
      
this.avgProjected this.hAvgProjected/20;
      
this.hAvgReal 0;
      
this.hAvgProjected 0;
      
this.frames 0;
    }
    
showtext(3010screenheight-48"Arial"null"Real updates: " this.realUpdates);
    
changeimgcolors(3011101);
    
findimg(301).layer 5;
    
findimg(301).zoom .7;
    
showtext(3020screenheight-32"Arial"null"Projected updates: " this.projectedUpdates);
    
changeimgcolors(3020101);
    
findimg(302).layer 5;
    
findimg(302).zoom .7;
    
showtext(3030screenheight-80"Arial"null"Average real updates: " this.avgReal);
    
changeimgcolors(3031101);
    
findimg(303).layer 5;
    
findimg(303).zoom .7;
    
showtext(3040screenheight-64"Arial"null"Average projected updates: " this.avgProjected);
    
changeimgcolors(3040101);
    
findimg(304).layer 5;
    
findimg(304).zoom .7;
  }
  
  if (
this.on == 2) {
    
showtext(3000screenheight-16"Arial"null"Cosine Interpolation on");
    
changeimgcolors(3000101);
  } else if (
this.on == 1) {
    
showtext(3000screenheight-16"Arial"null"Linear Interpolation on (Best)");
    
changeimgcolors(3000101);
  } else if (
this.on == 0) {
    
showtext(3000screenheight-16"Arial"null"All Interpolation off (Graal Default)");
    
changeimgcolors(3001001);
  }
  
findimg(300).layer 5;
  
findimg(300).zoom .7;
}

function 
onKeyPressed(keycodekey) {
  if (
key == "z") {
    
this.on++;
    if (
this.on 1) {
      
this.on 0;
    }
  }
}

function 
checkLevelChange() {
  if (
player.level.name != this.oldLevel) {
    for (
pl players) {
      
pl.lerp 0;
      
pl.oldX pl.newX pl.x;
      
pl.oldY pl.newY pl.y;
    }
    for (
npcs) {
      
n.lerp 0;
      
n.oldX n.newX n.x;
      
n.oldY n.newY n.y;
    }
    
this.oldLevel player.level.name;
  }
}

function 
saveOldPos() {
  
this.realUpdates 0;
  for (
pl players) {
    if (
pl == player) continue;
    if (
pl.lerp 0) continue;
    
temp.update false;
    if (
pl.!= pl.oldX) {
      
pl.oldX pl.newX;
      
pl.newX pl.x;
      
temp.update true;
    }
    if (
pl.!= pl.oldY) {
      
pl.oldY pl.newY;
      
pl.newY pl.y;
      
temp.update true;
    }
    if (
temp.update) {
      
//Get distance on each axis; Lerping very small values
      //looks awful.
      
temp.distX abs(pl.newX-pl.oldX);
      
temp.distY abs(pl.newY-pl.oldY);
      
this.realUpdates++;
      if ((
temp.distX .25 || temp.distY .25) && (temp.distX && temp.distY 2)) {
        
pl.doLerp true;
        
pl.oldPacketTime pl.newPacketTime;
        
pl.newPacketTime timevar2;
        
pl.lerpDiv pl.newPacketTime-pl.oldPacketTime;
        
pl.lerp 1;
      } else {
        
pl.doLerp false;
      }
    }
  }
}

function 
saveOldPosNPCs() {
  
this.realUpdates 0;
  for (
npcs) {
    if (
n.attr[9] == 0) continue;
    
temp.update false;
    if (
n.attr[9] != n.oldAttr) {
      
n.oldX n.newX;
      
n.newX n.x;
      
n.oldY n.newY;
      
n.newY n.y;
      
n.oldZ n.newZ;
      
n.newZ n.z;
      
n.oldAttr n.attr[9];
      
temp.update true;
    } else {
      continue;
    }
    if (
temp.update) {
      
//Get distance on each axis; Lerping very small values
      //looks awful.
      
temp.distX abs(n.newX-n.oldX);
      
temp.distY abs(n.newY-n.oldY);
      
this.realUpdates++;
      
n.oldPacketTime n.newPacketTime;
      
n.newPacketTime timevar2;
      
temp.div n.newPacketTime-n.oldPacketTime;
      if (
temp.div 1) continue;
      
n.doLerp true;
      
n.lerpDiv 0.05/(temp.div);
      
n.lerp 1;
      continue;
    } else {
      
n.doLerp false;
      continue;
    }
  }
}

function 
lerpPlayers() {
  
this.projectedUpdates 0;
  for (
pl players) {
    if (
pl == player) continue;
    
showpoly(this.img, {pl.x+.5pl.ypl.x+2.5pl.y});
    
changeimgcolors(this.img1001);
    
this.img++;
    
showpoly(this.img, {pl.x+2.5pl.ypl.x+2.5pl.y+3});
    
changeimgcolors(this.img1001);
    
this.img++;
    
showpoly(this.img, {pl.x+2.5pl.y+3pl.x+.5pl.y+3});
    
changeimgcolors(this.img1001);
    
this.img++;
    
showpoly(this.img, {pl.x+.5pl.y+3pl.x+.5pl.y});
    
changeimgcolors(this.img1001);
    
this.img++;
    if (!
pl.doLerp) continue;
    if (
pl.lerp >= -.5) {
      
this.projectedUpdates++;
      
pl.lerp -= .5;
      if (
this.on == 1) {
        
pl.linearInterpolateValue(pl.oldXpl.newXabs(1-pl.lerp));
        
pl.linearInterpolateValue(pl.oldYpl.newYabs(1-pl.lerp));
      } else if (
this.on == 2) {
        
pl.cosineInterpolateValue(pl.oldXpl.newXabs(1-pl.lerp));
        
pl.cosineInterpolateValue(pl.oldYpl.newYabs(1-pl.lerp));
      }
    }
    
showpoly(this.img, {pl.x+.5pl.ypl.x+2.5pl.y});
    
changeimgcolors(this.img0101);
    
this.img++;
    
showpoly(this.img, {pl.x+2.5pl.ypl.x+2.5pl.y+3});
    
changeimgcolors(this.img0101);
    
this.img++;
    
showpoly(this.img, {pl.x+2.5pl.y+3pl.x+.5pl.y+3});
    
changeimgcolors(this.img0101);
    
this.img++;
    
showpoly(this.img, {pl.x+.5pl.y+3pl.x+.5pl.y});
    
changeimgcolors(this.img0101);
    
this.img++;
  }
}

function 
lerpNPCs() {
  
this.projectedUpdates 0;
  for (
npcs) {
    if (
n.attr[9] == 0) continue;
    if (!
n.doLerp) continue;
    if (
n.lerpDiv == 0n.lerpDiv .5;
    if (
n.lerp >= 0) {
      
this.projectedUpdates++;
      
n.lerp -= n.lerpDiv*(this.frameTime*20);
      if (
this.on == 1) {
        
n.linearInterpolateValue(n.oldXn.newXabs(1-n.lerp));
        
n.linearInterpolateValue(n.oldYn.newYabs(1-n.lerp));
        
n.linearInterpolateValue(n.oldZn.newZabs(1-n.lerp));
      } else if (
this.on == 2) {
        
n.cosineInterpolateValue(n.oldXn.newXabs(1-n.lerp));
        
n.cosineInterpolateValue(n.oldYn.newYabs(1-n.lerp));
        
n.cosineInterpolateValue(n.oldZn.newZabs(1-n.lerp));
      }
    }
    
/*
    showpoly(this.img, {n.x+.5, n.y, n.x+2.5, n.y});
    changeimgcolors(this.img, 0, 1, 0, 1);
    this.img++;
    showpoly(this.img, {n.x+2.5, n.y, n.x+2.5, n.y+3});
    changeimgcolors(this.img, 0, 1, 0, 1);
    this.img++;
    showpoly(this.img, {n.x+2.5, n.y+3, n.x+.5, n.y+3});
    changeimgcolors(this.img, 0, 1, 0, 1);
    this.img++;
    showpoly(this.img, {n.x+.5, n.y+3, n.x+.5, n.y});
    changeimgcolors(this.img, 0, 1, 0, 1);
    this.img++;
    */
  
}
}

function 
linearInterpolateValue(temp.ovtemp.nvtemp.l) {
  return (
temp.ov*(1-temp.l)+temp.nv*temp.l);
}

function 
cosineInterpolateValue(temp.ovtemp.nytemp.l) {
  
temp.newLerp = (1-cos(temp.l*pi))/2;
  return (
temp.ov*(1-temp.newLerp)+temp.ny*temp.newLerp);

Please note this was a test script that was written rather hastily to see what interpolation would be like in Graal. If you want to have interpolation for NPCs/baddies or players on your server, use this as reference, but don't use this without modification.

EDIT: I didn't mean to put this in code gallery. It's not organized or structured well enough to be a finished product, I just wanted to share the concept of interpolation and how it works, while giving an example.
Reply With Quote
  #2  
Old 01-09-2011, 01:39 AM
fowlplay4 fowlplay4 is offline
team canada
fowlplay4's Avatar
Join Date: Jul 2004
Location: Canada
Posts: 5,200
fowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond repute
Any recommended documents (aimed towards game development) to read on this particular subject?
__________________
Quote:
Reply With Quote
  #3  
Old 01-09-2011, 01:44 AM
12171217 12171217 is offline
Banned
Join Date: Jan 2009
Posts: 453
12171217 has a spectacular aura about
Quote:
Originally Posted by fowlplay4 View Post
Any recommended documents (aimed towards game development) to read on this particular subject?
How to Network like a Boss, by Valve Software:
http://developer.valvesoftware.com/w...yer_Networking

FPS-independent movement, interpolation, extrapolation, storing old player frames and stepping into the past to check for paradoxes between players, movement prediction all covered there. One of my favorite articles for networking in games
Reply With Quote
  #4  
Old 01-09-2011, 02:03 AM
Crono Crono is offline
:pluffy:
Join Date: Feb 2002
Location: Sweden
Posts: 20,000
Crono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond repute
do you know downsider?
__________________
Reply With Quote
  #5  
Old 01-09-2011, 02:06 AM
12171217 12171217 is offline
Banned
Join Date: Jan 2009
Posts: 453
12171217 has a spectacular aura about
Quote:
Originally Posted by Crono View Post
do you know downsider?
but.. i am downsider..
Reply With Quote
  #6  
Old 01-09-2011, 06:24 PM
MrOmega MrOmega is offline
One More Time
MrOmega's Avatar
Join Date: Aug 2010
Location: TN, USA
Posts: 631
MrOmega is an unknown quantity at this point
Send a message via AIM to MrOmega Send a message via MSN to MrOmega Send a message via Yahoo to MrOmega
Just wondering the actual performance benefits of this, but if the server guesses where the player will be and he isn't there, does the server take steps to correct this to make it smooth or do it make them 'choppy' even for this correction wouldn't it cause higher lag and Client1->Server->Client2 lag?
__________________
Time is the fire in which we burn...
Up, Up, Down, Down, Left, Right, Left, Right, B, A, Select, Start! Now I got 99 LIVES!!!
Reply With Quote
  #7  
Old 01-09-2011, 06:40 PM
12171217 12171217 is offline
Banned
Join Date: Jan 2009
Posts: 453
12171217 has a spectacular aura about
Quote:
Originally Posted by MrOmega View Post
Just wondering the actual performance benefits of this, but if the server guesses where the player will be and he isn't there, does the server take steps to correct this to make it smooth or do it make them 'choppy' even for this correction wouldn't it cause higher lag and Client1->Server->Client2 lag?
I haven't gone as in depth for that much yet. I only do the visual aspect right now. The server doesn't guess where the player will be, and neither does the client. The client knows the old position and the new position, and uses this to find out what could be in between. You really can't have any large error because of that. With extrapolation, the errors are large and obvious (Unless the player accelerates and decelerates slowly, Halo uses extrapolation), which is why I chose to implement interpolation in Graal, where you can change direction instantly.

The "actual performance benefits" should be large, since you can run an NPC on a .5 timeout and have it look just as good as a clientside .05 timeout. The server never "guesses where the player is" in this implementation as doing what the Valve article suggests would require an entire systems rewrite. Classic could implement prediction and interpolation, along with the history-stepping and stuff, with relative ease.
Reply With Quote
  #8  
Old 01-09-2011, 09:30 PM
fowlplay4 fowlplay4 is offline
team canada
fowlplay4's Avatar
Join Date: Jul 2004
Location: Canada
Posts: 5,200
fowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond repute
Well I just implemented the player interpolation (on by default) on Zodiac. I am quite impressed with the differences, nice work.

For those interested in the differences, toggle it: /toggle interpolation
__________________
Quote:
Reply With Quote
  #9  
Old 01-10-2011, 06:44 AM
12171217 12171217 is offline
Banned
Join Date: Jan 2009
Posts: 453
12171217 has a spectacular aura about
Quote:
Originally Posted by fowlplay4 View Post
Well I just implemented the player interpolation (on by default) on Zodiac. I am quite impressed with the differences, nice work.

For those interested in the differences, toggle it: /toggle interpolation
Fantastic. Glad to have been of service

There also might be issues with transferring levels/off and on gmaps, I hadn't tested extensively enough to know. Look out for stuff like that.
Reply With Quote
  #10  
Old 01-11-2011, 12:46 AM
MrOmega MrOmega is offline
One More Time
MrOmega's Avatar
Join Date: Aug 2010
Location: TN, USA
Posts: 631
MrOmega is an unknown quantity at this point
Send a message via AIM to MrOmega Send a message via MSN to MrOmega Send a message via Yahoo to MrOmega
Using this on my server, do you want 12171217 or Donwsider in my Contributors sections? :P
__________________
Time is the fire in which we burn...
Up, Up, Down, Down, Left, Right, Left, Right, B, A, Select, Start! Now I got 99 LIVES!!!
Reply With Quote
  #11  
Old 03-06-2011, 04:08 AM
cbk1994 cbk1994 is offline
the fake one
cbk1994's Avatar
Join Date: Mar 2003
Location: San Francisco
Posts: 10,718
cbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond repute
Send a message via AIM to cbk1994
Added this to Era, enabled by default. You can disable it in the options menu.
__________________
Reply With Quote
  #12  
Old 03-06-2011, 04:57 AM
Crono Crono is offline
:pluffy:
Join Date: Feb 2002
Location: Sweden
Posts: 20,000
Crono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond repute
on zodiac if you go into a dungeon and the person hasn't moved yet you don't see them in their real positions
__________________
Reply With Quote
  #13  
Old 03-06-2011, 06:36 AM
DustyPorViva DustyPorViva is offline
Will work for food. Maybe
DustyPorViva's Avatar
Join Date: Sep 2003
Location: Maryland, USA
Posts: 9,589
DustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond repute
Send a message via AIM to DustyPorViva Send a message via MSN to DustyPorViva
Quote:
Originally Posted by Crono View Post
on zodiac if you go into a dungeon and the person hasn't moved yet you don't see them in their real positions
Should be an easy fix. Just make it interpolate when a previous position is known.
Reply With Quote
  #14  
Old 03-07-2011, 05:53 AM
fowlplay4 fowlplay4 is offline
team canada
fowlplay4's Avatar
Join Date: Jul 2004
Location: Canada
Posts: 5,200
fowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond repute
Quote:
Originally Posted by Crono View Post
on zodiac if you go into a dungeon and the person hasn't moved yet you don't see them in their real positions
Going need instructions to replicate it, because I've been unsuccessful.

Also... Plug-n-play Player Movement Interpolation system. Obviously customize isOn() to suit your server's needs.

PHP Code:
//#CLIENTSIDE

function onPlayerEnters() {
  if (
isOn()) {
    
checkLevelChange();
    
onTimeout();
  }
}

function 
isOn() {
  return !
client.option_disableinterpolation;
}

function 
onTimeout() {
  if (
isOn()) {
    if (
this.oTimerVar == 0this.oTimeVar timevar2-.05;
    
this.frameTime timevar2-this.oTimeVar;
    
this.oTimeVar timevar2;
    
saveOldPos();
    
lerpPlayers();
    
setTimer(0.05);
  }
}

function 
checkLevelChange() {
  if (
player.level.name != this.oldLevel) {
    for (
pl players) {
      
pl.lerp 0;
      
pl.oldX pl.newX pl.x;
      
pl.oldY pl.newY pl.y;
    }
    
this.oldLevel player.level.name;
    
this.forceupdate true;
  }
}

function 
saveOldPos() {
  for (
pl players) {
    if (
pl == player) continue;
    if (
pl.lerp 0) continue;
    
temp.update this.forceupdate;
    if (
pl.!= pl.oldX) {
      
pl.oldX pl.newX;
      
pl.newX pl.x;
      
temp.update true;
    }
    if (
pl.!= pl.oldY) {
      
pl.oldY pl.newY;
      
pl.newY pl.y;
      
temp.update true;
    }
    if (
temp.update) {
      
temp.distX abs(pl.newX-pl.oldX);
      
temp.distY abs(pl.newY-pl.oldY);
      if ((
temp.distX .25 || temp.distY .25) && (temp.distX && temp.distY 2)) {
        
pl.doLerp true;
        
pl.oldPacketTime pl.newPacketTime;
        
pl.newPacketTime timevar2;
        
pl.lerpDiv pl.newPacketTime-pl.oldPacketTime;
        
pl.lerp 1;
      } else {
        
pl.doLerp false;
      }
    }
  }
  if (
this.forceupdatethis.forceupdate false;
}

function 
lerpPlayers() {
  for (
pl players) {
    if (
pl == player) continue;
    if (!
pl.doLerp) continue;
    if (
pl.lerp >= -.5) {
      
pl.lerp -= .5;
      
pl.linearInterpolateValue(pl.oldXpl.newXabs(1-pl.lerp));
      
pl.linearInterpolateValue(pl.oldYpl.newYabs(1-pl.lerp));
    }
  }
}

function 
linearInterpolateValue(temp.ovtemp.nvtemp.l) {
  return (
temp.ov*(1-temp.l)+temp.nv*temp.l);

__________________
Quote:
Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 06:22 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2021, vBulletin Solutions Inc.
Copyright (C) 1998-2019 Toonslab All Rights Reserved.