-
Notifications
You must be signed in to change notification settings - Fork 0
/
FlappyBirdDuoGame.java
459 lines (393 loc) · 17 KB
/
FlappyBirdDuoGame.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
public class FlappyBirdDuoGame extends JPanel implements ActionListener, KeyListener {
private Timer timer;
private int birdX, birdY, bird2X, bird2Y, loser = 0;
private int birdVelocity, birdVelocityX, bird2Velocity, bird2VelocityX, hit; // Added velocities for second bird
private int rotationAngle, rotationAngle2, obstacleCount = 0, difficulty = 1;
private List<Rectangle> obstacles = new ArrayList<>();
private Random random = new Random();
private float gameVol = 0.8f;
private boolean endGame = false, startGame = false, Down = true, SHIFTED = false;
// Game settings
private int Tick, space, distance, velocity, gravity;
// Game assets
private ImageIcon base, deadBird, flappyBirdIcon, flappyBird2Icon, backgroundImage, upperPipeIcon, lowerPipeIcon;
public static void main(String[] args) {
// Setup main game window
JFrame frame = new JFrame("Flappy Bird Duo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setIconImage(new ImageIcon("Images/Flappy_Bird_Duo_icon.png").getImage());
frame.setSize(800, 600);
frame.setResizable(false);
frame.add(new FlappyBirdDuoGame());
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
public FlappyBirdDuoGame() {
setDifficulty(1); // Initialize difficulty
// Load and scale images
flappyBirdIcon = new ImageIcon("Images/bird.png");
flappyBird2Icon = new ImageIcon("Images/bird2.png"); // Load second bird image
backgroundImage = new ImageIcon("Images/background.png");
upperPipeIcon = new ImageIcon("Images/obsdown.png");
lowerPipeIcon = new ImageIcon("Images/obs.png");
base = new ImageIcon("Images/base.png");
// Scale images
backgroundImage = new ImageIcon(backgroundImage.getImage().getScaledInstance(800, 600, Image.SCALE_DEFAULT));
flappyBirdIcon = new ImageIcon(flappyBirdIcon.getImage().getScaledInstance(50, 50, Image.SCALE_DEFAULT));
flappyBird2Icon = new ImageIcon(flappyBird2Icon.getImage().getScaledInstance(50, 50, Image.SCALE_DEFAULT)); // Scale second bird
base = new ImageIcon(base.getImage().getScaledInstance(800, 100, Image.SCALE_DEFAULT));
// Initialize game variables
birdX = 200;
birdY = 200;
bird2X = 550; // Initial position for second bird
bird2Y = 200;
birdVelocity = birdVelocityX = 0;
bird2Velocity = bird2VelocityX = 0; // Initialize second bird's velocities
rotationAngle = rotationAngle2 = 0;
obstacles = new ArrayList<>();
random = new Random();
// Setup game
addKeyListener(this);
setFocusable(true);
timer = new Timer(150, this);
timer.start();
generateObstacle(); // Create the first obstacle
}
public void setDifficulty(int difficulty) {
// Set parameters based on difficulty level
switch (difficulty) {
case 1:
Tick = 16; space = 190; distance = 530; velocity = 4; gravity = 1; break;
case 2:
Tick = 12; space = 150; distance = 470; velocity = 6; gravity = 1; break;
case 3:
Tick = 8; space = 110; distance = 410; velocity = 8; gravity = 2; break;
default:
throw new IllegalArgumentException("Invalid difficulty level");
}
}
public void playSound(String soundFilePath, float volume) {
try {
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(getClass().getResourceAsStream(soundFilePath));
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
float range = gainControl.getMaximum() - gainControl.getMinimum();
float gain = (range * volume) + gainControl.getMinimum();
gainControl.setValue(gain);
}
clip.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void generateObstacle() {
int space = this.space; // Space between upper and lower obstacles
int width = 60;
int height = random.nextInt(300) + 50; // Random height for lower obstacle
// Add upper and lower obstacles
obstacles.add(new Rectangle(800, 0, width, height)); // Upper obstacle
obstacles.add(new Rectangle(800, height + space, width, 600 - height - space)); // Lower obstacle
obstacleCount++;
// Increase difficulty every 10 obstacles
if (obstacleCount % 10 == 0 && difficulty < 4) {
setDifficulty(difficulty);
playSound("Sounds/point.wav", gameVol);
difficulty++;
}
}
public void move() {
if (!endGame && startGame) {
// Apply gravity to both birds
if (!SHIFTED) {
birdVelocityX += (birdVelocityX > 0) ? -gravity : gravity;
bird2VelocityX += (bird2VelocityX > 0) ? -gravity : gravity; // Second bird's velocity adjustment
}
birdVelocity += gravity;
bird2Velocity += gravity;
birdY += birdVelocity;
birdX += birdVelocityX;
bird2Y += bird2Velocity; // Move second bird
bird2X += bird2VelocityX; // Move second bird
// Move obstacles
for (Rectangle obstacle : obstacles) {
obstacle.x -= velocity;
}
// Check for collisions for both birds
Rectangle bird1 = new Rectangle(birdX, birdY, 50, 40);
Rectangle bird2 = new Rectangle(bird2X, bird2Y, 50, 40); // Second bird's rectangle
for (Rectangle obstacle : obstacles) {
if (obstacle.intersects(bird1) || birdY < 0 || birdY > 475 || birdX < 0 || birdX > 800) {
playSound("Sounds/bird-hit.wav", gameVol);
loser = 1;
endGame = true;
return;
}
else if (obstacle.intersects(bird2) || bird2Y < 0 || bird2Y > 475 || bird2X < 0 || bird2X > 800) {
playSound("Sounds/bird-hit.wav", gameVol);
loser = 2;
endGame = true;
return;
}
else if(bird1.intersects(bird2)) { // Check for bird-to-bird collision
playSound("Sounds/bird-hit.wav", gameVol);
hit = 15;
if (Math.abs(birdVelocityX) < Math.abs(bird2VelocityX)) {
birdVelocityX += (birdX > bird2X) ? hit : -hit;
}
else if (Math.abs(birdVelocityX) > Math.abs(bird2VelocityX)) {
bird2VelocityX += (bird2X > birdX) ? hit : -hit;
}
return;
}
}
// Generate new obstacles
if (obstacles.get(obstacles.size() - 1).x < distance) {
generateObstacle();
}
// Remove off-screen obstacles
obstacles.removeIf(obstacle -> obstacle.x + obstacle.width < 0);
} else if (!endGame && !startGame) {
// Apply gravity to both birds in a different way when the game isn't started or ended
birdY += (Down) ? 10 : -10;
bird2Y += (Down) ? 10 : -10; // Apply gravity effect to second bird
Down = !Down;
// Move obstacles (no movement when the game isn't started)
obstacles.forEach(obstacle -> obstacle.x -= 0);
} else if (endGame && startGame) {
// Apply gravity to both birds during end game
birdVelocity += gravity;
birdVelocityX = 0;
birdY += birdVelocity;
birdX += birdVelocityX;
bird2Velocity += gravity; // Gravity for second bird
bird2VelocityX = 0;
bird2Y += bird2Velocity;
bird2X += bird2VelocityX;
// Move obstacles (no movement during end game)
for (Rectangle obstacle : obstacles) {
obstacle.x -= 0;
if (birdY > 475 || bird2Y > 475) { // Check for both birds
gameOver();
}
}
// Generate new obstacles
if (obstacles.get(obstacles.size() - 1).x < distance) {
generateObstacle();
}
// Remove off-screen obstacles
obstacles.removeIf(obstacle -> obstacle.x + obstacle.width < 0);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Draw background image
backgroundImage.paintIcon(this, g, 0, 0);
// Draw obstacles
for (Rectangle obstacle : obstacles) {
int pipeSpace = space;
// Draw upper and lower pipes
g.drawImage(upperPipeIcon.getImage(), obstacle.x, obstacle.y, obstacle.width, obstacle.height, this);
g.drawImage(lowerPipeIcon.getImage(), obstacle.x, obstacle.y + obstacle.height + pipeSpace, obstacle.width, obstacle.height, this);
}
// Draw ground
base.paintIcon(this, g, 0, 520);
// Rotate and draw first Flappy Bird image
Graphics2D g2d = (Graphics2D) g;
g2d.rotate(Math.toRadians(rotationAngle), birdX + 20, birdY + 22);
flappyBirdIcon.paintIcon(this, g2d, birdX, birdY);
g2d.rotate(-Math.toRadians(rotationAngle), birdX + 20, birdY + 22); // Reset rotation
// Draw second Flappy Bird image (no rotation needed if similar behavior)
g2d.rotate(Math.toRadians(rotationAngle2), bird2X + 20, bird2Y + 22);
flappyBird2Icon.paintIcon(this, g2d, bird2X, bird2Y);
g2d.rotate(-Math.toRadians(rotationAngle2), bird2X + 20, bird2Y + 22);
// Draw start button if the game hasn't started or ended
if (!endGame && !startGame) {
ImageIcon startButtonIcon = new ImageIcon("Images/StartButton.png");
startButtonIcon = new ImageIcon(startButtonIcon.getImage().getScaledInstance(150, 60, Image.SCALE_DEFAULT));
int centerX = (getWidth() - startButtonIcon.getIconWidth()) / 2;
startButtonIcon.paintIcon(this, g, centerX, 400);
}
else if (startGame){
ImageIcon shiftButton;
if(SHIFTED){
shiftButton = new ImageIcon("Images/Shifted.png");
}
else{
shiftButton = new ImageIcon("Images/Shift.png");
}
shiftButton = new ImageIcon(shiftButton.getImage().getScaledInstance(150, 50, Image.SCALE_DEFAULT));
shiftButton.paintIcon(this, g, 10, 10);
}
}
public void actionPerformed(ActionEvent e) {
move(); // Move game elements
repaint(); // Refresh screen
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(loser == 2 || loser == 0){
switch (keyCode) {
// Controls for the first bird
case KeyEvent.VK_SPACE:
startGame = true;
timer.setDelay(Tick);
break;
case KeyEvent.VK_W:
startGame = true;
timer.setDelay(Tick);
birdVelocity = -13;
rotationAngle = -20;
playSound("Sounds/flap.wav", gameVol);
break;
case KeyEvent.VK_S:
birdVelocity = 10;
rotationAngle = 20;
playSound("Sounds/flap.wav", gameVol);
break;
case KeyEvent.VK_D:
birdVelocityX = 15;
rotationAngle = -20;
playSound("Sounds/flap.wav", gameVol);
break;
case KeyEvent.VK_A:
birdVelocityX = -15;
rotationAngle = -20;
playSound("Sounds/flap.wav", gameVol);
break;
}}
if(loser == 1 || loser == 0){
switch (keyCode) {
// Controls for the second bird
case KeyEvent.VK_UP:
startGame = true;
timer.setDelay(Tick);
bird2Velocity = -13;
rotationAngle2 = -20;
playSound("Sounds/flap.wav", gameVol);
break;
case KeyEvent.VK_DOWN:
bird2Velocity = 10;
rotationAngle2 = 20;
playSound("Sounds/flap.wav", gameVol);
break;
case KeyEvent.VK_RIGHT:
bird2VelocityX = 15;
rotationAngle2 = -20;
playSound("Sounds/flap.wav", gameVol);
break;
case KeyEvent.VK_LEFT:
bird2VelocityX = -15;
rotationAngle2 = -20;
playSound("Sounds/flap.wav", gameVol);
break;
}
}
if(e.getKeyCode() == KeyEvent.VK_SHIFT) {
SHIFTED = true;
}
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
timer.stop();
ImageIcon pauseIcon = new ImageIcon("Images/pause.png");
pauseIcon = new ImageIcon(pauseIcon.getImage().getScaledInstance(30, 30, Image.SCALE_DEFAULT));
// Pause dialog with three options
int choice = JOptionPane.showOptionDialog(
this,
"Game Paused",
"Pause",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.INFORMATION_MESSAGE,
pauseIcon,
new Object[]{"Continue", "Restart", "Quit"},
"Continue"
);
// Handle user choice
if (choice == JOptionPane.YES_OPTION) {
timer.start();
} else if (choice == JOptionPane.NO_OPTION) {
restartGame();
} else {
System.exit(0);
}
}
}
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
// Common action for all birds
if (keyCode == KeyEvent.VK_SPACE || keyCode == KeyEvent.VK_W || keyCode == KeyEvent.VK_UP) {
rotationAngle = 30; // Set rotation angle when Space, W, or Up is released
rotationAngle2 = 30;
}
// Reset flags and velocities
if (keyCode == KeyEvent.VK_SHIFT) {
SHIFTED = false; // Reset SHIFTED flag
}
}
public void keyTyped(KeyEvent e) {} // No action required
public void gameOver() {
timer.stop(); // Stop the game timer
// Resize the dead bird image
if(loser == 1){
deadBird = new ImageIcon("Images/dead_bird.png");
}
else if(loser == 2){
deadBird = new ImageIcon("Images/dead_bird2.png");
}
deadBird = new ImageIcon(deadBird.getImage().getScaledInstance(45, 45, Image.SCALE_DEFAULT));
// Display game over dialog
int choice = JOptionPane.showOptionDialog(
this,
"Player "+loser+" Lose",
"Game Over",
JOptionPane.YES_NO_OPTION,
JOptionPane.INFORMATION_MESSAGE,
deadBird,
new Object[]{"Retry", "Quit"},
"Retry"
);
// Handle the user's choice
if (choice == JOptionPane.YES_OPTION) {
restartGame(); // Restart the game
} else {
System.exit(0); // Exit the game
}
}
public void restartGame() {
// Reset game variables for the first bird
birdX = 200;
birdY = 200;
birdVelocity = birdVelocityX = 0;
// Reset game variables for the second bird
bird2X = 550; // Reset position for second bird
bird2Y = 200;
bird2Velocity = bird2VelocityX = 0;
rotationAngle = 0;
rotationAngle2 = 0;
difficulty = 1;
obstacleCount = 0;
loser = 0;
SHIFTED = false;
startGame = endGame = false;
// Reset difficulty and obstacles
setDifficulty(1);
obstacles.clear();
generateObstacle();
// Restart game timer
timer.setDelay(150);
timer.start();
}
}