Jump to content
Sign in to follow this  
DavidRM

Node.JS Game Loop

Recommended Posts

Here is my Smart Mobile Studio adaptation of the Node.JS "node-gameloop" module.

My primary modification was some code to make the *next* interval line up with the desired number of ms per tick. I also tightened the window for choosing SetTimeout vs SetImmediate.

I needed this to provide the beating heart of Paintball Net's simulation engine. Which, sure, only beats 10 times per second, but I wanted it to be as accurate as possible. 🙂

-David

unit UPBNGameGameLoop;

interface

uses 
  System.Types,
  System.Types.Convert,
  System.Time,
  System.Streams,
  System.Reader,
  System.Writer,
  System.Device.Storage,
  SmartNJ.System,
  SmartNJ.Streams,
  SmartNJ.Device.Storage,
  SmartNJ.Application,
  NodeJS.Core;

var
  GameLoopActive: boolean;

type
  TGameLoopCallback = procedure(delta: float);

procedure StartGameLoop(update: TGameLoopCallback; msInterval: integer);
procedure StopGameLoop;

implementation

procedure SetImmediate(const Entrypoint: TProcedureRef); external 'setImmediate';

const
  Seconds2Nano = 1e9;
  Nano2Seconds = 1 / Seconds2Nano;
  MS2Nano = 1e6;
  Nano2MS = 1 / MS2Nano;

var
  _loopProc: TGameLoopCallback;
  _loopLengthHR: float;
  _loopPrevHR, _loopTargetHR: float;

function GetHRTime: float;
var
  hrTime: Variant;
begin
// I need a more up-to-date node to use the bigint() function.
//  asm
//    @Result = process.hrtime.bigint();
//  end;
  asm
    @hrTime = process.hrtime();
  end;
  Result := (hrTime[0] * Seconds2Nano) + hrtime[1];
end;

procedure DoGameLoop;
var
  now, delta: float;
  remaining: integer;
begin
  if GameLoopActive then
  begin
    now := GetHRTime;
    if now >= _loopTargetHR then
    begin
      delta := now - _loopPrevHR;
      _loopPrevHR := now;
      _loopTargetHR := now + _loopLengthHR;
      if delta > _loopLengthHR then
      begin
        _loopTargetHR -= (delta - _loopLengthHR);
      end;
      _loopProc(delta * Nano2Seconds);
    end;
    if GameLoopActive then
    begin
      remaining := Trunc((_loopTargetHR - GetHRTime) * Nano2MS);
      if remaining >= 4 {16} then
        SetTimeout(DoGameLoop, remaining)
      else
        SetImmediate(DoGameLoop);
    end;
  end;
end;

procedure StartGameLoop(update: TGameLoopCallback; msInterval: integer);
begin
  if not GameLoopActive then
  begin
    GameLoopActive := True;
    _loopLengthHR := msInterval * MS2Nano;
    _loopProc := update;
    _loopPrevHR := GetHRTime;
    _loopTargetHR := _loopPrevHR;
    SetImmediate(DoGameLoop);
  end;
end;

procedure StopGameLoop;
begin
  GameLoopActive := False;
  _loopProc := nil;
end;

end.

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×