// Copyright (c) 2026 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT // extends UIElement (not Label) deliberately: only a UIElement/SCRIPTED // instance's render()/update() override actually gets dispatched by the // engine (the native Label/Rectangle callback tables never consult a // subclass's override -- they're not scripted types themselves). So this // composes a child Label instead of subclassing one, same as Button. // // Overrides render() rather than update(): update()/uiUpdate() only run // on the one fixed-timestep tick per real frame that actually crosses a // DUSK_TIME_STEP boundary (see Time.renderDelta's doc), but an FPS // counter needs to sample every real frame -- which is exactly what // render() does. class FpsCounter extends UIElement { init() { this.label = new Label(); this.add(this.label); this._average = 0; } render() { const delta = Time.renderDelta; if(delta > 0) { const fps = 1 / delta; this._average = this._average === 0 ? fps : this._average + (fps - this._average) * FpsCounter.SMOOTHING; } this.label.text = 'FPS: ' + Math.round(this._average); this.renderChildren(); } } // Exponential-moving-average weight per sample -- lower is smoother/ // slower to react, higher tracks instantaneous frame time more closely. FpsCounter.SMOOTHING = 0.1; module.exports = FpsCounter;