Adding the display of a circle around the group
This PR adds the display of a circle around groups. This is useful to view where you need to go to speak to someone but also to debug. Note: implementation is suboptimal, relying on a "graphics" object that is known to be slow. In the future, we need to use a circle as a sprite instead.
This commit is contained in:
parent
05fbcad252
commit
02e6b50b16
@ -9,6 +9,7 @@ import {ExtRooms, RefreshUserPositionFunction} from "../Model/Websocket/ExtRoom"
|
|||||||
import {ExtRoomsInterface} from "../Model/Websocket/ExtRoomsInterface";
|
import {ExtRoomsInterface} from "../Model/Websocket/ExtRoomsInterface";
|
||||||
import {World} from "../Model/World";
|
import {World} from "../Model/World";
|
||||||
import {Group} from "_Model/Group";
|
import {Group} from "_Model/Group";
|
||||||
|
import {UserInterface} from "_Model/UserInterface";
|
||||||
|
|
||||||
enum SockerIoEvent {
|
enum SockerIoEvent {
|
||||||
CONNECTION = "connection",
|
CONNECTION = "connection",
|
||||||
@ -20,6 +21,8 @@ enum SockerIoEvent {
|
|||||||
WEBRTC_START = "webrtc-start",
|
WEBRTC_START = "webrtc-start",
|
||||||
WEBRTC_DISCONNECT = "webrtc-disconect",
|
WEBRTC_DISCONNECT = "webrtc-disconect",
|
||||||
MESSAGE_ERROR = "message-error",
|
MESSAGE_ERROR = "message-error",
|
||||||
|
GROUP_CREATE_UPDATE = "group-create-update",
|
||||||
|
GROUP_DELETE = "group-delete",
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IoSocketController {
|
export class IoSocketController {
|
||||||
@ -51,7 +54,38 @@ export class IoSocketController {
|
|||||||
this.connectedUser(user1, group);
|
this.connectedUser(user1, group);
|
||||||
}, (user1: string, group: Group) => {
|
}, (user1: string, group: Group) => {
|
||||||
this.disConnectedUser(user1, group);
|
this.disConnectedUser(user1, group);
|
||||||
}, MINIMUM_DISTANCE, GROUP_RADIUS);
|
}, MINIMUM_DISTANCE, GROUP_RADIUS, (group: Group) => {
|
||||||
|
this.sendUpdateGroupEvent(group);
|
||||||
|
}, (groupUuid: string, lastUser: UserInterface) => {
|
||||||
|
this.sendDeleteGroupEvent(groupUuid, lastUser);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendUpdateGroupEvent(group: Group): void {
|
||||||
|
// Let's get the room of the group. To do this, let's get anyone in the group and find its room.
|
||||||
|
// Note: this is suboptimal
|
||||||
|
let userId = group.getUsers()[0].id;
|
||||||
|
let client: ExSocketInterface|null = this.searchClientById(userId);
|
||||||
|
if (client === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let roomId = client.roomId;
|
||||||
|
this.Io.in(roomId).emit(SockerIoEvent.GROUP_CREATE_UPDATE, {
|
||||||
|
position: group.getPosition(),
|
||||||
|
groupId: group.getId()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendDeleteGroupEvent(uuid: string, lastUser: UserInterface): void {
|
||||||
|
// Let's get the room of the group. To do this, let's get anyone in the group and find its room.
|
||||||
|
// Note: this is suboptimal
|
||||||
|
let userId = lastUser.id;
|
||||||
|
let client: ExSocketInterface|null = this.searchClientById(userId);
|
||||||
|
if (client === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let roomId = client.roomId;
|
||||||
|
this.Io.in(roomId).emit(SockerIoEvent.GROUP_DELETE, uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
ioConnection() {
|
ioConnection() {
|
||||||
@ -149,7 +183,7 @@ export class IoSocketController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* TODO: each call to this method is suboptimal. It means that instead of passing an ID, we should pass a client object.
|
||||||
* @param userId
|
* @param userId
|
||||||
*/
|
*/
|
||||||
searchClientById(userId: string): ExSocketInterface | null {
|
searchClientById(userId: string): ExSocketInterface | null {
|
||||||
@ -286,7 +320,7 @@ export class IoSocketController {
|
|||||||
this.joinWebRtcRoom(Client, group.getId());
|
this.joinWebRtcRoom(Client, group.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
//connected user
|
//disconnect user
|
||||||
disConnectedUser(userId: string, group: Group) {
|
disConnectedUser(userId: string, group: Group) {
|
||||||
let Client = this.searchClientById(userId);
|
let Client = this.searchClientById(userId);
|
||||||
if (!Client) {
|
if (!Client) {
|
||||||
|
@ -9,6 +9,10 @@ import {PositionInterface} from "_Model/PositionInterface";
|
|||||||
export type ConnectCallback = (user: string, group: Group) => void;
|
export type ConnectCallback = (user: string, group: Group) => void;
|
||||||
export type DisconnectCallback = (user: string, group: Group) => void;
|
export type DisconnectCallback = (user: string, group: Group) => void;
|
||||||
|
|
||||||
|
// callback called when a group is created or moved or changes users
|
||||||
|
export type GroupUpdatedCallback = (group: Group) => void;
|
||||||
|
export type GroupDeletedCallback = (uuid: string, lastUser: UserInterface) => void;
|
||||||
|
|
||||||
export class World {
|
export class World {
|
||||||
private minDistance: number;
|
private minDistance: number;
|
||||||
private groupRadius: number;
|
private groupRadius: number;
|
||||||
@ -19,11 +23,15 @@ export class World {
|
|||||||
|
|
||||||
private connectCallback: ConnectCallback;
|
private connectCallback: ConnectCallback;
|
||||||
private disconnectCallback: DisconnectCallback;
|
private disconnectCallback: DisconnectCallback;
|
||||||
|
private groupUpdatedCallback: GroupUpdatedCallback;
|
||||||
|
private groupDeletedCallback: GroupDeletedCallback;
|
||||||
|
|
||||||
constructor(connectCallback: ConnectCallback,
|
constructor(connectCallback: ConnectCallback,
|
||||||
disconnectCallback: DisconnectCallback,
|
disconnectCallback: DisconnectCallback,
|
||||||
minDistance: number,
|
minDistance: number,
|
||||||
groupRadius: number)
|
groupRadius: number,
|
||||||
|
groupUpdatedCallback: GroupUpdatedCallback,
|
||||||
|
groupDeletedCallback: GroupDeletedCallback)
|
||||||
{
|
{
|
||||||
this.users = new Map<string, UserInterface>();
|
this.users = new Map<string, UserInterface>();
|
||||||
this.groups = [];
|
this.groups = [];
|
||||||
@ -31,6 +39,8 @@ export class World {
|
|||||||
this.disconnectCallback = disconnectCallback;
|
this.disconnectCallback = disconnectCallback;
|
||||||
this.minDistance = minDistance;
|
this.minDistance = minDistance;
|
||||||
this.groupRadius = groupRadius;
|
this.groupRadius = groupRadius;
|
||||||
|
this.groupUpdatedCallback = groupUpdatedCallback;
|
||||||
|
this.groupDeletedCallback = groupDeletedCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public join(userPosition: MessageUserPosition): void {
|
public join(userPosition: MessageUserPosition): void {
|
||||||
@ -86,6 +96,11 @@ export class World {
|
|||||||
this.leaveGroup(user);
|
this.leaveGroup(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// At the very end, if the user is part of a group, let's call the callback to update group position
|
||||||
|
if (typeof user.group !== 'undefined') {
|
||||||
|
this.groupUpdatedCallback(user.group);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,12 +116,15 @@ export class World {
|
|||||||
group.leave(user);
|
group.leave(user);
|
||||||
|
|
||||||
if (group.isEmpty()) {
|
if (group.isEmpty()) {
|
||||||
|
this.groupDeletedCallback(group.getId(), user);
|
||||||
group.destroy();
|
group.destroy();
|
||||||
const index = this.groups.indexOf(group, 0);
|
const index = this.groups.indexOf(group, 0);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
throw new Error("Could not find group");
|
throw new Error("Could not find group");
|
||||||
}
|
}
|
||||||
this.groups.splice(index, 1);
|
this.groups.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
this.groupUpdatedCallback(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ describe("World", () => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let world = new World(connect, disconnect, 160, 160);
|
let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
|
||||||
|
|
||||||
world.join(new MessageUserPosition({
|
world.join(new MessageUserPosition({
|
||||||
userId: "foo",
|
userId: "foo",
|
||||||
@ -62,7 +62,7 @@ describe("World", () => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let world = new World(connect, disconnect, 160, 160);
|
let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
|
||||||
|
|
||||||
world.join(new MessageUserPosition({
|
world.join(new MessageUserPosition({
|
||||||
userId: "foo",
|
userId: "foo",
|
||||||
@ -107,7 +107,7 @@ describe("World", () => {
|
|||||||
disconnectCallNumber++;
|
disconnectCallNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
let world = new World(connect, disconnect, 160, 160);
|
let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
|
||||||
|
|
||||||
world.join(new MessageUserPosition({
|
world.join(new MessageUserPosition({
|
||||||
userId: "foo",
|
userId: "foo",
|
||||||
|
@ -11,7 +11,9 @@ enum EventMessage{
|
|||||||
JOIN_ROOM = "join-room",
|
JOIN_ROOM = "join-room",
|
||||||
USER_POSITION = "user-position",
|
USER_POSITION = "user-position",
|
||||||
MESSAGE_ERROR = "message-error",
|
MESSAGE_ERROR = "message-error",
|
||||||
WEBRTC_DISCONNECT = "webrtc-disconect"
|
WEBRTC_DISCONNECT = "webrtc-disconect",
|
||||||
|
GROUP_CREATE_UPDATE = "group-create-update",
|
||||||
|
GROUP_DELETE = "group-delete",
|
||||||
}
|
}
|
||||||
|
|
||||||
class Message {
|
class Message {
|
||||||
@ -122,6 +124,16 @@ class ListMessageUserPosition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PositionInterface {
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupCreatedUpdatedMessageInterface {
|
||||||
|
position: PositionInterface,
|
||||||
|
groupId: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface ConnexionInterface {
|
export interface ConnexionInterface {
|
||||||
socket: any;
|
socket: any;
|
||||||
token: string;
|
token: string;
|
||||||
@ -184,6 +196,9 @@ export class Connexion implements ConnexionInterface {
|
|||||||
|
|
||||||
this.errorMessage();
|
this.errorMessage();
|
||||||
|
|
||||||
|
this.groupUpdatedOrCreated();
|
||||||
|
this.groupDeleted();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -254,6 +269,19 @@ export class Connexion implements ConnexionInterface {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private groupUpdatedOrCreated(): void {
|
||||||
|
this.socket.on(EventMessage.GROUP_CREATE_UPDATE, (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => {
|
||||||
|
//console.log('Group ', groupCreateUpdateMessage.groupId, " position :", groupCreateUpdateMessage.position.x, groupCreateUpdateMessage.position.y)
|
||||||
|
this.GameManager.shareGroupPosition(groupCreateUpdateMessage);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private groupDeleted(): void {
|
||||||
|
this.socket.on(EventMessage.GROUP_DELETE, (groupId: string) => {
|
||||||
|
this.GameManager.deleteGroup(groupId);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
sendWebrtcSignal(signal: any, roomId: string, userId? : string, receiverId? : string) {
|
sendWebrtcSignal(signal: any, roomId: string, userId? : string, receiverId? : string) {
|
||||||
return this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({
|
return this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({
|
||||||
userId: userId ? userId : this.userId,
|
userId: userId ? userId : this.userId,
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import {GameScene} from "./GameScene";
|
import {GameScene} from "./GameScene";
|
||||||
import {ROOM} from "../../Enum/EnvironmentVariable"
|
import {ROOM} from "../../Enum/EnvironmentVariable"
|
||||||
import {Connexion, ConnexionInterface, ListMessageUserPositionInterface} from "../../Connexion";
|
import {
|
||||||
|
Connexion,
|
||||||
|
ConnexionInterface,
|
||||||
|
GroupCreatedUpdatedMessageInterface,
|
||||||
|
ListMessageUserPositionInterface
|
||||||
|
} from "../../Connexion";
|
||||||
import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer";
|
import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer";
|
||||||
import {LogincScene} from "../Login/LogincScene";
|
import {LogincScene} from "../Login/LogincScene";
|
||||||
|
|
||||||
@ -66,6 +71,31 @@ export class GameManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share group position in game
|
||||||
|
*/
|
||||||
|
shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface): void {
|
||||||
|
if (this.status === StatusGameManagerEnum.IN_PROGRESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.currentGameScene.shareGroupPosition(groupPositionMessage)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteGroup(groupId: string): void {
|
||||||
|
if (this.status === StatusGameManagerEnum.IN_PROGRESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.currentGameScene.deleteGroup(groupId)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getPlayerName(): string {
|
getPlayerName(): string {
|
||||||
return this.playerName;
|
return this.playerName;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import {GameManager, gameManager, HasMovedEvent, StatusGameManagerEnum} from "./GameManager";
|
import {GameManager, gameManager, HasMovedEvent, StatusGameManagerEnum} from "./GameManager";
|
||||||
import {MessageUserPositionInterface} from "../../Connexion";
|
import {GroupCreatedUpdatedMessageInterface, MessageUserPositionInterface} from "../../Connexion";
|
||||||
import {CurrentGamerInterface, GamerInterface, hasMovedEventName, Player} from "../Player/Player";
|
import {CurrentGamerInterface, GamerInterface, hasMovedEventName, Player} from "../Player/Player";
|
||||||
import {DEBUG_MODE, RESOLUTION, ROOM, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable";
|
import {DEBUG_MODE, RESOLUTION, ROOM, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable";
|
||||||
import Tile = Phaser.Tilemaps.Tile;
|
import Tile = Phaser.Tilemaps.Tile;
|
||||||
import {ITiledMap, ITiledTileSet} from "../Map/ITiledMap";
|
import {ITiledMap, ITiledTileSet} from "../Map/ITiledMap";
|
||||||
import {cypressAsserter} from "../../Cypress/CypressAsserter";
|
import {cypressAsserter} from "../../Cypress/CypressAsserter";
|
||||||
import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter";
|
import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter";
|
||||||
|
import Circle = Phaser.Geom.Circle;
|
||||||
|
import Graphics = Phaser.GameObjects.Graphics;
|
||||||
|
|
||||||
export const GameSceneName = "GameScene";
|
export const GameSceneName = "GameScene";
|
||||||
export enum Textures {
|
export enum Textures {
|
||||||
@ -27,9 +29,12 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
|||||||
Layers : Array<Phaser.Tilemaps.StaticTilemapLayer>;
|
Layers : Array<Phaser.Tilemaps.StaticTilemapLayer>;
|
||||||
Objects : Array<Phaser.Physics.Arcade.Sprite>;
|
Objects : Array<Phaser.Physics.Arcade.Sprite>;
|
||||||
map: ITiledMap;
|
map: ITiledMap;
|
||||||
|
groups: Map<string, Circle>
|
||||||
startX = 704;// 22 case
|
startX = 704;// 22 case
|
||||||
startY = 32; // 1 case
|
startY = 32; // 1 case
|
||||||
|
|
||||||
|
// Note: graphics object is costly to generate. We should find another way (maybe sprite based way to draw circles)
|
||||||
|
graphics: Graphics;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
@ -37,6 +42,7 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
|||||||
});
|
});
|
||||||
this.GameManager = gameManager;
|
this.GameManager = gameManager;
|
||||||
this.Terrains = [];
|
this.Terrains = [];
|
||||||
|
this.groups = new Map<string, Circle>();
|
||||||
}
|
}
|
||||||
|
|
||||||
//hook preload scene
|
//hook preload scene
|
||||||
@ -115,6 +121,8 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
|||||||
|
|
||||||
//initialise camera
|
//initialise camera
|
||||||
this.initCamera();
|
this.initCamera();
|
||||||
|
|
||||||
|
this.graphics = this.add.graphics();
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: in a dedicated class/function?
|
//todo: in a dedicated class/function?
|
||||||
@ -199,6 +207,13 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
|||||||
*/
|
*/
|
||||||
update(time: number, delta: number) : void {
|
update(time: number, delta: number) : void {
|
||||||
this.CurrentPlayer.moveUser(delta);
|
this.CurrentPlayer.moveUser(delta);
|
||||||
|
|
||||||
|
// Also, let's redraw the circle (can be costly, we need to change this!)
|
||||||
|
this.graphics.clear();
|
||||||
|
this.graphics.lineStyle(1, 0x00ff00, 0.4);
|
||||||
|
this.groups.forEach((circle: Circle) => {
|
||||||
|
this.graphics.strokeCircleShape(circle);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,4 +287,21 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
|||||||
CurrentPlayer.say("Hello, how are you ? ");
|
CurrentPlayer.say("Hello, how are you ? ");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) {
|
||||||
|
let groupId = groupPositionMessage.groupId;
|
||||||
|
|
||||||
|
if (this.groups.has(groupId)) {
|
||||||
|
this.groups.get(groupId).setPosition(groupPositionMessage.position.x, groupPositionMessage.position.y);
|
||||||
|
} else {
|
||||||
|
//console.log('Adding group ', groupId, ' to the scene');
|
||||||
|
// TODO: circle radius should not be hard stored
|
||||||
|
this.groups.set(groupId, new Circle(groupPositionMessage.position.x, groupPositionMessage.position.y, 48));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteGroup(groupId: string): void {
|
||||||
|
//console.log('Deleting group ', groupId);
|
||||||
|
this.groups.delete(groupId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,6 @@ export const playAnimation = (Player : Phaser.GameObjects.Sprite, direction : st
|
|||||||
if (direction !== PlayerAnimationNames.None && (!Player.anims.currentAnim || Player.anims.currentAnim.key !== direction)) {
|
if (direction !== PlayerAnimationNames.None && (!Player.anims.currentAnim || Player.anims.currentAnim.key !== direction)) {
|
||||||
Player.anims.play(direction);
|
Player.anims.play(direction);
|
||||||
} else if (direction === PlayerAnimationNames.None && Player.anims.currentAnim) {
|
} else if (direction === PlayerAnimationNames.None && Player.anims.currentAnim) {
|
||||||
//Player.anims.currentAnim.destroy();
|
|
||||||
Player.anims.stop();
|
Player.anims.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user