mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 10:38:17 +08:00
Moving tutorials to the Flame main repository (#720)
* Moving tutorials to the Flame main repository * Update README.md Co-authored-by: Jochum van der Ploeg <jochum@vdploeg.net> * Upgraded scripts to support multi projects * Removed comment out code * Adding WIP disclaimer Co-authored-by: Jochum van der Ploeg <jochum@vdploeg.net>
This commit is contained in:
159
tutorials/1_basic_square/README.md
Normal file
159
tutorials/1_basic_square/README.md
Normal file
@ -0,0 +1,159 @@
|
||||
# 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) and is based on the Flame `1.0.0-rc8` version.
|
||||
|
||||
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 see just 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 and 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 so we can always know how 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 reprent 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 ommited
|
||||
}
|
||||
```
|
||||
|
||||
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 mehod ommited
|
||||
|
||||
@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 cool, 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.
|
||||
// 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, direction variabables) 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(square_speed * squareDirection * dt, 0);
|
||||
|
||||
// This simple condition verifies if the square is going right, and has reached the end of the screen
|
||||
// if so, we just invert our 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 little square bouncing off 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
Normal file
50
tutorials/1_basic_square/code/.gitignore
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
# 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
|
||||
10
tutorials/1_basic_square/code/.metadata
Normal file
10
tutorials/1_basic_square/code/.metadata
Normal file
@ -0,0 +1,10 @@
|
||||
# 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
|
||||
3
tutorials/1_basic_square/code/README.md
Normal file
3
tutorials/1_basic_square/code/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# basic_square
|
||||
|
||||
Code for the Basic Square tutorial
|
||||
40
tutorials/1_basic_square/code/lib/main.dart
Normal file
40
tutorials/1_basic_square/code/lib/main.dart
Normal file
@ -0,0 +1,40 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
20
tutorials/1_basic_square/code/pubspec.yaml
Normal file
20
tutorials/1_basic_square/code/pubspec.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
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
|
||||
BIN
tutorials/1_basic_square/media/preview.gif
Normal file
BIN
tutorials/1_basic_square/media/preview.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
21
tutorials/LICENSE
Normal file
21
tutorials/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Flame Engine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
5
tutorials/README.md
Normal file
5
tutorials/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user