MERGE and add floorLayer
This commit is contained in:
commit
95e51c7aa4
1
.env.template
Normal file
1
.env.template
Normal file
@ -0,0 +1 @@
|
||||
DEBUG_MODE=false
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.env
|
||||
.idea
|
||||
.vagrant
|
||||
Vagrantfile
|
13
README.md
13
README.md
@ -32,6 +32,19 @@ 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.
|
||||
- the tilesets in your map MUST be embedded. You can refer to an external typeset in a TSX file. Click the "embed tileset" button in the tileset tab to embed tileset data.
|
||||
- your map MUST be exported in JSON format. You need to use a recent version of Tiled to get JSON format export (1.3+)
|
||||
|
||||
![](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).
|
||||
|
BIN
doc/images/tiled_screenshot_1.png
Normal file
BIN
doc/images/tiled_screenshot_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
@ -13,6 +13,7 @@ services:
|
||||
front:
|
||||
image: thecodingmachine/nodejs:12
|
||||
environment:
|
||||
DEBUG_MODE: "$DEBUG_MODE"
|
||||
HOST: "0.0.0.0"
|
||||
NODE_ENV: development
|
||||
API_URL: http://api.workadventure.localhost
|
||||
|
13
front/dist/maps/map2.json
vendored
13
front/dist/maps/map2.json
vendored
File diff suppressed because one or more lines are too long
@ -1,9 +1,13 @@
|
||||
const DEBUG_MODE: boolean = !!process.env.DEBUG_MODE || false;
|
||||
const API_URL = process.env.API_URL || "http://api.workadventure.localhost";
|
||||
const ROOM = [process.env.ROOM || "THECODINGMACHINE"];
|
||||
const RESOLUTION = 2;
|
||||
const ZOOM_LEVEL = 3/4;
|
||||
|
||||
export {
|
||||
DEBUG_MODE,
|
||||
API_URL,
|
||||
RESOLUTION,
|
||||
ZOOM_LEVEL,
|
||||
ROOM
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import {RESOLUTION} from "../../Enum/EnvironmentVariable";
|
||||
import {Player} from "../Player/Player";
|
||||
import {GameSceneInterface} from "./GameScene";
|
||||
|
||||
export interface CameraManagerInterface {
|
||||
moveCamera(CurrentPlayer : Player) : void;
|
||||
}
|
||||
|
||||
export class CameraManager implements CameraManagerInterface{
|
||||
Scene : GameSceneInterface;
|
||||
Camera : Phaser.Cameras.Scene2D.Camera;
|
||||
|
||||
constructor(
|
||||
Scene: GameSceneInterface,
|
||||
Camera : Phaser.Cameras.Scene2D.Camera,
|
||||
) {
|
||||
this.Scene = Scene;
|
||||
this.Camera = Camera;
|
||||
}
|
||||
|
||||
moveCamera(CurrentPlayer : Player): void {
|
||||
//center of camera
|
||||
let startX = ((window.innerWidth / 2) / RESOLUTION);
|
||||
let startY = ((window.innerHeight / 2) / RESOLUTION);
|
||||
|
||||
let limit = {
|
||||
top: startY,
|
||||
left: startX,
|
||||
bottom : this.Scene.Map.heightInPixels - startY,
|
||||
right: this.Scene.Map.widthInPixels - startX,
|
||||
};
|
||||
|
||||
if(CurrentPlayer.x < limit.left){
|
||||
this.Camera.scrollX = 0;
|
||||
}else if(CurrentPlayer.x > limit.right){
|
||||
this.Camera.scrollX = (limit.right - startX);
|
||||
}else {
|
||||
this.Camera.scrollX = (CurrentPlayer.x - startX);
|
||||
}
|
||||
|
||||
if(CurrentPlayer.y < limit.top){
|
||||
this.Camera.scrollY = 0;
|
||||
}else if(CurrentPlayer.y > limit.bottom){
|
||||
this.Camera.scrollY = (limit.bottom - startY);
|
||||
}else {
|
||||
this.Camera.scrollY = (CurrentPlayer.y - startY);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,14 @@
|
||||
import {GameManagerInterface, StatusGameManagerEnum} from "./GameManager";
|
||||
import {MessageUserPositionInterface} from "../../Connexion";
|
||||
import {CameraManager, CameraManagerInterface} from "./CameraManager";
|
||||
import {CurrentGamerInterface, GamerInterface, Player} from "../Player/Player";
|
||||
import {RESOLUTION} from "../../Enum/EnvironmentVariable";
|
||||
import {DEBUG_MODE, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable";
|
||||
import Tile = Phaser.Tilemaps.Tile;
|
||||
import {ITiledMap, ITiledTileSet} from "../Map/ITiledMap";
|
||||
|
||||
export enum Textures {
|
||||
Rock = 'rock',
|
||||
Player = 'playerModel',
|
||||
Map = 'map',
|
||||
Tiles = 'tiles',
|
||||
Tiles2 = 'tiles2'
|
||||
Map = 'map'
|
||||
}
|
||||
|
||||
export interface GameSceneInterface extends Phaser.Scene {
|
||||
@ -22,29 +20,43 @@ export interface GameSceneInterface extends Phaser.Scene {
|
||||
export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
||||
GameManager : GameManagerInterface;
|
||||
RoomId : string;
|
||||
Terrain : Phaser.Tilemaps.Tileset;
|
||||
Camera: CameraManagerInterface;
|
||||
Terrains : Array<Phaser.Tilemaps.Tileset>;
|
||||
CurrentPlayer: CurrentGamerInterface;
|
||||
MapPlayers : Phaser.Physics.Arcade.Group;
|
||||
Map: Phaser.Tilemaps.Tilemap;
|
||||
Layers : Array<Phaser.Tilemaps.StaticTilemapLayer>;
|
||||
Objects : Array<Phaser.Physics.Arcade.Sprite>;
|
||||
map: ITiledMap;
|
||||
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.Terrains = [];
|
||||
}
|
||||
|
||||
//hook preload scene
|
||||
preload(): void {
|
||||
this.load.image(Textures.Tiles, 'maps/floortileset.png');
|
||||
this.load.image(Textures.Tiles2, 'maps/tilesets_deviant_milkian_1.png');
|
||||
this.load.tilemapTiledJSON(Textures.Map, 'maps/map.json');
|
||||
let mapUrl = 'maps/map.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
|
||||
this.map = data.data;
|
||||
this.map.tilesets.forEach((tileset) => {
|
||||
if (typeof tileset.name === 'undefined' || typeof tileset.image === 'undefined') {
|
||||
console.warn("Don't know how to handle tileset ", tileset)
|
||||
return;
|
||||
}
|
||||
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',
|
||||
@ -60,18 +72,27 @@ 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.Terrain = this.Map.addTilesetImage("tiles2", "tiles2");
|
||||
this.Map.createStaticLayer("tiles2", "tiles2");
|
||||
this.map.tilesets.forEach((tileset: ITiledTileSet) => {
|
||||
this.Terrains.push(this.Map.addTilesetImage(tileset.name, tileset.name));
|
||||
});
|
||||
|
||||
//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<Phaser.Tilemaps.StaticTilemapLayer>();
|
||||
this.addLayer( this.Map.createStaticLayer("bottom", [this.Terrain], 0, 0).setDepth(-2) );
|
||||
this.addLayer( this.Map.createStaticLayer("top", [this.Terrain], 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<Phaser.Physics.Arcade.Sprite>();
|
||||
@ -80,14 +101,22 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
||||
//init event click
|
||||
this.EventToClickOnTile();
|
||||
|
||||
//initialise camera
|
||||
this.Camera = new CameraManager(this, this.cameras.main);
|
||||
|
||||
//initialise list of other player
|
||||
this.MapPlayers = this.physics.add.group({ immovable: true });
|
||||
|
||||
//notify game manager can to create currentUser in map
|
||||
this.GameManager.createCurrentPlayer();
|
||||
|
||||
|
||||
//initialise camera
|
||||
this.initCamera();
|
||||
}
|
||||
|
||||
//todo: in a dedicated class/function?
|
||||
initCamera() {
|
||||
this.cameras.main.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels);
|
||||
this.cameras.main.startFollow(this.CurrentPlayer);
|
||||
this.cameras.main.setZoom(ZOOM_LEVEL);
|
||||
}
|
||||
|
||||
addLayer(Layer : Phaser.Tilemaps.StaticTilemapLayer){
|
||||
@ -101,13 +130,14 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
||||
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
|
||||
});
|
||||
Layer.setCollisionByProperty({collides: true});
|
||||
//debug code
|
||||
if (DEBUG_MODE) {
|
||||
//debug code to see the collision hitbox of the object in the top layer
|
||||
/*Layer.renderDebug(this.add.graphics(), {
|
||||
Layer.renderDebug(this.add.graphics(), {
|
||||
tileColor: null, //non-colliding tiles
|
||||
collidingTileColor: new Phaser.Display.Color(243, 134, 48, 200), // Colliding tiles,
|
||||
faceColor: new Phaser.Display.Color(40, 39, 37, 255) // Colliding face edges
|
||||
});*/
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -131,7 +161,6 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
||||
this,
|
||||
this.startX,
|
||||
this.startY,
|
||||
this.Camera,
|
||||
);
|
||||
this.CurrentPlayer.initAnimation();
|
||||
|
||||
@ -146,6 +175,7 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
||||
//pixel position toz tile position
|
||||
let tile = this.Map.getTileAt(this.Map.worldToTileX(pointer.worldX), this.Map.worldToTileY(pointer.worldY));
|
||||
if(tile){
|
||||
console.log(tile)
|
||||
this.CurrentPlayer.say("Your touch " + tile.layer.name);
|
||||
}
|
||||
});
|
||||
@ -214,7 +244,6 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
||||
this,
|
||||
MessageUserPosition.position.x,
|
||||
MessageUserPosition.position.y,
|
||||
this.Camera,
|
||||
);
|
||||
player.initAnimation();
|
||||
this.MapPlayers.add(player);
|
||||
|
113
front/src/Phaser/Map/ITiledMap.ts
Normal file
113
front/src/Phaser/Map/ITiledMap.ts
Normal file
@ -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;
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import {getPlayerAnimations, playAnimation, PlayerAnimationNames} from "./Animation";
|
||||
import {GameSceneInterface, Textures} from "../Game/GameScene";
|
||||
import {ConnexionInstance} from "../Game/GameManager";
|
||||
import {CameraManagerInterface} from "../Game/CameraManager";
|
||||
import {MessageUserPositionInterface} from "../../Connexion";
|
||||
import {ActiveEventList, UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
|
||||
import {PlayableCaracter} from "../Entity/PlayableCaracter";
|
||||
@ -9,7 +8,6 @@ import {PlayableCaracter} from "../Entity/PlayableCaracter";
|
||||
export interface CurrentGamerInterface extends PlayableCaracter{
|
||||
userId : string;
|
||||
PlayerValue : string;
|
||||
CameraManager: CameraManagerInterface;
|
||||
initAnimation() : void;
|
||||
moveUser() : void;
|
||||
say(text : string) : void;
|
||||
@ -18,7 +16,6 @@ export interface CurrentGamerInterface extends PlayableCaracter{
|
||||
export interface GamerInterface extends PlayableCaracter{
|
||||
userId : string;
|
||||
PlayerValue : string;
|
||||
CameraManager: CameraManagerInterface;
|
||||
initAnimation() : void;
|
||||
updatePosition(MessageUserPosition : MessageUserPositionInterface) : void;
|
||||
say(text : string) : void;
|
||||
@ -27,7 +24,6 @@ export interface GamerInterface extends PlayableCaracter{
|
||||
export class Player extends PlayableCaracter implements CurrentGamerInterface, GamerInterface {
|
||||
userId: string;
|
||||
PlayerValue: string;
|
||||
CameraManager: CameraManagerInterface;
|
||||
userInputManager: UserInputManager;
|
||||
|
||||
constructor(
|
||||
@ -35,7 +31,6 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G
|
||||
Scene: GameSceneInterface,
|
||||
x: number,
|
||||
y: number,
|
||||
CameraManager: CameraManagerInterface,
|
||||
PlayerValue: string = Textures.Player
|
||||
) {
|
||||
super(Scene, x, y, PlayerValue, 1);
|
||||
@ -46,7 +41,6 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G
|
||||
//set data
|
||||
this.userId = userId;
|
||||
this.PlayerValue = PlayerValue;
|
||||
this.CameraManager = CameraManager;
|
||||
|
||||
//the current player model should be push away by other players to prevent conflict
|
||||
this.setImmovable(false);
|
||||
@ -96,7 +90,6 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G
|
||||
this.stop();
|
||||
}
|
||||
this.sharePosition(direction);
|
||||
this.CameraManager.moveCamera(this);
|
||||
}
|
||||
|
||||
private sharePosition(direction: string) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'phaser';
|
||||
import GameConfig = Phaser.Types.Core.GameConfig;
|
||||
import {GameManager} from "./Phaser/Game/GameManager";
|
||||
import {RESOLUTION} from "./Enum/EnvironmentVariable";
|
||||
import {DEBUG_MODE, RESOLUTION} from "./Enum/EnvironmentVariable";
|
||||
|
||||
let gameManager = new GameManager();
|
||||
|
||||
@ -15,7 +15,7 @@ const config: GameConfig = {
|
||||
physics: {
|
||||
default: "arcade",
|
||||
arcade: {
|
||||
debug: true
|
||||
debug: DEBUG_MODE
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -29,6 +29,6 @@ module.exports = {
|
||||
new webpack.ProvidePlugin({
|
||||
Phaser: 'phaser'
|
||||
}),
|
||||
new webpack.EnvironmentPlugin(['API_URL'])
|
||||
new webpack.EnvironmentPlugin(['API_URL', 'DEBUG_MODE'])
|
||||
]
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user