Graal Forums  

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

 
 
Thread Tools Search this Thread Display Modes
Prev Previous Post   Next Post Next
  #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
 


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 12:34 PM.


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