Graphics
If you have questions, please ask them in our Discord server and we'll answer them promptly! 😊
Dreamlab uses Pixi.js for its graphics rendering. Anything you can draw with Pixi you can draw in Dreamlab.
Best Practices
For best performance, you should avoid re-drawing any graphical elements that don’t change from one frame to the next other than their position or rotation, and instead draw them when they are initialized or when they change, and update their position every frame.
If you have multiple graphics elements that share the same position relative to each other, you should group them using a Container
.
Make sure you call destroy()
on any graphics objects in teardown()
to free
any resources used, otherwise you may run into memory leaks or other
performance issues.
import type { SpawnableContext, RenderTime } from '@dreamlab.gg/core'
import { SpawnableEntity } from '@dreamlab.gg/core'
import { Vec } from '@dreamlab.gg/core/math'
import { camera, game, stage } from '@dreamlab.gg/core/labs'
import { z } from '@dreamlab.gg/core/sdk'
import { Container } from 'pixi.js'
type Args = typeof ArgsSchema
const ArgsSchema = z.object({})
export class ExampleGraphics extends SpawnableEntity<Args> {
// Field to reference the container
// Might be undefined when running this entity on the server
private readonly container: Container | undefined
public constructor(ctx: SpawnableContext<Args>) {
super(ctx)
// Get a client-only reference to the current game instance
const $game = game('client')
if ($game) {
// Run client-only init
// Create a Container to group any graphics objects
this.container = new Container()
// Enable sorting and set the container z-index
this.container.sortableChildren = true
this.container.zIndex = this.transform.zIndex
// Update the container z-index when it changes
this.transform.addZIndexListener(() => {
if (this.container) this.container.zIndex = this.transform.zIndex
})
// Add the container to the main graphics stage
stage().addChild(this.container)
// ... create any other graphics objects and add them to the stage
// ... with this.container.addChild(..)
}
}
public teardown(): void {
// Destroy the container and its children (if it exists)
this.container?.destroy({ children: true })
}
public override onRenderFrame({ delta, time, smooth }: RenderTime): void {
// Calculate the position in screen-space
const pos = Vec.add(this.transform.position, camera().offset)
if (this.container) {
// Set the position and rotation
this.container.position = pos
this.container.angle = this.transform.rotation
}
}
}
Utilities
Dreamlab has a handful of utility functions to allow you to more easily draw basic shapes, and instantiate Pixi.js sprites.
Basic Shapes
Utility functions to draw basic shapes into Pixi.js Graphics
primitives.
You can see the full list of them and their arguments here.
import type { SpawnableContext } from '@dreamlab.gg/core'
import { SpawnableEntity } from '@dreamlab.gg/core'
import { game, stage } from '@dreamlab.gg/core/labs'
import { z } from '@dreamlab.gg/core/sdk'
import type { BoxGraphics, CircleGraphics } from '@dreamlab.gg/core/utils'
import { drawBox, drawCircle } from '@dreamlab.gg/core/utils'
import { Container } from 'pixi.js'
type Args = typeof ArgsSchema
const ArgsSchema = z.object({})
export { ArgsSchema as PhysicsBallArgs }
export class GraphicsEntity extends SpawnableEntity<Args> {
private readonly container: Container | undefined
private readonly box: BoxGraphics | undefined
private readonly circle: CircleGraphics | undefined
public constructor(ctx: SpawnableContext<Args>) {
super(ctx)
// Ensure graphics components are only created on the client
const $game = game('client')
if ($game) {
this.container = new Container()
// ... clipped
// Draw a basic rectangle outline
this.box = drawBox({ width: 100, height: 50 })
// Draw a blue filled-in circle
this.circle = drawCircle(
{ radius: 25 },
{
strokeAlpha: 0,
fillAlpha: 1,
fill: 'blue',
},
)
// Add to graphics stage
this.container.addChild(this.box)
this.container.addChild(this.circle)
stage().addChild(this.container)
}
}
public teardown(): void {
// Cleanup resources on teardown
this.container?.destroy({ children: true })
}
// ... clipped
}
Sprites
While creating Pixi.js Sprites is fairly trivial to do manually, Dreamlab has a helper that can handle sprite tiling and anchoring with a quick one-liner.
Animated Sprites have their own dedicated docs page.
import type { SpawnableContext } from '@dreamlab.gg/core'
import { SpawnableEntity } from '@dreamlab.gg/core'
import { game, stage } from '@dreamlab.gg/core/labs'
import { z } from '@dreamlab.gg/core/sdk'
import { createSprite } from '@dreamlab.gg/core/textures'
import { Container } from 'pixi.js'
type Args = typeof ArgsSchema
const ArgsSchema = z.object({})
export { ArgsSchema as PhysicsBallArgs }
export class SpriteEntity extends SpawnableEntity<Args> {
private readonly container: Container | undefined
private readonly sprite: Container | undefined
public constructor(ctx: SpawnableContext<Args>) {
super(ctx)
// Ensure graphics components are only created on the client
const $game = game('client')
if ($game) {
this.container = new Container()
// ... clipped
// Create a sprite from an image URL
this.sprite = createSprite(
{
url: '...',
tile: true,
tileScale: 1,
},
{
width: 100,
height: 100,
},
)
// Add sprite and container to graphics stage
if (this.sprite) this.container.addChild(this.sprite)
stage().addChild(this.container)
}
}
public teardown(): void {
// Cleanup resources on teardown
this.container?.destroy({ children: true })
}
// ... clipped
}