Play systems

Input, sound, time, and the disk.

These APIs live on the main thread with your callbacks, except where a method says it is safe from any thread. Namespaces: WillEngine.Core.Controls, WillEngine.Core.Audio, WillEngine.Core.World, WillEngine.Core.Saving.

Input

Input is the state of the current game step. WasPressed and WasReleased are true for exactly one step.

using WillEngine.Core.Controls;

if (Input.WasPressed(Key.Space)) Jump();
if (Input.IsDown(Key.Left)) face = -1;
if (Input.IsDown(MouseButton.Left)) Place();

Keys are the Key enum by US-layout position: A through Z, digits, F1–F12, arrows, Space, Enter, Escape, modifiers, and the keypad. Mouse buttons are Left, Right, Middle, Button4, and Button5.

Input.MousePosition is in screen pixels of the game canvas, Y down. It can lie outside the canvas. The world point under it is Cameras.Main.ScreenToWorld(Input.MousePosition). MouseDelta is movement since the previous step. WheelDelta is wheel steps this step, positive away from the user.

Pointers unify the mouse and touch. Index 0 is always the mouse; 1 and up are fingers, up to 10. Input.IsPointerOverTouchArea is how gameplay ignores a click that landed on the HUD. Gamepads are slots 0 through 3: Input.Gamepad(slot) is never null. A missing pad reads as disconnected, with nothing held.

Audio

The mixer is flat 2D stereo. There is no listener and no panning. Distance is something your script does with volume. If no device is open, play calls are silent and return SoundHandle.None.

A Sound field on a component is a .wso dragged from the Project panel. Audio.Play uses volume 1 as imported, pitch 1 as recorded, and the SFX group unless you pass another. Audio.PlayMusic keeps one looping track in the Music group and crossfades the previous one.

using WillEngine.Core.Audio;

public Sound JumpSound;
public Sound BossTheme;

void OnJump()
{
    Audio.Play(JumpSound, volume: 0.5f);
    SoundHandle engine = Audio.Play(JumpSound, loop: true);
    engine.SetPitch(1.5f);
    Audio.PlayMusic(BossTheme, crossfade: 2f);
}

A SoundHandle can Stop, SetVolume, and SetPitch after the sound has ended; those calls do nothing. The default group tree is Master with Music, SFX, UI, and Voice under it. audio.json, edited in the Mixer panel, changes levels and can add groups. An AudioSource component plays a sound with its object: it starts on enable when PlayOnStart is set, and stops when the object disables.

Time

OnUpdate always receives GameTime.StepSeconds, which is 1 / GameTime.FrameRate. The default rate is 60, from game.json, clamped from 1 to 1000. A script may change GameTime.FrameRate (an options menu); it takes effect next frame. The host decides how many steps run per rendered frame. A slow frame catches up, a fast one waits, and steps beyond the cap are dropped. Your movement never sees a variable frame time.

Saves and preferences

Saves writes one .save file per name. A write replaces the previous file only after the new one is complete. It does not throw: a failed write returns false, and a missing or damaged file reads as null. Prefs is a small prefs.json of named settings, kept in memory and flushed at the end of a frame that changed them.

using WillEngine.Core.Saving;

public class Progress { public int Level; public int Coins; }

Saves.Write("slot1", progress);
Progress saved = Saves.Read<Progress>("slot1");

Prefs.SetFloat("musicVolume", 0.8f);
float volume = Prefs.GetFloat("musicVolume", 1f);

A built game stores these under local application data for that game’s name. Play in the editor uses a separate folder per project, so testing does not touch a real save. The web player keeps the folder in the browser’s IndexedDB. Write game data through Saves, not through your own paths.