now use custom emotes with tweens instead of transistions

This commit is contained in:
kharhamel 2021-05-21 16:25:12 +02:00
parent d93b30f982
commit 595c5ca64d
21 changed files with 106 additions and 62 deletions

View File

@ -68,6 +68,7 @@ export class SocketManager {
private rooms: Map<string, GameRoom> = new Map<string, GameRoom>(); private rooms: Map<string, GameRoom> = new Map<string, GameRoom>();
constructor() { constructor() {
clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => { clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => {
gaugeManager.incNbClientPerRoomGauge(roomId); gaugeManager.incNbClientPerRoomGauge(roomId);
}); });

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 747 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -11,7 +11,7 @@ class EmoteEventStream {
public stream = this._stream.asObservable(); public stream = this._stream.asObservable();
onMessage(userId: number, emoteName:string) { fire(userId: number, emoteName:string) {
this._stream.next({userId, emoteName}); this._stream.next({userId, emoteName});
} }
} }

View File

@ -149,7 +149,7 @@ export class RoomConnection implements RoomConnection {
payload = subMessage.getItemeventmessage(); payload = subMessage.getItemeventmessage();
} else if (subMessage.hasEmoteeventmessage()) { } else if (subMessage.hasEmoteeventmessage()) {
const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage; const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage;
emoteEventStream.onMessage(emoteMessage.getActoruserid(), emoteMessage.getEmote()); emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote());
} else { } else {
throw new Error('Unexpected batch message type'); throw new Error('Unexpected batch message type');
} }

View File

@ -3,12 +3,10 @@ import {DEPTH_UI_INDEX} from "../Game/DepthIndexes";
import {waScaleManager} from "../Services/WaScaleManager"; import {waScaleManager} from "../Services/WaScaleManager";
export interface RadialMenuItem { export interface RadialMenuItem {
sprite: string, image: string,
frame: number,
name: string, name: string,
} }
const menuRadius = 60;
export const RadialMenuClickEvent = 'radialClick'; export const RadialMenuClickEvent = 'radialClick';
export class RadialMenu extends Phaser.GameObjects.Container { export class RadialMenu extends Phaser.GameObjects.Container {
@ -27,24 +25,26 @@ export class RadialMenu extends Phaser.GameObjects.Container {
private initItems() { private initItems() {
const itemsNumber = this.items.length; const itemsNumber = this.items.length;
this.items.forEach((item, index) => this.createRadialElement(item, index, itemsNumber)) const menuRadius = 70 + (waScaleManager.uiScalingFactor - 1) * 20;
this.items.forEach((item, index) => this.createRadialElement(item, index, itemsNumber, menuRadius))
} }
private createRadialElement(item: RadialMenuItem, index: number, itemsNumber: number) { private createRadialElement(item: RadialMenuItem, index: number, itemsNumber: number, menuRadius: number) {
const image = new Sprite(this.scene, 0, menuRadius, item.sprite, item.frame); const image = new Sprite(this.scene, 0, menuRadius, item.image);
this.add(image); this.add(image);
this.scene.sys.updateList.add(image); this.scene.sys.updateList.add(image);
image.setDepth(DEPTH_UI_INDEX) const scalingFactor = waScaleManager.uiScalingFactor * 0.075;
image.setScale(scalingFactor)
image.setInteractive({ image.setInteractive({
hitArea: new Phaser.Geom.Circle(0, 0, 25),
hitAreaCallback: Phaser.Geom.Circle.Contains, //eslint-disable-line @typescript-eslint/unbound-method
useHandCursor: true, useHandCursor: true,
}); });
image.on('pointerdown', () => this.emit(RadialMenuClickEvent, item)); image.on('pointerdown', () => this.emit(RadialMenuClickEvent, item));
image.on('pointerover', () => { image.on('pointerover', () => {
this.scene.tweens.add({ this.scene.tweens.add({
targets: image, targets: image,
scale: 2, props: {
scale: 2 * scalingFactor,
},
duration: 500, duration: 500,
ease: 'Power3', ease: 'Power3',
}) })
@ -52,7 +52,9 @@ export class RadialMenu extends Phaser.GameObjects.Container {
image.on('pointerout', () => { image.on('pointerout', () => {
this.scene.tweens.add({ this.scene.tweens.add({
targets: image, targets: image,
scale: 1, props: {
scale: scalingFactor,
},
duration: 500, duration: 500,
ease: 'Power3', ease: 'Power3',
}) })

View File

@ -5,7 +5,6 @@ import Container = Phaser.GameObjects.Container;
import Sprite = Phaser.GameObjects.Sprite; import Sprite = Phaser.GameObjects.Sprite;
import {TextureError} from "../../Exception/TextureError"; import {TextureError} from "../../Exception/TextureError";
import {Companion} from "../Companion/Companion"; import {Companion} from "../Companion/Companion";
import {getEmoteAnimName} from "../Game/EmoteManager";
import type {GameScene} from "../Game/GameScene"; import type {GameScene} from "../Game/GameScene";
import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes"; import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes";
import {waScaleManager} from "../Services/WaScaleManager"; import {waScaleManager} from "../Services/WaScaleManager";
@ -32,6 +31,7 @@ export abstract class Character extends Container {
private invisible: boolean; private invisible: boolean;
public companion?: Companion; public companion?: Companion;
private emote: Phaser.GameObjects.Sprite | null = null; private emote: Phaser.GameObjects.Sprite | null = null;
private emoteTween: Phaser.Tweens.Tween|null = null;
constructor(scene: GameScene, constructor(scene: GameScene,
x: number, x: number,
@ -247,23 +247,75 @@ export abstract class Character extends Container {
playEmote(emoteKey: string) { playEmote(emoteKey: string) {
this.cancelPreviousEmote(); this.cancelPreviousEmote();
const scalingFactor = waScaleManager.uiScalingFactor * 0.05;
const emoteY = -30 - scalingFactor * 10;
this.playerName.setVisible(false); this.playerName.setVisible(false);
this.emote = new Sprite(this.scene, 0, -30 - waScaleManager.uiScalingFactor * 10, emoteKey, 1); this.emote = new Sprite(this.scene, 0, 0, emoteKey);
this.emote.setDepth(DEPTH_INGAME_TEXT_INDEX); this.emote.setAlpha(0);
this.emote.setScale(waScaleManager.uiScalingFactor) this.emote.setScale(0.1 * scalingFactor);
this.add(this.emote); this.add(this.emote);
this.scene.sys.updateList.add(this.emote); this.scene.sys.updateList.add(this.emote);
this.emote.play(getEmoteAnimName(emoteKey));
this.emote.on(Phaser.Animations.Events.ANIMATION_COMPLETE, () => { this.createStartTransition(scalingFactor, emoteY);
this.emote?.destroy(); }
this.emote = null;
this.playerName.setVisible(true); private createStartTransition(scalingFactor: number, emoteY: number) {
this.emoteTween = this.scene.tweens.add({
targets: this.emote,
props: {
scale: scalingFactor,
alpha: 1,
y: emoteY,
},
ease: 'Power2',
duration: 500,
onComplete: () => {
this.startPulseTransition(emoteY, scalingFactor);
}
});
}
private startPulseTransition(emoteY: number, scalingFactor: number) {
this.emoteTween = this.scene.tweens.add({
targets: this.emote,
props: {
y: emoteY * 1.3,
scale: scalingFactor * 1.1
},
duration: 250,
yoyo: true,
repeat: 1,
completeDelay: 200,
onComplete: () => {
this.startExitTransition(emoteY);
}
});
}
private startExitTransition(emoteY: number) {
this.emoteTween = this.scene.tweens.add({
targets: this.emote,
props: {
alpha: 0,
y: 2 * emoteY,
},
ease: 'Power2',
duration: 500,
onComplete: () => {
this.destroyEmote();
}
}); });
} }
cancelPreviousEmote() { cancelPreviousEmote() {
if (!this.emote) return; if (!this.emote) return;
this.emoteTween?.remove();
this.destroyEmote()
}
private destroyEmote() {
this.emote?.destroy(); this.emote?.destroy();
this.emote = null; this.emote = null;
this.playerName.setVisible(true); this.playerName.setVisible(true);

View File

@ -1,38 +1,30 @@
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {createLoadingPromise} from "../Entity/PlayerTexturesLoadingManager";
import {emoteEventStream} from "../../Connexion/EmoteEventStream"; import {emoteEventStream} from "../../Connexion/EmoteEventStream";
import type {GameScene} from "./GameScene"; import type {GameScene} from "./GameScene";
import type {RadialMenuItem} from "../Components/RadialMenu"; import type {RadialMenuItem} from "../Components/RadialMenu";
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import type {Subscription} from "rxjs";
enum RegisteredEmoteTypes {
short = 1,
long = 2,
}
interface RegisteredEmote extends BodyResourceDescriptionInterface { interface RegisteredEmote extends BodyResourceDescriptionInterface {
name: string; name: string;
img: string; img: string;
type: RegisteredEmoteTypes
} }
//the last 3 emotes are courtesy of @tabascoeye
export const emotes: {[key: string]: RegisteredEmote} = { export const emotes: {[key: string]: RegisteredEmote} = {
'emote-exclamation': {name: 'emote-exclamation', img: 'resources/emotes/pipo-popupemotes001.png', type: RegisteredEmoteTypes.short, }, 'emote-heart': {name: 'emote-heart', img: 'resources/emotes/heart-emote.png'},
'emote-interrogation': {name: 'emote-interrogation', img: 'resources/emotes/pipo-popupemotes002.png', type: RegisteredEmoteTypes.short}, 'emote-clap': {name: 'emote-clap', img: 'resources/emotes/clap-emote.png'},
'emote-sleep': {name: 'emote-sleep', img: 'resources/emotes/pipo-popupemotes021.png', type: RegisteredEmoteTypes.short}, 'emote-hand': {name: 'emote-hand', img: 'resources/emotes/hand-emote.png'},
'emote-clap': {name: 'emote-clap', img: 'resources/emotes/taba-clap-emote.png', type: RegisteredEmoteTypes.short}, 'emote-thanks': {name: 'emote-thanks', img: 'resources/emotes/thanks-emote.png'},
'emote-thumbsdown': {name: 'emote-thumbsdown', img: 'resources/emotes/taba-thumbsdown-emote.png', type: RegisteredEmoteTypes.long}, 'emote-thumb-up': {name: 'emote-thumb-up', img: 'resources/emotes/thumb-up-emote.png'},
'emote-thumbsup': {name: 'emote-thumbsup', img: 'resources/emotes/taba-thumbsup-emote.png', type: RegisteredEmoteTypes.long}, 'emote-thumb-down': {name: 'emote-thumb-down', img: 'resources/emotes/thumb-down-emote.png'},
}; };
export const getEmoteAnimName = (emoteKey: string): string => {
return 'anim-'+emoteKey;
}
export class EmoteManager { export class EmoteManager {
private subscription: Subscription;
constructor(private scene: GameScene) { constructor(private scene: GameScene) {
emoteEventStream.stream.subscribe((event) => { this.subscription = emoteEventStream.stream.subscribe((event) => {
const actor = this.scene.MapPlayersByKey.get(event.userId); const actor = this.scene.MapPlayersByKey.get(event.userId);
if (actor) { if (actor) {
this.lazyLoadEmoteTexture(event.emoteName).then(emoteKey => { this.lazyLoadEmoteTexture(event.emoteName).then(emoteKey => {
@ -41,45 +33,41 @@ export class EmoteManager {
} }
}) })
} }
createLoadingPromise(loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) {
return new Promise<string>((res) => {
if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
return res(playerResourceDescriptor.name);
}
loadPlugin.image(playerResourceDescriptor.name, playerResourceDescriptor.img);
loadPlugin.once('filecomplete-image-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor.name));
});
}
lazyLoadEmoteTexture(textureKey: string): Promise<string> { lazyLoadEmoteTexture(textureKey: string): Promise<string> {
const emoteDescriptor = emotes[textureKey]; const emoteDescriptor = emotes[textureKey];
if (emoteDescriptor === undefined) { if (emoteDescriptor === undefined) {
throw 'Emote not found!'; throw 'Emote not found!';
} }
const loadPromise = createLoadingPromise(this.scene.load, emoteDescriptor, { const loadPromise = this.createLoadingPromise(this.scene.load, emoteDescriptor);
frameWidth: 32,
frameHeight: 32,
});
this.scene.load.start(); this.scene.load.start();
return loadPromise.then(() => { return loadPromise
if (this.scene.anims.exists(getEmoteAnimName(textureKey))) {
return Promise.resolve(textureKey);
}
const frameConfig = emoteDescriptor.type === RegisteredEmoteTypes.short ? {frames: [0,1,2,2]} : {frames : [0,1,2,3,4,]};
this.scene.anims.create({
key: getEmoteAnimName(textureKey),
frames: this.scene.anims.generateFrameNumbers(textureKey, frameConfig),
frameRate: 5,
repeat: 2,
});
return textureKey;
});
} }
getMenuImages(): Promise<RadialMenuItem[]> { getMenuImages(): Promise<RadialMenuItem[]> {
const promises = []; const promises = [];
for (const key in emotes) { for (const key in emotes) {
const promise = this.lazyLoadEmoteTexture(key).then((textureKey) => { const promise = this.lazyLoadEmoteTexture(key).then((textureKey) => {
const emoteDescriptor = emotes[textureKey];
return { return {
sprite: textureKey, image: textureKey,
name: textureKey, name: textureKey,
frame: emoteDescriptor.type === RegisteredEmoteTypes.short ? 1 : 4,
} }
}); });
promises.push(promise); promises.push(promise);
} }
return Promise.all(promises); return Promise.all(promises);
} }
destroy() {
this.subscription.unsubscribe();
}
} }

View File

@ -940,6 +940,7 @@ ${escapedMessage}
this.messageSubscription?.unsubscribe(); this.messageSubscription?.unsubscribe();
this.userInputManager.destroy(); this.userInputManager.destroy();
this.pinchManager?.destroy(); this.pinchManager?.destroy();
this.emoteManager.destroy();
for(const iframeEvents of this.iframeSubscriptionList){ for(const iframeEvents of this.iframeSubscriptionList){
iframeEvents.unsubscribe(); iframeEvents.unsubscribe();

View File

@ -54,7 +54,7 @@ class WaScaleManager {
* This is used to scale back the ui components to counter-act the zoom. * This is used to scale back the ui components to counter-act the zoom.
*/ */
public get uiScalingFactor(): number { public get uiScalingFactor(): number {
return this.actualZoom > 1 ? 1 : 2; return this.actualZoom > 1 ? 1 : 1.2;
} }
} }

View File

@ -183,7 +183,7 @@ export class IoSocketController {
// If we get an HTTP 404, the token is invalid. Let's perform an anonymous login! // If we get an HTTP 404, the token is invalid. Let's perform an anonymous login!
console.warn('Cannot find user with uuid "'+userUuid+'". Performing an anonymous login instead.'); console.warn('Cannot find user with uuid "'+userUuid+'". Performing an anonymous login instead.');
} else if(err?.response?.status == 403) { } else if(err?.response?.status == 403) {
// If we get an HTTP 404, the world is full. We need to broadcast a special error to the client. // If we get an HTTP 403, the world is full. We need to broadcast a special error to the client.
// we finish immediately the upgrade then we will close the socket as soon as it starts opening. // we finish immediately the upgrade then we will close the socket as soon as it starts opening.
return res.upgrade({ return res.upgrade({
rejected: true, rejected: true,