Components¶
Components are attached to Node objects. A component may also implement:
Drawableto renderUpdatableto receiveupdate(dt)LateUpdatableto receivelateUpdate(dt)after all regular component updates
All components inherit from Component, which gives access to AppContext, Scene, AssetManager, Input, EventBus, and SaveSystem via helper functions.
Built-in Components¶
Transform(always present on every node)Camera(orthographic 2D by default; perspective and orthographic 3D when enabled)SpriteRenderer(draws a textured quad)QuadRenderer(draws a solid-color quad)TriangleRenderer(draws a solid-color triangle)CircleRenderer(draws a solid-color circle)LineRenderer(draws a thick polyline with optional smoothing and rounded caps)FontRenderer(MSDF text rendering)SpineRenderer(Spine skeletal animation)EffekseerRenderer(Effekseer runtime effects)TiledRenderer(Tiled JSON map rendering + query/mutation API)ModelRenderer(static/skinned mesh rendering;SNOWPULSE_HAS_RENDERING3D)Animator3D(clip playback, crossfading, GPU skinning, and sockets;SNOWPULSE_HAS_RENDERING3D)DirectionalLight3D,PointLight3D, andSpotLight3D(forward lights;SNOWPULSE_HAS_RENDERING3D)FbxRenderer(deprecated compatibility wrapper overModelRenderer)ParticleEffects(2D particle emitter)Trail(vertex trail that follows node movement)RigidBody(2D physics body)PhysicsCollider(2D collision shape/material/filter)RigidBody3D,Collider3D, andCharacterController3D(Jolt physics;SNOWPULSE_HAS_PHYSICS3D)NavigationAgent3D(Detour path planning and desired velocity;SNOWPULSE_HAS_NAVIGATION3D)Script(base class withonStart/onUpdate)ActionRunner(runs actions/tweens on a node)
FontRenderer supports MSDF text effects through effects.outline and
effects.shadow. Effect widths, spreads, softness, and offsets use the same
local units as fontSize and reuse the loaded font atlas. Very thick effects
require a font atlas generated with enough MSDF distance range and padding.
Text uses the solid color by default. Enable gradient for a two-color
vertical fill spanning the complete laid-out text block; outlines and shadows
remain solid-colored:
auto* labelNode = scene()->createNode("label");
auto* label = labelNode->addComponent<snowpulse::FontRenderer>();
label->font(assetManager()->loadFont("fonts/interface.json"));
label->text = "Ready";
label->fontSize = 36.0f;
label->effects.outline.width = 2.0f;
label->effects.shadow.enabled = true;
label->effects.shadow.setDirection({ 1.0f, -1.0f }, 3.0f);
label->gradient.enabled = true;
label->gradient.topColor = { 1.0f, 0.96f, 0.82f, 1.0f };
label->gradient.bottomColor = { 0.96f, 0.68f, 0.22f, 1.0f };
scene()->rootNode()->addChild(labelNode);
FontRenderer uses RenderSpace::World by default. Set screen render space
when its owning node's transform should use logical screen coordinates and
remain unaffected by camera position, rotation, zoom, or shake:
label->renderSpace = snowpulse::FontRenderer::RenderSpace::Screen;
labelNode->transform().position = { 320.0f, 180.0f, 0.0f };
For a complete self-cleaning floating-text presentation with screen anchors,
movement, fading, and either FontRenderer or UIText, use
snowpulse::fx::ToastEffect from Game Feedback.
Primitive Renderers¶
QuadRenderer, TriangleRenderer, and CircleRenderer draw simple
solid-color shapes without requiring textures. They are useful for prototypes,
debug overlays, hit-area visualization, and simple VFX.
The examples in this section assume node is already attached to the scene
tree. A quad needs only a size and color:
auto* panel = node->addComponent<snowpulse::QuadRenderer>();
panel->size = { 240.0f, 72.0f };
panel->color = { 0.08f, 0.12f, 0.18f, 0.9f };
TriangleRenderer renders exactly three local-space points transformed by the
owner node. Degenerate triangles are skipped. Set points, color,
sortOrder, and blendMode directly:
auto* triangle = node->addComponent<snowpulse::TriangleRenderer>();
triangle->points = {
glm::vec2 { 0.0f, 0.0f },
glm::vec2 { 120.0f, 0.0f },
glm::vec2 { 60.0f, 90.0f }
};
triangle->color = { 1.0f, 0.6f, 0.1f, 1.0f };
triangle->sortOrder = 5;
triangle->blendMode = snowpulse::BlendMode::Normal;
CircleRenderer draws a full circle by default. Set fillAmount between
0.0f and 1.0f to draw a radial sector, with angles measured in degrees from
the positive x-axis:
auto* timer = node->addComponent<snowpulse::CircleRenderer>();
timer->radius = 16.0f;
timer->color = { 1.0f, 0.0f, 0.0f, 1.0f };
timer->fillAmount = 0.5f;
timer->radialStartAngle = 90.0f;
timer->radialDirection = snowpulse::CircleRadialDirection::Clockwise;
Fill amounts are clamped to the valid range. A value of 0.0f draws nothing,
while 1.0f uses the normal full-circle rendering path.
Line Renderer¶
LineRenderer turns local-space anchor points into a thick triangle strip.
It can smooth corners with cubic Bézier sampling and optionally add rounded
end caps.
auto* route = node->addComponent<snowpulse::LineRenderer>();
route->thickness = 10.0f;
route->color = { 0.2f, 0.8f, 1.0f, 0.9f };
route->useBezierCorners = true;
route->useRoundedEndCaps = true;
route->smoothing = 0.75f;
route->sampleStep = 12.0f;
route->setAnchorPoints({
{ 0.0f, 0.0f, 0.0f },
{ 80.0f, 40.0f, 0.0f },
{ 160.0f, 0.0f, 0.0f },
});
Use clear() to remove all points. Fewer than two points, or non-positive
thickness, produces no geometry.
3D Models¶
ModelRenderer is available when the target selects RENDERING3D ON through
snowpulse_add_game() or snowpulse_target_link_engine(). Loading FBX,
glTF, or GLB files additionally requires ASSIMP ON. It submits static or
skinned mesh instances with material overrides, render-layer filtering, and
cast/receive-shadow state.
auto* model = node->addComponent<snowpulse::ModelRenderer>();
model->color = { 1.0f, 1.0f, 1.0f, 1.0f };
model->castsShadows = true;
model->receivesShadows = true;
model->load("models/character.glb");
Attach Animator3D for named clip playback, crossfading, skinning, and socket
poses. The deprecated FbxRenderer wrapper remains available for existing
code that combines loading and legacy animation playback.
DirectionalLight3D, PointLight3D, and SpotLight3D light these models.
Jolt's RigidBody3D, Collider3D, and CharacterController3D are controlled
by the independent PHYSICS3D switch. NavigationAgent3D is controlled by
NAVIGATION3D; it plans a path and produces desired velocity but does not move
the node or replace collision handling. See Forward-rendered 3D for
the complete build, camera, material, physics, and navigation contracts.
On that deprecated wrapper, animationNames() lists clips in the base model
and addAnimationClips() can load additional clips from another FBX file when
its skeleton bone layout matches the base model; an optional prefix prevents
name collisions.
Camera¶
The scene's active Camera exposes zoom and transient shake directly:
auto* activeCamera = scene()->camera();
activeCamera->zoom = 1.25f;
activeCamera->shakeFrequency = 30.0f;
activeCamera->shake(8.0f, 0.25f);
if (activeCamera->isShaking()) {
// Avoid starting another shake if the current one should finish first.
}
Particles and Trails¶
ParticleEffects can load an effect document, be controlled at runtime, and
report its active state. Trail records movement of its owner node.
auto* emitterNode = scene()->createNode("emitter");
auto* emitter = emitterNode->addComponent<snowpulse::ParticleEffects>();
scene()->rootNode()->addChild(emitterNode);
if (emitter->effects("effects/burst.json")) {
emitter->play(true);
}
emitter->emit(4); // Emit an additional burst without rebuilding the component.
auto* trailNode = scene()->createNode("trail");
auto* trail = trailNode->addComponent<snowpulse::Trail>();
trail->width = 10.0f;
trail->lifetime = 0.45f;
trail->color = { 0.3f, 0.8f, 1.0f, 0.8f };
scene()->rootNode()->addChild(trailNode);
Script Helpers¶
Script includes helpers for quick node creation:
createNodeAndComponent<T>(name, ...)creates a detached scene-owned node and adds a component; parent the returned component's owner explicitly.addChildNodeAndComponent<T>(name, ...)does the same, then parents the node under the script owner.
class Spawner final : public snowpulse::Script {
public:
void onStart() override {
auto* fx = createNodeAndComponent<snowpulse::ParticleEffects>("spawn_fx");
fx->effects("effects/spawn.json");
addChild(fx->owner());
auto* badge = addChildNodeAndComponent<snowpulse::SpriteRenderer>("badge");
badge->sprite(assetManager()->loadSprite("ui_badge.png"));
}
};
Late Update Components¶
Use LateUpdatable for behavior that should run after movement/physics and
other normal Updatable logic in the same frame (for example, camera follow
that must read final target positions).
class CameraFollow final : public snowpulse::Script, public snowpulse::LateUpdatable {
public:
void onUpdate(const float dt) override {
(void)dt;
// Normal application update.
}
void lateUpdate(const float dt) override {
(void)dt;
// runs after all Updatable components
}
};
Sample Usage¶
auto* node = scene()->createNode("content");
node->transform().position = { 0.0f, 0.0f, 0.0f };
auto* sprite = node->addComponent<snowpulse::SpriteRenderer>();
sprite->sprite(assetManager()->loadSprite("images/icon.png"));
auto* spine = node->addComponent<snowpulse::SpineRenderer>();
spine->load("spine/character/character.atlas",
"spine/character/character.json", 1.0f);
spine->setAnimation(0, "loop", true);
auto* tiled = node->addComponent<snowpulse::TiledRenderer>();
tiled->load("maps/layout.tmj");
#if SNOWPULSE_HAS_EFFEKSEER
// Also include <runtime/components/effekseer_renderer.h> when this feature is enabled.
auto* efk = node->addComponent<snowpulse::EffekseerRenderer>();
efk->sortOrder = 8;
efk->load("effects/sample.efkefc", 1.0f);
efk->playPrimary();
#endif
scene()->rootNode()->addChild(node);