7️⃣

Ch. 7 Main Functions

Our First Main Function we will call fitToWindow. The point of this function is to make sure that no matter the size of the window in our browser, the game will be able to adapt. We need to add this otherwise the game will only be visible for one type of window size.
function fitToWindow() { let screen_margin = 10; WIDTH = Math.min(TARGET_WIDTH, window.innerWidth - screen_margin); HEIGHT = Math.min(TARGET_HEIGHT, window.innerHeight - screen_margin - 300); WIDTH = Math.max(WIDTH, 100); HEIGHT = Math.max(HEIGHT, 100); CANVAS.width = WIDTH; CANVAS.height = HEIGHT + GUI_HEIGHT; PRERENDERED_CANVAS.width = WIDTH; PRERENDERED_CANVAS.height = HEIGHT + GUI_HEIGHT; }
 
I recommend that you dont change this code, because it should be like this for all window sizes.
Our next function is the main function.
function main() { // code }
 
There are multiple things that will go inside this function. It creates a new HTML5 canvas element, adds listeners for input and contains the main loop.
Basically, how the code runs, is it loops through the main function, aways listing for keyboard inputs, then going to different functions.
 
First we will initlize some variables, these say that our screen is 2d.
CANVAS = document.createElement("canvas"); CTX = CANVAS.getContext("2d");
 
Next, we will initialize another canvas for quickly drawing static objects.
PRERENDERED_CANVAS = document.createElement("canvas"); PRERENDERED_CTX = PRERENDERED_CANVAS.getContext("2d"); fitToWindow(); // Set the size of both canvas, from our previous function
 
We will attempt to find a document element with a specific id, otherwise attach the canvas to the document body.
attach_to = document.getElementById('game_window'); if (attach_to == null) attach_to = document.body; attach_to.appendChild(CANVAS);
 
We want to prevent scrolling with arrowkeys and spacebar.
KEYSTATE = {}; document.addEventListener("keydown", function(evt) { if([32, 37, 38, 39, 40].indexOf(evt.keyCode) > -1) { evt.preventDefault(); } KEYSTATE[evt.keyCode] = true; }); document.addEventListener("keyup", function(evt) { delete KEYSTATE[evt.keyCode]; });
 
We will call a function init(); that we have not created yet, so don’t worry yet.
init(); RESET_COUNTER = 0;
 
To make our loop work efficiently with the world around us, we will use milliseconds, to keep track of frames.
var previous_time = Date.now(); // Time in milliseconds var frames = 0; var iteration_time = 0;
 
Inside our main function, we need a “main” loop, what is actually being looped.
var loop = function() { // code }
 
This will step the game forwards and draw everything.
var start_time = Date.now(); spawn_powerup(); update(); draw(); iteration_time += Date.now() - start_time;
 
This is a FPS-counter for performance analysis.
if (DEBUG) { frames++; if (Date.now() - previous_time > 1000) { console.log(frames + " : " + (iteration_time/frames)); previous_time = Date.now(); iteration_time = 0; frames = 0; } }
 
This will start a new round if necessary.
if (END_ROUND === true) RESET_COUNTER++; if (RESET_COUNTER > RESET_COUNTER_MAX) { init(); END_ROUND = false; RESET_COUNTER = 0; }
 
We use our Canvas variable here to Wait for the browser to finish drawing before looping again.
window.requestAnimationFrame(loop, CANVAS); }; window.requestAnimationFrame(loop, CANVAS); }
 
Our next function will spawn powerups in random places at random times.
function spawn_powerup() { // Randomly spawns a random powerup to the level this.max_spawn_time = 50000; // Max time between spawns (Milliseconds) if (typeof this.last_spawn == 'undefined') { this.last_spawn = Date.now(); this.next_spawn_in = Math.random() * this.max_spawn_time; } if (Date.now() - this.last_spawn > this.next_spawn_in) { this.last_spawn = Date.now(); this.next_spawn_in = Math.random() * this.max_spawn_time; let pos = get_random_location(); new Powerup(pos.x, pos.y, PowerupType.get_random_type()); } }
 
The next main function that we have gets a location, which is in the middle of a random cell.
function get_random_location() { var x = (Math.floor(Math.random() * CELLS_X) + 0.5) * (CELL_SIZE); var y = (Math.floor(Math.random() * CELLS_Y) + 0.5) * (CELL_SIZE); return new Vector2d(x, y); }
 
Our next function is init. We used it before, but now we will create the function.
function init() { // code }
 
We need to initialize some global variables.
GAME_OBJECTS = []; KEYSTATE = {}; // Reset the keystate to avoid stuck buttons PRERENDERED_REDRAW_NEEDED = true; GUI_REDRAW_NEEDED = true; fitToWindow(); // Rescale the game window, if necessary maze_generator_kruskal(); // Generate map
 
At the beginning of each game, we want the tanks to spawn at random locations.
let pos = get_random_location(); TANK_P1 = new Tank(pos.x, pos.y, P1); let pos2 = pos; while (pos.x == pos2.x && pos.y == pos2.y) { pos2 = get_random_location(); } TANK_P2 = new Tank(pos2.x, pos2.y, P2); }
 
Our next function is called update, Because it handles game logic by moving all objects and checking collisions.
function update() { for (obj_ind in GAME_OBJECTS) { obj = GAME_OBJECTS[obj_ind]; obj.update(); } }
 
Our next function is called draw. This handles all drawing on the HTML5 canvas.
function draw() { // Game CTX.fillStyle = "#fff"; CTX.clearRect(0, 0, WIDTH, HEIGHT);
 
The CTX will come up a lot, but it is used to draw in HTML.
We also have to redraw static objects only if they have been changed.
if (PRERENDERED_REDRAW_NEEDED) { PRERENDERED_CTX.fillStyle = "#fff"; PRERENDERED_CTX.clearRect(0, 0, WIDTH, HEIGHT); PRERENDERED_REDRAW_NEEDED = false; for (obj_ind in GAME_OBJECTS) { obj = GAME_OBJECTS[obj_ind]; if (obj.movable === false) { obj.draw(PRERENDERED_CTX); } } }
 
This will draw prerendered static objects.
CTX.drawImage(PRERENDERED_CANVAS, 0, 0); // Draw other objects for (obj_ind in GAME_OBJECTS) { obj = GAME_OBJECTS[obj_ind]; if (obj.movable === true) { obj.draw(CTX); } }
 
The next part you can mess around with, for example the colors, or whatever is in the “ “.
This is the main chunk of what the user will see, however we show it through JS that acts through HTML.
if (GUI_REDRAW_NEEDED || END_ROUND) { GUI_REDRAW_NEEDED = false; var P2_offset_x = WIDTH - 270; CTX.save(); CTX.translate(0, HEIGHT); // Move to GUI space CTX.fillStyle = "#fff"; CTX.clearRect(0, 0, WIDTH, GUI_HEIGHT); CTX.font = "30px Arial"; CTX.fillStyle = (P1_SCORE >= P2_SCORE) ? "green" : "red"; CTX.fillText("Player One: " + P1_SCORE, 30, 50); CTX.fillStyle = (P2_SCORE >= P1_SCORE) ? "green" : "red"; CTX.fillText("Player Two: " + P2_SCORE, P2_offset_x, 50); if (END_ROUND === true) { CTX.fillStyle = "blue"; CTX.fillText("Next round in: " + (RESET_COUNTER_MAX - RESET_COUNTER), P2_offset_x/2, 50); } CTX.fillStyle = "#000"; CTX.font = "16px Arial"; CTX.fillText("Move: WASD", 30, 80); CTX.fillText("Fire: 1", 30, 100); CTX.fillText("Move: Arrow keys", P2_offset_x, 80); CTX.fillText("Fire: -", P2_offset_x, 100); CTX.font = "bold " + CTX.font; CTX.fillText("Weapon: " + TANK_P1.gun.get_name(), 30, 120); CTX.fillText("Ammo remaining: " + TANK_P1.gun.get_ammo_str(), 30, 140); CTX.fillText("Weapon: " + TANK_P2.gun.get_name(), P2_offset_x, 120); CTX.fillText("Ammo remaining: " + TANK_P2.gun.get_ammo_str(), P2_offset_x, 140); CTX.restore(); } }
 
ALL OF CHAPTER SEVEN CODE https://pastebin.com/fR7jjVKt