278 lines
6.4 KiB
Dart
278 lines
6.4 KiB
Dart
|
import 'dart:async';
|
||
|
import 'dart:math';
|
||
|
|
||
|
import 'package:flutter/cupertino.dart';
|
||
|
import 'package:flutter/material.dart';
|
||
|
import 'package:flutter/scheduler.dart';
|
||
|
|
||
|
import 'package:pong/pong_menu.dart';
|
||
|
|
||
|
void main() {
|
||
|
runApp(const PongGame());
|
||
|
}
|
||
|
|
||
|
class PongGame extends StatelessWidget {
|
||
|
const PongGame({super.key});
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return MaterialApp(
|
||
|
title: 'Pong Game',
|
||
|
theme:
|
||
|
ThemeData(scaffoldBackgroundColor: Color.fromARGB(255, 41, 38, 38)),
|
||
|
home: const StartScreen(),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class StartScreen extends StatelessWidget {
|
||
|
const StartScreen({super.key});
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
body: GestureDetector(
|
||
|
onTap: () {
|
||
|
Navigator.of(context).push(MaterialPageRoute(
|
||
|
builder: (context) {
|
||
|
return const GameScreen();
|
||
|
},
|
||
|
));
|
||
|
},
|
||
|
child: const Center(
|
||
|
child: Text(
|
||
|
'Berühren um zu beginnen!',
|
||
|
style: TextStyle(
|
||
|
fontSize: 30,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
color: Colors.white,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class GameScreen extends StatefulWidget {
|
||
|
const GameScreen({super.key});
|
||
|
|
||
|
@override
|
||
|
State<GameScreen> createState() => _GameScreenState();
|
||
|
}
|
||
|
|
||
|
@visibleForTesting
|
||
|
class _GameScreenState extends State<GameScreen> {
|
||
|
final ballSize = 20.0;
|
||
|
final racketWidth = 240.0;
|
||
|
final racketHeight = 25.0;
|
||
|
final racketBottomOffset = 100.0;
|
||
|
|
||
|
final initialBallSpeed = 2.0;
|
||
|
|
||
|
double ballX = 0;
|
||
|
double ballY = 0;
|
||
|
double ballSpeedX = 0;
|
||
|
double ballSpeedY = 0;
|
||
|
double racketX = 20;
|
||
|
int score = 0;
|
||
|
|
||
|
late Ticker ticker;
|
||
|
final double ballSpeedMultiplier = 2;
|
||
|
|
||
|
@override
|
||
|
void initState() {
|
||
|
super.initState();
|
||
|
startGame();
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
void dispose() {
|
||
|
super.dispose();
|
||
|
stopGame();
|
||
|
}
|
||
|
|
||
|
void startGame() {
|
||
|
final random = Random();
|
||
|
ballX = 0;
|
||
|
ballY = 0;
|
||
|
ballSpeedX = initialBallSpeed;
|
||
|
ballSpeedY = initialBallSpeed;
|
||
|
racketX = 200;
|
||
|
score = 0;
|
||
|
|
||
|
if (random.nextBool()) ballSpeedX = -ballSpeedX;
|
||
|
if (random.nextBool()) ballSpeedY = -ballSpeedY;
|
||
|
|
||
|
Future.delayed(const Duration(seconds: 1), () {
|
||
|
ticker = Ticker((elapsed) {
|
||
|
setState(() {
|
||
|
moveBall(ballSpeedMultiplier);
|
||
|
});
|
||
|
});
|
||
|
ticker.start();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void stopGame() {
|
||
|
ticker.dispose();
|
||
|
}
|
||
|
|
||
|
void moveBall(double ballSPeedMultiplier) {
|
||
|
ballX += ballSpeedX * ballSpeedMultiplier;
|
||
|
ballY += ballSpeedY * ballSpeedMultiplier;
|
||
|
final Size size = MediaQuery.of(context).size;
|
||
|
|
||
|
if (ballX < 0 || ballX > size.width - ballSize) {
|
||
|
ballSpeedX = -ballSpeedX;
|
||
|
}
|
||
|
|
||
|
if (ballY < 0) {
|
||
|
ballSpeedY = -ballSpeedY;
|
||
|
}
|
||
|
|
||
|
// Was prüft diese if-Anweisung?
|
||
|
if (ballY > size.height - ballSize - racketHeight - racketBottomOffset &&
|
||
|
ballX >= racketX &&
|
||
|
ballX <= racketX + racketWidth) {
|
||
|
ballSpeedY = -ballSpeedY;
|
||
|
} else if (ballY > size.height - ballSize) {
|
||
|
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();
|
||
|
},
|
||
|
),
|
||
|
);
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
moveRacket(double x) {
|
||
|
setState(() {
|
||
|
racketX = x - racketWidth / 2;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
body: Stack(
|
||
|
children: [
|
||
|
GestureDetector(
|
||
|
onHorizontalDragUpdate: (details) {
|
||
|
moveRacket(details.globalPosition.dx);
|
||
|
},
|
||
|
child: CustomPaint(
|
||
|
painter: PongGamePainter(
|
||
|
racketHeight: racketHeight,
|
||
|
racketWidth: racketWidth,
|
||
|
racketX: racketX,
|
||
|
racketBottomOffset: racketBottomOffset,
|
||
|
ballSize: ballSize,
|
||
|
ballX: ballX,
|
||
|
ballY: ballY,
|
||
|
),
|
||
|
size: Size.infinite,
|
||
|
),
|
||
|
),
|
||
|
Center(
|
||
|
child: Text(
|
||
|
'Punkte: $score',
|
||
|
style: const TextStyle(
|
||
|
fontSize: 30,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
Positioned(
|
||
|
top: 20,
|
||
|
right: 20,
|
||
|
child: IconButton(
|
||
|
icon: const Icon(
|
||
|
CupertinoIcons.pause,
|
||
|
size: 30,
|
||
|
),
|
||
|
onPressed: () {
|
||
|
stopGame();
|
||
|
showDialog(
|
||
|
context: context,
|
||
|
builder: (context) {
|
||
|
return PongMenu(
|
||
|
title: 'Pause',
|
||
|
subTitle: 'Deine Punkte: $score',
|
||
|
child: CupertinoButton(
|
||
|
onPressed: () {},
|
||
|
child: const Text('Weiter'),
|
||
|
),
|
||
|
);
|
||
|
});
|
||
|
},
|
||
|
),
|
||
|
)
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
|
||
|
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) {
|
||
|
final racketPaint = Paint()..color = Colors.white;
|
||
|
final ballPaint = Paint()..color = Colors.white;
|
||
|
|
||
|
canvas.drawOval(
|
||
|
Rect.fromLTWH(ballX, ballY, ballSize, ballSize),
|
||
|
ballPaint,
|
||
|
);
|
||
|
|
||
|
canvas.drawRect(
|
||
|
Rect.fromLTWH(
|
||
|
racketX,
|
||
|
size.height - racketHeight - racketBottomOffset,
|
||
|
racketWidth,
|
||
|
racketHeight,
|
||
|
),
|
||
|
racketPaint,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
bool shouldRepaint(covariant PongGamePainter oldDelegate) {
|
||
|
return ballX != oldDelegate.ballX ||
|
||
|
ballY != oldDelegate.ballY ||
|
||
|
racketX != oldDelegate.racketX;
|
||
|
}
|
||
|
}
|