1+ const canvas = document . getElementById ( 'gameCanvas' ) ;
2+ const ctx = canvas . getContext ( '2d' ) ;
3+ const scoreDisplay = document . querySelector ( '.score-display' ) ;
4+ const startScreen = document . querySelector ( '.start-screen' ) ;
5+ const gameOverScreen = document . querySelector ( '.game-over' ) ;
6+ const finalScoreDisplay = document . getElementById ( 'finalScore' ) ;
7+ const startButton = document . getElementById ( 'startButton' ) ;
8+ const restartButton = document . getElementById ( 'restartButton' ) ;
9+
10+ let gameRunning = false ;
11+ let score = 0 ;
12+ let frames = 0 ;
13+
14+ const bird = {
15+ x : 50 ,
16+ y : canvas . height / 2 ,
17+ width : 34 ,
18+ height : 24 ,
19+ gravity : 0.5 ,
20+ velocity : 0 ,
21+ jump : - 10 ,
22+
23+ draw : function ( ) {
24+ ctx . fillStyle = '#FFD700' ;
25+ ctx . beginPath ( ) ;
26+ ctx . arc ( this . x , this . y , 15 , 0 , Math . PI * 2 ) ;
27+ ctx . fill ( ) ;
28+
29+ ctx . fillStyle = 'black' ;
30+ ctx . beginPath ( ) ;
31+ ctx . arc ( this . x + 8 , this . y - 3 , 4 , 0 , Math . PI * 2 ) ;
32+ ctx . fill ( ) ;
33+
34+ ctx . fillStyle = '#FF6B35' ;
35+ ctx . beginPath ( ) ;
36+ ctx . moveTo ( this . x + 15 , this . y ) ;
37+ ctx . lineTo ( this . x + 30 , this . y ) ;
38+ ctx . lineTo ( this . x + 15 , this . y + 8 ) ;
39+ ctx . fill ( ) ;
40+
41+ ctx . fillStyle = '#FFA500' ;
42+ ctx . beginPath ( ) ;
43+ ctx . ellipse ( this . x - 5 , this . y + 5 , 10 , 6 , Math . PI / 4 , 0 , Math . PI * 2 ) ;
44+ ctx . fill ( ) ;
45+ } ,
46+
47+ update : function ( ) {
48+ if ( gameRunning ) {
49+ this . velocity += this . gravity ;
50+ this . y += this . velocity ;
51+
52+ if ( this . y + this . height / 2 >= canvas . height - ground . height ) {
53+ this . y = canvas . height - ground . height - this . height / 2 ;
54+ if ( gameRunning ) gameOver ( ) ;
55+ }
56+
57+ if ( this . y - this . height / 2 <= 0 ) {
58+ this . y = this . height / 2 ;
59+ this . velocity = 0 ;
60+ }
61+ }
62+ } ,
63+
64+ flap : function ( ) {
65+ this . velocity = this . jump ;
66+ } ,
67+
68+ reset : function ( ) {
69+ this . y = canvas . height / 2 ;
70+ this . velocity = 0 ;
71+ }
72+ } ;
73+
74+ const ground = {
75+ height : 80 ,
76+
77+ draw : function ( ) {
78+ ctx . fillStyle = '#D2691E' ;
79+ ctx . fillRect ( 0 , canvas . height - this . height , canvas . width , this . height ) ;
80+
81+ ctx . fillStyle = '#2E8B57' ;
82+ ctx . fillRect ( 0 , canvas . height - this . height , canvas . width , 15 ) ;
83+
84+ ctx . fillStyle = '#8B4513' ;
85+ for ( let i = 0 ; i < canvas . width ; i += 30 ) {
86+ ctx . fillRect ( i , canvas . height - 20 , 15 , 5 ) ;
87+ }
88+ }
89+ } ;
90+
91+ const pipes = {
92+ position : [ ] ,
93+ gap : 180 ,
94+ maxYPos : - 150 ,
95+ dx : 2 ,
96+
97+ draw : function ( ) {
98+ for ( let i = 0 ; i < this . position . length ; i ++ ) {
99+ let p = this . position [ i ] ;
100+
101+ ctx . fillStyle = '#2E8B57' ;
102+ ctx . fillRect ( p . x , p . y , p . width , p . height ) ;
103+
104+ ctx . fillStyle = '#228B22' ;
105+ ctx . fillRect ( p . x - 5 , p . y + p . height - 20 , p . width + 10 , 20 ) ;
106+
107+ ctx . fillStyle = '#2E8B57' ;
108+ ctx . fillRect ( p . x , p . y + p . height + this . gap , p . width , canvas . height ) ;
109+
110+ ctx . fillStyle = '#228B22' ;
111+ ctx . fillRect ( p . x - 5 , p . y + p . height + this . gap , p . width + 10 , 20 ) ;
112+ }
113+ } ,
114+
115+ update : function ( ) {
116+ if ( gameRunning ) {
117+ if ( frames % 100 === 0 ) {
118+ this . position . push ( {
119+ x : canvas . width ,
120+ y : this . maxYPos * ( Math . random ( ) + 1 ) ,
121+ width : 60 ,
122+ height : 300
123+ } ) ;
124+ }
125+
126+ for ( let i = 0 ; i < this . position . length ; i ++ ) {
127+ let p = this . position [ i ] ;
128+
129+ p . x -= this . dx ;
130+
131+ if ( p . x + p . width <= 0 ) {
132+ this . position . shift ( ) ;
133+ score ++ ;
134+ scoreDisplay . textContent = score ;
135+ }
136+
137+ if (
138+ bird . x + bird . width / 2 > p . x &&
139+ bird . x - bird . width / 2 < p . x + p . width &&
140+ bird . y - bird . height / 2 < p . y + p . height
141+ ) {
142+ gameOver ( ) ;
143+ }
144+
145+ if (
146+ bird . x + bird . width / 2 > p . x &&
147+ bird . x - bird . width / 2 < p . x + p . width &&
148+ bird . y + bird . height / 2 > p . y + p . height + this . gap
149+ ) {
150+ gameOver ( ) ;
151+ }
152+ }
153+ }
154+ } ,
155+
156+ reset : function ( ) {
157+ this . position = [ ] ;
158+ }
159+ } ;
160+
161+ const background = {
162+ draw : function ( ) {
163+ ctx . fillStyle = '#70c5ce' ;
164+ ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
165+
166+ ctx . fillStyle = 'rgba(255, 255, 255, 0.8)' ;
167+ ctx . beginPath ( ) ;
168+ ctx . arc ( 100 , 80 , 30 , 0 , Math . PI * 2 ) ;
169+ ctx . arc ( 130 , 70 , 35 , 0 , Math . PI * 2 ) ;
170+ ctx . arc ( 160 , 80 , 25 , 0 , Math . PI * 2 ) ;
171+ ctx . fill ( ) ;
172+
173+ ctx . beginPath ( ) ;
174+ ctx . arc ( 300 , 120 , 30 , 0 , Math . PI * 2 ) ;
175+ ctx . arc ( 330 , 110 , 35 , 0 , Math . PI * 2 ) ;
176+ ctx . arc ( 360 , 120 , 25 , 0 , Math . PI * 2 ) ;
177+ ctx . fill ( ) ;
178+ }
179+ } ;
180+
181+ function draw ( ) {
182+ background . draw ( ) ;
183+ pipes . draw ( ) ;
184+ ground . draw ( ) ;
185+ bird . draw ( ) ;
186+ }
187+
188+ function update ( ) {
189+ bird . update ( ) ;
190+ pipes . update ( ) ;
191+ }
192+
193+ function gameLoop ( ) {
194+ update ( ) ;
195+ draw ( ) ;
196+ frames ++ ;
197+
198+ requestAnimationFrame ( gameLoop ) ;
199+ }
200+
201+ function startGame ( ) {
202+ gameRunning = true ;
203+ score = 0 ;
204+ frames = 0 ;
205+ scoreDisplay . textContent = score ;
206+ bird . reset ( ) ;
207+ pipes . reset ( ) ;
208+ startScreen . style . display = 'none' ;
209+ gameOverScreen . style . display = 'none' ;
210+ }
211+
212+ function gameOver ( ) {
213+ gameRunning = false ;
214+ finalScoreDisplay . textContent = score ;
215+ gameOverScreen . style . display = 'block' ;
216+ }
217+
218+ document . addEventListener ( 'keydown' , function ( e ) {
219+ if ( e . code === 'Space' ) {
220+ if ( ! gameRunning && gameOverScreen . style . display === 'block' ) {
221+ startGame ( ) ;
222+ } else if ( gameRunning ) {
223+ bird . flap ( ) ;
224+ }
225+ }
226+ } ) ;
227+
228+ canvas . addEventListener ( 'click' , function ( ) {
229+ if ( ! gameRunning && gameOverScreen . style . display === 'block' ) {
230+ startGame ( ) ;
231+ } else if ( gameRunning ) {
232+ bird . flap ( ) ;
233+ }
234+ } ) ;
235+
236+ canvas . addEventListener ( 'touchstart' , function ( e ) {
237+ if ( ! gameRunning && gameOverScreen . style . display === 'block' ) {
238+ startGame ( ) ;
239+ } else if ( gameRunning ) {
240+ bird . flap ( ) ;
241+ }
242+ e . preventDefault ( ) ;
243+ } ) ;
244+
245+ startButton . addEventListener ( 'click' , startGame ) ;
246+ restartButton . addEventListener ( 'click' , startGame ) ;
247+
248+ draw ( ) ;
249+ gameLoop ( ) ;
0 commit comments