Page 1 of 2 12 LastLast
Results 1 to 10 of 13

Thread: Supraleiter - an anti-gravity futuristic racing indie game

  1. #1

    Supraleiter - an anti-gravity futuristic racing indie game

    Hello guys,

    i present to you my anti-gravity futuristic arcade-style racing indie game with the name "Supraleiter", which I'm developing in a team with five peoples, where I'm the lead programmer.

    It's in a heavily development phase.

    It's implemented fully in Object Pascal (compilable with Delphi at version 7 and up and FreePascal at version 2.6.2 and up) including own 3D game engine, own 3D HRTF stereo sound rendering engine, own 3D rigidbody physics engine, own truetype font rendering engine with hinting-bytecode support, etc.

    All of the game is own 100% native Object Pascal code, only with one exception, that the framework uses SDL2 as OS-API-abstraction layer.

    Supraleiter has in its current state 219745 lines of code, including our own-developed libraries, e.g. 3D graphics engine, 3D physics engine, audio engine, and so on. Supraleiter is almost free from third-party-libraries, except of SDL 2.0, which we use it as a system API abstraction layer.

    So it's a not yet another Unity or Unreal anti-gravity racing game, rather it's a anti-gravity racing game, where the all code including the engine and the framework (with SDL2-backend) is own code by the developers of the game itself, so by me (95% of the code) and by my co-worker (5% of the code). This makes the game different from other indie anti-gravity racing game, which are using often third game engines and frameworks.

    The team on this game has 5 members, where the most of us are also demosceners, for example I'm a farbrausch demogroup member, as you maybe know. The team members at this indie game are:

    - BeRo (this is me :-), Lead programmer)
    - red (Programmer for additional code)
    - brainbox (2D and 3D graphics)
    - h2o (mainly 2D graphics)
    - wotw (music and sound)

    It will be available in native 32-bit and 64-bit builds for Windows and Linux, but NOT for MacOSX, since MacOSX doesn't have support for newer OpenGL versions than 4.1, and Supraleiter needs OpenGL 4.3, because Supraleiter uses a lot of compute shaders, for to offload a lot of the work from the CPU to the GPU, and also not for mobile platforms, because it uses real time global illumination etc. technologies, which would be for mobile GPUs too much overkill. So a NVIDIA Geforce GTX660 / AMD Readon HD7850 is the playable minimum here, provided that AMD fix the bugs in their super buggy drivers, so that Supraleiter incl. the OpenGL 4.3 compute shader stuff can run stably on the AMD GPUs, otherwise it will be NVIDIA-only in the first time, until AMD have fixed your graphics drivers.

    Feature list:

    • Physically based shading pipeline
    • Purely HDR rendering pipeline
    • Real Time global illumination, so no precalculated light maps etc.
    • Real Time volumetric scattering
    • Real Time sky rendering (so a real time dynamic day & night cycle is possible)
    • Mostly physically correct depth of field
    • Plausible motion blur
    • Own physics engine called Kraft ( https://github.com/BeRo1985/kraft/ ), with working continous collision detection and response, which is the preferable method for fast games to avoid tunneling (missed collisions when objects are too fast), where the base discrete physics rate can however be comparatively still low at 120 Hz.
    • The AI is neural network based, that means the AI is able to learn, but this learn-feature is currently disabled due to some performance issues during the ”neural network training” while a game is running, it will be re-enabled as soon as we find a solution for this performance problem. Possibly i can fix it by training the neural network after the game has finished and not while the game is running. In any case the AI will learn, how good it will become depends on your own driving skills. But in a nutshell, the AI is not hardcoded, but for debugging comparison purposes i added an additional hardcoded AI. Here is an additional detail information for interested people: We’re using feed-forward neural networks, so one of the simplest neural network types.
    • Support for gamepad controllers
    • Multithreaded code design (job-based)
    • and other things that I have forgotten to mention here.


    And now some videos




    and more on the youtube playlist at https://www.youtube.com/playlist?lis...Oak3nsC4UoaC4-

    And at the Revision 2015 demo party was a talk about the Supraleiter rendering technologies:



    and the slides as PDF http://rootserver.rosseaux.net/demos...chnologies.pdf

    BeRo

  2. #2
    Quote Originally Posted by BeRo View Post
    The AI is neural network based, that means the AI is able to learn, but this learn-feature is currently disabled due to some performance issues during the ”neural network training” while a game is running, it will be re-enabled as soon as we find a solution for this performance problem. Possibly i can fix it by training the neural network after the game has finished and not while the game is running. In any case the AI will learn, how good it will become depends on your own driving skills. But in a nutshell, the AI is not hardcoded, but for debugging comparison purposes i added an additional hardcoded AI. Here is an additional detail information for interested people: We’re using feed-forward neural networks, so one of the simplest neural network types.
    Would you be willing to share more information about this AI learning process. I'm nowhere expert in neural networked based AI as I have so far read only one article about this topic a few years ago.
    But I'm mostly interested to know what information does the AI gather and how is that information then processed.

    Quote Originally Posted by BeRo View Post
    support for gamepad controllers
    Have you considered implementing mouse support as additional option. There are several games like Live For Speed or Euro Truck Simulator 2 that allows you to use mouse as a way to somehow simulate controller or in case of mentioned games the steering wheel.
    Yes I know that it is best in having of actual controller but for people who don't own one like me having mouse support is the next best thing.

  3. #3
    The FFNN-based AI is very simple. It's no big magic against it. The AI has the following inputs (mainly raycast distances):

    Code:
    {$ifdef FewerRays}
          CountSensorRays=9;
          CountSensorOpponentRays=5;
    {$else}
          CountSensorWallRays=19;
    
          CountSensorFloorRays=3;
    
          CountSensorCeilRays=3;
    
          CountSensorOpponentRays=36;
    {$endif}
    
         PGameShipSensors=^TGameShipSensors;
         TGameShipSensors=record
          WayPointAngle:single;
          Speed:single;
    {$ifdef FewerRays}
          Rays:array[0..CountSensorRays-1] of single;
    {$else}
          WallRays:array[0..CountSensorWallRays-1] of single;
          FloorRays:array[0..CountSensorFloorRays-1] of single;
          CeilRays:array[0..CountSensorCeilRays-1] of single;
    {$endif}
          OpponentRays:array[0..CountSensorOpponentRays-1] of single;
         end;
    and the following output:

    Code:
         PGameShipControllerInput=^TGameShipControllerInput;
         TGameShipControllerInput=record
          Steering:single;
          Pitch:single;
          Accelerate:single;
          AirBrakeLeft:single;
          AirBrakeRight:single;
          Nitro:single;
          Fire:single;
         end;
    All input & output values are in the 0.0 .. 1.0 range, which'll converted to or from the -1.0 .. 1.0 range, depending of the input/output value kind.

    Code:
    procedure TGameShip.UpdateAI(DeltaTime:double);
    var Counter,Index:longint;
        AnalogControlLowPassFilterCoef:single;
    begin
     if TGame(Game).Started then begin
    
      AnalogControlLowPassFilterCoef:=1.0-exp(-(DeltaTime*100.0));
    
      FFNN.Inputs[0]:=Sensors.WayPointAngle;
      FFNN.Inputs[1]:=Sensors.Speed;
      Index:=2;
    {$ifndef FewerRays}
      for Counter:=0 to CountSensorWallRays-1 do begin
       FFNN.Inputs[Index]:=Sensors.WallRays[Counter];
       inc(Index);
      end;
      for Counter:=0 to CountSensorFloorRays-1 do begin
       FFNN.Inputs[Index]:=Sensors.FloorRays[Counter];
       inc(Index);
      end;
      for Counter:=0 to CountSensorCeilRays-1 do begin
       FFNN.Inputs[Index]:=Sensors.CeilRays[Counter];
       inc(Index);
      end;
    {$endif}
      for Counter:=0 to CountSensorOpponentRays-1 do begin
       FFNN.Inputs[Index]:=Sensors.OpponentRays[Counter];
       inc(Index);
      end;
    
      FFNN.Calculate;
    
      ControllerInput.Steering:=FloatLerp(ControllerInput.Steering,Min(Max((FFNN.Outputs[0]*2.0)-1.0,-1.0),1.0),AnalogControlLowPassFilterCoef);
      ControllerInput.Accelerate:=FloatLerp(ControllerInput.Accelerate,Min(Max((FFNN.Outputs[1]*2.0)-1.0,-1.0),1.0),AnalogControlLowPassFilterCoef);
      ControllerInput.Pitch:=FloatLerp(ControllerInput.Pitch,Min(Max((FFNN.Outputs[2]*2.0)-1.0,-1.0),1.0),AnalogControlLowPassFilterCoef);
      ControllerInput.AirBrakeLeft:=Min(Max(FFNN.Outputs[3],0.0),1.0);
      ControllerInput.AirBrakeRight:=Min(Max(FFNN.Outputs[4],0.0),1.0);
    
     end else begin
    
      FillChar(ControllerInput,SizeOf(TGameShipControllerInput),#0);
    
     end;
    end;
    So the FFNN-based AI simulates just human analog-controller input. And the training code is:

    Code:
    procedure TrainAI;
    const TeachIterations=1024;
    var i,Inputs,Hidden,Outputs,TeachIteration,Index,SubIndex,TimeStatesIndex,i32,
        Counter:longint;
        TimeStates:array of TGameShipTimeStates;
        TimeState:PGameShipTimeState;
        pn,fn:ansistring;
        fs:TFileStream;
        FFNN:TGameFFNN;
    begin
     FFNN:=nil;
     TimeStates:=nil;
     try
    {$ifdef FewerRays}
      Inputs:=2+CountSensorRays+CountSensorOpponentRays;
    {$else}
      Inputs:=2+CountSensorWallRays+CountSensorFloorRays+CountSensorCeilRays+CountSensorOpponentRays;
    {$endif}
      Outputs:=3;
      Hidden:=(Inputs+Outputs)*4;
      FFNN:=TGameFFNN.Create([Inputs,Hidden,Hidden,Outputs],0.3,0.9);
      SetLength(TimeStates,0);
      pn:=IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)))+'aitrainingdata';
      if DirectoryExists(pn) then begin
       pn:=IncludeTrailingPathDelimiter(pn);
       i:=0;
       repeat
        fn:=pn+'dataset'+IntToStr(i)+'.bin';
        if FileExists(fn) then begin
         inc(i);
        end else begin
         break;
        end;                            
       until i>$40000000;
       SetLength(TimeStates,i);
       i:=0;
       repeat
        fn:=pn+'dataset'+IntToStr(i)+'.bin';
        if FileExists(fn) then begin
         if (i+1)>length(TimeStates) then begin
          SetLength(TimeStates,i+1);
         end;
         fs:=TFileStream.Create(fn,fmOpenRead);
         try
          if fs.Read(i32,SizeOf(longint))=SizeOf(longint) then begin
           TimeStates[i].Count:=Max(0,i32);
           SetLength(TimeStates[i].Items,TimeStates[i].Count);
           if TimeStates[i].Count>0 then begin
            for Index:=0 to TimeStates[i].Count-1 do begin
             fs.Read(TimeStates[i].Items[Index].Sensors,SizeOf(TGameShipSensors));
             fs.Read(TimeStates[i].Items[Index].ControllerInput,SizeOf(TGameShipControllerInput));
            end;
           end;
          end;
         finally
          fs.Free;
         end;
         inc(i);
        end else begin
         break;
        end;
       until i>$40000000;
       for TeachIteration:=1 to TeachIterations do begin
        write(#13,'Training AI... ',(TeachIteration/TeachIterations)*100.0:6:2,' %');
        for TimeStatesIndex:=0 to length(TimeStates)-1 do begin
         for Index:=0 to TimeStates[TimeStatesIndex].Count-1 do begin
          TimeState:=@TimeStates[TimeStatesIndex].Items[Index];
          begin
           FFNN.Inputs[0]:=TimeState^.Sensors.WayPointAngle;
           FFNN.Inputs[1]:=TimeState^.Sensors.Speed;
           SubIndex:=2;
    {$ifdef FewerRays}
           for Counter:=0 to CountSensorRays-1 do begin
            FFNN.Inputs[SubIndex]:=TimeState^.Sensors.Rays[Counter];
            inc(SubIndex);
           end;
    {$else}
           for Counter:=0 to CountSensorWallRays-1 do begin
            FFNN.Inputs[SubIndex]:=TimeState^.Sensors.WallRays[Counter];
            inc(SubIndex);
           end;
           for Counter:=0 to CountSensorFloorRays-1 do begin
            FFNN.Inputs[SubIndex]:=TimeState^.Sensors.FloorRays[Counter];
            inc(SubIndex);
           end;
           for Counter:=0 to CountSensorCeilRays-1 do begin
            FFNN.Inputs[SubIndex]:=TimeState^.Sensors.CeilRays[Counter];
            inc(SubIndex);
           end;
    {$endif}
           for Counter:=0 to CountSensorOpponentRays-1 do begin
            FFNN.Inputs[SubIndex]:=TimeState^.Sensors.OpponentRays[Counter];
            inc(SubIndex);
           end;
          end;
          FFNN.Calculate;
          begin
           FFNN.DesiredOutputs[0]:=Min(Max((TimeState^.ControllerInput.Steering+1.0)*0.5,0.0),1.0);
           FFNN.DesiredOutputs[1]:=Min(Max((TimeState^.ControllerInput.Accelerate+1.0)*0.5,0.0),1.0);
           FFNN.DesiredOutputs[2]:=Min(Max((TimeState^.ControllerInput.Pitch+1.0)*0.5,0.0),1.0);
           FFNN.DesiredOutputs[3]:=Min(Max(TimeState^.ControllerInput.AirBrakeLeft,0.0),1.0);
           FFNN.DesiredOutputs[4]:=Min(Max(TimeState^.ControllerInput.AirBrakeRight,0.0),1.0);
          end;
          FFNN.BackPropagate;
         end;
        end;
        write(#13,'Training AI...             ');
        write(#13,'Training AI... ');
        fn:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)))+'assets')+'aibrain.bin';
        fs:=TFileStream.Create(fn,fmCreate);
        try
         FFNN.SaveToStream(fs);
        finally
         fs.Free;
        end;
       end;
      end;
     finally
      FFNN.Free;
      SetLength(TimeStates,0);
     end;
    end;
    Overall very simple, but it works very well for the small size and simplicity of the code, since the FFNN-based AI is driving almost without any accident and in a very human-style behaviour way (since the recorded training datasets are by-human-drived track rounds).

    And Supraleiter supports already most all input possibles, keyboard, gamepad controller, touchscreen (but due to a SDL 2.0 bug, only under Win7, with Win8(.1) has SDL 2.0 some problems yet, due to some small multitouch system API changes from Win7 to Win, accelometer, mouse, touchpad, etc. etc. etc,.

  4. #4
    Why are you saving the AI information in so many files (1073741824)?
    I'm willing to bet that opening and closing so many file handles is actually the biggest deadlock.
    Wouldn't it be better if you would have all this data saved in a single file instead?

  5. #5
    You should read the code again and then this time better 1073741824 is only the theoretical maximum count, see the "break;" in the if-FileExists-else-branch.

    Every training data set file is a single own game total round, and these files can by different human players from the developer team, so the training routine searchs all data set files in the input directory and then processes these one after the other many times (<= TeachIterations).

    Did you understand it now?

  6. #6
    Hi Benjamin,

    Nice nice work ! Impressive !

    If I must keep one thing, it is the tremendous sky rendering. Optical camera effect and sun burn is just fabulous.

    I will check Kraft in a short time (I assume it is Pappe 2 ?) and try to make you feed back about it, as I do for pappe.

    Thanks for the show !

    Vincent

  7. #7
    Quote Originally Posted by BeRo View Post
    Did you understand it now?
    OK now I understand better.

  8. #8
    Quote Originally Posted by Vinzvega View Post
    Hi Benjamin,

    Nice nice work ! Impressive !

    If I must keep one thing, it is the tremendous sky rendering. Optical camera effect and sun burn is just fabulous.

    I will check Kraft in a short time (I assume it is Pappe 2 ?) and try to make you feed back about it, as I do for pappe.

    Thanks for the show !

    Vincent
    Kraft is PAPPE 4 in a manner of speaking, since PAPPE 2 (further development of PAPPE 1 with code refactoring, fixes, etc., but yet without complete rewrite, the first physics engine in the early development of Supraleiter) and PAPPE 3 (first complete rewrite, the second physics engine in the midage development of Supraleiter) did already before as private versions, so Kraft (the current physics engine in Supraleiter) is the fourth evolution level of my physics engine coding stuff with the second complete rewrite, this time with completely new API design and physics engine code design structure than before with PAPPE 1/2/3, therefore also the new name Kraft instead PAPPE again.

    And here some example code http://rootserver.rosseaux.net/stuff/PhysicsDemo.dpr for as quick&dirty fake-API-doc :-)

  9. #9
    Impressive. Truly amazing work. Can't wait to try it out (although I'd have to get a new machine first ).

  10. #10
    Looks awesome mate

    cheers,
    Paul

Page 1 of 2 12 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •