Handling in satisfactory manner scoro messages

This commit is contained in:
Clément Le Bihan
2023-09-14 17:13:42 +02:00
parent cea6d8d0bc
commit f83043a9c9
4 changed files with 94 additions and 25 deletions
@@ -2,6 +2,7 @@
import * as React from 'react'; import * as React from 'react';
import { useEffect, useContext } from 'react'; import { useEffect, useContext } from 'react';
import { Dimensions } from 'react-native';
import Phaser from 'phaser'; import Phaser from 'phaser';
import useColorScheme from '../../hooks/colorScheme'; import useColorScheme from '../../hooks/colorScheme';
import { RootState, useSelector } from '../../state/Store'; import { RootState, useSelector } from '../../state/Store';
@@ -22,6 +23,9 @@ const isValidSoundPlayer = (soundPlayer: SplendidGrandPiano | undefined) => {
return soundPlayer && soundPlayer.loaded; return soundPlayer && soundPlayer.loaded;
}; };
const min = (a: number, b: number) => (a < b ? a : b);
const max = (a: number, b: number) => (a > b ? a : b);
const myFindLast = <T,>(a: T[], p: (_: T, _2: number) => boolean) => { const myFindLast = <T,>(a: T[], p: (_: T, _2: number) => boolean) => {
for (let i = a.length - 1; i >= 0; i--) { for (let i = a.length - 1; i >= 0; i--) {
if (p(a[i]!, i)) { if (p(a[i]!, i)) {
@@ -75,19 +79,17 @@ const getPianoScene = (
this.cursor = this.add.rectangle(0, 0, 30, 350, 0x31ef8c, 0.5).setOrigin(0, 0); this.cursor = this.add.rectangle(0, 0, 30, 350, 0x31ef8c, 0.5).setOrigin(0, 0);
this.cameras.main.startFollow(this.cursor, true, 0.05, 0.05); this.cameras.main.startFollow(this.cursor, true, 0.05, 0.05);
// create an emitter the once called later will spawn 15 particules all around the sprite that it is attached to // create an emitter the once called later will spawn 15 particules all around the sprite that it is attached to
this.emitter = this.add.particles(0, 0, 'star', { this.emitter = this.add.particles(0, 0, 'star', {
lifespan: 700, lifespan: 700,
duration: 100, duration: 100,
quantity: 2,
follow: this.cursor, follow: this.cursor,
speed: { min: 10, max: 20 }, speed: { min: 10, max: 20 },
scale: { start: 0, end: 0.4 }, scale: { start: 0, end: 0.4 },
// rotate: { start: 0, end: 360 }, // rotate: { start: 0, end: 360 },
emitZone: { type: 'edge', source: this.cursor.getBounds(), quantity: 50 }, emitZone: { type: 'edge', source: this.cursor.getBounds(), quantity: 50 },
emitting: false emitting: false,
}); });
}); });
} }
@@ -113,7 +115,7 @@ const getPianoScene = (
} }
if (globalMessages.length > 0) { if (globalMessages.length > 0) {
handlePianoGameMsg(globalMessages, this.emitter); handlePianoGameMsg(globalMessages, this.emitter, undefined);
} }
return false; return false;
@@ -148,11 +150,7 @@ export type PhaserCanvasProps = {
onResume: () => void; onResume: () => void;
}; };
const PhaserCanvas = ({ const PhaserCanvas = ({ partitionB64, cursorPositions, onEndReached }: PhaserCanvasProps) => {
partitionB64,
cursorPositions,
onEndReached,
}: PhaserCanvasProps) => {
const colorScheme = useColorScheme(); const colorScheme = useColorScheme();
const dispatch = useDispatch(); const dispatch = useDispatch();
const pianoCC = useContext(PianoCC); const pianoCC = useContext(PianoCC);
@@ -185,13 +183,43 @@ const PhaserCanvas = ({
soundPlayer, soundPlayer,
colorScheme colorScheme
); );
const { width, height } = Dimensions.get('window');
class UIScene extends Phaser.Scene {
private statusTextValue: string;
private statusText!: Phaser.GameObjects.Text;
constructor() {
super({ key: 'UIScene', active: true });
this.statusTextValue = 'Score: 0 Streak: 0';
}
create() {
this.statusText = this.add.text(
this.cameras.main.width - 300,
10,
this.statusTextValue,
{
fontFamily: 'Arial',
fontSize: 25,
color: '#3A784B',
}
);
}
override update() {
if (globalMessages.length > 0) {
handlePianoGameMsg(globalMessages, undefined, this.statusText);
}
}
}
const config = { const config = {
type: Phaser.AUTO, type: Phaser.AUTO,
parent: 'phaser-canvas', parent: 'phaser-canvas',
width: 1000, width: max(width * 0.7, 850),
height: 400, height: min(max(height * 0.7, 400), 600),
scene: pianoScene, scene: [pianoScene, UIScene],
scale: { scale: {
mode: Phaser.Scale.FIT, mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_HORIZONTALLY, autoCenter: Phaser.Scale.CENTER_HORIZONTALLY,
@@ -1,34 +1,60 @@
import { NoteTiming, PianoCanvasMsg } from '../../models/PianoGame'; import { NoteTiming, PianoCanvasMsg, PianoScoreInfo } from '../../models/PianoGame';
const handleNoteTimingMsg = ( const handleNoteTimingMsg = (
noteTiming: NoteTiming, noteTiming: NoteTiming,
emitter: Phaser.GameObjects.Particles.ParticleEmitter emitter: Phaser.GameObjects.Particles.ParticleEmitter
) => { ) => {
if (noteTiming === NoteTiming.Perfect) { if (noteTiming === NoteTiming.Perfect) {
emitter.particleTint = 0x00ff00; // gold
emitter.start(10); emitter.particleTint = 0xffd700;
emitter.start(20);
} else if (noteTiming === NoteTiming.Great) { } else if (noteTiming === NoteTiming.Great) {
emitter.particleTint = 0x00ffff; emitter.particleTint = 0x00ffff;
emitter.start(5); emitter.start(10);
} else if (noteTiming === NoteTiming.Good) { } else if (noteTiming === NoteTiming.Good) {
emitter.particleTint = 0xffff00; // orange/brown
emitter.start(3); emitter.particleTint = 0xffa500;
emitter.start(5);
} else if (noteTiming === NoteTiming.Missed) { } else if (noteTiming === NoteTiming.Missed) {
emitter.particleTint = 0xff0000; emitter.particleTint = 0xff0000;
emitter.start(1); emitter.start(5);
} else if (noteTiming === NoteTiming.Wrong) { } else if (noteTiming === NoteTiming.Wrong) {
// maybe add some other effect // maybe add some other effect
} }
}; };
const handleScoreMsg = (score: PianoScoreInfo, statusText: Phaser.GameObjects.Text) => {
statusText.setText(`Score: ${score.score} Streak: ${score.streak}`);
};
const findAndRemove = <T>(arr: Array<T>, predicate: (el: T) => boolean): T | undefined => {
const idx = arr.findIndex(predicate);
if (idx === -1) {
return undefined;
}
return arr.splice(idx, 1)[0];
};
export const handlePianoGameMsg = ( export const handlePianoGameMsg = (
msgs: Array<PianoCanvasMsg>, msgs: Array<PianoCanvasMsg>,
emitter: Phaser.GameObjects.Particles.ParticleEmitter emitter: Phaser.GameObjects.Particles.ParticleEmitter | undefined,
statusText: Phaser.GameObjects.Text | undefined
) => { ) => {
const msg = msgs.shift(); // this is temporary way of hanlding messages it works ok but is laggy when
// pressing a lot of keys in a short time I will be using phaser events in the future I think
const msg = findAndRemove(msgs, (msg) => {
if (emitter && msg.type === 'noteTiming') {
return true;
} else if (statusText && msg.type === 'scoreInfo') {
return true;
}
return false;
});
if (msg) { if (msg) {
if (msg.type === 'noteTiming') { if (msg.type === 'noteTiming') {
handleNoteTimingMsg(msg.data as NoteTiming, emitter); handleNoteTimingMsg(msg.data as NoteTiming, emitter!);
} else if (msg.type === 'scoreInfo') {
handleScoreMsg(msg.data as PianoScoreInfo, statusText!);
} }
} }
}; };
+8 -2
View File
@@ -19,6 +19,12 @@ export type UpdateInfo = {
status: 'playing' | 'paused' | 'stopped'; status: 'playing' | 'paused' | 'stopped';
}; };
export type PianoScoreInfo = {
score: number;
streak: number;
};
export enum NoteTiming { export enum NoteTiming {
Perfect = 'Perfect', Perfect = 'Perfect',
Great = 'Great', Great = 'Great',
@@ -28,8 +34,8 @@ export enum NoteTiming {
} }
export type PianoCanvasMsg = { export type PianoCanvasMsg = {
type: 'noteTiming' | 'score' | 'gameUpdate'; type: 'noteTiming' | 'scoreInfo' | 'gameUpdate';
data: UpdateInfo | NoteTiming | number; data: UpdateInfo | NoteTiming | PianoScoreInfo | number;
}; };
export type PianoCanvasContext = { export type PianoCanvasContext = {
+10 -1
View File
@@ -32,7 +32,7 @@ import TextButton from '../components/TextButton';
import { MIDIAccess, MIDIMessageEvent, requestMIDIAccess } from '@motiz88/react-native-midi'; import { MIDIAccess, MIDIMessageEvent, requestMIDIAccess } from '@motiz88/react-native-midi';
import * as Linking from 'expo-linking'; import * as Linking from 'expo-linking';
import url from 'url'; import url from 'url';
import { PianoCanvasContext, PianoCanvasMsg, NoteTiming } from '../models/PianoGame'; import { PianoCanvasContext, PianoCanvasMsg, NoteTiming, PianoScoreInfo } from '../models/PianoGame';
type PlayViewProps = { type PlayViewProps = {
songId: number; songId: number;
@@ -187,9 +187,18 @@ const PlayView = ({ songId, type, route }: RouteProps<PlayViewProps>) => {
); );
return; return;
} }
const currentStreak = data.info.current_streak;
const points = data.info.score; const points = data.info.score;
const maxPoints = data.info.max_score || 1; const maxPoints = data.info.max_score || 1;
if (currentStreak && points) {
setPianoMsgs({
type: 'scoreInfo',
data: { streak: currentStreak, score: points },
});
}
setScore(Math.floor((Math.max(points, 0) * 100) / maxPoints)); setScore(Math.floor((Math.max(points, 0) * 100) / maxPoints));
let formattedMessage = ''; let formattedMessage = '';