Manual

Scripting and Gameplay

This guide covers the code you write around scenes, prefabs, and input.

Where Gameplay Code Lives

Gameplay code usually lives in generated or hand-written PHP classes under:

  • Assets/Scripts
  • Assets/Events

Most gameplay scripts extend Sendama\Engine\Core\Behaviours\Behaviour.

A Typical Behaviour

Behaviours are components attached to a GameObject. They are the main unit of gameplay logic.

Common reasons to create a behaviour:

  • move an actor
  • listen for input
  • update a UI label
  • control enemy spawning
  • manage a weapon or bullet pool
  • respond to collisions

Example:

<?php

namespace MyGame\Scripts\Player;

use Sendama\Engine\Core\Behaviours\Behaviour;
use Sendama\Engine\Core\Vector2;
use Sendama\Engine\IO\Input;

final class PlayerController extends Behaviour
{
    public function onUpdate(): void
    {
        $motion = new Vector2();

        if (Input::isKeyPressed('a')) {
            $motion->setX(-1);
        }

        if (Input::isKeyPressed('d')) {
            $motion->setX(1);
        }

        $this->getTransform()->translate($motion);
    }
}

Useful Behaviour Context

From a behaviour or component you can access:

  • getGameObject()
  • getTransform()
  • getRenderer()
  • activeScene
  • scene

That gives you enough context to:

  • move the current object
  • read or change its renderer
  • find other objects in the active scene
  • coordinate scene-wide systems

Lifecycle Hooks

The main lifecycle surface comes from Component and Behaviour.

Common hooks include:

  • awake()
  • onStart()
  • onUpdate()
  • onFixedUpdate()
  • onStop()
  • onResume()
  • onSuspend()

Collision-oriented hooks on Behaviour include:

  • onCollisionEnter()
  • onCollisionStay()
  • onCollisionExit()
  • onTriggerEnter()
  • onTriggerStay()
  • onTriggerExit()

Use onUpdate() for frame-to-frame gameplay logic and onFixedUpdate() for logic that should stay aligned with the physics step.

Serialized Component Data

Scene and prefab files can assign values to component fields.

The runtime can hydrate:

  • public properties
  • non-public properties marked with #[SerializeField]

In editor-authored files, the values are usually stored under a component data key.

This is ideal for values such as:

  • movement speed
  • fire rate
  • prefab references
  • pool sizes
  • bounds
  • booleans for behavior toggles

Use serialized fields when you want designers to tune behavior without editing PHP every time.

Working With Vector2

Vector2 is the basic 2D value type used throughout the engine for:

  • position
  • rotation
  • scale
  • sprite offsets and sizes
  • serialized gameplay values such as bounds and directions

In the editor, Vector2 fields appear as paired x and y controls. In scene and prefab data they are stored as arrays:

'minBound' => [
    'x' => 1,
    'y' => 1,
],

Loading And Spawning Prefabs

Prefabs are the best way to spawn project-authored objects.

Typical pattern:

  1. author a prefab in Assets/Prefabs
  2. expose a prefab reference in a component
  3. assign the prefab through the inspector
  4. instantiate or pool it at runtime

The runtime can automatically hydrate a serialized GameObject-typed property from a prefab path string.

Pooling

Object pooling is a good fit for bullets, enemies, pickups, and other short-lived objects.

Important behavior to remember:

  • GameObject::pool($template, $size) clones a template but does not add the clones to the scene for you
  • GameObject::instantiate(...) creates a live instance and adds it to the active scene
  • inactive pooled objects are ignored by collision checks, so deactivated bullets do not keep blocking gameplay

A practical pool loop looks like this:

  1. create or load a bullet template from a prefab
  2. build a fixed set of clones
  3. add them to the scene
  4. keep them deactivated until needed
  5. activate, reposition, and reuse them instead of destroying and recreating them constantly

Input

The Sendama\Engine\IO\Input facade exposes the most common input checks:

  • Input::getAxis(...)
  • Input::isKeyPressed(...)
  • Input::isKeyDown(...)
  • Input::isKeyUp(...)
  • Input::isAnyKeyPressed(...)
  • Input::areAllKeysPressed(...)
  • Input::isButtonDown(...)

Use the simplest method that matches the behavior you want.

Good Gameplay Authoring Pattern

For most features, this sequence works well:

  1. create a prefab for the gameplay object
  2. attach a behaviour script
  3. expose only the fields that need tuning
  4. set those values in the editor
  5. keep scene files declarative and PHP scripts behavioral

When the feature is ready to exercise, move on to Playtesting and Debugging.