Svg Plugin
The Clay SVG plugin provides comprehensive SVG (Scalable Vector Graphics) support for Clayground applications. It enables reading SVG files to extract game objects, writing SVG files programmatically, and using SVG elements as image sources. This makes it perfect for level design, vector graphics assets, and data-driven game content.
Getting Started
To use the Clay SVG plugin in your QML files:
import Clayground.Svg
Core Components
- SvgReader - Reads and parses SVG files, emitting signals for each shape found (rectangles, circles, polygons, polylines).
- SvgWriter - Creates SVG files programmatically with support for basic shapes and metadata.
- SvgImageSource - Provides access to individual SVG elements as image sources for use in QML Image components.
Usage Examples
Reading SVG for Level Data
import QtQuick
import Clayground.Svg
Item {
SvgReader {
id: levelReader
source: "levels/level1.svg"
onRectangle: (id, x, y, width, height, fillColor, strokeColor, description) => {
// Parse description for game object type
let data = JSON.parse(description)
if (data.type === "platform") {
createPlatform(x, y, width, height)
} else if (data.type === "enemy") {
createEnemy(x, y, width, height, data.enemyType)
}
}
onCircle: (id, x, y, radius, fillColor, strokeColor, description) => {
let data = JSON.parse(description)
if (data.type === "coin") {
createCoin(x, y, radius)
}
}
onPolyline: (id, points, fillColor, strokeColor, description) => {
let data = JSON.parse(description)
if (data.type === "patrol_path") {
createPatrolPath(id, points)
}
}
}
}
Writing Game Data to SVG
SvgWriter {
id: mapWriter
path: "output/game_map.svg"
function saveGameState(world) {
begin(world.width, world.height)
// Save platforms
for (let platform of world.platforms) {
rectangle(
platform.x,
platform.y,
platform.width,
platform.height,
JSON.stringify({
type: "platform",
material: platform.material
})
)
}
// Save collectibles
for (let coin of world.coins) {
circle(
coin.x,
coin.y,
coin.radius,
JSON.stringify({
type: "coin",
value: coin.value
})
)
}
// Save paths
for (let path of world.paths) {
polyline(
path.points,
JSON.stringify({
type: "path",
name: path.name
})
)
}
end()
}
}
Using SVG Elements as Images
import Clayground.Svg
Item {
SvgImageSource {
id: svgAssets
svgPath: "assets/game_sprites.svg"
annotationRRGGBB: "FF00FF" // Ignore magenta annotations
}
Image {
source: svgAssets.source("player_idle")
width: 64
height: 64
}
Image {
source: svgAssets.source("enemy_sprite")
visible: svgAssets.has("enemy_sprite")
}
}
Level Editor Integration
Item {
// Read level template
SvgReader {
id: templateReader
source: "templates/base_level.svg"
property var objects: []
onBegin: {
// Clear existing objects
for (let obj of objects) obj.destroy()
objects = []
}
onRectangle: (id, x, y, width, height, fillColor, strokeColor, description) => {
let obj = gameObjectComponent.createObject(gameWorld, {
x: x,
y: y,
width: width,
height: height,
objectData: JSON.parse(description)
})
objects.push(obj)
}
}
// Save edited level
SvgWriter {
id: levelSaver
function saveLevel(filename) {
path = filename
begin(gameWorld.width, gameWorld.height)
for (let obj of templateReader.objects) {
if (obj.shape === "rectangle") {
rectangle(
obj.x,
obj.y,
obj.width,
obj.height,
JSON.stringify(obj.objectData)
)
}
}
end()
}
}
}
Dynamic Asset Loading
Item {
property var svgSources: ({})
function loadSvgAssets(category) {
let source = svgSourceComponent.createObject(this, {
svgPath: `assets/${category}_sprites.svg`
})
svgSources[category] = source
}
Component {
id: svgSourceComponent
SvgImageSource {}
}
function getAssetSource(category, assetId) {
if (svgSources[category] && svgSources[category].has(assetId)) {
return svgSources[category].source(assetId)
}
return ""
}
}
SVG-Based Animation Frames
SvgImageSource {
id: animationFrames
svgPath: "animations/character_walk.svg"
}
AnimatedImage {
property int frame: 0
source: animationFrames.source("walk_frame_" + frame)
Timer {
interval: 100
repeat: true
running: true
onTriggered: {
parent.frame = (parent.frame + 1) % 8
}
}
}
Best Practices
-
Structured Metadata: Use JSON in description fields for structured game data.
-
Naming Conventions: Use consistent ID naming for SVG elements (e.g., “platform_01”, “enemy_goblin”).
-
Coordinate Systems: Ensure your SVG coordinate system matches your game world units.
-
Layer Organization: Use SVG groups to organize different types of game objects.
-
Color Annotations: Use a specific color (like magenta) for editor-only annotations.
-
File Watching: The SvgReader automatically watches files for changes, useful during development.
Technical Implementation
The Clay SVG plugin provides:
- Qt SVG Renderer: Uses Qt’s SVG rendering for image provider functionality
- Custom Parser: Implements streaming XML parser for efficient shape extraction
- Simple SVG Writer: Uses the simple-svg-writer library for SVG generation
- Image Provider: Registered as “claysvg” for accessing individual SVG elements
- File Watching: Automatic file change detection for live reloading
- Cache Management: Efficient caching of rendered SVG elements
The plugin supports standard SVG shapes (rectangles, circles, polygons, polylines) and preserves metadata through description attributes, making it ideal for level design workflows.