Pixscape is currently in public pre-release.

The runtime and documentation are already usable, but some APIs, workflows, and editor behavior may still evolve before a stable 1.0 release.

Physics and Collisions

Access Pixscape's Box2D world from LibGDX and work with entity bodies, contacts, ray casts, and collision queries.

On this page

Pixscape uses LibGDX Box2D for runtime physics. Bodies, collision shapes, and joints created in Studio are recreated automatically when the scene loads.

Use api.physics() when gameplay needs to inspect physics state or access the current scene’s Box2D world and bodies.

Use Pixscape physics

import games.pixscape.runtime.api.PhysicsAPI;

PhysicsAPI physics = api.physics();

if (physics.isRunning()) {
    float pixelsPerMeter = physics.pixelsPerMeter();
}

isRunning() tells you whether physics is currently active for the loaded scene. It describes the live simulation, not only the scene’s configured physics setting.

Pixels and Box2D meters

Pixscape scenes use world/pixel coordinates, while Box2D works in meters. pixelsPerMeter() returns the scale used by the current scene.

float ppm = api.physics().pixelsPerMeter();

float meters = pixels / ppm;
float pixelsAgain = meters * ppm;

Use this scale when passing Pixscape positions or distances to native Box2D operations.

Access the Box2D world

box2dWorld() gives you the native LibGDX/Box2D world used by Pixscape. It returns null when physics is unavailable.

import com.badlogic.gdx.physics.box2d.World;

World world = api.physics().box2dWorld();

if (world != null) {
    // Native Box2D operations
}

Access an entity body

Combine the Physics API with an EntityRef to get the live Box2D body for an entity:

import com.badlogic.gdx.physics.box2d.Body;
import games.pixscape.runtime.api.EntityRef;

EntityRef player = api.entities().requireTag("player");
Body body = api.physics().body(player);

if (body != null) {
    body.applyLinearImpulse(2f, 0f, body.getWorldCenter().x, body.getWorldCenter().y, true);
}

body(...) returns null when the entity is missing or inactive, has no live physics body, or physics is unavailable.

Ownership and lifetime

The World and Body objects returned by PhysicsAPI belong to Pixscape. You can query them and use normal Box2D body operations, but:

  • do not dispose the World;
  • do not destroy Pixscape-owned bodies directly;
  • do not run your own normal world.step(...) loop;
  • do not assume these native objects live forever.

Pixscape controls normal Box2D stepping as part of the Runtime loop. To remove a Pixscape entity, normally use EntityRef.remove() and let Runtime manage its native body.

The Box2D world belongs to the current Runtime scene. Pixscape can also recreate an entity’s body. After a scene change or Runtime rebuild, get the World and Body again from api.physics().

Collision events

Use a normal Box2D ContactListener for gameplay collision events:

import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.World;

World world = api.physics().box2dWorld();

if (world != null) {
    world.setContactListener(new ContactListener() {
        @Override
        public void beginContact(Contact contact) {
            Fixture a = contact.getFixtureA();
            Fixture b = contact.getFixtureB();

            // Gameplay collision logic
        }

        @Override
        public void endContact(Contact contact) {
        }

        @Override
        public void preSolve(Contact contact, Manifold oldManifold) {
        }

        @Override
        public void postSolve(Contact contact, ContactImpulse impulse) {
        }
    });
}

A Box2D World has one active contact listener. If several gameplay systems need collision events, route them from one listener into your own game code.

Ray casts

Ray casts use Box2D meters. Convert logical Pixscape world coordinates with the scene’s pixels-per-meter value:

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;

World world = api.physics().box2dWorld();

if (world != null) {
    float ppm = api.physics().pixelsPerMeter();
    Vector2 from = new Vector2(x1 / ppm, y1 / ppm);
    Vector2 to = new Vector2(x2 / ppm, y2 / ppm);

    world.rayCast((fixture, point, normal, fraction) -> {
        // Return the fraction to clip the ray at this hit.
        // Return -1f to ignore this fixture.
        return fraction;
    }, from, to);
}

Use the standard Box2D callback return values when you need different filtering or traversal behavior.

AABB queries

Use QueryAABB(...) for nearby-object checks, trigger areas, and other broad-phase gameplay queries. Its bounds are also in Box2D meters.

World world = api.physics().box2dWorld();

if (world != null) {
    world.QueryAABB(fixture -> {
        // Inspect the fixture. Return true to continue the query.
        return true;
    }, lowerX, lowerY, upperX, upperY);
}

Physics parallax

Physics coordinates may use the scene’s physics parallax settings:

float parallaxX = api.physics().parallaxX();
float parallaxY = api.physics().parallaxY();

An unset parallax value, or no active scene, behaves as 1.

If you unproject a screen position through a camera, the result is a rendered world position. removeParallax(...) converts it back to the logical Pixscape world position used for physics interaction:

import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;

Vector2 logicalWorld = new Vector2();

api.physics().removeParallax(
        renderedWorldPosition,
        camera,
        logicalWorld
);

The arguments are the rendered Vector2, the OrthographicCamera, and the output Vector2. The output may be the same object as the rendered position.

removeParallax(...) still returns Pixscape world coordinates. Divide by pixelsPerMeter() separately when a Box2D operation needs meters.

Studio-authored physics

Bodies, collision shapes, and joints authored in Studio are saved with the scene and recreated by Runtime when the scene loads. Gameplay can then retrieve their native bodies with api.physics().body(entity) or interact with all of them through the shared Box2D world. See the Studio Physics guide for visual body and collision-shape editing.

Tiled and Spatial collision geometry prepared by Pixscape participates in that same world. Their authoring and data rules are covered by the dedicated Tiled and Spatial documentation.

Advanced Runtime integration

Normal gameplay should prefer api.physics() and standard Box2D operations. Experienced integrations can still use the Expert ECS API, Box2dSyncSystem, and Box2dWorldService when extending Runtime physics synchronization or lifecycle behavior.

These expert surfaces are not required to retrieve the world or an entity body. Even in expert code, acquire native objects through PhysicsAPI instead of depending on Runtime body or fixture cache components.

Direct changes to authored Pixscape physics components must follow Runtime’s validation and dirty/rebuild rules. Keep that work in the expert ECS layer; the internal compiled fixture and runtime-body caches are not gameplay APIs.