Set Up a Flutter Project and Basic App Structure
Create a new Flutter project and open main.dart. Define a simple app structure with a main MyApp widget.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'BounceMaster 3D',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const BounceMasterGame(),
);
}
}
Create the Main Game Widget
Define a new BounceMasterGame widget and a corresponding BounceMasterGameState state. This widget will host the game.
class BounceMasterGame extends StatefulWidget {
const BounceMasterGame({super.key});
@override
BounceMasterGameState createState() => BounceMasterGameState();
}
class BounceMasterGameState extends State<BounceMasterGame> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('BounceMaster 3D')),
body: Container(color: Colors.black87), // Game area background
);
}
}
Define the Ball Class
Create a Ball class with properties for position, velocity, and constants for gravity and bounce. Include an update method to handle ball movement.
class Ball {
double x, y, z;
double velocityX, velocityY, velocityZ;
static const double gravity = 0.5;
static const double bounce = 0.7;
double bounceMultiplier = 1.0; // To increase bounce effect
Ball()
: x = 0.0,
y = 0.0,
z = 0.0,
velocityX = 2.0,
velocityY = 0.0,
velocityZ = 1.0;
void update() {
x += velocityX;
y += velocityY;
z += velocityZ;
velocityY -= gravity; // Apply gravity
if (x.abs() > 150) {
velocityX *= -bounce * bounceMultiplier;
x = x.sign * 150;
}
if (y < -200) {
velocityY *= -bounce * bounceMultiplier;
y = -200;
}
if (z.abs() > 100) {
velocityZ *= -bounce * bounceMultiplier;
z = z.sign * 100;
}
}
void increaseBounce() {
bounceMultiplier += 0.2; // Method to increase bounce
}
}
Add Game Control Logic
Update BounceMasterGameState to start and end the game using a timer. Each tick of the timer updates the ballβs position and increments the score.
class BounceMasterGameState extends State<BounceMasterGame> {
Ball? ball;
Timer? gameTimer;
int score = 0;
bool isGameRunning = false;
void startGame() {
if (!isGameRunning) {
setState(() {
ball = Ball();
score = 0;
isGameRunning = true;
});
gameTimer = Timer.periodic(const Duration(milliseconds: 16), (timer) {
setState(() {
ball!.update();
score += 1;
});
});
}
}
void endGame() {
gameTimer?.cancel();
setState(() {
isGameRunning = false;
ball = null;
});
}
void increaseBounce() {
setState(() {
ball?.increaseBounce();
});
}
@override
void dispose() {
gameTimer?.cancel();
super.dispose();
}
}
Add UI Controls
Add "Start Game", "End Game", and "Bounce" buttons in the BounceMasterGameState widget to control game actions.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BounceMaster 3D'),
actions: [
Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Score: $score',
style: const TextStyle(fontSize: 20),
),
),
),
],
),
body: Stack(
children: [
Container(color: Colors.black87), // Game area
Positioned(
bottom: 20,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: isGameRunning ? null : startGame, child: const Text('Start Game')),
ElevatedButton(onPressed: isGameRunning ? endGame : null, child: const Text('End Game')),
ElevatedButton(onPressed: isGameRunning ? increaseBounce : null, child: const Text('Bounce')),
],
),
),
],
),
);
}
Draw the Ball and Background
Create a GamePainter class to render the ball and grid. Update BounceMasterGameState to use this painter.
class GamePainter extends CustomPainter {
final Ball? ball;
GamePainter(this.ball);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white;
drawGrid(canvas, size);
if (ball != null) {
double perspective = (ball!.z + 200) / 300;
double ballSize = 20 * perspective;
paint.color = Colors.black54; // Shadow
canvas.drawCircle(Offset(size.width / 2 + ball!.x, size.height / 2 - 200), ballSize * 0.8, paint);
paint.color = Colors.blue; // Ball
canvas.drawCircle(Offset(size.width / 2 + ball!.x, size.height / 2 - ball!.y), ballSize, paint);
}
}
void drawGrid(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.grey.withOpacity(0.3);
for (double x = -200; x <= 200; x += 40) {
canvas.drawLine(Offset(size.width / 2 + x, 0), Offset(size.width / 2 + x, size.height), paint);
}
for (double y = 0; y <= size.height; y += 40) {
canvas.drawLine(Offset(size.width / 2 - 200, y), Offset(size.width / 2 + 200, y), paint);
}
}
@override
bool shouldRepaint(GamePainter oldDelegate) => true;
}
Update Game UI to Render the Ball
Add the GamePainter to the build method of BounceMasterGameState to display the ball.
body: Stack(
children: [
Container(
color: Colors.black87,
child: CustomPaint(
painter: GamePainter(ball),
size: Size.infinite,
),
),
// Other UI controls as defined earlier
],
),
Run the App and Test
Now, run the app. You should be able to see the ball moving and bouncing within the screen, and clicking the "Bounce" button will make the ball bounce with more intensity each time.
// Now, run the app.