mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 11:43:19 +08:00
Game as a Component (#906)
* Game as a component * Fix component stories * Effects are now components * Update effects docs * Handle swap of parent * Fix reAddChildren * Wait for children to be added * BaseComponent and PositionComponent to be non-abstract * Simplify HasGameRef * Revert so that onLoad can be null * Fix example description * Effects as components * Remove gameRef from addChildren * Fix hasGameRef * Start migrating effects * Updated comments of effect fields * Fix comments * Continue to fix sequence and combined effects * Upgrade ordered_set * Fix position_component_test * BaseComponent -> Component * Fix combined and sequence effects * Await components to be added in tests * Remove unnecessary game.update in tests * Fix some tests related to composition * BaseGame should be used in examples * Fix CombinedEffect test * Keyboard code to be based on Component * Fix keyboard tests * Fix analyze problems * Fix sequence_effect * Fix combined_effect_test * Store peak state instead of end state * Fix sequence_effect tests * Update tutorial * Fix tutorial 1 * Remove SimplePositionComponentEffect * Remove unused test variable * Update docs * Removed onMount * Remove onMount * Add missing dartdoc * Fix dart docs * Add super.update where needed * Move reAddChildren to component * Reorganize method order in game widget * preOffset -> initialDelay, postOffset -> peakDelay * Introduce component.onParentChange * Remove tests in wrong file * Fix composed component test * Add game lifecycle test * Use BaseGame for mouse cursor test * Oxygen should (?) not call super.update * Use BaseGame in keyboard_test * Fix onLoad to be properly cached * Re-add unintentionally removed override * Fix info for collision detection tests * Add test for correct lifecycle on parent change * Fix particles example * Add component lifecycle diagram to the docs * Add docs for the game lifecycle * onRemove should be called when a game is removed from the widget * Fix analyze errors * prepare should be called from the component itself, not its parent * Fix dartdoc * onParentChange -> onMount * onMount should have void as return type * Simplify the loaderFuture in GameWidget * Fix mock_canvas * Fix rebase problem * Remove asComponent * Less complex _loaderFuture * Add super.update to no_fcs parallax example * Fix async tests * Revert _loaderFuture * Fix analyze issues * await gameWithCollidables * Keep epsilon small where it can be * tappable methods should return bool * Game lifecycle is now the same as for Component * Remove mustCallSuper from component.update * Make onLoadCache protected * @internal on onLoadCache * Cache/Memoize debugPaint and debugTextPaint * Fix imports * Fix comments * Always call super.onLoad so that mixins can override it * Add forgotten super.onLoad * Bump coverage percentage * HasCollidables should override update * Fix Game comments * Fix some dartdoc * Apply suggestions from code review Co-authored-by: Erick <erickzanardoo@gmail.com> * Game + Loadable as mixins * Update packages/flame/lib/src/game/game_widget/game_widget.dart Co-authored-by: Luan Nico <luanpotter27@gmail.com> * Update loadable docs * Fix comments * Move fps_counter * Fix keyboard example * Fix dartdoc * Remove tutorials temporarily * Fix game lowlevel graph * Fix resize issue Co-authored-by: Erick <erickzanardoo@gmail.com> Co-authored-by: Luan Nico <luanpotter27@gmail.com>
This commit is contained in:
@ -1,185 +0,0 @@
|
||||
# Basic: Rendering a simple square on the screen
|
||||
|
||||
This tutorial will introduce you to:
|
||||
|
||||
- `Game`: The basic class to build a game on, you will understand its structure and how to use it
|
||||
to build a simple game
|
||||
- `GameWidget`: The widget used to place your game on your Flutter widget tree
|
||||
- Basic rendering: You will get introduced to the basics of using the `Canvas` class from `dart:ui`
|
||||
which is the class that Flame uses for rendering your game.
|
||||
|
||||
All the code of this tutorial code can be found [here](./code).
|
||||
|
||||
By the end of this tutorial you will have built a simple game that renders a square bouncing on the
|
||||
screen that will look like this:
|
||||
|
||||

|
||||
|
||||
## Building your first game
|
||||
|
||||
`Game` is the most basic class that you can use to build your game, it includes the necessary
|
||||
methods for creating a basic game loop and methods for the lifecycle of a Flame game.
|
||||
|
||||
For more complex games, you will probably want to use `BaseGame`, which has a lot of utilities that
|
||||
will make your life easier. We will cover that on later tutorials, for this one the `Game` class
|
||||
will be enough for the concepts that you will learn here.
|
||||
|
||||
`GameWidget` is, like the name suggests, a Flutter widget that will run your game and place it
|
||||
inside the Flutter widget tree.
|
||||
|
||||
As a first step, lets just get a Game instance running. For that, we will need to create our own
|
||||
class that extends Flame's Game class, implement its methods, and pass an instance of that `Game` to
|
||||
a `GameWidget`. Something like this:
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flame/game.dart';
|
||||
|
||||
void main() {
|
||||
final myGame = MyGame();
|
||||
runApp(
|
||||
GameWidget(
|
||||
game: myGame,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MyGame extends Game {
|
||||
@override
|
||||
void update(double dt) { /* TODO */ }
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) { /* TODO */ }
|
||||
}
|
||||
```
|
||||
|
||||
That is it! If you run this, you will only see an empty black screen for now, but now we have the
|
||||
bare-bones needed to start implementing our game.
|
||||
|
||||
Before going further, it is important to explain what those two methods mean.
|
||||
|
||||
Flame's `Game` class is an implementation of a Game Loop, which the basic structure on which most
|
||||
games are built on. It is called a Game Loop because it really works as an infinite loop that will
|
||||
continue to iterate as long as the game is running. The loop goes through the following steps:
|
||||
|
||||
1. Take input from the player
|
||||
2. Process the logic for the next frame
|
||||
3. Render the frame.
|
||||
|
||||
In this tutorial we will focus on both step two and three.
|
||||
|
||||
To process the logic of the game, we use the `update` method, which always runs before the frame is
|
||||
rendered. Update receives a single argument, a `double` value called `dt` (delta time), which is the
|
||||
amount of seconds between the current iteration and the last one. This delta time is very important
|
||||
since we use it to correctly calculate the speed of movement, animations and etc.
|
||||
|
||||
To render the frame, we use the `render` method, this method receives a single argument which is an
|
||||
instance of a `dart:ui` `Canvas` class. With that instance we can basically render anything we want.
|
||||
It is important to not have any game logic in this method, it should only contain render
|
||||
instructions, any game logic should be put in the `update` method.
|
||||
|
||||
Now that we have a better understanding of how the game structure works, lets start to plan our
|
||||
game.
|
||||
|
||||
So, lets think on what variables and data structure we would need. For that, lets recap what we are
|
||||
building: A simple game where a square keeps bouncing forever from one side of the screen to the
|
||||
other. Thinking about this, we will need:
|
||||
|
||||
- A constant to tell us the speed of the square in logical pixels per second.
|
||||
- A variable to keep track of which direction the square is moving.
|
||||
- A structure to represent our square, which has a position and dimensions.
|
||||
|
||||
With that in mind, check the example below, note the comments for explanations on each code section.
|
||||
|
||||
|
||||
```dart
|
||||
class MyGame extends Game {
|
||||
// A constant speed, represented in logical pixels per second
|
||||
static const int squareSpeed = 400;
|
||||
|
||||
// To represent our square we are using the Rect class from dart:ui
|
||||
// which is a handy class to represent this type of data. We will be
|
||||
// seeing other types of data classes in the future, but for this
|
||||
// example, Rect will do fine for us.
|
||||
late Rect squarePos;
|
||||
|
||||
// To represent our direction, we will be using an int value, where 1 means
|
||||
// going to the right, and -1 going to the left, this may seems like a too much
|
||||
// simple way of representing a direction, and indeed it is, but this will
|
||||
// will work fine for our small example and will make more sense when we implement
|
||||
// the update method
|
||||
int squareDirection = 1;
|
||||
|
||||
// The onLoad method is where all of the game initialization is supposed to go
|
||||
// For this example, you may think that this square could just be initialized on the field
|
||||
// declaration, and you are right, but for learning purposes and to present the life cycle method
|
||||
// for this example we will be initializing this field here.
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
squarePos = Rect.fromLTWH(0, 0, 100, 100);
|
||||
}
|
||||
|
||||
// Update and render omitted
|
||||
}
|
||||
```
|
||||
|
||||
Right, now we have all the data and variables we need to start implementing our game. For the next
|
||||
step, lets draw our little square on the screen.
|
||||
|
||||
```dart
|
||||
class MyGame extends Game {
|
||||
// BasicPalette is a help class from Flame, which provides default, pre-built instances
|
||||
// of Paint that can be used by your game
|
||||
static final squarePaint = BasicPalette.white.paint();
|
||||
|
||||
// Update method omitted
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
// Canvas is a class from dart:ui and is it responsible for all the rendering inside of Flame
|
||||
canvas.drawRect(squarePos, squarePaint);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You may now be seeing a static white square being rendered on the top left corner of your screen,
|
||||
which is not that impressive, so, to finish our example, lets add some movement to the game and
|
||||
implement our update method:
|
||||
|
||||
```dart
|
||||
@override
|
||||
void update(double dt) {
|
||||
// Here we move our square by calculating our movement using
|
||||
// the iteration delta time and our speed variable and direction.
|
||||
// Note that the Rect class is immutable and the translate method returns a new Rect instance
|
||||
// for us, so we just re-assign it to our square variable.
|
||||
//
|
||||
// It is important to remember that the result of the execution of this method,
|
||||
// must be the game state (in our case our rect, speed and direction variables) updated to be
|
||||
// consistent of how it should be after the amount of time stored on the dt variable,
|
||||
// that way your game will always run smooth and consistent even when a FPS drop or peak happen.
|
||||
//
|
||||
// To illustrate this, if our square moves at 200 logical pixels per second, and half a second
|
||||
// has passed, our square should have moved 100 logical pixels on this iteration
|
||||
squarePos = squarePos.translate(squareSpeed * squareDirection * dt, 0);
|
||||
|
||||
// This simple condition verifies if the square is going right, and has reached the end of the
|
||||
// screen and if so, we invert the direction.
|
||||
//
|
||||
// Note here that we have used the variable size, which is a variable provided
|
||||
// by the Game class which contains the size in logical pixels that the game is currently using.
|
||||
if (squareDirection == 1 && squarePos.right > size.x) {
|
||||
squareDirection = -1;
|
||||
// This does the same, but now checking the left direction
|
||||
} else if (squareDirection == -1 && squarePos.left < 0) {
|
||||
squareDirection = 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If we run our game again, we should see our square bouncing back and forth like we wanted from the
|
||||
beginning.
|
||||
|
||||
And that is it for this basic tutorial, in which we have covered the basics of Flame, its basic
|
||||
classes and some basic rendering. From that we can start to build more complex things and more
|
||||
exciting games.
|
||||
50
tutorials/1_basic_square/code/.gitignore
vendored
50
tutorials/1_basic_square/code/.gitignore
vendored
@ -1,50 +0,0 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
android
|
||||
ios
|
||||
web
|
||||
@ -1,10 +0,0 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 044f2cf5607a26f8818dab0f766400e85c52bdff
|
||||
channel: beta
|
||||
|
||||
project_type: app
|
||||
@ -1,3 +0,0 @@
|
||||
# basic_square
|
||||
|
||||
Code for the Basic Square tutorial
|
||||
@ -1,40 +0,0 @@
|
||||
import 'package:flame/palette.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flame/game.dart';
|
||||
|
||||
void main() {
|
||||
final myGame = MyGame();
|
||||
runApp(
|
||||
GameWidget(
|
||||
game: myGame,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MyGame extends Game {
|
||||
static const int squareSpeed = 400;
|
||||
static final squarePaint = BasicPalette.white.paint();
|
||||
late Rect squarePos;
|
||||
int squareDirection = 1;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
squarePos = Rect.fromLTWH(0, 0, 100, 100);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
squarePos = squarePos.translate(squareSpeed * squareDirection * dt, 0);
|
||||
|
||||
if (squareDirection == 1 && squarePos.right > size.x) {
|
||||
squareDirection = -1;
|
||||
} else if (squareDirection == -1 && squarePos.left < 0) {
|
||||
squareDirection = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
canvas.drawRect(squarePos, squarePaint);
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
name: basic_square
|
||||
description: Example code for the the tutorial (Basic Rendering a simple square on the screen)
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flame:
|
||||
path: ../../../packages/flame
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
dart_code_metrics: ^2.4.0
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 122 KiB |
@ -1,230 +0,0 @@
|
||||
# Basic: Sprites, Animations and Gestures
|
||||
|
||||
This tutorial will introduce you to:
|
||||
|
||||
- `Sprite`: Sprites are how we draw images, or portions of an image in Flame.
|
||||
- `SpriteAnimation`: SpriteAnimations are animations composed from sprites, where each sprite represents a frame.
|
||||
- Gesture input: the most basic input for your game, the tap detector.
|
||||
|
||||
All the code of this tutorial code can be found [here](./code).
|
||||
|
||||
By the end of this tutorial, you will have built a simple game which renders a button that, when pressed, makes a small vampire robot run. It will look like this:
|
||||
|
||||

|
||||
|
||||
## Sprite
|
||||
|
||||
Before starting coding our game, it is important to understand what sprites are and what they are used for.
|
||||
|
||||
Sprites are images, or a portion of an image, loaded into the memory, and can then be used to render graphics on your game canvas.
|
||||
|
||||
Sprites can be (and usually are) bundled into single images, called Sprite Sheets. That is a very useful technique as it lowers the amount of I/O operations needed to load the game assets because it is faster to load 1 image of 10 KB than to load 10 images of 1 KB each (among other advantages).
|
||||
|
||||
For example, on this tutorial, we will have a button that makes our robot run. This button needs two sprites, for the unpressed and pressed states. We can have the following image containing both, this technique is often called sprite sheet.
|
||||
|
||||

|
||||
|
||||
|
||||
## Animations
|
||||
|
||||
Animation is what gives 2D games life. Flame provides a handy class called `SpriteAnimation` which lets you create an animation out of a list of sprites representing each frame, in sequence. Animations are usually bundled in a single Sprite Sheet, like this one:
|
||||
|
||||

|
||||
|
||||
Flame provides a way for easily turning that Sprite Sheet into an animation (we will see how in a few moments).
|
||||
|
||||
## Hands on
|
||||
|
||||
To get started, let's get a Flame `Game` instance running with a structure similar to our [first tutorial](https://github.com/flame-engine/flame/tree/main/tutorials/1_basic_square#building-your-first-game) (if you haven't yet, you can follow it to better understand this initial setup).
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
final myGame = MyGame();
|
||||
runApp(GameWidget(game: myGame));
|
||||
}
|
||||
|
||||
class MyGame extends Game {
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
}
|
||||
|
||||
@override
|
||||
Color backgroundColor() => const Color(0xFF222222);
|
||||
}
|
||||
```
|
||||
|
||||
Great! This will just gets us a plain, almost black screen. Now lets add our running robot:
|
||||
|
||||
```dart
|
||||
class MyGame extends Game {
|
||||
late SpriteAnimation runningRobot;
|
||||
|
||||
// Vector2 is a class from `package:vector_math/vector_math_64.dart` and is widely used
|
||||
// in Flame to represent vectors. Here we need two vectors, one to define where we are
|
||||
// going to draw our robot and another one to define its size
|
||||
final robotPosition = Vector2(240, 50);
|
||||
final robotSize = Vector2(48, 60);
|
||||
|
||||
// Now, on the `onLoad` method, we need to load our animation. To do that we can use the
|
||||
// `loadSpriteAnimation` method, which is present on our game class.
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
runningRobot = await loadSpriteAnimation(
|
||||
'robot.png',
|
||||
// `SpriteAnimationData` is a class used to tell Flame how the animation Sprite Sheet
|
||||
// is organized. In this case we are describing that our frames are laid out in a horizontal
|
||||
// sequence on the image, that there are 8 frames, that each frame is a sprite of 16x18 pixels,
|
||||
// and, finally, that each frame should appear for 0.1 seconds when the animation is running.
|
||||
SpriteAnimationData.sequenced(
|
||||
amount: 8,
|
||||
textureSize: Vector2(16, 18),
|
||||
stepTime: 0.1,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
// Here we just need to "hook" our animation into the game loop update method so the current frame is updated with the specified frequency
|
||||
runningRobot.update(dt);
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
// Since an animation is basically a list of sprites, to render it, we just need to get its
|
||||
// current sprite and render it on our canvas. Which frame is the current sprite is updated on the `update` method.
|
||||
runningRobot
|
||||
.getSprite()
|
||||
.render(canvas, position: robotPosition, size: robotSize);
|
||||
}
|
||||
|
||||
@override
|
||||
Color backgroundColor() => const Color(0xFF222222);
|
||||
}
|
||||
```
|
||||
|
||||
When running the game now, you should see our vampire robot running endlessly on the screen.
|
||||
|
||||
For the next step, let's implement our on/off button and render it on the screen.
|
||||
|
||||
The first thing we need to do is to add a couple of variables needed to reference our button:
|
||||
|
||||
```dart
|
||||
// One sprite for each button state
|
||||
late Sprite pressedButton;
|
||||
late Sprite unpressedButton;
|
||||
// Just like our robot needs its position and size, here we create two
|
||||
// variables for the button as well
|
||||
final buttonPosition = Vector2(200, 120);
|
||||
final buttonSize = Vector2(120, 30);
|
||||
// Simple boolean variable to tell if the button is pressed or not
|
||||
bool isPressed = false;
|
||||
```
|
||||
|
||||
Next, we can load our two sprites:
|
||||
|
||||
```dart
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
// runningRobot loading omitted
|
||||
|
||||
// Just like we have a `loadSpriteAnimation` function, here we can use
|
||||
// `loadSprite`. To use it, we just need to inform the asset's path
|
||||
// and the position and size defining the section of the whole image
|
||||
// that we want. If we wanted to have a sprite with the full image, `srcPosition`
|
||||
// and `srcSize` could just be omitted
|
||||
unpressedButton = await loadSprite(
|
||||
'buttons.png',
|
||||
// `srcPosition` and `srcSize` here tells `loadSprite` that we want
|
||||
// just a rect (starting at (0, 0) with the dimensions (60, 20)) of the image
|
||||
// which gives us only the first button
|
||||
srcPosition: Vector2.zero(), // this is zero by default
|
||||
srcSize: Vector2(60, 20),
|
||||
);
|
||||
|
||||
pressedButton = await loadSprite(
|
||||
'buttons.png',
|
||||
// Same thing here, but now a rect starting at (0, 20)
|
||||
// which gives us only the second button
|
||||
srcPosition: Vector2(0, 20),
|
||||
srcSize: Vector2(60, 20),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Finally, we just render it on the game `render` function:
|
||||
|
||||
```dart
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
// Running robot render omitted
|
||||
|
||||
final button = isPressed ? pressedButton : unpressedButton;
|
||||
button.render(canvas, position: buttonPosition, size: buttonSize);
|
||||
}
|
||||
```
|
||||
|
||||
You now should see the button on the screen, but right now, it is pretty much useless as it has no action at all.
|
||||
|
||||
So, to change that, we will now add some interactivity to our game and make the button tappable/clickable.
|
||||
|
||||
Flame provides several input handlers, about which you can check with more in depth on [our docs](https://github.com/flame-engine/flame/blob/main/doc/gesture-input.md).
|
||||
For this tutorial, we will be using the `TapDetector` which enables us to detect taps on the screen, as well as mouse click when running on web or desktop.
|
||||
|
||||
All Flame input detectors are mixins which can be added to your game, enabling you to override listener methods related to that detector. For the `TapDetector`, we will need to override three methods:
|
||||
|
||||
- `onTapDown`: Called when touch/click has started, i.e., the user just touced the screen or clicked the mouse button.
|
||||
- `onTapUp`: Called when the touch/click has stop occurring because the event was released, i.e., the user lifted the finger from the screen or released the mouse button.
|
||||
- `onTapCancel`: Called when the event was cancelled. This can happen for several reasons; one of the most common is when the event has changed into another type, for example the user started to move the finger/mouse and the touch event now turned into a pan/drag. Usually, we can just treat this event as being the same as `onTapUp`.
|
||||
|
||||
Now that we have a better understanding of `TapDetector` and the events that we will need to handle, let's implement it on the game:
|
||||
|
||||
```dart
|
||||
// We need to add our `TapDetector` mixin here
|
||||
class MyGame extends Game with TapDetector {
|
||||
// Variables declaration, onLoad and render methods omited...
|
||||
|
||||
@override
|
||||
void onTapDown(TapDownInfo event) {
|
||||
// On tap down we need to check if the event ocurred on the
|
||||
// button area. There are several ways of doing it, for this
|
||||
// tutorial we do that by transforming ours position and size
|
||||
// vectors into a dart:ui Rect by using the `&` operator, and
|
||||
// with that rect we can use its `contains` method which checks
|
||||
// if a point (Offset) is inside that rect
|
||||
final buttonArea = buttonPosition & buttonSize;
|
||||
|
||||
isPressed = buttonArea.contains(event.eventPosition.game.toOffset());
|
||||
}
|
||||
|
||||
// On both tap up and tap cancel we just set the isPressed
|
||||
// variable to false
|
||||
@override
|
||||
void onTapUp(TapUpInfo event) {
|
||||
isPressed = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void onTapCancel() {
|
||||
isPressed = false;
|
||||
}
|
||||
|
||||
// Finally, we just modify our update method so the animation is
|
||||
// updated only if the button is pressed
|
||||
@override
|
||||
void update(double dt) {
|
||||
if (isPressed) {
|
||||
runningRobot.update(dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If we run our game again, we should be able to see the complete example, with our on/off button for our little vampire robot.
|
||||
|
||||
And with that, we finished this tutorial. Now, with an understanding of sprites, animations and gestures, we can start on building more interactive and beautiful games.
|
||||
@ -1,50 +0,0 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
android
|
||||
ios
|
||||
web
|
||||
@ -1,10 +0,0 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 044f2cf5607a26f8818dab0f766400e85c52bdff
|
||||
channel: beta
|
||||
|
||||
project_type: app
|
||||
@ -1,3 +0,0 @@
|
||||
# basic_sprites_animations
|
||||
|
||||
Code for the Basic Sprites, Animations and Gestures
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 235 B |
Binary file not shown.
|
Before Width: | Height: | Size: 769 B |
@ -1,84 +0,0 @@
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flame/sprite.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() {
|
||||
final myGame = MyGame();
|
||||
runApp(GameWidget(game: myGame));
|
||||
}
|
||||
|
||||
class MyGame extends Game with TapDetector {
|
||||
late SpriteAnimation runningRobot;
|
||||
late Sprite pressedButton;
|
||||
late Sprite unpressedButton;
|
||||
|
||||
bool isPressed = false;
|
||||
|
||||
final buttonPosition = Vector2(200, 120);
|
||||
final buttonSize = Vector2(120, 30);
|
||||
|
||||
final robotPosition = Vector2(240, 50);
|
||||
final robotSize = Vector2(48, 60);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
runningRobot = await loadSpriteAnimation(
|
||||
'robot.png',
|
||||
SpriteAnimationData.sequenced(
|
||||
amount: 8,
|
||||
stepTime: 0.1,
|
||||
textureSize: Vector2(16, 18),
|
||||
),
|
||||
);
|
||||
|
||||
unpressedButton = await loadSprite(
|
||||
'buttons.png',
|
||||
srcPosition: Vector2.zero(),
|
||||
srcSize: Vector2(60, 20),
|
||||
);
|
||||
|
||||
pressedButton = await loadSprite(
|
||||
'buttons.png',
|
||||
srcPosition: Vector2(0, 20),
|
||||
srcSize: Vector2(60, 20),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onTapDown(TapDownInfo info) {
|
||||
final buttonArea = buttonPosition & buttonSize;
|
||||
|
||||
isPressed = buttonArea.contains(info.eventPosition.game.toOffset());
|
||||
}
|
||||
|
||||
@override
|
||||
void onTapUp(TapUpInfo info) {
|
||||
isPressed = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void onTapCancel() {
|
||||
isPressed = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
if (isPressed) {
|
||||
runningRobot.update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
runningRobot
|
||||
.getSprite()
|
||||
.render(canvas, position: robotPosition, size: robotSize);
|
||||
|
||||
final button = isPressed ? pressedButton : unpressedButton;
|
||||
button.render(canvas, position: buttonPosition, size: buttonSize);
|
||||
}
|
||||
|
||||
@override
|
||||
Color backgroundColor() => const Color(0xFF222222);
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
name: basic_sprites_animations
|
||||
description: Example code for the tutorial
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flame:
|
||||
path: ../../../packages/flame
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
dart_code_metrics: ^2.4.0
|
||||
|
||||
flutter:
|
||||
assets:
|
||||
- assets/images/
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 109 KiB |
@ -1,6 +0,0 @@
|
||||
# Flame tutorials
|
||||
|
||||
This repository includes a collection of official tutorials created and maintained by the Flame team. Use the index below to navigate through the available tutorials.
|
||||
|
||||
- [1. Basic: Rendering a simple square on the screen](./1_basic_square/README.md)
|
||||
- [2. Basic: Sprites, Animations and Gestures](./2_sprite_animations_gestures/README.md)
|
||||
Reference in New Issue
Block a user