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>();
constructor() {
clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => {
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();
onMessage(userId: number, emoteName:string) {
fire(userId: number, emoteName:string) {
this._stream.next({userId, emoteName});
}
}

View file

@ -149,7 +149,7 @@ export class RoomConnection implements RoomConnection {
payload = subMessage.getItemeventmessage();
} else if (subMessage.hasEmoteeventmessage()) {
const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage;
emoteEventStream.onMessage(emoteMessage.getActoruserid(), emoteMessage.getEmote());
emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote());
} else {
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";
export interface RadialMenuItem {
sprite: string,
frame: number,
image: string,
name: string,
}
const menuRadius = 60;
export const RadialMenuClickEvent = 'radialClick';
export class RadialMenu extends Phaser.GameObjects.Container {
@ -27,24 +25,26 @@ export class RadialMenu extends Phaser.GameObjects.Container {
private initItems() {
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) {
const image = new Sprite(this.scene, 0, menuRadius, item.sprite, item.frame);
private createRadialElement(item: RadialMenuItem, index: number, itemsNumber: number, menuRadius: number) {
const image = new Sprite(this.scene, 0, menuRadius, item.image);
this.add(image);
this.scene.sys.updateList.add(image);
image.setDepth(DEPTH_UI_INDEX)
const scalingFactor = waScaleManager.uiScalingFactor * 0.075;
image.setScale(scalingFactor)
image.setInteractive({
hitArea: new Phaser.Geom.Circle(0, 0, 25),
hitAreaCallback: Phaser.Geom.Circle.Contains, //eslint-disable-line @typescript-eslint/unbound-method
useHandCursor: true,
});
image.on('pointerdown', () => this.emit(RadialMenuClickEvent, item));
image.on('pointerover', () => {
this.scene.tweens.add({
targets: image,
scale: 2,
props: {
scale: 2 * scalingFactor,
},
duration: 500,
ease: 'Power3',
})
@ -52,7 +52,9 @@ export class RadialMenu extends Phaser.GameObjects.Container {
image.on('pointerout', () => {
this.scene.tweens.add({
targets: image,
scale: 1,
props: {
scale: scalingFactor,
},
duration: 500,
ease: 'Power3',
})

View file

@ -5,7 +5,6 @@ import Container = Phaser.GameObjects.Container;
import Sprite = Phaser.GameObjects.Sprite;
import {TextureError} from "../../Exception/TextureError";
import {Companion} from "../Companion/Companion";
import {getEmoteAnimName} from "../Game/EmoteManager";
import type {GameScene} from "../Game/GameScene";
import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes";
import {waScaleManager} from "../Services/WaScaleManager";
@ -32,6 +31,7 @@ export abstract class Character extends Container {
private invisible: boolean;
public companion?: Companion;
private emote: Phaser.GameObjects.Sprite | null = null;
private emoteTween: Phaser.Tweens.Tween|null = null;
constructor(scene: GameScene,
x: number,
@ -246,24 +246,76 @@ export abstract class Character extends Container {
playEmote(emoteKey: string) {
this.cancelPreviousEmote();
const scalingFactor = waScaleManager.uiScalingFactor * 0.05;
const emoteY = -30 - scalingFactor * 10;
this.playerName.setVisible(false);
this.emote = new Sprite(this.scene, 0, -30 - waScaleManager.uiScalingFactor * 10, emoteKey, 1);
this.emote.setDepth(DEPTH_INGAME_TEXT_INDEX);
this.emote.setScale(waScaleManager.uiScalingFactor)
this.emote = new Sprite(this.scene, 0, 0, emoteKey);
this.emote.setAlpha(0);
this.emote.setScale(0.1 * scalingFactor);
this.add(this.emote);
this.scene.sys.updateList.add(this.emote);
this.emote.play(getEmoteAnimName(emoteKey));
this.emote.on(Phaser.Animations.Events.ANIMATION_COMPLETE, () => {
this.emote?.destroy();
this.emote = null;
this.playerName.setVisible(true);
this.createStartTransition(scalingFactor, emoteY);
}
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() {
if (!this.emote) return;
this.emoteTween?.remove();
this.destroyEmote()
}
private destroyEmote() {
this.emote?.destroy();
this.emote = null;
this.playerName.setVisible(true);

View file

@ -1,38 +1,30 @@
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {createLoadingPromise} from "../Entity/PlayerTexturesLoadingManager";
import {emoteEventStream} from "../../Connexion/EmoteEventStream";
import type {GameScene} from "./GameScene";
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 {
name: string;
img: string;
type: RegisteredEmoteTypes
}
//the last 3 emotes are courtesy of @tabascoeye
export const emotes: {[key: string]: RegisteredEmote} = {
'emote-exclamation': {name: 'emote-exclamation', img: 'resources/emotes/pipo-popupemotes001.png', type: RegisteredEmoteTypes.short, },
'emote-interrogation': {name: 'emote-interrogation', img: 'resources/emotes/pipo-popupemotes002.png', type: RegisteredEmoteTypes.short},
'emote-sleep': {name: 'emote-sleep', img: 'resources/emotes/pipo-popupemotes021.png', type: RegisteredEmoteTypes.short},
'emote-clap': {name: 'emote-clap', img: 'resources/emotes/taba-clap-emote.png', type: RegisteredEmoteTypes.short},
'emote-thumbsdown': {name: 'emote-thumbsdown', img: 'resources/emotes/taba-thumbsdown-emote.png', type: RegisteredEmoteTypes.long},
'emote-thumbsup': {name: 'emote-thumbsup', img: 'resources/emotes/taba-thumbsup-emote.png', type: RegisteredEmoteTypes.long},
'emote-heart': {name: 'emote-heart', img: 'resources/emotes/heart-emote.png'},
'emote-clap': {name: 'emote-clap', img: 'resources/emotes/clap-emote.png'},
'emote-hand': {name: 'emote-hand', img: 'resources/emotes/hand-emote.png'},
'emote-thanks': {name: 'emote-thanks', img: 'resources/emotes/thanks-emote.png'},
'emote-thumb-up': {name: 'emote-thumb-up', img: 'resources/emotes/thumb-up-emote.png'},
'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 {
private subscription: Subscription;
constructor(private scene: GameScene) {
emoteEventStream.stream.subscribe((event) => {
this.subscription = emoteEventStream.stream.subscribe((event) => {
const actor = this.scene.MapPlayersByKey.get(event.userId);
if (actor) {
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> {
const emoteDescriptor = emotes[textureKey];
if (emoteDescriptor === undefined) {
throw 'Emote not found!';
}
const loadPromise = createLoadingPromise(this.scene.load, emoteDescriptor, {
frameWidth: 32,
frameHeight: 32,
});
const loadPromise = this.createLoadingPromise(this.scene.load, emoteDescriptor);
this.scene.load.start();
return loadPromise.then(() => {
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;
});
return loadPromise
}
getMenuImages(): Promise<RadialMenuItem[]> {
const promises = [];
for (const key in emotes) {
const promise = this.lazyLoadEmoteTexture(key).then((textureKey) => {
const emoteDescriptor = emotes[textureKey];
return {
sprite: textureKey,
image: textureKey,
name: textureKey,
frame: emoteDescriptor.type === RegisteredEmoteTypes.short ? 1 : 4,
}
});
promises.push(promise);
}
return Promise.all(promises);
}
destroy() {
this.subscription.unsubscribe();
}
}

View file

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

View file

@ -54,7 +54,7 @@ class WaScaleManager {
* This is used to scale back the ui components to counter-act the zoom.
*/
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!
console.warn('Cannot find user with uuid "'+userUuid+'". Performing an anonymous login instead.');
} 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.
return res.upgrade({
rejected: true,