{"id":1322,"date":"2018-01-08T12:43:12","date_gmt":"2018-01-08T17:43:12","guid":{"rendered":"http:\/\/bluegalaxy.info\/codewalk\/?p=1322"},"modified":"2021-01-17T20:19:36","modified_gmt":"2021-01-18T01:19:36","slug":"javascript-intro-to-web-game-development-part-6-add-win-screen","status":"publish","type":"post","link":"https:\/\/bluegalaxy.info\/codewalk\/2018\/01\/08\/javascript-intro-to-web-game-development-part-6-add-win-screen\/","title":{"rendered":"JavaScript: Intro to Web Game Development &#8211; Part 6: add win screen and replay"},"content":{"rendered":"<p>In the previous article, scoring was introduced, but there was no win detection that would end the game. The previous article in the series can be read here:<\/p>\n<blockquote class=\"wp-embedded-content\" data-secret=\"ZlPyG7ywP6\"><p><a href=\"http:\/\/bluegalaxy.info\/codewalk\/2018\/01\/03\/javascript-intro-web-game-development-part-5-ball-handling-scoring\/\">JavaScript: Intro to Web Game Development &#8211; Part 5: ball handling, scoring, and A.I.<\/a><\/p><\/blockquote>\n<p><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" src=\"http:\/\/bluegalaxy.info\/codewalk\/2018\/01\/03\/javascript-intro-web-game-development-part-5-ball-handling-scoring\/embed\/#?secret=ZlPyG7ywP6\" data-secret=\"ZlPyG7ywP6\" width=\"600\" height=\"338\" title=\"&#8220;JavaScript: Intro to Web Game Development &#8211; Part 5: ball handling, scoring, and A.I.&#8221; &#8212; Chris Nielsen Code Walk\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe><\/p>\n<p>In this article, I will show how to detect when a game ends and display a win screen that if clicked will start a new game.<\/p>\n<p><strong>Step 1: Create new variables<\/strong><\/p>\n<p>We need to add these two new variables to the top of the script:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">const WINNING_SCORE = 3;\nvar showingWinScreen = false;<\/pre>\n<p>The WINNING_SCORE variable will be used to determine when the game has a winner. The showWinScreen variable will be used to tell the game when to display the &#8220;game winner&#8221; screen.<\/p>\n<p><strong>Step 2: Add logic to check for the WINNING_SCORE and display the winner screen<br \/>\n<\/strong><\/p>\n<p>Inside the ballReset() function, add this logic at the top:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">if(player1Score &gt;= WINNING_SCORE || player2Score &gt;= WINNING_SCORE) {\n    showingWinScreen = true;\n}<\/pre>\n<p>At the top of the ballMovement() function, place this logic:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">if(showingWinScreen) {\n   return;\n}<\/pre>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">return;<\/code> there essentially means that if the condition is met, exit out of the current function. Finally, inside the drawComponents() function, add the following logic:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">if(showingWinScreen) {\n    gameOverScreen();\n} else {<\/pre>\n<p>Everything that was previously in this function should now be indented in the else block. This means that when it comes time to draw to the screen, if showingWinScreen equals true, then display the gameOverScreen() instead.<\/p>\n<p><strong>Step 3: Create gameOverScreen() function<br \/>\n<\/strong><\/p>\n<p>This function will display who won the game, while leaving the score and the net on the screen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">function gameOverScreen() {\n    canvasContext.fillStyle = 'white';\n    if(player1Score &gt;= WINNING_SCORE) {\n        canvasContext.font=\"54px monospace\";\n        canvasContext.fillText(\"Winner\", canvas_width\/8, canvas_height\/4);\n        canvasContext.font=\"44px monospace\";\n        canvasContext.fillText(\"Left Player\", canvas_width\/12, canvas_height\/3);\n        canvasContext.fillText(\"Play Again\", (canvas_width\/10), (0 + canvas_height\/2));\n    } else if(player2Score &gt;= WINNING_SCORE) {\n        canvasContext.font=\"54px monospace\";\n        canvasContext.fillText(\"Winner\", (canvas_width\/2 + canvas_width\/8), (0 + canvas_height\/4));\n        canvasContext.font=\"44px monospace\";\n        canvasContext.fillText(\"Right Player\", (canvas_width\/2 + canvas_width\/24), (0 + canvas_height\/3));\n        canvasContext.fillText(\"Play Again\", (canvas_width\/2 + canvas_width\/12), (0 + canvas_height\/2));\n    }\n    return;\n}<\/pre>\n<p>There is text that says &#8220;Play again&#8221;. All the player has to do is click anywhere on the screen to start a new game. This is implemented with a &#8216;mousedown&#8217; addEventListener.<\/p>\n<p><strong>Step 4: Create addEventListener and function for mouse clicks on the winner screen<br \/>\n<\/strong><\/p>\n<p>Inside the window.onload function, add this addEventListener code:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">canvas.addEventListener('mousedown', handleMouseClick);<\/pre>\n<p>This addEventListener will look for mouse clicks. If there is a mouse click it will launch a function called handleMouseClick(). Then the handleMouseClick() function will make sure that the mouse click is coming from the winner screen, reset the scores, and set the showWinScreen variable back to false. Since all of this is happening inside the game loop, the other functions will then be free to launch the game play again, since they are actively checking the status of this variable. Here is what the function looks like:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">function handleMouseClick(evt) {\n    if(showingWinScreen) {\n\tplayer1Score = 0;\n\tplayer2Score = 0;\n\tshowingWinScreen = false;\n    }\n}<\/pre>\n<p>Here is what the complete code looks like so far:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">var canvas;\nvar canvasContext;\nvar canvas_width;\nvar canvas_height;\n\n\/\/ offsets to account for the rounded corners\nvar offsetA = 30;\nvar offsetB = 60;\n\n\/\/ starting y position of the left paddle\nvar paddle1Y = 100;\nvar paddle2Y = 100;\n\n\/\/ defaults for paddle thickness and height\nconst PADDLE_THICKNESS = 10;\nconst PADDLE_HEIGHT = 70;\n\n\/\/ ball variables\nvar ballX = offsetA;\nvar ballY = 0;\nvar ballSpeedX = 10;\nvar ballSpeedY = 4;\n\nvar player1Score = 0;\nvar player2Score = 0;\nconst WINNING_SCORE = 3;\nvar showingWinScreen = false;\n\nwindow.onload = function() {\n\n    canvas = document.getElementById(\"gameCanvas\");\n    canvasContext = canvas.getContext(\"2d\");\n    canvas_width = canvas.width;\n    canvas_height = canvas.height;\n\n    var framesPerSecond = 30;\n    setInterval(\n        function() {\n            ballMovement();\n            drawComponents();\n        }, 1000\/framesPerSecond);\n\n    canvas.addEventListener('mousedown', handleMouseClick);\n\n    canvas.addEventListener('mousemove',\n    \tfunction(evt) {\n    \t    var mousePos = calculateMousePos(evt);\n    \t    paddle1Y = mousePos.y - (PADDLE_HEIGHT\/2);\n    \t});\n\n}\n\n\/\/ Function to create rounded corners on the rectangle\nCanvasRenderingContext2D.prototype.roundRect = function(x, y, width, height, radius, fill, stroke) {\n    \/\/ If stroke argument not provided, provide a stroke by default\n    if (typeof stroke == \"undefined\" ) {\n        stroke = true;\n    }\n    \/\/ If no radius is defined, then give it a default of 5\n    if (typeof radius === \"undefined\") {\n        radius = 5;\n    }\n\n    \/\/ Start of shape definition\n    this.beginPath();\n\n    this.moveTo(x + radius, y);\n    \/\/ Draw the top border line\n    this.lineTo(x + width - radius, y);\n\n    \/\/ Draw the top right curve\n    this.quadraticCurveTo(x + width, y, x + width, y + radius);\n\n    \/\/ Draw the right border line\n    this.lineTo(x + width, y + height - radius);\n\n    \/\/ Draw the bottom right curve\n    this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);\n\n    \/\/ Draw the bottom border line\n    this.lineTo(x + radius, y + height);\n\n    \/\/ Draw the bottom left curve\n    this.quadraticCurveTo(x, y + height, x, y + height - radius);\n\n    \/\/ Draw the left border line\n    this.lineTo(x, y + radius);\n\n    \/\/ Draw the final (top left) curve\n    this.quadraticCurveTo(x, y, x + radius, y);\n\n    \/\/ End of shape definition\n    this.closePath();\n\n    \/\/ Draw the shape with a line\n    if (stroke) {\n        this.stroke();\n    }\n    \/\/ Fill in the shape with color\n    if (fill) {\n        this.fill();\n    }\n}\n\n\nfunction calculateMousePos(evt) {\n    var rect = canvas.getBoundingClientRect();\n    var root = document.documentElement;\n    var mouseX = evt.clientX - rect.left - root.scrollLeft;\n    var mouseY = evt.clientY - rect.top - root.scrollTop;\n    return {\n        x:mouseX,\n        y:mouseY\n    };\n}\n\nfunction handleMouseClick(evt) {\n    if(showingWinScreen) {\n\tplayer1Score = 0;\n\tplayer2Score = 0;\n\tshowingWinScreen = false;\n    }\n}\n\nfunction ballMovement() {\n    if(showingWinScreen) {\n        return;\n    }\n    computerPaddleAI();\n    ballX = ballX + ballSpeedX;\n    ballY = ballY + ballSpeedY;\n\n    \/\/ left wall\n    if(ballX &lt; offsetA + PADDLE_THICKNESS) {\n\n        \/\/ Detect a hit. Check if the ball is between the top and bottom of the paddle\n    \tif(ballY &gt; paddle1Y &amp;&amp; ballY &lt; (paddle1Y + PADDLE_HEIGHT)) {\n            \/\/ console.log(\"HIT! Ball position: \" + ballY)\n    \t    ballSpeedX = -ballSpeedX;\n\n            \/\/ change ball speed of Y to change the angle of return\n    \t    var deltaY = ballY - (paddle1Y + PADDLE_HEIGHT\/2);\n    \t    ballSpeedY = deltaY * 0.35;\n\n    \t} else if (ballX &lt; offsetA) {\n            player2Score++; \/\/ must be BEFORE ballReset()\n            ballReset();\n        }\n\n\t}\n    \/\/ right wall\n\tif(ballX &gt; canvas_width-40 - PADDLE_THICKNESS) {\n\n        \/\/ Detect a hit. If the ball is between the top and bottom of the paddle\n    \tif(ballY &gt; paddle2Y &amp;&amp; ballY &lt; (paddle2Y + PADDLE_HEIGHT)) {\n            \/\/ console.log(\"HIT! Ball position: \" + ballY)\n    \t    ballSpeedX = -ballSpeedX;\n\n            \/\/ change ball speed of Y to change the angle of return\n    \t    var deltaY = ballY - (paddle2Y + PADDLE_HEIGHT\/2 );\n    \t    ballSpeedY = deltaY * 0.35;\n    \t} else if (ballX &gt; canvas_width-40) {\n    \t    player1Score++; \/\/ must be BEFORE ballReset()\n    \t    ballReset();\n    \t}\n\n    }\n    \/\/ top (ceiling)\n    if(ballY &lt; 0) {\n        \/\/ bounce off by reversing course\n\tballSpeedY = -ballSpeedY;\n    }\n    \/\/ bottom (floor)\n    if(ballY &gt; canvas_height-10) {\n        \/\/ bounce off by reversing course\n        ballSpeedY = -ballSpeedY;\n    }\n}\n\nfunction computerPaddleAI() {\n    var paddle2YCenter = paddle2Y + (PADDLE_HEIGHT\/2);\n    if(paddle2YCenter &lt; ballY - 35) {\n\tpaddle2Y = paddle2Y + 6;\n    } else if(paddle2YCenter &gt; ballY + 35) {\n\tpaddle2Y = paddle2Y - 6;\n    }\n}\n\nfunction ballReset() {\n    if(player1Score &gt;= WINNING_SCORE || player2Score &gt;= WINNING_SCORE) {\n        showingWinScreen = true;\n    }\n    computerPaddleAI();\n\n    ballSpeedX = -ballSpeedX;\n    ballX = canvas_width\/2;\n    ballY = canvas_height\/2;\n}\n\nfunction drawComponents() {\n\n    \/\/ this line blacks out the background to give the appearance of animation\n    canvasContext.fillStyle = 'black';\n    canvasContext.roundRect(0, 0, canvas_width, canvas_height, 40, \"black\", 0);\n\n    if(showingWinScreen) {\n        gameOverScreen();\n    } else {\n\n        \/\/ Left paddle\n        canvasContext.fillStyle = 'white';\n        canvasContext.fillRect(offsetA, paddle1Y, PADDLE_THICKNESS, PADDLE_HEIGHT);\n\n        \/\/ Right paddle\n        canvasContext.fillStyle = 'white';\n        \/\/ canvasContext.fillRect(canvas_width-40, canvas_height-120, PADDLE_THICKNESS, PADDLE_HEIGHT);\n        canvasContext.fillRect(canvas_width-40, paddle2Y, PADDLE_THICKNESS, PADDLE_HEIGHT);\n\n        \/\/ Draw the ball\n        canvasContext.fillStyle = 'white';\n        canvasContext.fillRect(ballX, ballY, 10, 10);\n        \n    }\n\n    \/\/ Draw scores\n    canvasContext.font=\"40px monospace\";\n    \/\/ Left side score - make room for more digits\n    if (player1Score &lt; 10) {\n        canvasContext.fillText(player1Score, (canvas_width\/2)-60, 40);\n    } else if (player1Score &gt; 99) {\n        canvasContext.fillText(player1Score, (canvas_width\/2)-100, 40);\n    } else {\n        canvasContext.fillText(player1Score, (canvas_width\/2)-80, 40);\n    }\n\n    \/\/ Right side score\n    canvasContext.fillText(player2Score, (canvas_width\/2)+35, 40);\n\n    drawNet();\n}\n\nfunction gameOverScreen() {\n    canvasContext.fillStyle = 'white';\n    if(player1Score &gt;= WINNING_SCORE) {\n        canvasContext.font=\"54px monospace\";\n        canvasContext.fillText(\"Winner\", canvas_width\/8, canvas_height\/4);\n        canvasContext.font=\"44px monospace\";\n        canvasContext.fillText(\"Left Player\", canvas_width\/12, canvas_height\/3);\n        canvasContext.fillText(\"Play Again\", (canvas_width\/10), (0 + canvas_height\/2));\n    } else if(player2Score &gt;= WINNING_SCORE) {\n        canvasContext.font=\"54px monospace\";\n        canvasContext.fillText(\"Winner\", (canvas_width\/2 + canvas_width\/8), (0 + canvas_height\/4));\n        canvasContext.font=\"44px monospace\";\n        canvasContext.fillText(\"Right Player\", (canvas_width\/2 + canvas_width\/24), (0 + canvas_height\/3));\n        canvasContext.fillText(\"Play Again\", (canvas_width\/2 + canvas_width\/12), (0 + canvas_height\/2));\n    }\n    return;\n}\n\nfunction drawNet() {\n    for(var i=0; i &lt; canvas_height; i += 30) {\n        canvasContext.fillStyle = 'white';\n        canvasContext.fillRect(canvas_width\/2, i, 2, 10, 'white');\n    }\n}<\/pre>\n<p>Here is the game so far:<\/p>\n\n<!-- iframe plugin v.5.2 wordpress.org\/plugins\/iframe\/ -->\n<iframe loading=\"lazy\" id=\"iframe_drop_shadow\" src=\"https:\/\/bluegalaxy.info\/js\/pong_pt6.html\" width=\"770\" height=\"600\" scrolling=\"no\" name=\"pong\" class=\"iframe-class\" frameborder=\"0\"><\/iframe>\n\n","protected":false},"excerpt":{"rendered":"<p>In the previous article, scoring was introduced, but there was no win detection that would end the game. The previous article in the series can be read here: JavaScript: Intro to Web Game Development &#8211; Part 5: ball handling, scoring, and A.I. In this article, I will show how to detect when a game ends &hellip; <a href=\"https:\/\/bluegalaxy.info\/codewalk\/2018\/01\/08\/javascript-intro-to-web-game-development-part-6-add-win-screen\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">JavaScript: Intro to Web Game Development &#8211; Part 6: add win screen and replay<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[44],"tags":[68,45],"class_list":["post-1322","post","type-post","status-publish","format-standard","hentry","category-javascript-language","tag-addeventlistener","tag-javascript"],"_links":{"self":[{"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/posts\/1322","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/comments?post=1322"}],"version-history":[{"count":12,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/posts\/1322\/revisions"}],"predecessor-version":[{"id":3326,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/posts\/1322\/revisions\/3326"}],"wp:attachment":[{"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/media?parent=1322"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/categories?post=1322"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/tags?post=1322"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}