Popular
- Get link
- X
- Other Apps
What is Flutter Flame?
Flame is a cross-platform game library for Flutter, enabling
developers to build simple and powerful mobile and web games. Built on top of
the Flutter framework, Flame provides an easy and efficient approach to
creating games with high performance across multiple devices.
Features of Flame
Sprites and Animation: Flame offers easy-to-use features for
handling sprites and animations. This allows developers to create visually
appealing characters and graphics effects for their games.
Simple Controls: Flame provides simple control tools such as
touch, swipe, and accelerometer support for interacting with games. This
ensures intuitive gameplay experiences for players.
High Performance: Flame is optimized for high performance on
mobile devices, delivering smooth frame rates and quick response times. This
ensures a seamless gaming experience even on lower-end devices.
Audio and Music Support: Flame provides APIs for loading and
playing sound and music files, making games more immersive and engaging.
Installation
Add the flame package as a dependency in
your pubspec.yaml by running the following command:
flutter pub add flame
GameWidget: GameWidget is a widget provided by Flame that allows you to integrate a Flame game into your Flutter application. It serves as the bridge between your Flutter UI and the Flame game engine.
FlameGame: This is the base class for creating games in Flame. It provides a framework for managing game loops, rendering graphics, handling input events, and managing game state.
Component: The Component class represents any object that can be rendered or updated in the game world. It is the base class for all game elements such as sprites, animations, and UI elements.
SpriteComponent: This class extends Component and represents a graphical sprite in the game world. It is used to display images, animations, or other graphical elements.
AnimationComponent: This class extends SpriteComponent and represents an animated sprite. It is used to display sprite animations created from a series of images.
TextComponent: This class extends Component and represents a text element in the game world. It is used to display text strings with customizable fonts, sizes, and colors.
CameraComponent: This class represents the camera in the game world. It is used to define the view area and position of the camera, and it can be used to implement scrolling and zooming effects.
InputComponent: This class extends Component and represents an input area in the game world. It is used to handle input events such as taps, swipes, and keyboard input.
In the code sample, the DodgeTheBoxes class is the core class of the game. It inherits from FlameGame, which is a class provided by the Flame library for creating games. In the onLoad method, the game initializes its camera, sets up the play area (PlayArea), and sets the initial state to welcome. It adds overlays for the welcome and game over screens. The playState property manages the state of the game. It can be welcome, playing, or gameOver. Depending on the state, different overlay screens are shown.
The DodgeTheBoxes class creates and manages various game elements such as the ball, ground, moving box, and trigger area. These elements are added to the game world.
Gameplay Logic: Methods like bounceTheBall and startGame handle gameplay logic such as starting the game, bouncing the ball, and updating the score.
The PlayArea, Ball, RectTrigger, Ground, MovingBox classes represent the graphical component in a game. They are used to create and manage shapes within the game world. They include collision detection functionality, allowing them to interact with other game elements. This enables developers to implement collision-based gameplay mechanics involving objects.
FlameGame: This is the base class for creating games in Flame. It provides a framework for managing game loops, rendering graphics, handling input events, and managing game state.
Component: The Component class represents any object that can be rendered or updated in the game world. It is the base class for all game elements such as sprites, animations, and UI elements.
SpriteComponent: This class extends Component and represents a graphical sprite in the game world. It is used to display images, animations, or other graphical elements.
AnimationComponent: This class extends SpriteComponent and represents an animated sprite. It is used to display sprite animations created from a series of images.
TextComponent: This class extends Component and represents a text element in the game world. It is used to display text strings with customizable fonts, sizes, and colors.
CameraComponent: This class represents the camera in the game world. It is used to define the view area and position of the camera, and it can be used to implement scrolling and zooming effects.
InputComponent: This class extends Component and represents an input area in the game world. It is used to handle input events such as taps, swipes, and keyboard input.
Example game by Flame
To run the code sample, please prepare an environment to work with Flutter by downloading Flutter SDK. It also need an IDE like Android Studio or Visual Studio Code for coding
Steps to run the sample:
1. Create a new Flutter project on Android Studio
2. Add Flame library by command line
flutter pub add flame
3. In main.dart file, copy below code and paste them in the file
const gameWidth = 820.0;
const gameHeight = 1600.0;
const ballRadius = gameWidth * 0.06;
const movingBoxSize = gameWidth * 0.12;
void main() {
runApp(const GameApp());
}
class GameApp extends StatefulWidget { // Modify this line
const GameApp({
super.key
});
@override // Add from here...
State < GameApp > createState() = > _GameAppState();
}
class _GameAppState extends State < GameApp > {
late final DodgeTheBoxes game;
@override
void initState() {
super.initState();
game = DodgeTheBoxes();
} // To here.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
textTheme: GoogleFonts.pressStart2pTextTheme().apply(
bodyColor: const Color(0xff184e77),
displayColor: const Color(0xff184e77), ), ),
home: Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xffa9d6e5),
Color(0xfff2e8cf), ], ), ),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Column( // Modify from here...
children: [
ScoreCard(score: game.score),
Expanded(
child: FittedBox(
child: SizedBox(
width: gameWidth,
height: gameHeight,
child: GameWidget(
game: game,
overlayBuilderMap: {
PlayState.welcome.name: (context, game) = > const OverlayScreen(
title: 'TAP TO PLAY',
subtitle: 'Tap to bounce', ),
PlayState.gameOver.name: (context, game) = > const OverlayScreen(
title: 'G A M E O V E R',
subtitle: 'Tap to Play Again', ),
}, ), ), ), ), ], ), // To here.
), ), ), ), ), );
}
}
class PlayArea extends RectangleComponent with HasGameReference < DodgeTheBoxes > {
PlayArea(): super(
paint: Paint()..color = const Color(0xfff2e8cf),
children: [RectangleHitbox()], // Add this parameter
);
@override
FutureOr < void > onLoad() async {
super.onLoad();
size = Vector2(game.width, game.height);
}
}
enum PlayState {
welcome, playing, gameOver
} // Add this enumeration
class DodgeTheBoxes extends FlameGame
with HasCollisionDetection, KeyboardEvents, TapDetector {
DodgeTheBoxes(): super(
camera: CameraComponent.withFixedResolution(
width: gameWidth,
height: gameHeight, ), );
final ValueNotifier < int > score = ValueNotifier(0); // Add this line
final rand = math.Random();
double get width = > size.x;
double get height = > size.y;
late PlayState _playState;
PlayState get playState = > _playState;
set playState(PlayState playState) {
_playState = playState;
switch (playState) {
case PlayState.welcome:
case PlayState.gameOver:
overlays.add(playState.name);
break;
case PlayState.playing:
overlays.remove(PlayState.welcome.name);
overlays.remove(PlayState.gameOver.name);
}
}
@override
FutureOr < void > onLoad() async {
super.onLoad();
camera.viewfinder.anchor = Anchor.topLeft;
world.add(PlayArea());
playState = PlayState.welcome;
}
void bounceTheBall() {
List < Ball > list = world.children.query < Ball > ();
if (list.isNotEmpty) {
list[0].bounce();
}
}
void startGame() {
if (playState == PlayState.playing) return;
world.removeAll(world.children.query < Ball > ());
world.removeAll(world.children.query < Ground > ());
world.removeAll(world.children.query < MovingBox > ());
world.removeAll(world.children.query < RectTrigger > ());
playState = PlayState.playing;
score.value = 0; // Add this line
world.add(Ball(Vector2(width / 3, height / 1.5 - ballRadius / 2)));
world.add(Ground(Vector2(0, height / 1.5), Vector2(width, 30)));
world.add(MovingBox(width, height / 1.5 - movingBoxSize));
world.add(RectTrigger(Vector2(width / 3 - ballRadius, 0), Vector2(10, height)));
}
@override
void onTap() {
super.onTap();
startGame();
bounceTheBall();
}
@override
Color backgroundColor() = > const Color(0xfff2e8cf);
}
class Ball extends CircleComponent with CollisionCallbacks {
bool isFalling = true;
double velocityY = 0;
final gravity = 600;
Ball(Vector2 position): super(
radius: ballRadius,
anchor: Anchor.center,
position: position,
paint: Paint()
..color = const Color(0xff1e6091)
..style = PaintingStyle.fill,
children: [RectangleHitbox()], );
void bounce() {
isFalling = true;
velocityY = -500;
}
@override
void update(double dt) {
super.update(dt);
// Falling by gravity
if (isFalling) {
velocityY += dt * gravity;
position.y += velocityY * dt;
}
}
@override
void onCollisionStart(Set < Vector2 > intersectionPoints, PositionComponent other) {
if (other is Ground) {
isFalling = false;
velocityY = 0;
position.y = other.position.y - size.y / 2;
}
super.onCollisionStart(intersectionPoints, other);
}
}
class RectTrigger extends RectangleComponent with CollisionCallbacks, HasGameReference < DodgeTheBoxes > {
RectTrigger(Vector2 position, Vector2 size): super(position: position, size: size,
anchor: Anchor.topRight,
paint: Paint()
..color = const Color(0x001e6091)
..style = PaintingStyle.fill,
children: [RectangleHitbox()], );
@override
void onCollisionStart(Set < Vector2 > intersectionPoints, PositionComponent other) {
game.score.value += 1;
super.onCollisionStart(intersectionPoints, other);
}
}
class Ground extends RectangleComponent with CollisionCallbacks {
Ground(Vector2 position, Vector2 size): super(
size: size,
anchor: Anchor.topLeft,
position: position,
paint: Paint()
..color = const Color(0xff163832)
..style = PaintingStyle.fill,
children: [RectangleHitbox()], );
}
class MovingBox extends RectangleComponent with CollisionCallbacks, HasGameReference < DodgeTheBoxes > {
double startX, groundY;
MovingBox(this.startX, this.groundY): super(
size: Vector2(movingBoxSize, movingBoxSize),
anchor: Anchor.topLeft,
position: Vector2(startX, groundY),
paint: Paint()
..color = const Color(0xfff44336)
..style = PaintingStyle.fill,
children: [RectangleHitbox()], );
@override
void update(double dt) {
super.update(dt);
position.x -= 400 * dt;
if (position.x + size.x < 0) {
position.x = startX;
if (math.Random().nextBool()) {
position.y = groundY;
} else {
position.y = groundY - movingBoxSize * 1.5;
}
}
}
@override
void onCollisionStart(Set < Vector2 > intersectionPoints, PositionComponent other) {
if (other is Ball) {
add(RemoveEffect(
delay: 0.35,
onComplete: () { // Modify from here
game.playState = PlayState.gameOver;
}));
}
super.onCollisionStart(intersectionPoints, other);
}
}
class ScoreCard extends StatelessWidget {
const ScoreCard({
super.key,
required this.score,
});
final ValueNotifier < int > score;
@override
Widget build(BuildContext context) {
return ValueListenableBuilder < int > (
valueListenable: score,
builder: (context, score, child) {
return Padding(
padding: const EdgeInsets.fromLTRB(12, 6, 12, 18),
child: Text(
'Score: $score'.toUpperCase(),
style: Theme.of(context).textTheme.titleLarge!, ), );
}, );
}
}
class OverlayScreen extends StatelessWidget {
const OverlayScreen({
super.key,
required this.title,
required this.subtitle,
});
final String title;
final String subtitle;
@override
Widget build(BuildContext context) {
return Container(
alignment: const Alignment(0, -0.15),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: Theme.of(context).textTheme.headlineLarge, ).animate().slideY(duration: 750.ms, begin: -3, end: 0),
const SizedBox(height: 16),
Text(
subtitle,
style: Theme.of(context).textTheme.headlineSmall, )
.animate(onPlay: (controller) = > controller.repeat())
.fadeIn(duration: 1.seconds)
.then()
.fadeOut(duration: 1.seconds), ], ), );
}
}
4. Run the project on Simulator or Real device
Explain some components in the code
class DodgeTheBoxes extends FlameGame
with HasCollisionDetection, KeyboardEvents, TapDetector {
...
}
The DodgeTheBoxes class creates and manages various game elements such as the ball, ground, moving box, and trigger area. These elements are added to the game world.
Gameplay Logic: Methods like bounceTheBall and startGame handle gameplay logic such as starting the game, bouncing the ball, and updating the score.
class PlayArea extends RectangleComponent with HasGameReference<DodgeTheBoxes> {
...
}
class Ball extends CircleComponent with CollisionCallbacks {
...
}
class RectTrigger extends RectangleComponent with CollisionCallbacks,
HasGameReference<DodgeTheBoxes> {
...
}
class Ground extends RectangleComponent with CollisionCallbacks {
Ground(Vector2 position, Vector2 size) : super(
...
}
class MovingBox extends RectangleComponent with CollisionCallbacks,
HasGameReference<DodgeTheBoxes> {
...
}
Future of Flame and Community Involvement
1. Upcoming Features and Improvements:
Flame is continuously evolving, with developers actively
working on new features and improvements to enhance its capabilities. Some of
the anticipated developments include:
Enhanced Graphics: Future versions of Flame may introduce
advanced graphics features, such as support for shaders, particle effects, and
dynamic lighting, to enable developers to create even more visually stunning
games.
Expanded Platform Support: While Flame already supports
multiple platforms, including mobile and web, efforts are underway to further
expand platform compatibility. This may involve optimizing Flame for emerging
platforms or integrating with additional technologies.
Performance Enhancements: Developers are constantly striving
to improve Flame's performance, making optimizations to reduce resource usage
and enhance overall efficiency. This ensures that games built with Flame
continue to run smoothly on a wide range of devices.
New Tools and Utilities: Future releases of Flame may
introduce new tools and utilities to streamline game development processes,
such as debugging tools, asset management systems, and integration with
external services.
2. Community Strengths:
The Flame community plays a crucial role in the ongoing
development and success of the library. Some key strengths of the community
include:
Active Collaboration: Developers within the Flame community
actively collaborate on projects, sharing code, ideas, and resources to help
each other overcome challenges and achieve their goals.
Supportive Ecosystem: The Flame community is known for its
supportive and inclusive nature, welcoming developers of all skill levels and
backgrounds. Whether you're a beginner seeking guidance or an experienced
developer sharing your expertise, there's a place for you in the Flame
community.
Feedback and Contribution: Community members provide
valuable feedback to the Flame development team, helping to identify bugs,
suggest new features, and shape the direction of the library. Additionally,
many developers contribute code, documentation, and tutorials to the Flame
ecosystem, enriching the experience for everyone.
Educational Resources: The Flame community produces a wealth
of educational resources, including tutorials, articles, videos, and sample
projects, to help newcomers get started with game development using Flame.
These resources play a crucial role in growing the community and empowering
aspiring game developers.
- Get link
- X
- Other Apps
Comments
Post a Comment