piured-engine

Engine

PIURED is a Pump It Up stage simulator that works directly in your browser. There are already a number of dance simulators for Windows and Linux, most of them being StepMania-based. Stepmania is a great choice for DDR-style rhythm games, however it lacks to capture the behaviour as well as the feel & look of a Pump It Up arcade.

In this sense, PIURED's goal is to recreate as accurately as possible the Pump It Up-style experience whilst keeping the engine cross-platform (web-based), so it can be enjoyed anywhere and anytime.

PIURED is written in its whole in Javascript using ThreeJS. The code is organized trying to mimick the structure of any game engine. The source code can be found in this repo under the folder js.

This engine features:

  1. A Stepmania SSC parser. Stepcharts can be directly read from bare Stepmania files. No need to convert them into another intermediate format. This means that all the songs that are available for Pump-style would work off-the-shelf in PIURED.
  2. A loader that supports both MP3 and OGG audio formats. These are the most common formats used in Stepmania audio files.
  3. Support for changes of BPM, SCROLL, STOPS, DELAYS as well as changes of SPEED (attributes used in Stepmania to create effects).
  4. Pump-single, Pump-double and Pump-Halfdouble styles.
  5. VS. mode, including two or more players playing any combination of Pump-single and Pump-double styles.
  6. Remote input capabilities to create online-like battles.
  7. On-the-fly tuning of the offset parameter.
  8. Variable speed rates.
  9. A number of Noteskins to choose from (sprite-based).
  10. Game performance metrics.
  11. Visual effects close to the original arcade.
  12. A background theme which "FEELS THE BEAT".

There are, however, some features available in Stepmania that PIURED does not support:

  1. The engine only supports a 4/4 bar.
  2. BGA in any video format is not supported.
  3. Dance pads or Joysticks are not supported as input methods.
  4. Performance metrics may not be deterministic.
  5. Only Pump-single, Pump-double and Pump-halfdouble styles are supported. Any other style may cause the engine to crash.

The Engine class is the main abstraction for creating a pump it up stage and playing a chart. It contains all methods necessary to create a new stage, add an indefinite number of local and remote players, load Stepmania SSC files, and report players' performance.

This class is intended to be used only as javascript code on the client-side, as it will attempt to draw stuff on the browser.

new Engine()
Example

Importing the engine via ES6 modules

import {PiuredEngine, StageConfig, PlayerConfig, KeyInputConfig} from '@piured/engine'

Full example to configure a working engine.

import {PiuredEngine, StageConfig, PlayerConfig, KeyInputConfig} from '@piured/engine'

async function setUpEngine (dom) {
    let engine = new Engine()

    function stageCleared(performance) {
        console.log(performance);
        engine = null;
    }

    function onKeyDown(event) {
        engine.keyDown(event);
    }

    function onKeyUp(event) {
        engine.keyUp(event);
    }

    let speed = 4.0;
    let playback = 1.0;
    let offset = 0.0;
    let noteskin = 'NX';
    let touchpadSize = 1.0;
    let leftKeyMap = {
        dl: 'Z',
        ul: 'Q',
        c: 'S',
        ur: 'E',
        dr: 'C'
    }
    let rightKeyMap = {
        dl: 'V',
        ul: 'R',
        c: 'G',
        ur: 'Y',
        dr: 'N'
    }
    let chartLevel = 0;
    let innerWidth = 800
    let innerHeight = 800
    let pixelRatio = window.devicePixelRatio

    engine.init(innerWidth, innerHeight, pixelRatio, window)

    let stageConfig = new StageConfig('<mp3-url>',
        '<ssc-url>',
        playback,
        offset,
        [noteskin],
        () => {
            let dateToStart = new Date();
            // delay of 2 secs
            dateToStart.setMilliseconds(dateToStart.getMilliseconds() + 2000.0);
            engine.startPlayBack(dateToStart, () => {
                return new Date();
            });
        }
    );

    await engine.configureStage(stageConfig);

    let p1InputConfig;
    let accuracyMargin = 0.15;

    accuracyMargin = 0.25;

    window.onkeydown = onKeyDown;
    window.onkeyup = onKeyUp;

    p1InputConfig = new KeyInputConfig(leftKeyMap, rightKeyMap);

    let p1Config = new PlayerConfig(p1InputConfig,
        noteskin,
        chartLevel,
        speed,
        accuracyMargin);


    engine.addPlayer(p1Config);

    engine.addToDOM(dom);

    window.addEventListener( 'resize', engine.onWindowResize.bind(engine), false );

    engine.onStageCleared = stageCleared;

    engine.start();
}

await setUpEngine('<container>')
Instance Members
init(width, height, pixelRatio, window)
configureStage(stageConfig)
addPlayer(playerConfig)
addToDOM(containerId)
updateOffset(playerId, newOffsetOffset)
tunePlayBackSpeed(playBackSpeed)
keyDown(event)
keyUp(event)
touchDown(event)
touchUp(event)
setCameraPosition(X, Y, Z)
queryStageType(level)
start()
startPlayBack(dateToStart, getDateFn = ()=>{return new Date();})
stop()
resize(newWidth, newHeight)
logFrame(frameLog)
onFrameLog
onStageCleared

Configuration Files

Here you can find the configuration helper classes to configure the engine.

StageConfig

This class holds the configuration of a stage.

new StageConfig(SSCFile: string, parsedSSC: string, audioFile: string, playBackSpeed: number, offset: number, noteskins: Array, onReadyToStart: Function)
Parameters
SSCFile (string) path to the Stepmania SSC file describing the levels and steps available. This parameter can be undefined if parsedSSC is provided.
parsedSSC (string) parsed SSC file in JSON format (output of ssc-parser). If this parameter is undefined then, parameter SSCFile must be provided.
audioFile (string) path to the audio file in mp3 or ogg format associated with the SSC file
playBackSpeed (number) song playback rate. A playBackSpeed equal to 1.0 configure the engine to play the song at the normal speed
offset (number) synchronization offset. Use it to synchronize off-the-beat charts with the audio
noteskins (Array) list of noteskins available to use for playerStages. Include those only used in them.
onReadyToStart (Function = ()=>{}) callback function. This function will be called once the engine has loaded up completely the stage and it's ready to begin the playback
Example
let stageConfig = new StageConfig('song.ssc',
         undefined,
         'song.mp3',
         1.0,
         0.0,
         'piured-engine,
         ['NXA'],
         () => {
             let dateToStart = new Date() ;
             // delay of 2 secs
             dateToStart.setMilliseconds(dateToStart.getMilliseconds() + 2000.0) ;
             engine.startPlayBack(dateToStart, () => {return new Date() ;}) ;
         }
) ;

PlayerConfig

This class holds the configuration for a player.

new PlayerConfig(inputConfig: (KeyInputConfig | TouchInputConfig | RemoteInput), noteskin: string, level: number, speed: number, accuracyMargin: number, receptorX: number, receptorY: number, scale: number)
Parameters
inputConfig ((KeyInputConfig | TouchInputConfig | RemoteInput)) input configuration
noteskin (string) player's noteskin. Note that noteskin needs to be previously passed in the constructor of StageConfig .
level (number) levelId to be played by the player
speed (number = 1.0) rate in which the steps traverse the screen from the bottom to the receptor
accuracyMargin (number = 0.15) span of time (in seconds) in which a step is considered to be pressed
receptorX (number = 0) X position of player stage with respect to the stage
receptorY (number = 0) Y position of player stage with respect to the stage
scale (number = 1.0) scale applied to the player stage
Example
let p1InputConfig = RemoteInput() ;
let p1Config = new PlayerConfig(p1InputConfig,
                     'NXA',
                     0,
                     3.0,
                     0.15) ;

KeyInputConfig

Class for configuring the keyboard as input method. For registering key strokes, it is necessary to pass the key events into the engine through methods Engine#keyDown and Engine#keyUp.

new KeyInputConfig(lpad: {dl: string, ul: string, c: string, ur: string, dr: string}, rpad: {dl: string, ul: string, c: string, ur: string, dr: string})
Parameters
lpad ({dl: string, ul: string, c: string, ur: string, dr: string}) left pad keymap
rpad ({dl: string, ul: string, c: string, ur: string, dr: string}) right pad keymap
Example
let p1InputConfig = new KeyInputConfig({
   dl: 'Z',
   ul : 'Q',
   c : 'S',
   ur : 'E',
   dr: 'C'
},
{
   dl: 'V',
   ul : 'R',
   c : 'G',
   ur : 'Y',
   dr: 'N'
}) ;

TouchInputConfig

Class for configuring the input for a touch-capable device. The input pad will be drawn on the screen. For registering the touch input, it is necessary to pass the touch events into the engine through methods Engine#touchDown and Engine#touchUp.

new TouchInputConfig(scale: number, X: number, Y: number)
Parameters
scale (number = 1.0) scaling factor
X (number = 0) X position of the pad with respect to the player's stage
Y (number = 0) Y position of the pad with respect to the player's stage
Example
let p1InputConfig = new TouchInputConfig() ;

RemoteInput

This class configures a remote input for a remote player. Make sure you frame the logs corresponding to players using remote input through Engine#logFrame

new RemoteInput()
Example
let remoteInputConfig = new remoteInput() ;