Skip to content

Scripting

JamJar has a number of systems to allow JavaScript scripts to be loaded in at runtime and executed - while providing a series of hooks and callbacks to allow scripts to have useful control over the core game engine.

Scripts can:

  • Get entities + components they are associated with.
  • Look up entities by ID.
  • Look up entities by tag and layer.
  • Send messages into the JamJar engine.
  • Modify components.
  • Remove components.
  • Destroy entities.

Scripts can be attached to run on:

  • Updates.
  • Collisions.
  • Manually triggered.

Loading Scripts

Scripts can be loaded using the HTTPScriptSystem, which allows scripts to be loaded at runtime over HTTP(S).

new HTTPScriptSystem(messageBus);

Once this system has been set up, scripts can be loaded in by sending a message with a ScriptRequest as the payload.

this.messageBus.Publish(Message.New<ScriptRequest>(ScriptRequest.MESSAGE_REQUEST_LOAD,
    new ScriptRequest(
        "test-script",
        "assets/script.js"
    ))
);

This will attempt to load the assets/script.js file, storing it and referencing it with the test-script name. This script can now be triggered by referencing this name.

Executing Scripts

Scripts can be executed in a number of ways, through a Script component, on Collision, or with manual execution.

Executing scripts requires a ScriptingEngineSystem running.

new ScriptingEngineSystem(messageBus, "script-test");

The second parameter, in this instance script-test, provides a reference for scripts to use when interacting with a game instance. For more details, see writing scripts below.

Executing Scripts using the Script component

The Script component simply allows a script to be attached to an entity, with a configurable trigger on when the referenced script should be executed.

To use the Script component, you must set up a ScriptTriggerSystem to handle parsing Script components and queuing script executions.

new ScriptTriggerSystem(messageBus);

Once this system is set up, you can then use the Script component.

const scriptable = new Entity(this.messageBus);
scriptable.Add(new Script("test-script", ScriptTrigger.UPDATE));

This entity will be set up to execute the script called test-script on every update.

Executing Scripts on Collision

Scripts can be configured to be executed when an entity with a Collider collides with another.

This requires a CollisionSystem to be running.

new CollisionSystem(messageBus);

The Collider component takes a parameter which allows the script to be referenced.

const collidable = new Entity(this.messageBus);
collidable.Add(new Transform(Vector.New(0, 0), Vector.New(1, 1)));
collidable.Add(new Collider(Polygon.RectangleByDimensions(1, 1), "test-script"));

Executing Scripts Manually

Scripts can be manually executed by sending a message with a ScriptTriggerRequest.

this.messageBus.Publish(Message.New<ScriptTriggerRequest<string>>(ScriptRequest.MESSAGE_REQUEST_LOAD,
    new ScriptTriggerRequest(
        "test-script",
        "custom-execution",
        undefined,
        "this data will be sent to the script"
    ))
);

This message will trigger the test-script, with a descriptor of the conditions it is being executed under as custom-execution. This descriptor is just to give the script some context of why it is being executed, for example if it is executed under an update event, it will be update.

There is additional string data provided to the script too, in this instance it is this data will be sent to the script, but this can be any arbitrary data.

Writing Scripts

Scripts are designed to be simple, imperative code chunks that can be loaded and executed at runtime. There are some hooks and functions available to scripts to allow them to determine context, while also having the ability to affect the game.

Contextual Script Data

Contextual data is provided to the script by a variable request which all scripts have access to, this is a ScriptTriggerRequest and as such contains useful information, such as the descriptor of why the script was executed, the entity associated with the script execution, the name of the script and additional contextual information.

The additional contextual information differs depending on how the script is called, if it was part of an update event, this is the update deltatime, if it is on collision, it is the collision information.

console.log(request.descriptor); // e.g. update/collision
console.log(request.data); // e.g. delta time/collision info

Interacting with the Game

Scripts have access to a [ScriptReference] for the game, allowing interaction with the game and its entities, this can be retrieved by referencing the reference key that the ScriptingEngineSystem was set up with, allowing access to a number of useful functions.

const gameRef = window.JamJar.Refs.get("script-test");
console.log(gameRef.GetScriptEntity());
console.log(gameRef.GetEntityByID(0));
console.log(gameRef.GetEntitiesByTag("test"));
console.log(gameRef.GetEntitiesByLayer("test"));
gameRef.SendMessage({ type: "test"});

This grabs the reference to a ScriptingEngineSystem set up with the reference script-test and calls a number of functions.