Skip to main content

  | मौसम…

411 समाचार
Public archive edition · Attributed source reporting with original dates · AI drafts are excluded

411 REFERENCE · Wikibooks

JavaScript/Exercises/MovingWalls

Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.

The example combines some of the shown features: It contains a class Rect and its sub-class Smiley. Some walls with a rectangular shape move from right to left across the scene. They reappear on the right side after reaching the left border. Two smileys populate the scene. One smiley can be moved by using the buttons or the keyboard keys 'ArrowRight', ... . Two helper-functions support the detection of rectangles: detectBorderCollision and detectRectangleCollision. The first one detects the collision of a graphical object with a surrounding rectangle, e.g., the canvas. The second one detects a collision of two rectangles. You can extend the example in various ways: Create walls randomly Math.random. Introduce some kind of a 'level' by changing the speed, the number, the size, or the shape of walls. 100% <!DOCTYPE html> <html> <head> <title>Collision</title> <style> .grid-container { display: grid; justify-content: left; grid-template-columns: auto auto auto auto auto auto; gap: 20px; margin-left: 1em; margin-top: 2em; } </style> <script> "use strict"; // ---------------------------------------------------------------------------- // class Rect (should be implemented in a separate file 'Rect.js') // ---------------------------------------------------------------------------- class Rect { // constructor with default values constructor(context, x = 0, y = 0, width = 10, height = 10, color = "red") { this.ctx = context; this.x = x; this.y = y; this.width = width this.height = height; this.color = color; // movement this.speedX = 0; this.speedY = 0; } // set the speed (= step size) setSpeed(x, y) { this.speedX = x; this.speedY = y; } // change the position according to the speed move() { this.x += this.speedX; this.y += this.speedY; } render() { this.ctx.fillStyle = this.color; this.ctx.fillRect(this.x, this.y, this.width, this.height); } } // end of class // ---------------------------------------------- // class 'Smiley'. It's derived from 'Rect' to use // - the MBB for collision detection // - methods of 'Rect' for movements // ----------------------------------------------- class Smiley extends Rect { // constructor with default values constructor(context, text = "?", x = 10, y = 10, width = 30, height = 30) { // super (context, x, y, width, height); this.ctx = context; this.text = text; } render() { this.ctx.font = "30px Arial"; this.ctx.fillText(this.text, this.x, this.y); //this.ctx.fillRect(this.x, this.y-25, this.width, this.height); } } // end of class // ------------------------------------------------------------- // constants and variables which are known in the complete file // ------------------------------------------------------------- // global constants const SPEED_WALLS = -0.3; // px const SPEED_SMILEY = 1; // px // global variables let obstacles = []; let he, she; let requestId, stopFlag; // ---------------------------------------------------- // functions // ---------------------------------------------------- function start() { // init all graphical objects const canvas = document.getElementById("canvas"); const context = canvas.getContext("2d"); obstacles[0] = new Rect(context, 200, 80, 10, 180, "red"); obstacles[1] = new Rect(context, 350, 20, 10, 100, "red"); obstacles[2] = new Rect(context, 500, 80, 10, 200, "red"); for (let i = 0; i < obstacles.length; i++) { obstacles[i].setSpeed(SPEED_WALLS, 0); } he = new Smiley(context, "\u{1F60E}", 20, canvas.height - 40); she = new Smiley(context, "\u{1F60D}", canvas.width - 50, canvas.height - 40); // (re-)start the game if (requestId) { // cancel old frame chain cancelAnimationFrame(requestId); } stopFlag = false; playTheGame(canvas, context); } // the game's logic function playTheGame(canvas, context) { // collision of 'he' with border? // y-direction is different because (x,y) of text is at left/bottom const [crashL, crashR, crashT, crashB] = detectBorderCollision( 0, 0, canvas.width, canvas.height, he.x, he.y-25, he.width, he.height); if (!crashL && !crashR && !crashT && !crashB) { he.move(); } else { switch (true) { // move back a single px case crashL: he.x++; break; case crashR: he.x--; break; case crashT: he.y++; break; case crashB: he.y--; break; } } for (let i = 0; i < obstacles.length; i++) { // collision of 'he' with wall ? // y-direction is different because (x,y) of text is at left/bottom if (detectRectangleCollision( obstacles[i].x, obstacles[i].y, obstacles[i].width, obstacles[i].height, he.x, he.y-25, he.width, he.height)) { stopFlag = true; continue; } // if an obstacle reaches the left border, restart at the right side if (obstacles[i].x <= 0) { obstacles[i].x = canvas.width - 100; } obstacles[i].move(); } // 'he' and 'she' meets? if (detectRectangleCollision( she.x, she.y-25, she.width, she.height, he.x, he.y-25, he.width, he.height)) { alert("Hola chica"); stopFlag = true; } renderAll(canvas, context); } // rendering consists off: // - clear the complete scene // - re-paint the complete scene // - call the game's logic again via requestAnimationFrame() function renderAll(canvas, context) { if (stopFlag) { // cancel the old animation and don't request frames cancelAnimationFrame(requestId); return; } // clear complete canvas context.clearRect(0, 0, canvas.width, canvas.height); // draw objects at their current position for (let i = 0; i < obstacles.length; i++) { obstacles[i].render(); } he.render(); she.render(); // request a new frame with a parameter that re-starst the game's logic // (and lastly leads to this line again) requestId = window.requestAnimationFrame(() => playTheGame(canvas, context)); } // control the game via buttons function stopEvent() { stopFlag = true; } function leftEvent() { he.setSpeed(-SPEED_SMILEY, 0); } function rightEvent() { he.setSpeed(+SPEED_SMILEY, 0); } function upEvent() { he.setSpeed(0, -SPEED_SMILEY); } function downEvent() { he.setSpeed(0, +SPEED_SMILEY); } // control the game via keybord function keyDownEvent(event) { switch (event.code) { case 'ArrowUp': upEvent(); break; case 'ArrowDown': downEvent(); break; case 'ArrowLeft': leftEvent(); break; case 'ArrowRight': rightEvent(); break; } } function keyUpEvent(event) { switch (event.code) { case 'ArrowUp': case 'ArrowDown': case 'ArrowLeft': case 'ArrowRight': he.setSpeed(0, 0); } } // ---------------------------------------------------------------------------- // helper functions (should be in a separate file, e.g.: 'tools.js') // ---------------------------------------------------------------------------- function detectBorderCollision(borderX, borderY, borderWidth, borderHeight, rectX, rectY, rectWidth, rectHeight) { // the rectangle touches the (outer) border, if x <= borderX, ... let collisionLeft = false; let collisionRight = false; let collisionTop = false; let collisionBottom = false; if (rectX <= borderX ) {collisionLeft = true} if (rectX + rectWidth >= borderX + borderWidth ) {collisionRight = true} if (rectY <= borderY ) {collisionTop = true} if (rectY + rectHeight >= borderY + borderHeight ) {collisionBottom= true} return [collisionLeft, collisionRight, collisionTop, collisionBottom]; } // --- function detectRectangleCollision(x1, y1, width1, height1, x2, y2, width2, height2) { // The algorithm takes its decision by detecting areas // WITHOUT ANY overlapping // No overlapping if one rectangle is COMPLETELY on the // left side of the other if (x1 > x2 + width2 || x2 > x1 + width1) { return false; } // No overlapping if one rectangle is COMPLETELY // above the other if (y1 > y2 + height2 || y2 > y1 + height1) { return false; } // all other cases return true; } </script> </head> <body onload="start()" onkeydown="keyDownEvent(event)" onkeyup="keyUpEvent(event)" > <h4 style="margin-left:1em; text-align:center">Initiate motions by buttons or keyboard keys (ArrowUp, ...)</h4> <!-- the drawing area --> <canvas id="canvas" width="700" height="300" style="margin-left:1em; background-color:yellow" > </canvas> <!-- a grid for the buttons: 3 rows, 6 columns per row --> <div class="grid-container"> <div></div> <div></div> <div></div> <div></div> <button onclick="upEvent()">Up</button> <div></div> <button onclick="start()">Reset</button> <button onclick="stopEvent()">Stop</button> <div style="margin-right: 2em"></div> <button onclick="leftEvent()">Left</button> <div></div> <button onclick="rightEvent()">Right</button> <div></div> <div></div> <div></div> <div></div> <button onclick="downEvent()">Down</button> <div></div> </div> </body> </html>