1+ // grab a reference of our "canvas" using its id
2+ const canvas = document . getElementById ( 'canvas' ) ;
3+ /* get a "context". Without "context", we can't draw on canvas */
4+ const ctx = canvas . getContext ( '2d' ) ;
5+
6+ // some sounds
7+ const hitSound = new Audio ( ) ;
8+ const scoreSound = new Audio ( ) ;
9+ const wallHitSound = new Audio ( ) ;
10+
11+ hitSound . src = 'https://raw.githubusercontent.com/the-coding-pie/Ping-Pong-Javascript/master/sounds/hitSound.wav' ;
12+ scoreSound . src = 'https://raw.githubusercontent.com/the-coding-pie/Ping-Pong-Javascript/master/sounds/scoreSound.wav' ;
13+ wallHitSound . src = 'https://raw.githubusercontent.com/the-coding-pie/Ping-Pong-Javascript/master/sounds/wallHitSound.wav' ;
14+
15+ /* some extra variables */
16+ const netWidth = 4 ;
17+ const netHeight = canvas . height ;
18+
19+ const paddleWidth = 10 ;
20+ const paddleHeight = 100 ;
21+
22+ let upArrowPressed = false ;
23+ let downArrowPressed = false ;
24+
25+ /* some extra variables ends */
26+
27+ /* objects */
28+ // net
29+ const net = {
30+ x : canvas . width / 2 - netWidth / 2 ,
31+ y : 0 ,
32+ width : netWidth ,
33+ height : netHeight ,
34+ color : "#FFF"
35+ } ;
36+
37+ // user paddle
38+ const user = {
39+ x : 10 ,
40+ y : canvas . height / 2 - paddleHeight / 2 ,
41+ width : paddleWidth ,
42+ height : paddleHeight ,
43+ color : '#FFF' ,
44+ score : 0
45+ } ;
46+
47+ const ai = {
48+ x : canvas . width - ( paddleWidth + 10 ) ,
49+ y : canvas . height / 2 - paddleHeight / 2 ,
50+ width : paddleWidth ,
51+ height : paddleHeight ,
52+ color : '#FFF' ,
53+ score : 0
54+ } ;
55+
56+ // ball
57+ const ball = {
58+ x : canvas . width / 2 ,
59+ y : canvas . height / 2 ,
60+ radius : 7 ,
61+ speed : 7 ,
62+ velocityX : 5 ,
63+ velocityY : 5 ,
64+ color : '#05EDFF'
65+ } ;
66+
67+ /* objects declaration ends */
68+
69+ /* drawing functions */
70+ // function to draw net
71+ function drawNet ( ) {
72+ // set the color of net
73+ ctx . fillStyle = net . color ;
74+
75+ // syntax --> fillRect(x, y, width, height)
76+ ctx . fillRect ( net . x , net . y , net . width , net . height ) ;
77+ }
78+
79+ // function to draw score
80+ function drawScore ( x , y , score ) {
81+ ctx . fillStyle = '#fff' ;
82+ ctx . font = '35px sans-serif' ;
83+
84+ // syntax --> fillText(text, x, y)
85+ ctx . fillText ( score , x , y ) ;
86+ }
87+
88+ // function to draw paddle
89+ function drawPaddle ( x , y , width , height , color ) {
90+ ctx . fillStyle = color ;
91+ ctx . fillRect ( x , y , width , height ) ;
92+ }
93+
94+ // function to draw ball
95+ function drawBall ( x , y , radius , color ) {
96+ ctx . fillStyle = color ;
97+ ctx . beginPath ( ) ;
98+ // syntax --> arc(x, y, radius, startAngle, endAngle, antiClockwise_or_not)
99+ ctx . arc ( x , y , radius , 0 , Math . PI * 2 , true ) ; // π * 2 Radians = 360 degrees
100+ ctx . closePath ( ) ;
101+ ctx . fill ( ) ;
102+ }
103+
104+ /* drawing functions end */
105+
106+ /* moving Paddles */
107+ // add an eventListener to browser window
108+ window . addEventListener ( 'keydown' , keyDownHandler ) ;
109+ window . addEventListener ( 'keyup' , keyUpHandler ) ;
110+
111+ // gets activated when we press down a key
112+ function keyDownHandler ( event ) {
113+ // get the keyCode
114+ switch ( event . keyCode ) {
115+ // "up arrow" key
116+ case 38 :
117+ // set upArrowPressed = true
118+ upArrowPressed = true ;
119+ break ;
120+ // "down arrow" key
121+ case 40 :
122+ downArrowPressed = true ;
123+ break ;
124+ }
125+ }
126+
127+ // gets activated when we release the key
128+ function keyUpHandler ( event ) {
129+ switch ( event . keyCode ) {
130+ // "up arraow" key
131+ case 38 :
132+ upArrowPressed = false ;
133+ break ;
134+ // "down arrow" key
135+ case 40 :
136+ downArrowPressed = false ;
137+ break ;
138+ }
139+ }
140+
141+ /* moving paddles section end */
142+
143+ // reset the ball
144+ function reset ( ) {
145+ // reset ball's value to older values
146+ ball . x = canvas . width / 2 ;
147+ ball . y = canvas . height / 2 ;
148+ ball . speed = 7 ;
149+
150+ // changes the direction of ball
151+ ball . velocityX = - ball . velocityX ;
152+ ball . velocityY = - ball . velocityY ;
153+ }
154+
155+ // collision Detect function
156+ function collisionDetect ( player , ball ) {
157+ // returns true or false
158+ player . top = player . y ;
159+ player . right = player . x + player . width ;
160+ player . bottom = player . y + player . height ;
161+ player . left = player . x ;
162+
163+ ball . top = ball . y - ball . radius ;
164+ ball . right = ball . x + ball . radius ;
165+ ball . bottom = ball . y + ball . radius ;
166+ ball . left = ball . x - ball . radius ;
167+
168+ return ball . left < player . right && ball . top < player . bottom && ball . right > player . left && ball . bottom > player . top ;
169+ }
170+
171+ // update function, to update things position
172+ function update ( ) {
173+ // move the paddle
174+ if ( upArrowPressed && user . y > 0 ) {
175+ user . y -= 8 ;
176+ } else if ( downArrowPressed && ( user . y < canvas . height - user . height ) ) {
177+ user . y += 8 ;
178+ }
179+
180+ // check if ball hits top or bottom wall
181+ if ( ball . y + ball . radius >= canvas . height || ball . y - ball . radius <= 0 ) {
182+ // play wallHitSound
183+ wallHitSound . play ( ) ;
184+ ball . velocityY = - ball . velocityY ;
185+ }
186+
187+ // if ball hit on right wall
188+ if ( ball . x + ball . radius >= canvas . width ) {
189+ // play scoreSound
190+ scoreSound . play ( ) ;
191+ // then user scored 1 point
192+ user . score += 1 ;
193+ reset ( ) ;
194+ }
195+
196+ // if ball hit on left wall
197+ if ( ball . x - ball . radius <= 0 ) {
198+ // play scoreSound
199+ scoreSound . play ( ) ;
200+ // then ai scored 1 point
201+ ai . score += 1 ;
202+ reset ( ) ;
203+ }
204+
205+ // move the ball
206+ ball . x += ball . velocityX ;
207+ ball . y += ball . velocityY ;
208+
209+ // ai paddle movement
210+ ai . y += ( ( ball . y - ( ai . y + ai . height / 2 ) ) ) * 0.09 ;
211+
212+ // collision detection on paddles
213+ let player = ( ball . x < canvas . width / 2 ) ? user : ai ;
214+
215+ if ( collisionDetect ( player , ball ) ) {
216+ // play hitSound
217+ hitSound . play ( ) ;
218+ // default angle is 0deg in Radian
219+ let angle = 0 ;
220+
221+ // if ball hit the top of paddle
222+ if ( ball . y < ( player . y + player . height / 2 ) ) {
223+ // then -1 * Math.PI / 4 = -45deg
224+ angle = - 1 * Math . PI / 4 ;
225+ } else if ( ball . y > ( player . y + player . height / 2 ) ) {
226+ // if it hit the bottom of paddle
227+ // then angle will be Math.PI / 4 = 45deg
228+ angle = Math . PI / 4 ;
229+ }
230+
231+ /* change velocity of ball according to on which paddle the ball hitted */
232+ ball . velocityX = ( player === user ? 1 : - 1 ) * ball . speed * Math . cos ( angle ) ;
233+ ball . velocityY = ball . speed * Math . sin ( angle ) ;
234+
235+ // increase ball speed
236+ ball . speed += 0.2 ;
237+ }
238+ }
239+
240+ // render function draws everything on to canvas
241+ function render ( ) {
242+ // set a style
243+ ctx . fillStyle = "#000" ; /* whatever comes below this acquires black color (#000). */
244+ // draws the black board
245+ ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
246+
247+ // draw net
248+ drawNet ( ) ;
249+ // draw user score
250+ drawScore ( canvas . width / 4 , canvas . height / 6 , user . score ) ;
251+ // draw ai score
252+ drawScore ( 3 * canvas . width / 4 , canvas . height / 6 , ai . score ) ;
253+ // draw user paddle
254+ drawPaddle ( user . x , user . y , user . width , user . height , user . color ) ;
255+ // draw ai paddle
256+ drawPaddle ( ai . x , ai . y , ai . width , ai . height , ai . color ) ;
257+ // draw ball
258+ drawBall ( ball . x , ball . y , ball . radius , ball . color ) ;
259+ }
260+
261+ // gameLoop
262+ function gameLoop ( ) {
263+ // update() function here
264+ update ( ) ;
265+ // render() function here
266+ render ( ) ;
267+ }
268+
269+ // calls gameLoop() function 60 times per second
270+ setInterval ( gameLoop , 1000 / 60 ) ;
0 commit comments