mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Support dynamic tick rates
This commit is contained in:
parent
65529cce1a
commit
ca0e17f3dd
10
src/css/ingame_hud/debug_info.scss
Normal file
10
src/css/ingame_hud/debug_info.scss
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ingame_HUD_DebugInfo {
|
||||||
|
position: absolute;
|
||||||
|
@include S(bottom, 5px);
|
||||||
|
@include S(left, 5px);
|
||||||
|
|
||||||
|
font-size: 15px;
|
||||||
|
display: flex;
|
||||||
|
line-height: 15px;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
@ -39,6 +39,7 @@
|
|||||||
@import "ingame_hud/pinned_shapes";
|
@import "ingame_hud/pinned_shapes";
|
||||||
@import "ingame_hud/notifications";
|
@import "ingame_hud/notifications";
|
||||||
@import "ingame_hud/settings_menu";
|
@import "ingame_hud/settings_menu";
|
||||||
|
@import "ingame_hud/debug_info";
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
$elements:
|
$elements:
|
||||||
@ -57,6 +58,7 @@ ingame_HUD_GameMenu,
|
|||||||
ingame_HUD_KeybindingOverlay,
|
ingame_HUD_KeybindingOverlay,
|
||||||
ingame_HUD_Notifications,
|
ingame_HUD_Notifications,
|
||||||
ingame_HUD_MassSelector,
|
ingame_HUD_MassSelector,
|
||||||
|
ingame_HUD_DebugInfo,
|
||||||
|
|
||||||
// Overlays
|
// Overlays
|
||||||
ingame_HUD_BetaOverlay,
|
ingame_HUD_BetaOverlay,
|
||||||
|
@ -23,13 +23,8 @@ export const globalConfig = {
|
|||||||
statisticsGraphSlices: 100,
|
statisticsGraphSlices: 100,
|
||||||
analyticsSliceDurationSeconds: 10,
|
analyticsSliceDurationSeconds: 10,
|
||||||
|
|
||||||
// [Calculated] physics step size
|
minimumTickRate: 30,
|
||||||
physicsDeltaMs: 0,
|
maximumTickRate: 500,
|
||||||
physicsDeltaSeconds: 0,
|
|
||||||
|
|
||||||
// Update physics at N fps, independent of rendering
|
|
||||||
// physicsUpdateRate: 55,
|
|
||||||
physicsUpdateRate: 120,
|
|
||||||
|
|
||||||
// Map
|
// Map
|
||||||
mapChunkSize: 32,
|
mapChunkSize: 32,
|
||||||
@ -76,7 +71,7 @@ export const globalConfig = {
|
|||||||
|
|
||||||
debug: {
|
debug: {
|
||||||
/* dev:start */
|
/* dev:start */
|
||||||
fastGameEnter: true,
|
// fastGameEnter: true,
|
||||||
noArtificialDelays: true,
|
noArtificialDelays: true,
|
||||||
// disableSavegameWrite: true,
|
// disableSavegameWrite: true,
|
||||||
showEntityBounds: false,
|
showEntityBounds: false,
|
||||||
@ -111,7 +106,4 @@ export const IS_MOBILE = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
|||||||
|
|
||||||
// Automatic calculations
|
// Automatic calculations
|
||||||
|
|
||||||
globalConfig.physicsDeltaMs = 1000.0 / globalConfig.physicsUpdateRate;
|
|
||||||
globalConfig.physicsDeltaSeconds = 1.0 / globalConfig.physicsUpdateRate;
|
|
||||||
|
|
||||||
globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5;
|
globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5;
|
||||||
|
@ -62,7 +62,7 @@ export class UndergroundBeltComponent extends Component {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pendingItems.push([item, 1 / beltSpeed / globalConfig.itemSpacingOnBelts]);
|
this.pendingItems.push([item, 0]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,8 @@ export class UndergroundBeltComponent extends Component {
|
|||||||
// NOTICE:
|
// NOTICE:
|
||||||
// This corresponds to the item ejector - it needs 0.5 additional tiles to eject the item.
|
// This corresponds to the item ejector - it needs 0.5 additional tiles to eject the item.
|
||||||
// So instead of adding 1 we add 0.5 only.
|
// So instead of adding 1 we add 0.5 only.
|
||||||
const travelDuration = (travelDistance + 0.5) / beltSpeed / globalConfig.itemSpacingOnBelts;
|
// Additionally it takes 1 tile for the acceptor which we just add on top.
|
||||||
|
const travelDuration = (travelDistance + 1.5) / beltSpeed / globalConfig.itemSpacingOnBelts;
|
||||||
|
|
||||||
this.pendingItems.push([item, travelDuration]);
|
this.pendingItems.push([item, travelDuration]);
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import { GameTime } from "./time/game_time";
|
|||||||
import { ProductionAnalytics } from "./production_analytics";
|
import { ProductionAnalytics } from "./production_analytics";
|
||||||
import { randomInt } from "../core/utils";
|
import { randomInt } from "../core/utils";
|
||||||
import { defaultBuildingVariant } from "./meta_building";
|
import { defaultBuildingVariant } from "./meta_building";
|
||||||
|
import { DynamicTickrate } from "./dynamic_tickrate";
|
||||||
|
|
||||||
const logger = createLogger("ingame/core");
|
const logger = createLogger("ingame/core");
|
||||||
|
|
||||||
@ -53,16 +54,6 @@ export class GameCore {
|
|||||||
/** @type {GameRoot} */
|
/** @type {GameRoot} */
|
||||||
this.root = null;
|
this.root = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Time budget (seconds) for logic updates
|
|
||||||
*/
|
|
||||||
this.logicTimeBudget = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Time budget (seconds) for user interface updates
|
|
||||||
*/
|
|
||||||
this.uiTimeBudget = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true at the beginning of a logic update and cleared when its finished.
|
* Set to true at the beginning of a logic update and cleared when its finished.
|
||||||
* This is to prevent doing a recursive logic update which can lead to unexpected
|
* This is to prevent doing a recursive logic update which can lead to unexpected
|
||||||
@ -97,6 +88,9 @@ export class GameCore {
|
|||||||
// This isn't nice, but we need it right here
|
// This isn't nice, but we need it right here
|
||||||
root.gameState.keyActionMapper = new KeyActionMapper(root, this.root.gameState.inputReciever);
|
root.gameState.keyActionMapper = new KeyActionMapper(root, this.root.gameState.inputReciever);
|
||||||
|
|
||||||
|
// Needs to come first
|
||||||
|
root.dynamicTickrate = new DynamicTickrate(root);
|
||||||
|
|
||||||
// Init classes
|
// Init classes
|
||||||
root.camera = new Camera(root);
|
root.camera = new Camera(root);
|
||||||
root.map = new MapView(root);
|
root.map = new MapView(root);
|
||||||
@ -250,17 +244,6 @@ export class GameCore {
|
|||||||
// Perform logic ticks
|
// Perform logic ticks
|
||||||
this.root.time.performTicks(deltaMs, this.boundInternalTick);
|
this.root.time.performTicks(deltaMs, this.boundInternalTick);
|
||||||
|
|
||||||
// Update UI particles
|
|
||||||
this.uiTimeBudget += deltaMs;
|
|
||||||
const maxUiSteps = 3;
|
|
||||||
if (this.uiTimeBudget > globalConfig.physicsDeltaMs * maxUiSteps) {
|
|
||||||
this.uiTimeBudget = globalConfig.physicsDeltaMs;
|
|
||||||
}
|
|
||||||
while (this.uiTimeBudget >= globalConfig.physicsDeltaMs) {
|
|
||||||
this.uiTimeBudget -= globalConfig.physicsDeltaMs;
|
|
||||||
// root.uiParticleMgr.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update analytics
|
// Update analytics
|
||||||
root.productionAnalytics.update();
|
root.productionAnalytics.update();
|
||||||
|
|
||||||
@ -288,6 +271,9 @@ export class GameCore {
|
|||||||
|
|
||||||
updateLogic() {
|
updateLogic() {
|
||||||
const root = this.root;
|
const root = this.root;
|
||||||
|
|
||||||
|
root.dynamicTickrate.beginTick();
|
||||||
|
|
||||||
this.duringLogicUpdate = true;
|
this.duringLogicUpdate = true;
|
||||||
|
|
||||||
// Update entities, this removes destroyed entities
|
// Update entities, this removes destroyed entities
|
||||||
@ -296,6 +282,8 @@ export class GameCore {
|
|||||||
// IMPORTANT: At this point, the game might be game over. Stop if this is the case
|
// IMPORTANT: At this point, the game might be game over. Stop if this is the case
|
||||||
if (!this.root) {
|
if (!this.root) {
|
||||||
logger.log("Root destructed, returning false");
|
logger.log("Root destructed, returning false");
|
||||||
|
root.dynamicTickrate.endTick();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +291,7 @@ export class GameCore {
|
|||||||
// root.particleMgr.update();
|
// root.particleMgr.update();
|
||||||
|
|
||||||
this.duringLogicUpdate = false;
|
this.duringLogicUpdate = false;
|
||||||
|
root.dynamicTickrate.endTick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
104
src/js/game/dynamic_tickrate.js
Normal file
104
src/js/game/dynamic_tickrate.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { GameRoot } from "./root";
|
||||||
|
import { createLogger } from "../core/logging";
|
||||||
|
import { globalConfig } from "../core/config";
|
||||||
|
import { performanceNow, Math_min, Math_round, Math_max } from "../core/builtins";
|
||||||
|
import { round3Digits } from "../core/utils";
|
||||||
|
|
||||||
|
const logger = createLogger("dynamic_tickrate");
|
||||||
|
|
||||||
|
export class DynamicTickrate {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {GameRoot} root
|
||||||
|
*/
|
||||||
|
constructor(root) {
|
||||||
|
this.root = root;
|
||||||
|
|
||||||
|
this.setTickRate(120);
|
||||||
|
|
||||||
|
this.currentTickStart = null;
|
||||||
|
this.capturedTicks = [];
|
||||||
|
this.averageTickDuration = 0;
|
||||||
|
|
||||||
|
// Exposed
|
||||||
|
this.deltaSeconds = 0;
|
||||||
|
this.deltaMs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tick rate to N updates per second
|
||||||
|
* @param {number} rate
|
||||||
|
*/
|
||||||
|
setTickRate(rate) {
|
||||||
|
logger.log("Applying tick-rate of", rate);
|
||||||
|
this.currentTickRate = rate;
|
||||||
|
this.deltaMs = 1000.0 / this.currentTickRate;
|
||||||
|
this.deltaSeconds = 1.0 / this.currentTickRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increases the tick rate marginally
|
||||||
|
*/
|
||||||
|
increaseTickRate() {
|
||||||
|
this.setTickRate(Math_round(Math_min(globalConfig.maximumTickRate, this.currentTickRate * 1.1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decreases the tick rate marginally
|
||||||
|
*/
|
||||||
|
decreaseTickRate() {
|
||||||
|
this.setTickRate(Math_round(Math_min(globalConfig.maximumTickRate, this.currentTickRate * 0.9)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call whenever a tick began
|
||||||
|
*/
|
||||||
|
beginTick() {
|
||||||
|
assert(this.currentTickStart === null, "BeginTick called twice");
|
||||||
|
this.currentTickStart = performanceNow();
|
||||||
|
|
||||||
|
if (this.capturedTicks.length > this.currentTickRate * 4) {
|
||||||
|
// Take only a portion of the ticks
|
||||||
|
this.capturedTicks.sort();
|
||||||
|
this.capturedTicks.splice(0, 10);
|
||||||
|
this.capturedTicks.splice(this.capturedTicks.length - 11, 10);
|
||||||
|
|
||||||
|
let average = 0;
|
||||||
|
for (let i = 0; i < this.capturedTicks.length; ++i) {
|
||||||
|
average += this.capturedTicks[i];
|
||||||
|
}
|
||||||
|
average /= this.capturedTicks.length;
|
||||||
|
|
||||||
|
// Calculate tick duration to cover X% of the frame
|
||||||
|
const ticksPerFrame = this.currentTickRate / 60;
|
||||||
|
const maxFrameDurationMs = 8;
|
||||||
|
const maxTickDuration = maxFrameDurationMs / ticksPerFrame;
|
||||||
|
// const maxTickDuration = (1000 / this.currentTickRate) * 0.75;
|
||||||
|
logger.log(
|
||||||
|
"Average time per tick:",
|
||||||
|
round3Digits(average) + "ms",
|
||||||
|
"allowed are",
|
||||||
|
maxTickDuration
|
||||||
|
);
|
||||||
|
this.averageTickDuration = average;
|
||||||
|
|
||||||
|
if (average < maxTickDuration) {
|
||||||
|
this.increaseTickRate();
|
||||||
|
} else {
|
||||||
|
this.decreaseTickRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.capturedTicks = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call whenever a tick ended
|
||||||
|
*/
|
||||||
|
endTick() {
|
||||||
|
assert(this.currentTickStart !== null, "EndTick called without BeginTick");
|
||||||
|
const duration = performanceNow() - this.currentTickStart;
|
||||||
|
this.capturedTicks.push(duration);
|
||||||
|
this.currentTickStart = null;
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ import { HUDPinnedShapes } from "./parts/pinned_shapes";
|
|||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
import { HUDNotifications, enumNotificationType } from "./parts/notifications";
|
import { HUDNotifications, enumNotificationType } from "./parts/notifications";
|
||||||
import { HUDSettingsMenu } from "./parts/settings_menu";
|
import { HUDSettingsMenu } from "./parts/settings_menu";
|
||||||
|
import { HUDDebugInfo } from "./parts/debug_info";
|
||||||
|
|
||||||
export class GameHUD {
|
export class GameHUD {
|
||||||
/**
|
/**
|
||||||
@ -57,6 +58,7 @@ export class GameHUD {
|
|||||||
settingsMenu: new HUDSettingsMenu(this.root),
|
settingsMenu: new HUDSettingsMenu(this.root),
|
||||||
|
|
||||||
// betaOverlay: new HUDBetaOverlay(this.root),
|
// betaOverlay: new HUDBetaOverlay(this.root),
|
||||||
|
debugInfo: new HUDDebugInfo(this.root),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.signals = {
|
this.signals = {
|
||||||
|
19
src/js/game/hud/parts/debug_info.js
Normal file
19
src/js/game/hud/parts/debug_info.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
import { makeDiv, round3Digits } from "../../../core/utils";
|
||||||
|
|
||||||
|
export class HUDDebugInfo extends BaseHUDPart {
|
||||||
|
createElements(parent) {
|
||||||
|
this.element = makeDiv(parent, "ingame_HUD_DebugInfo", []);
|
||||||
|
|
||||||
|
this.tickRateElement = makeDiv(this.element, null, ["tickRate"], "Ticks /s: 120");
|
||||||
|
this.tickDurationElement = makeDiv(this.element, null, ["tickDuration"], "Update time: 0.5ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.tickRateElement.innerText = "Tickrate: " + this.root.dynamicTickrate.currentTickRate;
|
||||||
|
this.tickDurationElement.innerText =
|
||||||
|
"Avg. Dur: " + round3Digits(this.root.dynamicTickrate.averageTickDuration) + "ms";
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,7 @@ import { ProductionAnalytics } from "./production_analytics";
|
|||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { ShapeDefinition } from "./shape_definition";
|
import { ShapeDefinition } from "./shape_definition";
|
||||||
import { BaseItem } from "./base_item";
|
import { BaseItem } from "./base_item";
|
||||||
|
import { DynamicTickrate } from "./dynamic_tickrate";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
const logger = createLogger("game/root");
|
const logger = createLogger("game/root");
|
||||||
@ -115,6 +116,9 @@ export class GameRoot {
|
|||||||
/** @type {ProductionAnalytics} */
|
/** @type {ProductionAnalytics} */
|
||||||
this.productionAnalytics = null;
|
this.productionAnalytics = null;
|
||||||
|
|
||||||
|
/** @type {DynamicTickrate} */
|
||||||
|
this.dynamicTickrate = null;
|
||||||
|
|
||||||
this.signals = {
|
this.signals = {
|
||||||
// Entities
|
// Entities
|
||||||
entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
|
@ -109,21 +109,30 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityItems.bind(this));
|
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityItems.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
/**
|
||||||
|
* Updates a given entity
|
||||||
|
* @param {Entity} entity
|
||||||
|
* @param {Set} processedEntities
|
||||||
|
*/
|
||||||
|
updateBelt(entity, processedEntities) {
|
||||||
|
if (processedEntities.has(entity.uid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
processedEntities.add(entity.uid);
|
||||||
|
|
||||||
// Divide by item spacing on belts since we use throughput and not speed
|
// Divide by item spacing on belts since we use throughput and not speed
|
||||||
const beltSpeed =
|
const beltSpeed =
|
||||||
this.root.hubGoals.getBeltBaseSpeed() *
|
this.root.hubGoals.getBeltBaseSpeed() *
|
||||||
globalConfig.physicsDeltaSeconds *
|
this.root.dynamicTickrate.deltaSeconds *
|
||||||
globalConfig.itemSpacingOnBelts;
|
globalConfig.itemSpacingOnBelts;
|
||||||
|
|
||||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
|
||||||
const entity = this.allEntities[i];
|
|
||||||
const beltComp = entity.components.Belt;
|
const beltComp = entity.components.Belt;
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
const items = beltComp.sortedItems;
|
const items = beltComp.sortedItems;
|
||||||
|
|
||||||
if (items.length === 0) {
|
if (items.length === 0) {
|
||||||
// Fast out for performance
|
// Fast out for performance
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ejectorComp = entity.components.ItemEjector;
|
const ejectorComp = entity.components.ItemEjector;
|
||||||
@ -144,6 +153,9 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
if (followUpEntity) {
|
if (followUpEntity) {
|
||||||
const followUpBeltComp = followUpEntity.components.Belt;
|
const followUpBeltComp = followUpEntity.components.Belt;
|
||||||
if (followUpBeltComp) {
|
if (followUpBeltComp) {
|
||||||
|
// Update follow up belt first
|
||||||
|
this.updateBelt(followUpEntity, processedEntities);
|
||||||
|
|
||||||
const spacingOnBelt = followUpBeltComp.getDistanceToFirstItemCenter();
|
const spacingOnBelt = followUpBeltComp.getDistanceToFirstItemCenter();
|
||||||
maxProgress = Math_min(1, 1 - globalConfig.itemSpacingOnBelts + spacingOnBelt);
|
maxProgress = Math_min(1, 1 - globalConfig.itemSpacingOnBelts + spacingOnBelt);
|
||||||
}
|
}
|
||||||
@ -182,6 +194,14 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const processedEntities = new Set();
|
||||||
|
|
||||||
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
|
const entity = this.allEntities[i];
|
||||||
|
this.updateBelt(entity, processedEntities);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,7 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
|
|||||||
for (let animIndex = 0; animIndex < aceptorComp.itemConsumptionAnimations.length; ++animIndex) {
|
for (let animIndex = 0; animIndex < aceptorComp.itemConsumptionAnimations.length; ++animIndex) {
|
||||||
const anim = aceptorComp.itemConsumptionAnimations[animIndex];
|
const anim = aceptorComp.itemConsumptionAnimations[animIndex];
|
||||||
anim.animProgress +=
|
anim.animProgress +=
|
||||||
globalConfig.physicsDeltaSeconds *
|
this.root.dynamicTickrate.deltaSeconds *
|
||||||
this.root.hubGoals.getBeltBaseSpeed() *
|
this.root.hubGoals.getBeltBaseSpeed() *
|
||||||
2 *
|
2 *
|
||||||
globalConfig.itemSpacingOnBelts;
|
globalConfig.itemSpacingOnBelts;
|
||||||
|
@ -14,7 +14,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
|
|
||||||
update() {
|
update() {
|
||||||
const effectiveBeltSpeed = this.root.hubGoals.getBeltBaseSpeed() * globalConfig.itemSpacingOnBelts;
|
const effectiveBeltSpeed = this.root.hubGoals.getBeltBaseSpeed() * globalConfig.itemSpacingOnBelts;
|
||||||
const progressGrowth = (effectiveBeltSpeed / 0.5) * globalConfig.physicsDeltaSeconds;
|
const progressGrowth = (effectiveBeltSpeed / 0.5) * this.root.dynamicTickrate.deltaSeconds;
|
||||||
|
|
||||||
// Try to find acceptors for every ejector
|
// Try to find acceptors for every ejector
|
||||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
|
@ -23,7 +23,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
|||||||
// First of all, process the current recipe
|
// First of all, process the current recipe
|
||||||
processorComp.secondsUntilEject = Math_max(
|
processorComp.secondsUntilEject = Math_max(
|
||||||
0,
|
0,
|
||||||
processorComp.secondsUntilEject - globalConfig.physicsDeltaSeconds
|
processorComp.secondsUntilEject - this.root.dynamicTickrate.deltaSeconds
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if we have any finished items we can eject
|
// Check if we have any finished items we can eject
|
||||||
|
@ -31,7 +31,7 @@ export class UndergroundBeltSystem extends GameSystemWithFilter {
|
|||||||
// Decrease remaining time of all items in belt
|
// Decrease remaining time of all items in belt
|
||||||
for (let k = 0; k < undergroundComp.pendingItems.length; ++k) {
|
for (let k = 0; k < undergroundComp.pendingItems.length; ++k) {
|
||||||
const item = undergroundComp.pendingItems[k];
|
const item = undergroundComp.pendingItems[k];
|
||||||
item[1] = Math_max(0, item[1] - globalConfig.physicsDeltaSeconds);
|
item[1] = Math_max(0, item[1] - this.root.dynamicTickrate.deltaSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (undergroundComp.mode === enumUndergroundBeltMode.sender) {
|
if (undergroundComp.mode === enumUndergroundBeltMode.sender) {
|
||||||
|
@ -6,7 +6,7 @@ import { types, BasicSerializableObject } from "../../savegame/serialization";
|
|||||||
import { RegularGameSpeed } from "./regular_game_speed";
|
import { RegularGameSpeed } from "./regular_game_speed";
|
||||||
import { BaseGameSpeed } from "./base_game_speed";
|
import { BaseGameSpeed } from "./base_game_speed";
|
||||||
import { PausedGameSpeed } from "./paused_game_speed";
|
import { PausedGameSpeed } from "./paused_game_speed";
|
||||||
import { performanceNow } from "../../core/builtins";
|
import { performanceNow, Math_max } from "../../core/builtins";
|
||||||
import { FastForwardGameSpeed } from "./fast_forward_game_speed";
|
import { FastForwardGameSpeed } from "./fast_forward_game_speed";
|
||||||
import { gGameSpeedRegistry } from "../../core/global_registries";
|
import { gGameSpeedRegistry } from "../../core/global_registries";
|
||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
@ -102,7 +102,7 @@ export class GameTime extends BasicSerializableObject {
|
|||||||
* Internal method to generate new logic time budget
|
* Internal method to generate new logic time budget
|
||||||
* @param {number} deltaMs
|
* @param {number} deltaMs
|
||||||
*/
|
*/
|
||||||
înternalAddDeltaToBudget(deltaMs) {
|
internalAddDeltaToBudget(deltaMs) {
|
||||||
// Only update if game is supposed to update
|
// Only update if game is supposed to update
|
||||||
if (this.root.hud.shouldPauseGame()) {
|
if (this.root.hud.shouldPauseGame()) {
|
||||||
this.logicTimeBudget = 0;
|
this.logicTimeBudget = 0;
|
||||||
@ -112,9 +112,13 @@ export class GameTime extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for too big pile of updates -> reduce it to 1
|
// Check for too big pile of updates -> reduce it to 1
|
||||||
const maxLogicSteps = this.speed.getMaxLogicStepsInQueue();
|
const maxLogicSteps = Math_max(
|
||||||
if (this.logicTimeBudget > globalConfig.physicsDeltaMs * maxLogicSteps) {
|
3,
|
||||||
this.logicTimeBudget = globalConfig.physicsDeltaMs * maxLogicSteps;
|
(this.speed.getMaxLogicStepsInQueue() * this.root.dynamicTickrate.currentTickRate) / 60
|
||||||
|
);
|
||||||
|
if (this.logicTimeBudget > this.root.dynamicTickrate.deltaMs * maxLogicSteps) {
|
||||||
|
// logger.warn("Skipping logic time steps since more than", maxLogicSteps, "are in queue");
|
||||||
|
this.logicTimeBudget = this.root.dynamicTickrate.deltaMs * maxLogicSteps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,13 +128,13 @@ export class GameTime extends BasicSerializableObject {
|
|||||||
* @param {function():boolean} updateMethod
|
* @param {function():boolean} updateMethod
|
||||||
*/
|
*/
|
||||||
performTicks(deltaMs, updateMethod) {
|
performTicks(deltaMs, updateMethod) {
|
||||||
this.înternalAddDeltaToBudget(deltaMs);
|
this.internalAddDeltaToBudget(deltaMs);
|
||||||
|
|
||||||
const speedAtStart = this.root.time.getSpeed();
|
const speedAtStart = this.root.time.getSpeed();
|
||||||
|
|
||||||
// Update physics & logic
|
// Update physics & logic
|
||||||
while (this.logicTimeBudget >= globalConfig.physicsDeltaMs) {
|
while (this.logicTimeBudget >= this.root.dynamicTickrate.deltaMs) {
|
||||||
this.logicTimeBudget -= globalConfig.physicsDeltaMs;
|
this.logicTimeBudget -= this.root.dynamicTickrate.deltaMs;
|
||||||
|
|
||||||
if (!updateMethod()) {
|
if (!updateMethod()) {
|
||||||
// Gameover happened or so, do not update anymore
|
// Gameover happened or so, do not update anymore
|
||||||
@ -138,7 +142,7 @@ export class GameTime extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step game time
|
// Step game time
|
||||||
this.timeSeconds = quantizeFloat(this.timeSeconds + globalConfig.physicsDeltaSeconds);
|
this.timeSeconds = quantizeFloat(this.timeSeconds + this.root.dynamicTickrate.deltaSeconds);
|
||||||
|
|
||||||
// Game time speed changed, need to abort since our logic steps are no longer valid
|
// Game time speed changed, need to abort since our logic steps are no longer valid
|
||||||
if (speedAtStart.getId() !== this.speed.getId()) {
|
if (speedAtStart.getId() !== this.speed.getId()) {
|
||||||
|
@ -179,8 +179,13 @@ export class MainMenuState extends GameState {
|
|||||||
this.trackClicks(qs(".mainContainer .importButton"), this.requestImportSavegame);
|
this.trackClicks(qs(".mainContainer .importButton"), this.requestImportSavegame);
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.fastGameEnter) {
|
if (G_IS_DEV && globalConfig.debug.fastGameEnter) {
|
||||||
|
const games = this.app.savegameMgr.getSavegamesMetaData();
|
||||||
|
if (games.length > 0) {
|
||||||
|
this.resumeGame(games[0]);
|
||||||
|
} else {
|
||||||
this.onPlayButtonClicked();
|
this.onPlayButtonClicked();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize video
|
// Initialize video
|
||||||
this.videoElement = this.htmlElement.querySelector("video");
|
this.videoElement = this.htmlElement.querySelector("video");
|
||||||
|
Loading…
Reference in New Issue
Block a user