From 482a344f459fe64d95ddb5f95cf834503ba30ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 15 Apr 2020 19:23:06 +0200 Subject: [PATCH 1/3] Autoload tiles This commit adds a listener in the preload function that will be triggered as soon as the map is loaded. This function will load the resources from the map (tilesets) defined in the map. That way, we don't have to define manually the list of tiles that have to be loaded (at the expense of a slight delay in loading since we must wait for the map to be loaded to start loading the tiles). --- front/src/Phaser/Game/GameScene.ts | 14 +++- front/src/Phaser/Map/ITiledMap.ts | 113 +++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 front/src/Phaser/Map/ITiledMap.ts diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 582312dc..a089842b 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -3,6 +3,7 @@ import {MessageUserPositionInterface} from "../../Connexion"; import {CurrentGamerInterface, GamerInterface, Player} from "../Player/Player"; import {DEBUG_MODE, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable"; import Tile = Phaser.Tilemaps.Tile; +import {ITiledMap} from "../Map/ITiledMap"; export enum Textures { Rock = 'rock', @@ -39,8 +40,17 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ //hook preload scene preload(): void { - this.load.image(Textures.Tiles, 'maps/tiles.png'); - this.load.tilemapTiledJSON(Textures.Map, 'maps/map2.json'); + let mapUrl = 'maps/map2.json'; + this.load.on('filecomplete-tilemapJSON-'+Textures.Map, (key: string, type: string, data: any) => { + // Triggered when the map is loaded + // Load tiles attached to the map recursively + let map: ITiledMap = data.data; + map.tilesets.forEach((tileset) => { + let path = mapUrl.substr(0, mapUrl.lastIndexOf('/')); + this.load.image(tileset.name, path + '/' + tileset.image); + }) + }); + this.load.tilemapTiledJSON(Textures.Map, mapUrl); this.load.image(Textures.Rock, 'resources/objects/rockSprite.png'); this.load.spritesheet(Textures.Player, 'resources/characters/pipoya/Male 01-1.png', diff --git a/front/src/Phaser/Map/ITiledMap.ts b/front/src/Phaser/Map/ITiledMap.ts new file mode 100644 index 00000000..ca10f218 --- /dev/null +++ b/front/src/Phaser/Map/ITiledMap.ts @@ -0,0 +1,113 @@ +/** + * Tiled Map Interface + * + * Represents the interface for the Tiled exported data structure (JSON). Used + * when loading resources via Resource loader. + */ +export interface ITiledMap { + width: number; + height: number; + layers: ITiledMapLayer[]; + nextobjectid: number; + + /** + * Map orientation (orthogonal) + */ + orientation: string; + properties: {[key: string]: string}; + + /** + * Render order (right-down) + */ + renderorder: string; + tileheight: number; + tilewidth: number; + tilesets: ITiledTileSet[]; + version: number; +} + +export interface ITiledMapLayer { + data: number[]|string; + height: number; + name: string; + opacity: number; + properties: {[key: string]: string}; + encoding: string; + compression?: string; + + /** + * Type of layer (tilelayer, objectgroup) + */ + type: string; + visible: boolean; + width: number; + x: number; + y: number; + + /** + * Draw order (topdown (default), index) + */ + draworder: string; + objects: ITiledMapObject[]; +} + +export interface ITiledMapObject { + id: number; + + /** + * Tile object id + */ + gid: number; + height: number; + name: string; + properties: {[key: string]: string}; + rotation: number; + type: string; + visible: boolean; + width: number; + x: number; + y: number; + + /** + * Whether or not object is an ellipse + */ + ellipse: boolean; + + /** + * Polygon points + */ + polygon: {x: number, y: number}[]; + + /** + * Polyline points + */ + polyline: {x: number, y: number}[]; +} + +export interface ITiledTileSet { + firstgid: number; + image: string; + + imageheight: number; + imagewidth: number; + margin: number; + name: string; + properties: {[key: string]: string}; + spacing: number; + tilecount: number; + tileheight: number; + tilewidth: number; + transparentcolor: string; + terrains: ITiledMapTerrain[]; + tiles: {[key: string]: { terrain: number[] }}; + + /** + * Refers to external tileset file (should be JSON) + */ + source: string; +} + +export interface ITiledMapTerrain { + name: string; + tile: number; +} From 5f118c2a4a5eb13867f6f60735ef80442e74bbdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 15 Apr 2020 19:39:26 +0200 Subject: [PATCH 2/3] Removing all reference to 'Tiles' constant in code --- front/src/Phaser/Game/GameScene.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index a089842b..e8597d73 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -8,8 +8,7 @@ import {ITiledMap} from "../Map/ITiledMap"; export enum Textures { Rock = 'rock', Player = 'playerModel', - Map = 'map', - Tiles = 'tiles' + Map = 'map' } export interface GameSceneInterface extends Phaser.Scene { @@ -21,21 +20,25 @@ export interface GameSceneInterface extends Phaser.Scene { export class GameScene extends Phaser.Scene implements GameSceneInterface{ GameManager : GameManagerInterface; RoomId : string; - Terrain : Phaser.Tilemaps.Tileset; + Terrains : Array; CurrentPlayer: CurrentGamerInterface; MapPlayers : Phaser.Physics.Arcade.Group; Map: Phaser.Tilemaps.Tilemap; Layers : Array; Objects : Array; + tilesetKeys : Array; startX = (window.innerWidth / 2) / RESOLUTION; startY = (window.innerHeight / 2) / RESOLUTION; + constructor(RoomId : string, GameManager : GameManagerInterface) { super({ key: "GameScene" }); this.RoomId = RoomId; this.GameManager = GameManager; + this.tilesetKeys = []; + this.Terrains = []; } //hook preload scene @@ -48,6 +51,7 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ map.tilesets.forEach((tileset) => { let path = mapUrl.substr(0, mapUrl.lastIndexOf('/')); this.load.image(tileset.name, path + '/' + tileset.image); + this.tilesetKeys.push(tileset.name); }) }); this.load.tilemapTiledJSON(Textures.Map, mapUrl); @@ -66,16 +70,17 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ //initalise map this.Map = this.add.tilemap("map"); - this.Terrain = this.Map.addTilesetImage("tiles", "tiles"); - this.Map.createStaticLayer("tiles", "tiles"); + this.tilesetKeys.forEach((key: string) => { + this.Terrains.push(this.Map.addTilesetImage(key, key)); + }); //permit to set bound collision this.physics.world.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels); //add layer on map this.Layers = new Array(); - this.addLayer( this.Map.createStaticLayer("Calque 1", [this.Terrain], 0, 0).setDepth(-2) ); - this.addLayer( this.Map.createStaticLayer("Calque 2", [this.Terrain], 0, 0).setDepth(-1) ); + this.addLayer( this.Map.createStaticLayer("Calque 1", this.Terrains, 0, 0).setDepth(-2) ); + this.addLayer( this.Map.createStaticLayer("Calque 2", this.Terrains, 0, 0).setDepth(-1) ); //add entities this.Objects = new Array(); From 8ddd4656e64c27bb47a5da82067c5d97893dd4c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 15 Apr 2020 23:10:12 +0200 Subject: [PATCH 3/3] Adding automatic loading of layers --- README.md | 13 ++++++++++++- doc/images/tiled_screenshot_1.png | Bin 0 -> 26594 bytes front/dist/maps/map2.json | 25 ++++++++++++++++++------- front/src/Phaser/Game/GameScene.ts | 28 ++++++++++++++++++---------- 4 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 doc/images/tiled_screenshot_1.png diff --git a/README.md b/README.md index d71c3f0b..6953dace 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,17 @@ Note: on some OSes, you will need to add this line to your `/etc/hosts` file: workadventure.localhost 127.0.0.1 ``` +## Designing a map + +If you want to design your own map, you can use [Tiled](https://www.mapeditor.org/). + +A few things to notice: + +- your map can have as many layers as your want +- your map MUST contain a layer named "floorLayer" of type "objectgroup" that represents the layer on which characters will be drawn. + +![](doc/images/tiled_screenshot_1.png) + ### MacOS developers, your environment with Vagrant If you are using MacOS, you can increase Docker performance using Vagrant. If you want more explanations, you can read [this medium article](https://medium.com/better-programming/vagrant-to-increase-docker-performance-with-macos-25b354b0c65c). @@ -98,4 +109,4 @@ Vagrant destroy * `Vagrant destroy`: delete your VM Vagrant. ## Features developed -You have more details of features developed in back [README.md](./back/README.md). \ No newline at end of file +You have more details of features developed in back [README.md](./back/README.md). diff --git a/doc/images/tiled_screenshot_1.png b/doc/images/tiled_screenshot_1.png new file mode 100644 index 0000000000000000000000000000000000000000..108dfed8989bddcb327a1a962a5bea4a31a1e1b8 GIT binary patch literal 26594 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 Objects : Array; - tilesetKeys : Array; + map: ITiledMap; startX = (window.innerWidth / 2) / RESOLUTION; startY = (window.innerHeight / 2) / RESOLUTION; @@ -37,7 +37,6 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ }); this.RoomId = RoomId; this.GameManager = GameManager; - this.tilesetKeys = []; this.Terrains = []; } @@ -47,11 +46,10 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ this.load.on('filecomplete-tilemapJSON-'+Textures.Map, (key: string, type: string, data: any) => { // Triggered when the map is loaded // Load tiles attached to the map recursively - let map: ITiledMap = data.data; - map.tilesets.forEach((tileset) => { + this.map = data.data; + this.map.tilesets.forEach((tileset) => { let path = mapUrl.substr(0, mapUrl.lastIndexOf('/')); this.load.image(tileset.name, path + '/' + tileset.image); - this.tilesetKeys.push(tileset.name); }) }); this.load.tilemapTiledJSON(Textures.Map, mapUrl); @@ -70,8 +68,8 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ //initalise map this.Map = this.add.tilemap("map"); - this.tilesetKeys.forEach((key: string) => { - this.Terrains.push(this.Map.addTilesetImage(key, key)); + this.map.tilesets.forEach((tileset: ITiledTileSet) => { + this.Terrains.push(this.Map.addTilesetImage(tileset.name, tileset.name)); }); //permit to set bound collision @@ -79,8 +77,18 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ //add layer on map this.Layers = new Array(); - this.addLayer( this.Map.createStaticLayer("Calque 1", this.Terrains, 0, 0).setDepth(-2) ); - this.addLayer( this.Map.createStaticLayer("Calque 2", this.Terrains, 0, 0).setDepth(-1) ); + let depth = -2; + this.map.layers.forEach((layer) => { + if (layer.type === 'tilelayer') { + this.addLayer( this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth) ); + } else if (layer.type === 'objectgroup' && layer.name === 'floorLayer') { + depth = -1; + } + }); + + if (depth === -2) { + throw new Error('Your map MUST contain a layer of type "objectgroup" whose name is "floorLayer" that represents the layer characters are drawn at.'); + } //add entities this.Objects = new Array();