Skip to content

Instantly share code, notes, and snippets.

@timetocode
Last active June 15, 2019 19:32
Show Gist options
  • Save timetocode/5412558e4dacc08b1504759406114b70 to your computer and use it in GitHub Desktop.
Save timetocode/5412558e4dacc08b1504759406114b70 to your computer and use it in GitHub Desktop.
Demonstration of representing 2D tile maps as 1D arrays and sending their data via nengi. Includes a tilemap which is a "2d array"(not really) of integers, and examples for a "2d array" of objects.
var TileEnum = {
EMPTY: 0,
GRASS: 1,
DIRT: 2,
STONEWALL: 3
}
class TileMap {
constructor(width, height) {
this.width = width
this.height = height
this.tiles = []
for (var i = 0; i < width * height; i++) {
this.tiles.push(TileEnum.EMPTY)
}
}
// converts 2D coords to 1D index
getIndex(x, y) {
return x + this.width * y
}
// converts 1D index to 2D coords
getXY(index) {
return {
x: index % this.width,
y: Math.floor(index / this.width)
}
}
boundsCheck(x, y) {
// probably off by 1, untested
return (x >= 0 && x <= this.width && y >= 0 && y <= this.height)
}
setTile(x, y, value) {
if (!this.boundsCheck(x,y)) {
return // perhaps warn rather than silently fail?
}
this.tiles[this.getIndex(x, y)] = value
}
getTile(x, y) {
if (!this.boundsCheck(x,y)) {
return // perhaps warn rather than silently fail?
}
return this.tiles[this.getIndex(x, y)]
}
/* prints the map to console, only viable for small maps, example:
* 0 0 0 0 0
* 0 1 0 0 1
* 2 0 0 0 3
* 0 0 0 0 3
* 0 0 0 0 0
*/
debugPrint() {
for (var y = 0; y < this.height; y++) {
var lineOfText = ''
for (var x = 0; x < this.width; x++) {
var tile = this.getTile(x, y)
lineOfText += tile + ' '
}
console.log(lineOfText)
}
}
}
// example usage
var map = new TileMap(10, 10)
map.setTile(1, 1, TileEnum.STONEWALL)
map.setTile(2, 1, TileEnum.STONEWALL)
// if the tiles were objects..
// map.setTile(x, y, new Tile(foo,bar,baz))
// or more likely...
// map.getTile(x, y).bar = newBarValue
map.debugPrint()
// sending a map via nengi
class MapDataMessage {
constructor(width, height, tileData) {
this.width = width
this.height = height
this.tiles = tileData
}
}
MapDataMessage.protocol = {
width: nengi.UInt32, // or whatever size is needed, but no need to optimize this really, so big is fine
height: nengi.UInt32, // not *really* needed tbh.. the math that goes 1D <=> 2D only uses x,y,index,width not height
// this is the nengi syntax for sending
// an array of `type` (UInt8) // ints from 0 to 255 (overkill for this example)
// with a max length of `index' (UInt32) // ints from 0 to 4294967295
// optimizing the `type` to be the smallest needed binary value
// can be valueable if the the array sent is very long and has small values
// optimizing the `index` is pretty pointless, unless sending many small arrays
// see all available types and their values: nengi\core\binary\Binary.js
tiles: { type: nengi.UInt8, index: nengi.UInt32 }
}
// usage of the message
...
instance.onConnect(client => {
// hypothetically sending the map to the player who just connected
var mapData = new MapDataMessage(map.width, map.height, map.tiles)
instance.message(mapData, client)
})
// usage on the clientside
...
snapshot.messages.forEach(message => {
if (message.protocol.name === 'MapDataMessage') {
var foo = new ClientSideRepresentationOfTheMap()
foo.populate(message.width, message.height, message.tiles)
}
})
// tilemaps where the tiles are Objects instead of integers
// TODO: modify tilemap as appropriate (skipped)
// and now the nengi side of things:
// Tile.js -- a tile that has numerous properties instead of just being an int
class Tile {
constructor() {
this.foo = 0
this.bar = 0
this.baz = 0
}
}
Tile.protocol = {
// if maps are huge, this is an area that pays to pick optimal types
// note that this would still probably work fine in dev env even with nengi.Number (which is the biggest type)
foo: nengi.Boolean, // true false
bar: nengi.UInt3, // 0 to 7
baz: nengi.UInt8 // 0 to 255
}
// MapDataMessage.js
var Tile = require('./path/to/Tile.js')
class MapDataMessage {
constructor(width, height, tileData) {
this.width = width
this.height = height
this.tiles = tileData
}
}
MapDataMessage.protocol = {
width: nengi.UInt32,
height: nengi.UInt32,
// this is the nengi syntax for sending an ARRAY of OBJECTS that themselves are defined by a protocol
tiles: { type: Tile.protocol, index: nengi.UInt32 }
}
// Final piece of the puzzle
// nengiConfig.js
...
messages: [
['MapDataMessage', require('./path/MapDataMessage.js')]
]
basics: [ // this is what the `basics` section of the config is for - protocols that are used inside of other protocols
['Tile', require('./path/Tile.js')]
]
@SupremeTechnopriest
Copy link

SupremeTechnopriest commented Jun 15, 2019

Line 157 now needs to be tiles: { type: Tile, index: nengi.UInt32 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment