extra-game-loop 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/lib/game-loop-lite.d.ts +19 -0
- package/lib/game-loop-lite.js +58 -0
- package/lib/game-loop-lite.js.map +1 -0
- package/lib/game-loop.js +5 -5
- package/lib/game-loop.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/game-loop-lite.ts +88 -0
- package/src/game-loop.ts +11 -11
- package/src/index.ts +1 -0
package/README.md
CHANGED
|
@@ -53,3 +53,27 @@ class GameLoop<FixedDeltaTime extends number = number> {
|
|
|
53
53
|
nextFrame(deltaTime: number): void
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
|
+
|
|
57
|
+
### GameLoopLite
|
|
58
|
+
```ts
|
|
59
|
+
class GameLoopLite {
|
|
60
|
+
constructor(options: {
|
|
61
|
+
update?: (deltaTime: number /* ms */) => void
|
|
62
|
+
render?: () => void
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
start(): void
|
|
66
|
+
stop(): void
|
|
67
|
+
getFramesOfSecond(): number
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* This method allows you to manually advance to the next frame.
|
|
71
|
+
*/
|
|
72
|
+
nextFrame(deltaTime: number): void
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The lite version of `GameLoop` for situations where the physics system is not needed.
|
|
77
|
+
|
|
78
|
+
Basically, it can be seen as an OOP wrapper for `requestAnimationFrame`,
|
|
79
|
+
so it can be used for non-game projects as well.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface IGameLoopLiteOptions {
|
|
2
|
+
update?: (deltaTime: number) => void;
|
|
3
|
+
render?: () => void;
|
|
4
|
+
}
|
|
5
|
+
export declare class GameLoopLite {
|
|
6
|
+
private readonly fsm;
|
|
7
|
+
private readonly update?;
|
|
8
|
+
private readonly render?;
|
|
9
|
+
private requstId?;
|
|
10
|
+
private lastTimestamp?;
|
|
11
|
+
private lastDeltaTime;
|
|
12
|
+
constructor(options: IGameLoopLiteOptions);
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
getFramesOfSecond(): number;
|
|
16
|
+
private loop;
|
|
17
|
+
nextFrame(deltaTime: number): void;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GameLoopLite = void 0;
|
|
4
|
+
const extra_fsm_1 = require("extra-fsm");
|
|
5
|
+
var State;
|
|
6
|
+
(function (State) {
|
|
7
|
+
State["Stopped"] = "stopped";
|
|
8
|
+
State["Running"] = "running";
|
|
9
|
+
})(State || (State = {}));
|
|
10
|
+
const schema = {
|
|
11
|
+
[State.Stopped]: { start: State.Running },
|
|
12
|
+
[State.Running]: { stop: State.Stopped }
|
|
13
|
+
};
|
|
14
|
+
class GameLoopLite {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.fsm = new extra_fsm_1.FiniteStateMachine(schema, State.Stopped);
|
|
17
|
+
this.lastDeltaTime = 0;
|
|
18
|
+
this.loop = (timestamp) => {
|
|
19
|
+
const deltaTime = Math.max(timestamp - this.lastTimestamp, 0);
|
|
20
|
+
this.lastDeltaTime = deltaTime;
|
|
21
|
+
this.lastTimestamp = timestamp;
|
|
22
|
+
this.nextFrame(deltaTime);
|
|
23
|
+
this.requstId = requestAnimationFrame(this.loop);
|
|
24
|
+
};
|
|
25
|
+
this.update = options.update;
|
|
26
|
+
this.render = options.render;
|
|
27
|
+
}
|
|
28
|
+
start() {
|
|
29
|
+
this.fsm.send('start');
|
|
30
|
+
const timestamp = performance.now();
|
|
31
|
+
this.lastTimestamp = timestamp;
|
|
32
|
+
this.loop(timestamp);
|
|
33
|
+
}
|
|
34
|
+
stop() {
|
|
35
|
+
this.fsm.send('stop');
|
|
36
|
+
cancelAnimationFrame(this.requstId);
|
|
37
|
+
delete this.requstId;
|
|
38
|
+
delete this.lastTimestamp;
|
|
39
|
+
this.lastDeltaTime = 0;
|
|
40
|
+
}
|
|
41
|
+
getFramesOfSecond() {
|
|
42
|
+
if (this.fsm.matches(State.Running)) {
|
|
43
|
+
return this.lastDeltaTime !== 0
|
|
44
|
+
? 1000 / this.lastDeltaTime
|
|
45
|
+
: 0;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
nextFrame(deltaTime) {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
(_a = this.update) === null || _a === void 0 ? void 0 : _a.call(this, deltaTime);
|
|
54
|
+
(_b = this.render) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.GameLoopLite = GameLoopLite;
|
|
58
|
+
//# sourceMappingURL=game-loop-lite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-loop-lite.js","sourceRoot":"","sources":["../src/game-loop-lite.ts"],"names":[],"mappings":";;;AAAA,yCAAyE;AAczE,IAAK,KAGJ;AAHD,WAAK,KAAK;IACR,4BAAmB,CAAA;IACnB,4BAAmB,CAAA;AACrB,CAAC,EAHI,KAAK,KAAL,KAAK,QAGT;AAMD,MAAM,MAAM,GAA4C;IACtD,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE;IACzC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE;CACzC,CAAA;AAED,MAAa,YAAY;IAQvB,YAAY,OAA6B;QAPxB,QAAG,GAAG,IAAI,8BAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QAK5D,kBAAa,GAAG,CAAC,CAAA;QAmCjB,SAAI,GAAG,CAAC,SAAiB,EAAQ,EAAE;YAGzC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,aAAc,EAAE,CAAC,CAAC,CAAA;YAE9D,IAAI,CAAC,aAAa,GAAG,SAAS,CAAA;YAC9B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAA;YAE9B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;YAEzB,IAAI,CAAC,QAAQ,GAAG,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAClD,CAAC,CAAA;QA3CC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;QAC5B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;IAC9B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAEtB,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;QACnC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAA;QAC9B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACtB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAErB,oBAAoB,CAAC,IAAI,CAAC,QAAS,CAAC,CAAA;QAEpC,OAAO,IAAI,CAAC,QAAQ,CAAA;QACpB,OAAO,IAAI,CAAC,aAAa,CAAA;QACzB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAA;IACxB,CAAC;IAED,iBAAiB;QACf,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;YACnC,OAAO,IAAI,CAAC,aAAa,KAAK,CAAC;gBAC1B,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,aAAa;gBAC3B,CAAC,CAAC,CAAC,CAAA;SACT;aAAM;YACL,OAAO,CAAC,CAAA;SACT;IACH,CAAC;IAeD,SAAS,CAAC,SAAiB;;QACzB,MAAA,IAAI,CAAC,MAAM,qDAAG,SAAS,CAAC,CAAA;QAExB,MAAA,IAAI,CAAC,MAAM,oDAAI,CAAA;IACjB,CAAC;CACF;AA3DD,oCA2DC"}
|
package/lib/game-loop.js
CHANGED
|
@@ -17,9 +17,8 @@ class GameLoop {
|
|
|
17
17
|
this.fsm = new extra_fsm_1.FiniteStateMachine(schema, State.Stopped);
|
|
18
18
|
this.deltaTimeAccumulator = 0;
|
|
19
19
|
this.lastDeltaTime = 0;
|
|
20
|
-
this.loop = () => {
|
|
21
|
-
const
|
|
22
|
-
const deltaTime = timestamp - this.lastTimestamp;
|
|
20
|
+
this.loop = (timestamp) => {
|
|
21
|
+
const deltaTime = Math.max(timestamp - this.lastTimestamp, 0);
|
|
23
22
|
this.lastDeltaTime = deltaTime;
|
|
24
23
|
this.lastTimestamp = timestamp;
|
|
25
24
|
this.nextFrame(deltaTime);
|
|
@@ -35,8 +34,9 @@ class GameLoop {
|
|
|
35
34
|
}
|
|
36
35
|
start() {
|
|
37
36
|
this.fsm.send('start');
|
|
38
|
-
|
|
39
|
-
this.
|
|
37
|
+
const timestamp = performance.now();
|
|
38
|
+
this.lastTimestamp = timestamp;
|
|
39
|
+
this.loop(timestamp);
|
|
40
40
|
}
|
|
41
41
|
stop() {
|
|
42
42
|
this.fsm.send('stop');
|
package/lib/game-loop.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"game-loop.js","sourceRoot":"","sources":["../src/game-loop.ts"],"names":[],"mappings":";;;AAAA,yCAAyE;AACzE,iDAA4C;AAwC5C,IAAK,KAGJ;AAHD,WAAK,KAAK;IACR,4BAAmB,CAAA;IACnB,4BAAmB,CAAA;AACrB,CAAC,EAHI,KAAK,KAAL,KAAK,QAGT;AAMD,MAAM,MAAM,GAA4C;IACtD,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE;IACzC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE;CACzC,CAAA;AAED,MAAa,QAAQ;IAanB,YAAY,OAAyC;QAZpC,QAAG,GAAG,IAAI,8BAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QAS5D,yBAAoB,GAAG,CAAC,CAAA;QACxB,kBAAa,GAAG,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"game-loop.js","sourceRoot":"","sources":["../src/game-loop.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -15,4 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./game-loop"), exports);
|
|
18
|
+
__exportStar(require("./game-loop-lite"), exports);
|
|
18
19
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA2B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA2B;AAC3B,mDAAgC"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { FiniteStateMachine, IFiniteStateMachineSchema } from 'extra-fsm'
|
|
2
|
+
|
|
3
|
+
interface IGameLoopLiteOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 每帧运行一次, 总是最早运行.
|
|
6
|
+
*/
|
|
7
|
+
update?: (deltaTime: number) => void
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 每帧运行一次, 总是最晚运行.
|
|
11
|
+
*/
|
|
12
|
+
render?: () => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
enum State {
|
|
16
|
+
Stopped = 'stopped'
|
|
17
|
+
, Running = 'running'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type Event =
|
|
21
|
+
| 'start'
|
|
22
|
+
| 'stop'
|
|
23
|
+
|
|
24
|
+
const schema: IFiniteStateMachineSchema<State, Event> = {
|
|
25
|
+
[State.Stopped]: { start: State.Running }
|
|
26
|
+
, [State.Running]: { stop: State.Stopped }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class GameLoopLite {
|
|
30
|
+
private readonly fsm = new FiniteStateMachine(schema, State.Stopped)
|
|
31
|
+
private readonly update?: (deltaTime: number) => void
|
|
32
|
+
private readonly render?: () => void
|
|
33
|
+
private requstId?: number
|
|
34
|
+
private lastTimestamp?: number
|
|
35
|
+
private lastDeltaTime = 0
|
|
36
|
+
|
|
37
|
+
constructor(options: IGameLoopLiteOptions) {
|
|
38
|
+
this.update = options.update
|
|
39
|
+
this.render = options.render
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
start(): void {
|
|
43
|
+
this.fsm.send('start')
|
|
44
|
+
|
|
45
|
+
const timestamp = performance.now()
|
|
46
|
+
this.lastTimestamp = timestamp
|
|
47
|
+
this.loop(timestamp)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
stop(): void {
|
|
51
|
+
this.fsm.send('stop')
|
|
52
|
+
|
|
53
|
+
cancelAnimationFrame(this.requstId!)
|
|
54
|
+
|
|
55
|
+
delete this.requstId
|
|
56
|
+
delete this.lastTimestamp
|
|
57
|
+
this.lastDeltaTime = 0
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getFramesOfSecond(): number {
|
|
61
|
+
if (this.fsm.matches(State.Running)) {
|
|
62
|
+
return this.lastDeltaTime !== 0
|
|
63
|
+
? 1000 / this.lastDeltaTime
|
|
64
|
+
: 0
|
|
65
|
+
} else {
|
|
66
|
+
return 0
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private loop = (timestamp: number): void => {
|
|
71
|
+
// 如果传入的timestamp可能来自`performance.now()`, 则有可能出现deltaTime小于0的情况.
|
|
72
|
+
// 因为rAF的时间戳来自V-Sync, 出于动画帧同步方面的原因, 它的值可能会小于`performance.now()`.
|
|
73
|
+
const deltaTime = Math.max(timestamp - this.lastTimestamp!, 0)
|
|
74
|
+
|
|
75
|
+
this.lastDeltaTime = deltaTime
|
|
76
|
+
this.lastTimestamp = timestamp
|
|
77
|
+
|
|
78
|
+
this.nextFrame(deltaTime)
|
|
79
|
+
|
|
80
|
+
this.requstId = requestAnimationFrame(this.loop)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
nextFrame(deltaTime: number): void {
|
|
84
|
+
this.update?.(deltaTime)
|
|
85
|
+
|
|
86
|
+
this.render?.()
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/game-loop.ts
CHANGED
|
@@ -15,7 +15,7 @@ interface IGameLoopOptions<FixedDeltaTime extends number = number> {
|
|
|
15
15
|
maximumDeltaTime: number
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* 每帧运行一次,
|
|
18
|
+
* 每帧运行一次, 总是最早运行.
|
|
19
19
|
* 在此处理用户输入等与物理计算无关的操作, 在update里改变的状态直到下一物理帧时才会反应.
|
|
20
20
|
*/
|
|
21
21
|
update?: (deltaTime: number) => void
|
|
@@ -34,7 +34,7 @@ interface IGameLoopOptions<FixedDeltaTime extends number = number> {
|
|
|
34
34
|
lateUpdate?: (deltaTime: number, alpha: number) => void
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* 每帧运行一次,
|
|
37
|
+
* 每帧运行一次, 总是最晚运行.
|
|
38
38
|
*/
|
|
39
39
|
render?: (alpha: number) => void
|
|
40
40
|
}
|
|
@@ -83,8 +83,9 @@ export class GameLoop<FixedDeltaTime extends number> {
|
|
|
83
83
|
start(): void {
|
|
84
84
|
this.fsm.send('start')
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
this.
|
|
86
|
+
const timestamp = performance.now()
|
|
87
|
+
this.lastTimestamp = timestamp
|
|
88
|
+
this.loop(timestamp)
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
stop(): void {
|
|
@@ -107,12 +108,11 @@ export class GameLoop<FixedDeltaTime extends number> {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
private loop = (): void => {
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const deltaTime = timestamp - this.lastTimestamp!
|
|
111
|
+
private loop = (timestamp: number): void => {
|
|
112
|
+
// 如果传入的timestamp可能来自`performance.now()`, 则有可能出现deltaTime小于0的情况.
|
|
113
|
+
// 因为rAF的时间戳来自V-Sync, 出于动画帧同步方面的原因, 它的值可能会小于`performance.now()`.
|
|
114
|
+
const deltaTime = Math.max(timestamp - this.lastTimestamp!, 0)
|
|
115
|
+
|
|
116
116
|
this.lastDeltaTime = deltaTime
|
|
117
117
|
this.lastTimestamp = timestamp
|
|
118
118
|
|
|
@@ -122,7 +122,7 @@ export class GameLoop<FixedDeltaTime extends number> {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
*
|
|
125
|
+
* nextFrame依序做以下几件事:
|
|
126
126
|
* 1. 响应用户输入, 为这一帧的游戏世界做出非物理方面的更新, 例如角色转向, 改变武器瞄准角度等.
|
|
127
127
|
* 2. 响应游戏世界从上一帧更新后到这一帧更新之前发生的物理变化.
|
|
128
128
|
* 注意, 如果渲染帧率比物理帧率快, 且运行性能良好, 则物理帧不一定会在此帧更新.
|
package/src/index.ts
CHANGED