CPD/Pong/lib/main.dart

346 lines
9.5 KiB
Dart
Raw Normal View History

2024-01-10 14:49:33 +01:00
import 'dart:async';
import 'dart:math';
import 'package:flutter/cupertino.dart';
2023-11-21 15:53:43 +01:00
import 'package:flutter/material.dart';
2024-01-10 14:49:33 +01:00
import 'package:flutter/scheduler.dart';
import 'package:pong/pong_menu.dart';
2023-11-21 15:53:43 +01:00
2023-11-21 15:58:06 +01:00
void main() {
2024-01-10 22:59:41 +01:00
// Run the PongGame app
2023-11-21 17:28:39 +01:00
runApp(const PongGame());
2023-11-21 15:53:43 +01:00
}
2023-11-21 17:28:39 +01:00
class PongGame extends StatelessWidget {
const PongGame({super.key});
2023-11-21 15:53:43 +01:00
@override
Widget build(BuildContext context) {
2023-11-21 23:41:52 +01:00
return MaterialApp(
2024-01-10 14:49:33 +01:00
// Set the app title and theme
2023-11-21 17:28:39 +01:00
title: 'Pong Game',
2024-01-10 14:49:33 +01:00
theme: ThemeData(
scaffoldBackgroundColor: const Color.fromARGB(255, 41, 38, 38)),
// Set the initial screen to the StartScreen
2023-11-21 23:41:52 +01:00
home: const StartScreen(),
2023-11-21 15:53:43 +01:00
);
}
}
2023-11-21 17:28:39 +01:00
class StartScreen extends StatelessWidget {
const StartScreen({super.key});
2023-11-21 15:53:43 +01:00
@override
2023-11-21 17:28:39 +01:00
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () {
2024-01-10 14:49:33 +01:00
// Navigate to the GameScreen when tapped
2023-11-21 23:41:52 +01:00
Navigator.of(context).push(MaterialPageRoute(
2023-11-21 17:28:39 +01:00
builder: (context) {
return const GameScreen();
},
2023-11-21 23:41:52 +01:00
));
2023-11-21 17:28:39 +01:00
},
2023-11-21 23:41:52 +01:00
child: const Center(
2024-01-10 14:49:33 +01:00
// Display a message prompting the user to touch the screen to begin
2023-11-21 23:41:52 +01:00
child: Text(
'Berühren um zu beginnen!',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
2023-11-21 17:28:39 +01:00
),
);
}
2023-11-21 15:53:43 +01:00
}
2023-11-21 17:28:39 +01:00
class GameScreen extends StatefulWidget {
const GameScreen({super.key});
2023-11-21 15:53:43 +01:00
2023-11-21 17:28:39 +01:00
@override
2024-01-10 14:49:33 +01:00
State<GameScreen> createState() => GameScreenState();
2023-11-21 17:28:39 +01:00
}
2023-11-21 15:53:43 +01:00
2024-01-10 14:49:33 +01:00
class GameScreenState extends State<GameScreen> {
// Constants defining properties of the game
final ballSize = 20.0;
final racketWidth = 100.0;
final racketHeight = 10.0;
2023-11-21 23:41:52 +01:00
final racketBottomOffset = 100.0;
2024-01-10 14:49:33 +01:00
bool isPaused = false;
// Initial speed of the ball
final initialBallSpeed = 3.0;
2023-11-21 23:41:52 +01:00
2024-01-10 14:49:33 +01:00
// Variables to track ball and racket positions, speed, score, and game state
double ballPositionX = 0;
double ballPositionY = 0;
double ballSpeedX = 0;
double ballSpeedY = 0;
2023-11-21 23:41:52 +01:00
double racketX = 20;
2024-01-10 14:49:33 +01:00
int score = 0;
// Ticker for updating the game state
late Ticker ticker;
double ballSpeedMultiplier = 1.1;
@override
void initState() {
super.initState();
// Initialize the game state when the widget is created
startGame();
}
// Initialize the game state
void startGame() {
final random = Random();
ballPositionX = 0;
ballPositionY = 0;
ballSpeedX = initialBallSpeed;
ballSpeedY = initialBallSpeed;
ballSpeedMultiplier = 1.1;
racketX = 200;
score = 0;
// Randomize initial ball direction
if (random.nextBool()) ballSpeedX = -ballSpeedX;
if (random.nextBool()) ballSpeedY = -ballSpeedY;
// Start the game loop after a delay
continueGame();
}
// Stop the game loop
void stopGame() {
ticker.dispose();
}
// Continue the game loop with a delay
void continueGame() {
if (!isPaused) {
Future.delayed(const Duration(seconds: 1), () {
ticker = Ticker((elapsed) {
setState(() {
moveBall(ballSpeedMultiplier);
});
});
ticker.start();
});
}
}
void moveBall(double ballSpeedMultiplier) {
// Update ball position based on speed and direction
ballPositionX += ballSpeedX * ballSpeedMultiplier;
ballPositionY += ballSpeedY * ballSpeedMultiplier;
// Get the screen size
final Size screenSize = MediaQuery.of(context).size;
// Check for collisions with horizontal walls and bounce the ball
if (ballPositionX < 0 || ballPositionX > screenSize.width - ballSize) {
ballSpeedX = -ballSpeedX;
}
// Check for collision with the top wall and bounce the ball
if (ballPositionY < 0) {
ballSpeedY = -ballSpeedY;
}
2023-11-21 23:41:52 +01:00
2024-01-10 14:49:33 +01:00
// Check if the ball has touched the floor
var ballTouchesFloor = ballPositionY > screenSize.height - ballSize;
// Check if the ball collides with the racket
var ballCollidesWithRacket =
ballPositionY + ballSize >= screenSize.height - racketBottomOffset &&
ballPositionY <= screenSize.height - racketBottomOffset &&
ballPositionX + ballSize >= racketX &&
ballPositionX <= racketX + racketWidth;
// Handle collision with the racket
if (ballCollidesWithRacket) {
// Check if the ball hits the top of the racket
if (ballPositionY <= screenSize.height - racketBottomOffset) {
ballSpeedY =
-ballSpeedY * ballSpeedMultiplier; // Bounce the ball upward
setState(() {
score += 1; // Increase the score
debugPrint('ballSpeedMultiplier: $ballSpeedMultiplier');
});
ballPositionY = screenSize.height - racketBottomOffset - ballSize;
// Adjust the Y position to stay above the racket
} else {
ballSpeedX =
-ballSpeedX * ballSpeedMultiplier; // Bounce the ball backward
// Adjust the Y position to prevent the ball from going through the racket
double adjustedY = screenSize.height - racketBottomOffset - ballSize;
if (ballPositionY < adjustedY) {
ballPositionY = adjustedY;
}
}
// Handle game over condition if the ball touches the floor
} else if (ballTouchesFloor) {
debugPrint('Game over');
stopGame();
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return PongMenu(
title: 'Game over!',
subTitle: 'Deine Punkte: $score',
child: CupertinoButton(
child: const Text('Nochmal spielen'),
onPressed: () {
Navigator.of(context).pop();
startGame();
},
),
);
},
);
}
}
void moveRacket(double x) {
// Move the racket horizontally based on the user's touch position
2023-11-21 23:41:52 +01:00
setState(() {
racketX = x - racketWidth / 2;
});
}
2023-11-21 15:53:43 +01:00
@override
Widget build(BuildContext context) {
2023-11-21 23:41:52 +01:00
return Scaffold(
2024-01-10 14:49:33 +01:00
body: Stack(
children: [
GestureDetector(
// Listen for horizontal drag updates to move the racket
onHorizontalDragUpdate: (details) {
moveRacket(details.globalPosition.dx);
},
child: CustomPaint(
painter: PongGamePainter(
racketHeight: racketHeight,
racketWidth: racketWidth,
racketX: racketX,
racketBottomOffset: racketBottomOffset,
ballSize: ballSize,
ballX: ballPositionX,
ballY: ballPositionY,
),
size: Size.infinite,
),
),
Center(
// Display the player's score in the center of the screen
child: Text(
'Punkte: $score',
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
),
Positioned(
top: 40,
right: 20,
child: IconButton(
icon: const Icon(
CupertinoIcons.pause,
size: 30,
color: Colors.white,
),
onPressed: () {
// Pause the game and show dialog
stopGame();
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return PongMenu(
title: 'Pause',
subTitle: 'Deine Punkte: $score',
child: CupertinoButton(
onPressed: () {
// Continues the game
Navigator.of(context).pop();
isPaused = false;
continueGame();
},
child: const Text('Fortfahren'),
),
);
});
},
),
)
],
2023-11-21 23:41:52 +01:00
),
);
}
}
class PongGamePainter extends CustomPainter {
final double ballSize;
final double ballX;
final double ballY;
final double racketX;
final double racketWidth;
final double racketHeight;
final double racketBottomOffset;
2024-01-10 14:49:33 +01:00
// Constructor to initialize the painter with required properties
2023-11-21 23:41:52 +01:00
PongGamePainter({
required this.ballSize,
required this.ballX,
required this.ballY,
required this.racketX,
required this.racketWidth,
required this.racketHeight,
required this.racketBottomOffset,
});
@override
void paint(Canvas canvas, Size size) {
2024-01-10 14:49:33 +01:00
// Paint object for drawing the racket
2023-11-21 23:41:52 +01:00
final racketPaint = Paint()..color = Colors.white;
2024-01-10 14:49:33 +01:00
// Paint object for drawing the ball
2023-11-21 23:41:52 +01:00
final ballPaint = Paint()..color = Colors.white;
2024-01-10 14:49:33 +01:00
// Draw the ball as an oval on the canvas
2023-11-21 23:41:52 +01:00
canvas.drawOval(
Rect.fromLTWH(ballX, ballY, ballSize, ballSize),
ballPaint,
);
2024-01-10 14:49:33 +01:00
// Draw the racket as a rectangle on the canvas
2023-11-21 23:41:52 +01:00
canvas.drawRect(
Rect.fromLTWH(
racketX,
size.height - racketHeight - racketBottomOffset,
racketWidth,
racketHeight,
),
racketPaint,
);
}
@override
bool shouldRepaint(covariant PongGamePainter oldDelegate) {
2024-01-10 14:49:33 +01:00
// Repaint only if the ball or racket position changes
2023-11-21 23:41:52 +01:00
return ballX != oldDelegate.ballX ||
ballY != oldDelegate.ballY ||
racketX != oldDelegate.racketX;
2023-11-21 15:53:43 +01:00
}
}