Skip to content

Commit

Permalink
Implement isometric level support
Browse files Browse the repository at this point in the history
Refactor isometric grid rendering

Fix isometric level demo

Improve isometric level support and add more demo examples

Lint

addIsometricLevel: Mirror width and height property names from addLevel

Remove unnecessary from2dTo1dIndex method of isometricLevel

Rename and lint isometricLevel interface to match levels
  • Loading branch information
gugiserman committed Aug 20, 2022
1 parent 9c4c3f3 commit dcba23c
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 0 deletions.
Binary file added assets/sprites/iso_grass.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/sprites/iso_grass_darker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions demo/isometricLevel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Build levels with addIsometricLevel()

// Start game
kaboom()

// Load assets
loadSprite("grass", "/sprites/iso_grass.png")
loadSprite("darker_grass", "/sprites/iso_grass_darker.png")

gravity(0)
camScale(0.33)
camPos(vec2(100, 1000))

addIsometricLevel([
// Design the isometric level layout with symbols
// 15x15 grid in this case so we have a perfect tiled square diamond shaped map
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@!@@@@!@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@!@@@!@@@@@",
"@@@@@@!!!@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
], {
// The size of each grid
width: 144,
height: 71,
// Define what each symbol means (in components)
"@": () => [
sprite("grass"),
],
"!": () => [
sprite("darker_grass"),
],
})

addIsometricLevel([
// 15x9 grid
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@!@@@@@@@",
"@@@@@@@!@@@@@@@",
"@@@@@@@!@@@@@@@",
"@@@@@@@!@@@@@@@",
"@@@@@@@!@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@!@@@@@@@",
"@@@@@@@@@@@@@@@",
"@@@@@@@@@@@@@@@",
], {
width: 144,
height: 71,
"@": () => [
sprite("grass"),
],
"!": () => [
sprite("darker_grass"),
],
// The position of the top left block
pos: vec2(0, 1200),
})
116 changes: 116 additions & 0 deletions src/kaboom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ import {
BoomOpt,
PeditFile,
Shape,
IsometricLevelOpt,
IsometricLevel,
} from "./types"

import FPSCounter from "./fps"
Expand Down Expand Up @@ -5094,6 +5096,18 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {

}

function isometricGrid(isometricLevel: IsometricLevel, position: Vec2) {

return {

id: "isometricGrid",
gridPos: position.clone(),
level: isometricLevel,

}

}

function addLevel(map: string[], opt: LevelOpt): GameObj<PosComp | LevelComp> {

if (!opt.width || !opt.height) {
Expand Down Expand Up @@ -5193,6 +5207,106 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {

}

function addIsometricLevel(map: string[], options: IsometricLevelOpt): IsometricLevel {
if (!options.width || !options.height) {
throw new Error("Must provide isometric level tile width & height.")
}

const objects: GameObj[] = []
const offset = vec2(options.pos || vec2(0))

const halfTileWidth = Math.floor(options.width / 2)
const halfTileHeight = Math.floor(options.height / 2)

const maxWidthInTiles = map.reduce((width, row): number => Math.max(width, row.length), 0)
const heightInTiles = map.length

const isometricLevel = {
offset() {
return offset.clone()
},

gridWidth() {
return options.width
},

gridHeight() {
return options.height
},

getPos: (...args): Vec2 => {
const p = vec2(...args)
return vec2((p.x - p.y) * halfTileWidth, (p.x + p.y) * halfTileHeight)
},

spawn: (position: Vec2, symbol: string): GameObj => {
const comps = (() => {
if (options[symbol]) {
if (typeof options[symbol] !== "function") {
throw new Error("isometric level symbol def must be a function returning a component list")
}
return options[symbol](position)
} else if (options.any) {
return options.any(symbol, position)
}
})()

if (!comps) {
return
}

const posComp = vec2(
offset.x + position.x,
offset.y + position.y,
)

for (const comp of comps) {
if (comp.id === "pos") {
posComp.x += comp.pos.x
posComp.y += comp.pos.y

break
}
}

comps.push(pos(posComp))
comps.push(isometricGrid(this, position))

const object = add(comps)
objects.push(object)

return object
},

width() {
return maxWidthInTiles * options.width
},

height() {
return heightInTiles * options.height
},

destroy() {
for (const someObject of objects) {
destroy(someObject)
}
},
}

for (let row = 0; row < heightInTiles; row++) {
for (let col = 0; col < maxWidthInTiles; col++) {
const position = isometricLevel.getPos(col, row)
const rowContent: string = map[row]
const symbols: string[] = rowContent.split("")
const symbol = symbols[col]

isometricLevel.spawn(position, symbol)
}
}

return isometricLevel
}

function record(frameRate?): Recording {

const stream = app.canvas.captureStream(frameRate)
Expand Down Expand Up @@ -6180,6 +6294,8 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
go,
// level
addLevel,
// isometric level
addIsometricLevel,
// storage
getData,
setData,
Expand Down
73 changes: 73 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,46 @@ export interface KaboomCtx {
* ```
*/
addLevel(map: string[], options: LevelOpt): GameObj,
/**
* Construct a isometric level based on symbols.
*
* @section IsometricLevel
*
* @example
* ```js
* addIsometricLevel([
* // Design the isometric level layout with symbols
* // 15x15 grid in this case so we have a perfect tiled square diamond shaped map
* "@@@@@@@@@@@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@!@@@@!@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@@!@@@!@@@@@",
* "@@@@@@!!!@@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@@@@@@@@@@@@",
* "@@@@@@@@@@@@@@@",
* ], {
* // The size of each grid
* width: 144,
* height: 71,
* // Define what each symbol means (in components)
* "@": () => [
* sprite("grass"),
* ],
* "!": () => [
* sprite("darker_grass"),
* ],
* })
* ```
*/
addIsometricLevel(map: string[], options: IsometricLevelOpt): IsometricLevel,
/**
* Get data from local storage, if not present can set to a default value.
*
Expand Down Expand Up @@ -4313,6 +4353,39 @@ export interface LevelComp extends Comp {
levelHeight(): number,
}

export interface IsometricLevelOpt {
/**
* Width of each block.
*/
width: number,
/**
* Height of each block.
*/
height: number,
/**
* Position of the first block.
*/
pos?: Vec2,
/**
* Called when encountered an undefined symbol.
*/
any?: (s: string, pos: Vec2) => CompList<any> | undefined,
// TODO: should return CompList<any>
[sym: string]: any,
}

export interface IsometricLevel {
spawn(position: Vec2, symbol: string): GameObj,
gridWidth(): number,
gridHeight(): number,
offset(): Vec2,
getPos(p: Vec2): Vec2,
getPos(x: number, y: number): Vec2,
width(): number,
height(): number,
destroy(): void,
}

export interface BoomOpt {
/**
* Animation speed.
Expand Down

0 comments on commit dcba23c

Please sign in to comment.