UNPKG

4.2 kBJavaScriptView Raw
1"use strict";
2
3var Camera = require("./camera");
4
5/**
6 * A Scene handles the render loop for the game. Inside of initFunc, simulationFunc, and drawFunc `this` refers to the current scene.
7 * @constructor
8 * @alias Splat.Scene
9 * @param {external:canvas} canvas The canvas to render on.
10 * @param {emptyCallback} initFunc A callback to be called every time the Scene is {@link Splat.Scene#start started}.
11 * @param {simulationCallback} simulationFunc A callback that updates the state of the game's simulation.
12 * @param {drawCallback} drawFunc A callback that draws the game.
13 */
14function Scene(canvas, initFunc, simulationFunc, drawFunc) {
15 /**
16 * The canvas to render on.
17 * @member {external:canvas}
18 * @private
19 */
20 this.canvas = canvas;
21 /**
22 * A callback to be called ever time the Scene is {@link Splat.Scene#start started}.
23 * @member {emptyCallback}
24 * @private
25 */
26 this.initFunc = initFunc;
27 /**
28 * A callback that updates the state of the game's simulation.
29 * @member {simulationCallback}
30 * @private
31 */
32 this.simulationFunc = simulationFunc;
33 /**
34 * A callback that draws the game.
35 * @member {drawCallback}
36 * @private
37 */
38 this.drawFunc = drawFunc;
39
40 /**
41 * The drawing context for {@link Scene#canvas}
42 * @member {external:CanvasRenderingContext2D}
43 * @private
44 */
45 this.context = canvas.getContext("2d");
46 /**
47 * The timestamp of the last frame. Used to determine how many milliseconds elapsed between frames.
48 * @member {number}
49 * @private
50 */
51 this.lastTimestamp = -1;
52 /**
53 * Whether or not the Scene is currently running.
54 * @member {boolean}
55 * @private
56 */
57 this.running = false;
58 /**
59 * A key-value store of named timers. Timers in this object will be automatically {@link Splat.Timer#tick tick()}ed for you when the scene is running..
60 * @member {object}
61 */
62 this.timers = {};
63
64 /**
65 * The Camera used to offset the Scene's drawing.
66 * This Camera's {@link Splat.Entity#move move} and {@link Splat.Camera#draw draw} methods are called automatically for you. The default Camera starts at the origin (0,0).
67 * @member {Splat.Camera}
68 */
69 this.camera = new Camera(0, 0, canvas.width, canvas.height);
70 /**
71 * A flag that enables/disables a frame rate counter in the corner of the screen. This is useful during development.
72 * @member {boolean}
73 */
74 this.showFrameRate = false;
75}
76/**
77 * Start running the scene.
78 */
79Scene.prototype.start = function() {
80 this.lastTimestamp = -1;
81 this.running = true;
82 this.initFunc.call(this);
83 var scene = this;
84 window.requestAnimationFrame(function(t) { mainLoop(scene, t); });
85};
86/**
87 * Stop running the scene.
88 */
89Scene.prototype.stop = function() {
90 this.running = false;
91};
92/**
93 * Reset the simulation by re-running the {@link Splat.Scene#initFunc}.
94 */
95Scene.prototype.reset = function() {
96 this.initFunc.call(this);
97};
98
99function mainLoop(scene, timestamp) {
100 if (!scene.running) {
101 return;
102 }
103 if (scene.lastTimestamp === -1) {
104 scene.lastTimestamp = timestamp;
105 }
106 var elapsedMillis = timestamp - scene.lastTimestamp;
107 scene.lastTimestamp = timestamp;
108
109 incrementTimers(scene.timers, elapsedMillis);
110 if (!scene.running) {
111 return;
112 }
113 scene.simulationFunc.call(scene, elapsedMillis);
114 scene.camera.move(elapsedMillis);
115
116 scene.context.save();
117 scene.camera.draw(scene.context);
118 scene.drawFunc.call(scene, scene.context);
119
120 if (scene.showFrameRate) {
121 drawFrameRate(scene, elapsedMillis);
122 }
123
124 scene.context.restore();
125
126 if (scene.running) {
127 window.requestAnimationFrame(function(t) { mainLoop(scene, t); });
128 }
129}
130
131function incrementTimers(timers, elapsedMillis) {
132 for (var i in timers) {
133 if (timers.hasOwnProperty(i)) {
134 timers[i].tick(elapsedMillis);
135 }
136 }
137}
138
139function drawFrameRate(scene, elapsedMillis) {
140 var fps = (1000 / elapsedMillis) |0;
141
142 scene.context.font = "24px mono";
143 if (fps < 30) {
144 scene.context.fillStyle = "#ff0000";
145 } else if (fps < 50) {
146 scene.context.fillStyle = "#ffff00";
147 } else {
148 scene.context.fillStyle = "#00ff00";
149 }
150 var msg = fps + " FPS";
151 var w = scene.context.measureText(msg).width;
152 scene.camera.drawAbsolute(scene.context, function() {
153 scene.context.fillText(msg, scene.canvas.width - w - 50, 50);
154 });
155}
156
157module.exports = Scene;