Skip to content

PrimitiveShapes API

vape edited this page Aug 11, 2026 · 1 revision

PrimitiveShapes API

PrimitiveShapes renders 3D vector graphics directly on Bedrock clients using the native PrimitiveShapesPacket.

Architecture: Traditional PMMP vs PrimitiveShapes

Aspect Traditional PMMP Approach PrimitiveShapes API
Mechanism Spawns fake ArmorStands, Invisible Entities, or Particle tasks Native Bedrock PrimitiveShapesPacket
Server Load High CPU entity tick loops & physics collision checks 0 server entity overhead, rendered on client GPU
Viewer Replay Requires custom player join listeners & chunk tracking tasks Automatic chunk shape replay on chunk load
Lifecycle Manual entity management & despawn packets ShapeHandle lifecycle token with idempotent .remove()

1. Quick Example

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\BoxShape;

// Add a 2x2x2 box to the world
$handle = $world->addShape($pos, new BoxShape(
    origin: $pos,
    bound: new Vector3(2, 2, 2),
    color: new Color(255, 100, 0)
));

// Update the shape in real-time without client flicker
$handle->update(new BoxShape($pos, new Vector3(3, 3, 3), color: new Color(0, 255, 0)));

// Remove the shape
$handle->remove();

2. Automatic Chunk Shape Replay

When you call $world->addShape($pos, $shape), PocketmineMV tracks the shape inside $activeShapes by chunk hash.

When a player enters render range or loads a chunk, NetworkSession::startUsingChunk() automatically replays all active shapes in that chunk to the client.

3. Shape Classes Reference

All shape classes are in pocketmine\world\shape\shapes\*.

LineShape

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\LineShape;

$shape = new LineShape(
    start: Vector3 $start,
    end: Vector3 $end,
    color: ?Color $color = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

BoxShape

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\BoxShape;

$shape = new BoxShape(
    origin: Vector3 $origin,
    bound: Vector3 $bound,
    scale: ?float $scale = null,
    color: ?Color $color = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

SphereShape

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\SphereShape;

$shape = new SphereShape(
    center: Vector3 $center,
    segments: int $segments, // e.g. 16 or 20
    scale: ?float $scale = null,
    color: ?Color $color = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

CircleShape

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\CircleShape;

$shape = new CircleShape(
    center: Vector3 $center,
    segments: int $segments,
    scale: ?float $scale = null,
    color: ?Color $color = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

TextShape

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\TextShape;

$shape = new TextShape(
    position: Vector3 $position,
    text: string $text,
    useRotation: bool $useRotation = false,
    backgroundColor: ?Color $backgroundColor = null,
    depthTest: bool $depthTest = true, // false = visible through walls (X-Ray)
    showBackface: bool $showBackface = true,
    showTextBackface: bool $showTextBackface = true,
    color: ?Color $color = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

ArrowShape

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\ArrowShape;

$shape = new ArrowShape(
    tail: Vector3 $tail,
    head: Vector3 $head,
    scale: ?float $scale = null,
    color: ?Color $color = null,
    arrowHeadLength: ?float $arrowHeadLength = null,
    arrowHeadRadius: ?float $arrowHeadRadius = null,
    segments: ?int $segments = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

CylinderShape

use pocketmine\color\Color;
use pocketmine\math\Vector2;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\CylinderShape;

$shape = new CylinderShape(
    base: Vector3 $base,
    radiusX: Vector2 $radiusX, // top and bottom radius X
    radiusZ: Vector2 $radiusZ, // top and bottom radius Z
    height: float $height,
    segments: int $segments,
    color: ?Color $color = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

PyramidShape

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\PyramidShape;

$shape = new PyramidShape(
    base: Vector3 $base,
    width: float $width,
    height: float $height,
    depth: ?float $depth = null,
    color: ?Color $color = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

EllipsoidShape

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\EllipsoidShape;

$shape = new EllipsoidShape(
    center: Vector3 $center,
    radii: Vector3 $radii,
    segmentsPerAxis: int $segmentsPerAxis,
    color: ?Color $color = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

ConeShape

use pocketmine\color\Color;
use pocketmine\math\Vector2;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\ConeShape;

$shape = new ConeShape(
    base: Vector3 $base,
    radii: Vector2 $radii,
    height: float $height,
    segments: int $segments,
    color: ?Color $color = null,
    dimensionId: ?int $dimensionId = null,
    attachedEntityId: ?int $attachedEntityId = null,
    rotation: ?Vector3 $rotation = null
);

4. 3D Rotation

Pass $rotation (Vector3($yaw, $pitch, $roll)) into any shape class:

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\BoxShape;

$world->addShape($pos, new BoxShape(
    origin: $pos,
    bound: new Vector3(2, 2, 2),
    color: new Color(255, 100, 0),
    rotation: new Vector3(45, 0, 0)
));

5. Temporary Shapes

addTemporaryShape accepts duration in ticks. The client auto-destroys the visual representation, and the server cleans up memory when expired.

use pocketmine\world\shape\shapes\SphereShape;

$handle = $world->addTemporaryShape(
    pos: $pos,
    shape: new SphereShape($pos, segments: 16, scale: 2.0),
    durationTicks: 100 // 5 seconds
);

6. ShapeGroup

Group multiple shapes into a single ShapeGroup token to manage complex structures:

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\BoxShape;
use pocketmine\world\shape\shapes\TextShape;

$arena = $world->addShapeGroup([
    [$pos->add(-5, 0, -5), new BoxShape($pos->add(-5, 0, -5), new Vector3(1, 4, 1), color: new Color(255, 0, 0))],
    [$pos->add( 5, 0, -5), new BoxShape($pos->add( 5, 0, -5), new Vector3(1, 4, 1), color: new Color(255, 0, 0))],
    [$pos->add( 0, 4,  0), new TextShape($pos->add(0, 4, 0), "§eARENA")],
]);

$arena->update(2, new TextShape($pos->add(0, 4, 0), "§aROUND 2"));
$arena->remove();

7. Entity Attachment

Attach shapes directly to players or entities. The client tracks position automatically:

use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\world\shape\shapes\CircleShape;

$player->attachShape(
    shape: new CircleShape($player->getPosition(), segments: 32, scale: 1.2, color: new Color(255, 215, 0)),
    offset: new Vector3(0, 0.1, 0)
);

$player->detachShapes();

8. Player-Private Shapes

Send shapes visible only to a specific player:

use pocketmine\world\shape\shapes\TextShape;

$handle = $player->addShape(new TextShape($pos, "§aPrivate Quest Marker"));
$player->removeShape($handle);
$player->clearShapes();

9. WorldShapeEvent

Event listener for shape creation:

use pocketmine\event\Listener;
use pocketmine\event\world\WorldShapeEvent;

class ShapeListener implements Listener{
    public function onWorldShape(WorldShapeEvent $event) : void{
        if($event->getWorld()->getFolderName() === "protected_hub"){
            $event->cancel();
        }
    }
}

Clone this wiki locally