From cb4b2d831f797c435b1098098442d24b688d8eb3 Mon Sep 17 00:00:00 2001 From: Lars Berning <151194+laberning@users.noreply.github.com> Date: Wed, 16 Feb 2022 20:04:49 +0100 Subject: [PATCH] adds raw power metric and memory cleanup in games --- app/client/arcade/RowingGames.js | 8 ++++++ app/client/arcade/StrokeFighterBattleScene.js | 25 ++++++++++++++++--- app/client/components/GameComponent.js | 6 +++++ app/client/lib/app.js | 11 ++------ app/engine/RowingStatistics.js | 1 + app/engine/averager/WeightedAverager.js | 5 ++++ app/engine/averager/WeightedAverager.test.js | 16 ++++++++++++ rollup.config.js | 3 +-- 8 files changed, 60 insertions(+), 15 deletions(-) diff --git a/app/client/arcade/RowingGames.js b/app/client/arcade/RowingGames.js index 763b157..25f40db 100644 --- a/app/client/arcade/RowingGames.js +++ b/app/client/arcade/RowingGames.js @@ -41,6 +41,7 @@ export function createRowingGames (canvasElement, clientWidth, clientHeight) { // todo: check if there is some kaboomish way to get the active scene let activeScene k.scene('strokeFighterBattle', () => { activeScene = StrokeFighterBattleScene(k) }) + k.scene('disposed', () => { }) k.go('strokeFighterBattle') @@ -50,8 +51,15 @@ export function createRowingGames (canvasElement, clientWidth, clientHeight) { } } + // todo: currently we move to an empty scene to dispose the game as there does not seem to be + // a mechanism in kaboom to dispose the instance. + function dispose () { + k.go('disposed') + } + return { k, + dispose, appState } } diff --git a/app/client/arcade/StrokeFighterBattleScene.js b/app/client/arcade/StrokeFighterBattleScene.js index 5ce03e8..291dfb7 100644 --- a/app/client/arcade/StrokeFighterBattleScene.js +++ b/app/client/arcade/StrokeFighterBattleScene.js @@ -182,17 +182,34 @@ export default function StrokeFighterBattleScene (k) { }) const timer = k.add([ - k.text('0'), + k.text('00:00'), + k.scale(0.8), k.pos(12, 32), k.fixed(), k.layer('ui') ]) + let trainingTimeRounded = 0 timer.onUpdate(() => { trainingTime += k.dt() - timer.text = trainingTime.toFixed(2) + const newTrainingTimeRounded = Math.round(trainingTime) + if (trainingTimeRounded !== newTrainingTimeRounded) { + timer.text = `${secondsToTimeString(newTrainingTimeRounded)} / ${k.debug.fps()}fps` + trainingTimeRounded = newTrainingTimeRounded + } }) + // converts a timestamp in seconds to a human readable hh:mm:ss format + function secondsToTimeString (secondsTimeStamp) { + if (secondsTimeStamp === Infinity) return '∞' + const hours = Math.floor(secondsTimeStamp / 60 / 60) + const minutes = Math.floor(secondsTimeStamp / 60) - (hours * 60) + const seconds = Math.floor(secondsTimeStamp % 60) + let timeString = hours > 0 ? ` ${hours.toString().padStart(2, '0')}:` : '' + timeString += `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}` + return timeString + } + k.onCollide('bullet', 'enemy', (bullet, enemy) => { k.destroy(bullet) enemy.hurt(1) @@ -218,9 +235,9 @@ export default function StrokeFighterBattleScene (k) { } function driveFinished (metrics) { - if (metrics.power < THRESHOLD_POWER * 0.75) { + if (metrics.powerRaw < THRESHOLD_POWER * 0.75) { fireWeapons(1) - } else if (metrics.power < THRESHOLD_POWER) { + } else if (metrics.powerRaw < THRESHOLD_POWER) { fireWeapons(2) } else { fireWeapons(3) diff --git a/app/client/components/GameComponent.js b/app/client/components/GameComponent.js index 75d9b39..1cc934b 100644 --- a/app/client/components/GameComponent.js +++ b/app/client/components/GameComponent.js @@ -92,4 +92,10 @@ export class GameComponent extends AppElement { } } } + + disconnectedCallback () { + if (this.rowingGames !== undefined) { + this.rowingGames.dispose() + } + } } diff --git a/app/client/lib/app.js b/app/client/lib/app.js index 105f78f..1c69f48 100644 --- a/app/client/lib/app.js +++ b/app/client/lib/app.js @@ -8,9 +8,6 @@ import NoSleep from 'nosleep.js' import { filterObjectByKeys } from './helper.js' -const rowingMetricsFields = ['strokesTotal', 'distanceTotal', 'caloriesTotal', 'power', 'heartrate', - 'heartrateBatteryLevel', 'splitFormatted', 'strokesPerMinute', 'durationTotalFormatted', 'strokeState'] - export function createApp (app) { const urlParameters = new URLSearchParams(window.location.search) const mode = urlParameters.get('mode') @@ -74,13 +71,9 @@ export function createApp (app) { break } case 'metrics': { - let activeFields = rowingMetricsFields + const resetStateFields = ['heartrate', 'heartrateBatteryLevel'] // if we are in reset state only update heart rate - if (data.strokesTotal === 0) { - activeFields = ['heartrate', 'heartrateBatteryLevel'] - } - - const filteredData = filterObjectByKeys(data, activeFields) + const filteredData = (data.strokesTotal === 0) ? filterObjectByKeys(data, resetStateFields) : data app.updateState({ ...app.getState(), metrics: filteredData }) break } diff --git a/app/engine/RowingStatistics.js b/app/engine/RowingStatistics.js index f683ddd..275c4b1 100644 --- a/app/engine/RowingStatistics.js +++ b/app/engine/RowingStatistics.js @@ -158,6 +158,7 @@ function createRowingStatistics (config) { strokeTime: lastStrokeDuration, // seconds distance: lastStrokeDistance > 0 && lastStrokeSpeed > 0 && sessionState === 'rowing' ? lastStrokeDistance : 0, // meters power: powerAverager.getAverage() > 0 && lastStrokeSpeed > 0 && sessionState === 'rowing' ? powerAverager.getAverage() : 0, // watts + powerRaw: powerAverager.getLastPushedValue() > 0 && lastStrokeSpeed > 0 && sessionState === 'rowing' ? powerAverager.getLastPushedValue() : 0, // watts split: splitTime, // seconds/500m splitFormatted: secondsToTimeString(splitTime), powerRatio: powerRatioAverager.getAverage() > 0 && lastStrokeSpeed > 0 && sessionState === 'rowing' ? powerRatioAverager.getAverage() : 0, diff --git a/app/engine/averager/WeightedAverager.js b/app/engine/averager/WeightedAverager.js index 4b266b9..c34c236 100644 --- a/app/engine/averager/WeightedAverager.js +++ b/app/engine/averager/WeightedAverager.js @@ -29,6 +29,10 @@ function createWeightedAverager (maxNumOfDataPoints) { } } + function getLastPushedValue () { + return dataPoints[0] + } + function reset () { dataPoints = [] } @@ -36,6 +40,7 @@ function createWeightedAverager (maxNumOfDataPoints) { return { pushValue, getAverage, + getLastPushedValue, reset } } diff --git a/app/engine/averager/WeightedAverager.test.js b/app/engine/averager/WeightedAverager.test.js index 28e1583..48af787 100644 --- a/app/engine/averager/WeightedAverager.test.js +++ b/app/engine/averager/WeightedAverager.test.js @@ -25,6 +25,22 @@ test('average of a and b is (2*b + a) / 3', () => { assert.is(weightedAverager.getAverage(), 3) }) +test('average of a, b and c is (2*c + b) / 3 if maxNumOfDataPoints is 2', () => { + const weightedAverager = createWeightedAverager(2) + weightedAverager.pushValue(5) // a + weightedAverager.pushValue(2) // b + weightedAverager.pushValue(17) // c + assert.is(weightedAverager.getAverage(), 12) +}) + +test('lastPushedValue of a, b and c is c', () => { + const weightedAverager = createWeightedAverager(10) + weightedAverager.pushValue(5) // a + weightedAverager.pushValue(2) // b + weightedAverager.pushValue(17) // c + assert.is(weightedAverager.getLastPushedValue(), 17) +}) + test('average should be 0 after reset', () => { const weightedAverager = createWeightedAverager(10) weightedAverager.pushValue(5) diff --git a/rollup.config.js b/rollup.config.js index df2a223..33c4a4a 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -37,14 +37,13 @@ export default { terser({ ecma: 2020, module: true, - warnings: true, mangle: { properties: { regex: /^__/ } } }), - summary() + summary({}) ], output: {