Adding a notion of instances per mapAdding a notion of instances to room
The URL signature becomes: https://workadventu.re/_/[instance]/[path_to_map.json] This allows us to create many instances of the same map (and therefore to create several different worlds for different people) An exit on a map can target another "instance" by passing the "exitInstance" property.
This commit is contained in:
parent
1f2b33baec
commit
2448fef53a
@ -47,8 +47,9 @@ A few things to notice:
|
|||||||
|
|
||||||
In order to place an on your scene that leads to another scene:
|
In order to place an on your scene that leads to another scene:
|
||||||
|
|
||||||
- You must create an specific layer. When a character reaches ANY tile of that layer, it will exit the scene.
|
- You must create a specific layer. When a character reaches ANY tile of that layer, it will exit the scene.
|
||||||
- In layer properties, you must add "exitSceneUrl" property. It represents the map URL of the next scene. For example : `/<map folder>/<map>.json`. Be careful, if you want the next map to be correctly loaded, you must check that the map files are in folder `back/src/Assets/Maps/<your map folder>`. The files will be accessible by url `<HOST>/map/files/<your map folder>/...`.
|
- In layer properties, you MUST add "exitSceneUrl" property. It represents the map URL of the next scene. For example : `/<map folder>/<map>.json`. Be careful, if you want the next map to be correctly loaded, you must check that the map files are in folder `back/src/Assets/Maps/<your map folder>`. The files will be accessible by url `<HOST>/map/files/<your map folder>/...`.
|
||||||
|
- In layer properties, you CAN add an "exitInstance" property. If set, you will join the map of the specified instance. Otherwise, you will stay on the same instance.
|
||||||
- If you want to have multiple exits, you can create many layers with name "exit". Each layer has a different key `exitSceneUrl` and have tiles that represent exits to another scene.
|
- If you want to have multiple exits, you can create many layers with name "exit". Each layer has a different key `exitSceneUrl` and have tiles that represent exits to another scene.
|
||||||
|
|
||||||
![](doc/images/exit_layer_map.png)
|
![](doc/images/exit_layer_map.png)
|
||||||
|
@ -20,7 +20,8 @@ export class MapController {
|
|||||||
getMaps() {
|
getMaps() {
|
||||||
this.App.get("/maps", (req: Request, res: Response) => {
|
this.App.get("/maps", (req: Request, res: Response) => {
|
||||||
return res.status(OK).send({
|
return res.status(OK).send({
|
||||||
mapUrlStart: req.headers.host + "/map/files" + URL_ROOM_STARTED
|
mapUrlStart: req.headers.host + "/map/files" + URL_ROOM_STARTED,
|
||||||
|
startInstance: "global"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,6 @@ import {
|
|||||||
PointInterface
|
PointInterface
|
||||||
} from "../../Connexion";
|
} from "../../Connexion";
|
||||||
import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer";
|
import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer";
|
||||||
import {getMapKeyByUrl} from "../Login/LogincScene";
|
|
||||||
import ScenePlugin = Phaser.Scenes.ScenePlugin;
|
|
||||||
import {AddPlayerInterface} from "./AddPlayerInterface";
|
import {AddPlayerInterface} from "./AddPlayerInterface";
|
||||||
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
||||||
|
|
||||||
@ -169,13 +167,13 @@ export class GameManager {
|
|||||||
this.ConnexionInstance.sharePosition(event.x, event.y, event.direction, event.moving);
|
this.ConnexionInstance.sharePosition(event.x, event.y, event.direction, event.moving);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMap(mapUrl: string, scene: ScenePlugin): string {
|
loadMap(mapUrl: string, scene: Phaser.Scenes.ScenePlugin, instance: string): string {
|
||||||
let sceneKey = getMapKeyByUrl(mapUrl);
|
let sceneKey = GameScene.getMapKeyByUrl(mapUrl);
|
||||||
|
|
||||||
let gameIndex = scene.getIndex(sceneKey);
|
let gameIndex = scene.getIndex(sceneKey);
|
||||||
let game : Phaser.Scene = null;
|
let game : Phaser.Scene = null;
|
||||||
if(gameIndex === -1){
|
if(gameIndex === -1){
|
||||||
game = new GameScene(sceneKey, mapUrl);
|
game = GameScene.createFromUrl(mapUrl, instance);
|
||||||
scene.add(sceneKey, game, false);
|
scene.add(sceneKey, game, false);
|
||||||
}
|
}
|
||||||
return sceneKey;
|
return sceneKey;
|
||||||
|
@ -40,10 +40,17 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
MapKey: string;
|
MapKey: string;
|
||||||
MapUrlFile: string;
|
MapUrlFile: string;
|
||||||
|
RoomId: string;
|
||||||
|
instance: string;
|
||||||
|
|
||||||
PositionNextScene: Array<any> = new Array<any>();
|
PositionNextScene: Array<any> = new Array<any>();
|
||||||
|
|
||||||
constructor(MapKey : string = "", MapUrlFile: string = "") {
|
static createFromUrl(mapUrlFile: string, instance: string): GameScene {
|
||||||
|
let key = GameScene.getMapKeyByUrl(mapUrlFile);
|
||||||
|
return new GameScene(key, mapUrlFile, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(MapKey : string, MapUrlFile: string, instance: string) {
|
||||||
super({
|
super({
|
||||||
key: MapKey
|
key: MapKey
|
||||||
});
|
});
|
||||||
@ -51,9 +58,11 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.GameManager = gameManager;
|
this.GameManager = gameManager;
|
||||||
this.Terrains = [];
|
this.Terrains = [];
|
||||||
this.groups = new Map<string, Sprite>();
|
this.groups = new Map<string, Sprite>();
|
||||||
|
this.instance = instance;
|
||||||
|
|
||||||
this.MapKey = MapKey;
|
this.MapKey = MapKey;
|
||||||
this.MapUrlFile = MapUrlFile;
|
this.MapUrlFile = MapUrlFile;
|
||||||
|
this.RoomId = this.instance + '__' + this.MapKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
//hook preload scene
|
//hook preload scene
|
||||||
@ -158,7 +167,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
// Let's alter browser history
|
// Let's alter browser history
|
||||||
let url = new URL(this.MapUrlFile);
|
let url = new URL(this.MapUrlFile);
|
||||||
let path = '/_/'+url.host+url.pathname;
|
let path = '/_/'+this.instance+'/'+url.host+url.pathname;
|
||||||
if (url.hash) {
|
if (url.hash) {
|
||||||
// FIXME: entry should be dictated by a property passed to init()
|
// FIXME: entry should be dictated by a property passed to init()
|
||||||
path += '#'+url.hash;
|
path += '#'+url.hash;
|
||||||
@ -178,6 +187,18 @@ export class GameScene extends Phaser.Scene {
|
|||||||
return obj.value;
|
return obj.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getExitSceneInstance(layer: ITiledMapLayer): string|undefined {
|
||||||
|
let properties : any = layer.properties;
|
||||||
|
if (!properties) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let obj = properties.find((property:any) => property.name === "exitInstance");
|
||||||
|
if (obj === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return obj.value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param layer
|
* @param layer
|
||||||
@ -187,10 +208,14 @@ export class GameScene extends Phaser.Scene {
|
|||||||
*/
|
*/
|
||||||
private loadNextGame(layer: ITiledMapLayer, mapWidth: number, tileWidth: number, tileHeight: number){
|
private loadNextGame(layer: ITiledMapLayer, mapWidth: number, tileWidth: number, tileHeight: number){
|
||||||
let exitSceneUrl = this.getExitSceneUrl(layer);
|
let exitSceneUrl = this.getExitSceneUrl(layer);
|
||||||
|
let instance = this.getExitSceneInstance(layer);
|
||||||
|
if (instance === undefined) {
|
||||||
|
instance = this.instance;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: eventually compute a relative URL
|
// TODO: eventually compute a relative URL
|
||||||
let absoluteExitSceneUrl = new URL(exitSceneUrl, this.MapUrlFile).href;
|
let absoluteExitSceneUrl = new URL(exitSceneUrl, this.MapUrlFile).href;
|
||||||
let exitSceneKey = gameManager.loadMap(absoluteExitSceneUrl, this.scene);
|
let exitSceneKey = gameManager.loadMap(absoluteExitSceneUrl, this.scene, instance);
|
||||||
|
|
||||||
let tiles : any = layer.data;
|
let tiles : any = layer.data;
|
||||||
tiles.forEach((objectKey : number, key: number) => {
|
tiles.forEach((objectKey : number, key: number) => {
|
||||||
@ -264,11 +289,6 @@ export class GameScene extends Phaser.Scene {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addSpite(Object : Phaser.Physics.Arcade.Sprite){
|
|
||||||
Object.setImmovable(true);
|
|
||||||
this.Objects.push(Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
createCollisionObject(){
|
createCollisionObject(){
|
||||||
this.Objects.forEach((Object : Phaser.Physics.Arcade.Sprite) => {
|
this.Objects.forEach((Object : Phaser.Physics.Arcade.Sprite) => {
|
||||||
this.physics.add.collider(this.CurrentPlayer, Object, (object1: any, object2: any) => {
|
this.physics.add.collider(this.CurrentPlayer, Object, (object1: any, object2: any) => {
|
||||||
@ -279,7 +299,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
createCurrentPlayer(){
|
createCurrentPlayer(){
|
||||||
//initialise player
|
//initialise player
|
||||||
//TODO create animation moving between exit and strat
|
//TODO create animation moving between exit and start
|
||||||
this.CurrentPlayer = new Player(
|
this.CurrentPlayer = new Player(
|
||||||
null, // The current player is not has no id (because the id can change if connexion is lost and we should check that id using the GameManager.
|
null, // The current player is not has no id (because the id can change if connexion is lost and we should check that id using the GameManager.
|
||||||
this,
|
this,
|
||||||
@ -296,7 +316,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.createCollisionObject();
|
this.createCollisionObject();
|
||||||
|
|
||||||
//join room
|
//join room
|
||||||
this.GameManager.joinRoom(this.scene.key, this.startX, this.startY, PlayerAnimationNames.WalkDown, false);
|
this.GameManager.joinRoom(this.RoomId, this.startX, this.startY, PlayerAnimationNames.WalkDown, false);
|
||||||
|
|
||||||
//listen event to share position of user
|
//listen event to share position of user
|
||||||
this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this))
|
this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this))
|
||||||
@ -495,4 +515,15 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.groups.get(groupId).destroy();
|
this.groups.get(groupId).destroy();
|
||||||
this.groups.delete(groupId);
|
this.groups.delete(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getMapKeyByUrl(mapUrlStart: string) : string {
|
||||||
|
// FIXME: the key should be computed from the full URL of the map.
|
||||||
|
let startPos = mapUrlStart.indexOf('://')+3;
|
||||||
|
let endPos = mapUrlStart.indexOf(".json");
|
||||||
|
return mapUrlStart.substring(startPos, endPos);
|
||||||
|
|
||||||
|
|
||||||
|
let tab = mapUrlStart.split("/");
|
||||||
|
return tab[tab.length -1].substr(0, tab[tab.length -1].indexOf(".json"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,6 @@ import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter";
|
|||||||
import {cypressAsserter} from "../../Cypress/CypressAsserter";
|
import {cypressAsserter} from "../../Cypress/CypressAsserter";
|
||||||
import {GroupCreatedUpdatedMessageInterface, MessageUserJoined, MessageUserPositionInterface} from "../../Connexion";
|
import {GroupCreatedUpdatedMessageInterface, MessageUserJoined, MessageUserPositionInterface} from "../../Connexion";
|
||||||
|
|
||||||
export function getMapKeyByUrl(mapUrlStart: string){
|
|
||||||
// FIXME: the key should be computed from the full URL of the map.
|
|
||||||
let startPos = mapUrlStart.indexOf('://')+3;
|
|
||||||
let endPos = mapUrlStart.indexOf(".json");
|
|
||||||
return mapUrlStart.substring(startPos, endPos);
|
|
||||||
|
|
||||||
|
|
||||||
let tab = mapUrlStart.split("/");
|
|
||||||
return tab[tab.length -1].substr(0, tab[tab.length -1].indexOf(".json"));
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo: put this constants in a dedicated file
|
//todo: put this constants in a dedicated file
|
||||||
export const LoginSceneName = "LoginScene";
|
export const LoginSceneName = "LoginScene";
|
||||||
enum LoginTextures {
|
enum LoginTextures {
|
||||||
@ -104,9 +93,10 @@ export class LogincScene extends Phaser.Scene {
|
|||||||
private async login(name: string) {
|
private async login(name: string) {
|
||||||
return gameManager.connect(name, this.selectedPlayer.texture.key).then(() => {
|
return gameManager.connect(name, this.selectedPlayer.texture.key).then(() => {
|
||||||
// Do we have a start URL in the address bar? If so, let's redirect to this address
|
// Do we have a start URL in the address bar? If so, let's redirect to this address
|
||||||
let mapUrl = this.findMapUrl();
|
let instanceAndMapUrl = this.findMapUrl();
|
||||||
if (mapUrl !== null) {
|
if (instanceAndMapUrl !== null) {
|
||||||
let key = gameManager.loadMap(mapUrl, this.scene);
|
let [mapUrl, instance] = instanceAndMapUrl;
|
||||||
|
let key = gameManager.loadMap(mapUrl, this.scene, instance);
|
||||||
this.scene.start(key);
|
this.scene.start(key);
|
||||||
return mapUrl;
|
return mapUrl;
|
||||||
} else {
|
} else {
|
||||||
@ -115,7 +105,7 @@ export class LogincScene extends Phaser.Scene {
|
|||||||
if (!scene) {
|
if (!scene) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let key = gameManager.loadMap(window.location.protocol+"//"+scene.mapUrlStart, this.scene);
|
let key = gameManager.loadMap(window.location.protocol + "//" + scene.mapUrlStart, this.scene, scene.startInstance);
|
||||||
this.scene.start(key);
|
this.scene.start(key);
|
||||||
return scene;
|
return scene;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
@ -129,12 +119,21 @@ export class LogincScene extends Phaser.Scene {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private findMapUrl(): string|null {
|
/**
|
||||||
|
* Returns the map URL and the instance from the current URL
|
||||||
|
*/
|
||||||
|
private findMapUrl(): [string, string]|null {
|
||||||
let path = window.location.pathname;
|
let path = window.location.pathname;
|
||||||
if (!path.startsWith('/_/')) {
|
if (!path.startsWith('/_/')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return window.location.protocol+'//'+path.substr(3);
|
let instanceAndMap = path.substr(3);
|
||||||
|
let firstSlash = instanceAndMap.indexOf('/');
|
||||||
|
if (firstSlash === -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let instance = instanceAndMap.substr(0, firstSlash);
|
||||||
|
return [window.location.protocol+'//'+instanceAndMap.substr(firstSlash+1), instance];
|
||||||
}
|
}
|
||||||
|
|
||||||
Map: Phaser.Tilemaps.Tilemap;
|
Map: Phaser.Tilemaps.Tilemap;
|
||||||
|
@ -45,12 +45,6 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G
|
|||||||
this.setImmovable(false);
|
this.setImmovable(false);
|
||||||
this.initAnimation();
|
this.initAnimation();
|
||||||
|
|
||||||
console.warn("Start direction for added player ", direction)
|
|
||||||
console.warn("Position ", x, y)
|
|
||||||
/*this.play(`${PlayerTexture}-${direction}`, true);
|
|
||||||
if (!moving) {
|
|
||||||
this.stop();
|
|
||||||
}*/
|
|
||||||
this.playAnimation(direction, moving);
|
this.playAnimation(direction, moving);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user