Adds the camera to available APIs with retrieving of the worldView

This commit is contained in:
Benedicte Quimbert 2021-11-23 16:51:39 +01:00
parent 99dfd77600
commit 5b6a8ca4d7
9 changed files with 86 additions and 7 deletions

View File

@ -69,7 +69,7 @@ You need to wait for the end of the initialization before calling `WA.player.get
```typescript ```typescript
WA.onInit().then(() => { WA.onInit().then(() => {
console.log('Tags: ', WA.player.getPosition()); console.log('Position: ', WA.player.getPosition());
}) })
``` ```

View File

@ -0,0 +1,18 @@
import * as tg from "generic-type-guard";
export const isHasCameraMovedEvent = new tg.IsInterface()
.withProperties({
x: tg.isNumber,
y: tg.isNumber,
width: tg.isNumber,
height: tg.isNumber,
})
.get();
/**
* A message sent from the game to the iFrame to notify a movement from the camera.
*/
export type HasCameraMovedEvent = tg.GuardedType<typeof isHasCameraMovedEvent>;
export type HasCameraMovedEventCallback = (event: HasCameraMovedEvent) => void;

View File

@ -30,6 +30,7 @@ import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEv
import type { ChangeLayerEvent } from "./ChangeLayerEvent"; import type { ChangeLayerEvent } from "./ChangeLayerEvent";
import { isPlayerPropertyEvent } from "./PlayerPropertyEvent"; import { isPlayerPropertyEvent } from "./PlayerPropertyEvent";
import { isPlayerPosition } from "./PlayerPosition"; import { isPlayerPosition } from "./PlayerPosition";
import type { HasCameraMovedEvent } from "./HasCameraMovedEvent";
export interface TypedMessageEvent<T> extends MessageEvent { export interface TypedMessageEvent<T> extends MessageEvent {
data: T; data: T;
@ -50,6 +51,7 @@ export type IframeEventMap = {
displayBubble: null; displayBubble: null;
removeBubble: null; removeBubble: null;
onPlayerMove: undefined; onPlayerMove: undefined;
onCameraMove: undefined;
showLayer: LayerEvent; showLayer: LayerEvent;
hideLayer: LayerEvent; hideLayer: LayerEvent;
setProperty: SetPropertyEvent; setProperty: SetPropertyEvent;
@ -80,6 +82,7 @@ export interface IframeResponseEventMap {
leaveLayerEvent: ChangeLayerEvent; leaveLayerEvent: ChangeLayerEvent;
buttonClickedEvent: ButtonClickedEvent; buttonClickedEvent: ButtonClickedEvent;
hasPlayerMoved: HasPlayerMovedEvent; hasPlayerMoved: HasPlayerMovedEvent;
hasCameraMoved: HasCameraMovedEvent;
menuItemClicked: MenuItemClickedEvent; menuItemClicked: MenuItemClickedEvent;
setVariable: SetVariableEvent; setVariable: SetVariableEvent;
messageTriggered: MessageReferenceEvent; messageTriggered: MessageReferenceEvent;

View File

@ -31,6 +31,7 @@ import type { SetVariableEvent } from "./Events/SetVariableEvent";
import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent"; import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore"; import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore";
import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent"; import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent";
import type { HasCameraMovedEvent } from "./Events/HasCameraMovedEvent";
type AnswererCallback<T extends keyof IframeQueryMap> = ( type AnswererCallback<T extends keyof IframeQueryMap> = (
query: IframeQueryMap[T]["query"], query: IframeQueryMap[T]["query"],
@ -94,6 +95,7 @@ class IframeListener {
private readonly iframeCloseCallbacks = new Map<HTMLIFrameElement, (() => void)[]>(); private readonly iframeCloseCallbacks = new Map<HTMLIFrameElement, (() => void)[]>();
private readonly scripts = new Map<string, HTMLIFrameElement>(); private readonly scripts = new Map<string, HTMLIFrameElement>();
private sendPlayerMove: boolean = false; private sendPlayerMove: boolean = false;
private sendCameraMove: boolean = false;
// Note: we are forced to type this in unknown and later cast with "as" because of https://github.com/microsoft/TypeScript/issues/31904 // Note: we are forced to type this in unknown and later cast with "as" because of https://github.com/microsoft/TypeScript/issues/31904
private answerers: { private answerers: {
@ -225,6 +227,8 @@ class IframeListener {
this._removeBubbleStream.next(); this._removeBubbleStream.next();
} else if (payload.type == "onPlayerMove") { } else if (payload.type == "onPlayerMove") {
this.sendPlayerMove = true; this.sendPlayerMove = true;
} else if (payload.type == "onCameraMove") {
this.sendCameraMove = true;
} else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) { } else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) {
this._setTilesStream.next(payload.data); this._setTilesStream.next(payload.data);
} else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) { } else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) {
@ -423,6 +427,15 @@ class IframeListener {
} }
} }
hasCameraMoved(event: HasCameraMovedEvent) {
if (this.sendCameraMove) {
this.postMessage({
type: "hasCameraMoved",
data: event,
});
}
}
sendButtonClickedEvent(popupId: number, buttonId: number): void { sendButtonClickedEvent(popupId: number, buttonId: number): void {
this.postMessage({ this.postMessage({
type: "buttonClickedEvent", type: "buttonClickedEvent",

View File

@ -13,7 +13,7 @@ export class EmbeddedWebsite {
private _allowApi: boolean; private _allowApi: boolean;
private _position: Rectangle; private _position: Rectangle;
private readonly origin: "map" | "player" | undefined; private readonly origin: "map" | "player" | undefined;
private _scale: number | undefined; private _scale: number;
constructor(private config: CreateEmbeddedWebsiteEvent) { constructor(private config: CreateEmbeddedWebsiteEvent) {
this.name = config.name; this.name = config.name;
@ -23,7 +23,7 @@ export class EmbeddedWebsite {
this._allowApi = config.allowApi ?? false; this._allowApi = config.allowApi ?? false;
this._position = config.position; this._position = config.position;
this.origin = config.origin; this.origin = config.origin;
this._scale = config.scale; this._scale = config.scale ?? 1;
} }
public get url() { public get url() {
@ -116,7 +116,7 @@ export class EmbeddedWebsite {
}); });
} }
public get scale() { public get scale(): number {
return this._scale; return this._scale;
} }

View File

@ -0,0 +1,29 @@
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
import { Subject } from "rxjs";
import type { HasCameraMovedEvent, HasCameraMovedEventCallback } from "../Events/HasCameraMovedEvent";
import { apiCallback } from "./registeredCallbacks";
import { isHasCameraMovedEvent } from "../Events/HasCameraMovedEvent";
const moveStream = new Subject<HasCameraMovedEvent>();
export class WorkAdventureCameraCommands extends IframeApiContribution<WorkAdventureCameraCommands> {
callbacks = [
apiCallback({
type: "hasCameraMoved",
typeChecker: isHasCameraMovedEvent,
callback: (payloadData) => {
moveStream.next(payloadData);
},
}),
];
onCameraMove(callback: HasCameraMovedEventCallback): void {
moveStream.subscribe(callback);
sendToWorkadventure({
type: "onCameraMove",
data: null,
});
}
}
export default new WorkAdventureCameraCommands();

View File

@ -30,6 +30,7 @@ export class EmbeddedWebsiteManager {
height: rect["height"], height: rect["height"],
}, },
origin: website.origin, origin: website.origin,
scale: website.scale,
}; };
}); });

View File

@ -89,6 +89,8 @@ import { get } from "svelte/store";
import { contactPageStore } from "../../Stores/MenuStore"; import { contactPageStore } from "../../Stores/MenuStore";
import { GameMapProperties } from "./GameMapProperties"; import { GameMapProperties } from "./GameMapProperties";
import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile; import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile;
import Camera = Phaser.Cameras.Scene2D.Camera;
import type { HasCameraMovedEvent } from "../../Api/Events/HasCameraMovedEvent";
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
initPosition: PointInterface | null; initPosition: PointInterface | null;
@ -750,6 +752,17 @@ export class GameScene extends DirtyScene {
this.gameMap.setPosition(event.x, event.y); this.gameMap.setPosition(event.x, event.y);
}); });
//listen event to share the actual worldView when the camera is updated
this.cameras.main.on("followupdate", (camera: Camera) => {
const worldView: HasCameraMovedEvent = {
x: camera.worldView.x,
y: camera.worldView.y,
width: camera.worldView.width,
height: camera.worldView.height,
};
iframeListener.hasCameraMoved(worldView);
});
// Set up variables manager // Set up variables manager
this.sharedVariablesManager = new SharedVariablesManager( this.sharedVariablesManager = new SharedVariablesManager(
this.connection, this.connection,

View File

@ -20,6 +20,7 @@ import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor";
import type { Popup } from "./Api/iframe/Ui/Popup"; import type { Popup } from "./Api/iframe/Ui/Popup";
import type { Sound } from "./Api/iframe/Sound/Sound"; import type { Sound } from "./Api/iframe/Sound/Sound";
import { answerPromises, queryWorkadventure } from "./Api/iframe/IframeApiContribution"; import { answerPromises, queryWorkadventure } from "./Api/iframe/IframeApiContribution";
import camera from "./Api/iframe/camera";
const globalState = createState("global"); const globalState = createState("global");
@ -45,6 +46,7 @@ const wa = {
sound, sound,
room, room,
player, player,
camera,
state: globalState, state: globalState,
onInit(): Promise<void> { onInit(): Promise<void> {