Suggestion: Return multiple entities for single tile
It would be very useful if addLevel() allowed a single tile to return multiple entities.
Here's an example of how this would be useful:
"X": () => [
[
sprite("tile-background"),
layer("background")
],
[
sprite("enemy"),
layer("gameObjects")
],
[
sprite("tile-foreground"),
layer("foreground")
]
]
If it helps anyone else, I figured out how to do this. Basically, I use the normally-created object's "add" trigger to create multiple game objects offset by the grid position of that empty object.
- I define my symbols like this:
"X": () = funcThatAddsSomeGameObjects()
- And my functions like this:
const funcThatAddsSomeGameObjects = () => {
return [
{
add() {
// first object in the grid cell
k.add([
k.sprite("bean"),
k.pos(this.gridPos.scale(160)) // This is the important part. 160 is my grid size.
])
// another object in the same grid cell
k.add([
k.sprite("friendOfBean"),
k.pos(this.gridPos.scale(160))
])
}
}
]
}
It's not pretty but it works. I still think it would be a useful feature to make this easier to achieve.
A better way:
I ended up creating my ownbuildLevel() function that looks up a function based on the symbol used in the map:
- Create a plan and a mapping of symbols to functions:
export const level = buildLevel({
cellSize: CELL_SIZE,
map: [
"#############",
"[..].|..o..r]",
"[.o...P|..r.]",
"[..|r....|.o]",
"#############",
],
keys: {
"P": (x,y) => spawnPlayer(x, y),
".": (x,y) => emptyCell(x, y),
"|": (x,y) => doorCell(x, y),
"o": (x,y) => holeCell(x, y),
"[": (x,y) => leftWall(x, y),
"]": (x,y) => rightWall(x, y),
"#": (x,y) => topWall(x, y),
"r": (x,y) => faucetCell(x, y),
}
})
- A function for parsing:
export function buildLevel(config) {
const rows = config.map.length - 1
const cols = config.map[0].length - 1
for (let y = 0; y <= rows; y++) {
for (let x = 0; x <= cols; x++) {
config.keys[config.map[y][x]](
x * config.cellSize, y * config.cellSize
)
}
}
}
- The function themselves can add as many elements to the same position of the scene as necessary: (NOTE: this example uses parent/child elements as layers, which are planned in an upcoming version of Kaboom)
export function rightWall(x, y) {
farPlane.add([
pos(x, y),
sprite("rightWall"),
area(),
solid(),
outview({
hide: true
})
])
nearPlane.add([
pos(x, y),
sprite("rightWallFront"),
area(),
outview({
hide: true
})
])
}
Hey sorry was meaning to respond but don't have a good answer yet. The current way I'm doing it is using multiple addLevels like in the spriteatlas demo, but I also like your way posted above. Another syntax could be to have a children() component that just adds the children in the add() event, like
"[": () => [
children([
sprite("rightWall"),
area(),
solid(),
outview({
hide: true
})
], [
sprite("rightWallFront"),
area(),
outview({
hide: true
})
]),
],
Also like the buildLevel API style here, with the explicit map and keys fields, looks more intuitive than the current addLevel() 🤔
Thanks @slmjkdbtl
Yeah, the reason I needed a solution other than what's in the spriteatlas demo is that I wanted all the logic to render each grid cell to be encapsulated into a single function, even though the elements within span multiple layers. (My game uses foreground and background layers to create an illusion of depth).
I'm not sure if your solution above would let me freely choose which game layer each entity within the cell should belong to.
But regardless, in short, I think the most flexible approach would be to pass a grid position into a function that decides what happens there. But I don't know if my suggestion is necessarily best for beginners and the spirit of the framework.
Glad you like the API approach I did withmap and keys!