{"id":1260,"date":"2018-01-03T23:38:43","date_gmt":"2018-01-04T04:38:43","guid":{"rendered":"http:\/\/bluegalaxy.info\/codewalk\/?p=1260"},"modified":"2021-01-17T20:51:16","modified_gmt":"2021-01-18T01:51:16","slug":"javascript-intro-web-game-development-part-5-ball-handling-scoring","status":"publish","type":"post","link":"https:\/\/bluegalaxy.info\/codewalk\/2018\/01\/03\/javascript-intro-web-game-development-part-5-ball-handling-scoring\/","title":{"rendered":"JavaScript: Intro to Web Game Development &#8211; Part 5: ball handling, scoring, and A.I."},"content":{"rendered":"\n<p>In the previous article of the series, I described how to animate the left paddle using mouse movements. See:<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-chris-nielsen-code-walk wp-block-embed-chris-nielsen-code-walk\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"VMdTMyXUoN\"><a href=\"http:\/\/bluegalaxy.info\/codewalk\/2018\/01\/02\/javascript-intro-web-game-development-part-4-animate-left-paddle\/\">JavaScript: Intro to Web Game Development &#8211; Part 4: animate left paddle<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"&#8220;JavaScript: Intro to Web Game Development &#8211; Part 4: animate left paddle&#8221; &#8212; Chris Nielsen Code Walk\" src=\"http:\/\/bluegalaxy.info\/codewalk\/2018\/01\/02\/javascript-intro-web-game-development-part-4-animate-left-paddle\/embed\/#?secret=VMdTMyXUoN\" data-secret=\"VMdTMyXUoN\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>In this article, I will detail how to animate the ball, use an AI function to animate the right (computer) paddle, and take care of scoring.<\/p>\n\n\n\n<p><strong>Step 1: Get the ball moving<br><\/strong><\/p>\n\n\n\n<p>First I will need to set up some variables for the ball location and speed. ballX and ballY will be used for the ball location and ballsSpeedX and ballSpeedY will be used for the ball movement. When ballX and ballY are initialized, the ball starts out in the upper left hand corner of the screen.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ ball variables\nvar ballX = offsetA;\nvar ballY = 0;\nvar ballSpeedX = 10;\nvar ballSpeedY = 4;<\/pre>\n\n\n\n<p>Where I previously hardcoded the ball location in the drawComponents() function:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Draw the ball\ncanvasContext.fillStyle = 'white';\ncanvasContext.fillRect(200, 300, 10, 10);<\/pre>\n\n\n\n<p>I now want to use the ballX and ballY variables. For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Draw the ball\ncanvasContext.fillStyle = 'white';\ncanvasContext.fillRect(ballX, ballY, 10, 10);<\/pre>\n\n\n\n<p>Next, I need a new function that will control the ball movement and this function needs to be called from inside the game loop. For example, here is the function:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">function ballMovement() {\n    ballX = ballX + ballSpeedX;\n    ballY = ballY + ballSpeedY;\n}<\/pre>\n\n\n\n<p>For every iteration of the game loop, the ball location is updated by adding the ball speed. The function call in the game loop looks like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">var framesPerSecond = 30;\nsetInterval(\nfunction() {\n    ballMovement();\n    drawComponents();\n}, 1000\/framesPerSecond);<\/pre>\n\n\n\n<p>This is all it takes to get the ball moving, but since there is no ball handling yet, the ball will fly from the upper left corner of the playing area to the right side of the screen and disappear.<\/p>\n\n\n\n<p><strong>Step 2: Add ball handling for top and bottom<br><\/strong><\/p>\n\n\n\n<p>When the ball hits a left or right paddle or hits the top or bottom of the screen, we want the ball&#8217;s direction to change with some reflection. The direction of the ball is controlled using the ballSpeed variables ballSpeedY and ballSpeedX. If the ball hits the top or bottom wall, we want the ball to bounce off of the wall and move in the other direction. This is accomplished by reversing the speed on the Y axis. For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ballSpeedY = -ballSpeedY;<\/pre>\n\n\n\n<p>If the ball hits a left or right paddle, we want the reflection on the X axis. For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ballSpeedX = -ballSpeedX;<\/pre>\n\n\n\n<p>Here is the code to handle these two situations (added to the ballMovement() function):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ top (ceiling)\nif(ballY &lt; 0) {\n    \/\/ bounce off by reflection on the Y axis\n    ballSpeedY = -ballSpeedY;\n}\n\/\/ bottom (floor)\nif(ballY > canvas_height-10) {\n    \/\/ bounce off by reflection on the Y axis\n    ballSpeedY = -ballSpeedY;\n}<\/pre>\n\n\n\n<p><strong>Step 3: Add ball handling for left and right<br><\/strong><\/p>\n\n\n\n<p>When the ball hits the left or right side of the screen, it will either hit a paddle or miss the paddle. When the ball hits the right or left edge of the screen, we can detect if the ball hits a paddle by checking to see if the vertical ball location &#8220;ballY&#8221; is between the top of the paddle (paddle1Y) and the bottom of the paddle (paddle1Y + PADDLE_HEIGHT). If it is, we want to reflect the ball on the X axis. For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">if(ballY > paddle1Y &amp;&amp; ballY &lt; (paddle1Y + PADDLE_HEIGHT)) {<\/pre>\n\n\n\n<p>To make the game more interesting, we also want to add some variability to the reflection angles of the ball based on where the ball made contact with the paddle. This is done by adding some &#8220;speed&#8221; to the&nbsp; ballSpeedY variable. For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ change ball speed of Y to change the angle of return\nvar deltaY = ballY - (paddle1Y + PADDLE_HEIGHT\/2);\nballSpeedY = deltaY * 0.35;<\/pre>\n\n\n\n<p>A hit in the middle of the paddle will result in a gentle, straight return but a hit near the tip of the paddle will return the ball at a steeper angle.<\/p>\n\n\n\n<p>If the ball misses the paddle, then the ball needs to be &#8220;reset&#8221; by being served again from the middle of the screen and the opposing player should register 1 point. Here is what the ballReset() function looks like:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">function ballReset() {\n    ballSpeedX = -ballSpeedX;\n    ballX = canvas_width\/2;\n    ballY = canvas_height\/2;\n}<\/pre>\n\n\n\n<p>Its basically a ball reflection plus a resetting of the X and Y positions of the ball to the middle of the screen.<\/p>\n\n\n\n<p>Here is the code for the right and left ball handling, inside the ballMovement() function:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ left wall\nif(ballX &lt; offsetA + PADDLE_THICKNESS) {\n    \/\/ Detect a hit. Check if the ball is between the top and bottom of the paddle\n    if(ballY > paddle1Y &amp;&amp; ballY &lt; (paddle1Y + PADDLE_HEIGHT)) {\n        \/\/ console.log(\"HIT! Ball position: \" + ballY)\n        ballSpeedX = -ballSpeedX;\n\n        \/\/ change ball speed of Y to change the angle of return\n        var deltaY = ballY - (paddle1Y + PADDLE_HEIGHT\/2);\n        ballSpeedY = deltaY * 0.35;\n\n    } else if (ballX &lt; offsetA) {\n        ballReset();\n    }\n}\n\n\/\/ right wall\nif(ballX > canvas_width-40 - PADDLE_THICKNESS) {\n    \/\/ Detect a hit. If the ball is between the top and bottom of the paddle\n    if(ballY > paddle2Y &amp;&amp; ballY &lt; (paddle2Y + PADDLE_HEIGHT)) {\n        \/\/ console.log(\"HIT! Ball position: \" + ballY)\n\tballSpeedX = -ballSpeedX;\n\n        \/\/ change ball speed of Y to change the angle of return\n\tvar deltaY = ballY - (paddle2Y + PADDLE_HEIGHT\/2 );\n\tballSpeedY = deltaY * 0.35;\n    } else if (ballX > canvas_width-40) {\n        ballReset();\n    }\n}<\/pre>\n\n\n\n<p><strong>Step 4: A.I. for computer controlled paddle<br><\/strong><\/p>\n\n\n\n<p>To enable single player, we need the computer to control one of the paddles. Here is a function that handles the computer A.I. for this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">function computerPaddleAI() {\n    var paddle2YCenter = paddle2Y + (PADDLE_HEIGHT\/2);\n    if(paddle2YCenter &lt; ballY - 35) {\n\tpaddle2Y = paddle2Y + 6;\n    } else if(paddle2YCenter > ballY + 35) {\n\tpaddle2Y = paddle2Y - 6;\n    }\n}<\/pre>\n\n\n\n<p>Essentially it works by checking the location of the center of the paddle compared to the current location of the ball in each frame of the game loop. If paddle is above or below the ball, then it will move by six units on the Y axis in the direction of the ball.<\/p>\n\n\n\n<p>The computerPaddleAI() function should be called from both the ballMovement() and ballReset() functions.<\/p>\n\n\n\n<p><strong>Step 5: Add scoring<br><\/strong><\/p>\n\n\n\n<p>When the ball hits the left or right side of the screen without being hit by a paddle, the opposing side gets 1 point. First we need to set up some variables:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">var player1Score = 0;\nvar player2Score = 0;<\/pre>\n\n\n\n<p>In the &#8220;else if&#8221; blocks of the ballMovement() function, just before the call to ballReset(), we can update the score like so:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">player2Score++; \/\/ must be BEFORE ballReset()<\/pre>\n\n\n\n<p>Then in the drawComponents() function, we should reference the score variables rather than use place holders. For example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Place holder for the scores\ncanvasContext.font=\"40px monospace\";\n\/\/ Left side score- make room for more digits\nif (player1Score &lt; 10) {\n    canvasContext.fillText(player1Score, (canvas_width\/2)-60, 40);\n} else if (player1Score > 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\ncanvasContext.fillText(player2Score, (canvas_width\/2)+35, 40);<\/pre>\n\n\n\n<p>Here is the complete code so far:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">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;\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('mousemove',\n    \tfunction(evt) {\n    \t    var mousePos = calculateMousePos(evt);\n    \t    paddle1Y = mousePos.y - (PADDLE_HEIGHT\/2);\n    \t});\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\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 ballMovement() {\n    computerPaddleAI();\n    ballX = ballX + ballSpeedX;\n    ballY = ballY + ballSpeedY;\n\n    \/\/ left wall\n    if(ballX &lt; offsetA + PADDLE_THICKNESS) {\n        \/\/ Detect a hit. Check if the ball is between the top and bottom of the paddle\n    \tif(ballY > paddle1Y &amp;&amp; ballY &lt; (paddle1Y + PADDLE_HEIGHT)) {\n            \/\/ console.log(\"HIT! Ball position: \" + ballY)\n            ballSpeedX = -ballSpeedX;\n\n            \/\/ change ball speed of Y to change the angle of return\n            var deltaY = ballY - (paddle1Y + PADDLE_HEIGHT\/2);\n            ballSpeedY = deltaY * 0.35;\n\n    \t} else if (ballX &lt; offsetA) {\n            player2Score++; \/\/ must be BEFORE ballReset()\n            ballReset();\n        }\n    }\n    \/\/ right wall\n    if(ballX > canvas_width-40 - PADDLE_THICKNESS) {\n        \/\/ Detect a hit. If the ball is between the top and bottom of the paddle\n    \tif(ballY > 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 > canvas_width-40) {\n    \t    player1Score++; \/\/ must be BEFORE ballReset()\n    \t    ballReset();\n    \t}\n    }\n    \/\/ top (ceiling)\n    if(ballY &lt; 0) {\n        \/\/ bounce off by reflection on the Y axis\n\tballSpeedY = -ballSpeedY;\n    }\n    \/\/ bottom (floor)\n    if(ballY > canvas_height-10) {\n        \/\/ bounce off by reflection on the Y axis\n\tballSpeedY = -ballSpeedY;\n    }\n}\n\nfunction computerPaddleAI() {\n    var paddle2YCenter = paddle2Y + (PADDLE_HEIGHT\/2);\n\u00a0\u00a0  if(paddle2YCenter &lt; ballY - 35) {\n        paddle2Y = paddle2Y + 6;\n    } else if(paddle2YCenter > ballY + 35) {\n\tpaddle2Y = paddle2Y - 6;\n    }\n}\n\nfunction ballReset() {\n    computerPaddleAI();\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    \/\/ 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    \/\/ Place holder for the 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 > 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}\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\n\n\n<p>And here is how the game plays so far:<\/p>\n\n\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_pt5b.html\" width=\"770\" height=\"600\" scrolling=\"no\" name=\"pong\" class=\"iframe-class\" frameborder=\"0\"><\/iframe>\n\n\n\n\n<p><\/p>\n\n\n\n<br><p><button class=\"btn\" onclick=\"refreshIframe('pong');\">Start Over<\/button><\/p>\n\n\n<script language=\"JavaScript\">\n    function refreshIframe(name) {\n        var ifr = document.getElementsByName(name)[0];\n        ifr.src = ifr.src;\n    }\n<\/script>\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous article of the series, I described how to animate the left paddle using mouse movements. See: In this article, I will detail how to animate the ball, use an AI function to animate the right (computer) paddle, and take care of scoring. Step 1: Get the ball moving First I will need &hellip; <a href=\"https:\/\/bluegalaxy.info\/codewalk\/2018\/01\/03\/javascript-intro-web-game-development-part-5-ball-handling-scoring\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">JavaScript: Intro to Web Game Development &#8211; Part 5: ball handling, scoring, and A.I.<\/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":[45],"class_list":["post-1260","post","type-post","status-publish","format-standard","hentry","category-javascript-language","tag-javascript"],"_links":{"self":[{"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/posts\/1260","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=1260"}],"version-history":[{"count":24,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/posts\/1260\/revisions"}],"predecessor-version":[{"id":3348,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/posts\/1260\/revisions\/3348"}],"wp:attachment":[{"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/media?parent=1260"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/categories?post=1260"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bluegalaxy.info\/codewalk\/wp-json\/wp\/v2\/tags?post=1260"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}