flutter-flame-games
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFlame Game Development
Flame游戏开发
Expert guidelines for building 2D games with Flutter and the Flame engine.
使用Flutter和Flame引擎构建2D游戏的专家指南。
Quick Start
快速开始
When starting a new Flame game or reviewing code:
- Game Class: Extend with appropriate mixins (
FlameGame,HasCollisionDetection)HasKeyboardHandlerComponents - Components: Build entities with ,
SpriteComponent, orPositionComponentSpriteAnimationComponent - Game Loop: Implement for logic and
update(double dt)for drawingrender(Canvas canvas) - Collision: Add mixin and define hitboxes (
CollisionCallbacks,CircleHitbox)RectangleHitbox - Input: Handle keyboard with , touch with
KeyboardHandler, joystick withTapCallbacksJoystickComponent
当启动新的Flame游戏或审查代码时:
- 游戏类:使用合适的mixin(、
HasCollisionDetection)扩展HasKeyboardHandlerComponentsFlameGame - 组件:使用、
SpriteComponent或PositionComponent构建实体SpriteAnimationComponent - 游戏循环:实现处理逻辑,
update(double dt)负责绘制render(Canvas canvas) - 碰撞检测:添加mixin并定义碰撞盒(
CollisionCallbacks、CircleHitbox)RectangleHitbox - 输入处理:使用处理键盘输入,
KeyboardHandler处理触摸输入,TapCallbacks处理摇杆输入JoystickComponent
Project Setup
项目设置
bash
flutter create my_game
cd my_game
flutter pub add flameFor audio support:
bash
flutter pub add flame_audioFor Tiled map support:
bash
flutter pub add flame_tiledbash
flutter create my_game
cd my_game
flutter pub add flame如需音频支持:
bash
flutter pub add flame_audio如需Tiled地图支持:
bash
flutter pub add flame_tiledAssets Structure
资源结构
assets/
├── images/
│ ├── player.png
│ ├── enemy_spritesheet.png
│ └── background.png
├── audio/
│ ├── bgm.mp3
│ └── sfx_jump.wav
└── tiles/
└── map.tmxRegister in :
pubspec.yamlyaml
flutter:
assets:
- assets/images/
- assets/audio/
- assets/tiles/assets/
├── images/
│ ├── player.png
│ ├── enemy_spritesheet.png
│ └── background.png
├── audio/
│ ├── bgm.mp3
│ └── sfx_jump.wav
└── tiles/
└── map.tmx在中注册:
pubspec.yamlyaml
flutter:
assets:
- assets/images/
- assets/audio/
- assets/tiles/Core Architecture
核心架构
Game Class Selection
游戏类选择
FlameGame (Recommended for most games):
dart
class MyGame extends FlameGame with HasCollisionDetection {
Future<void> onLoad() async {
await add(Player());
}
}Low-level Game (For custom implementations):
dart
class MyGame extends Game {
void render(Canvas canvas) { /* Custom rendering */ }
void update(double dt) { /* Custom logic */ }
}FlameGame(大多数游戏推荐使用):
dart
class MyGame extends FlameGame with HasCollisionDetection {
Future<void> onLoad() async {
await add(Player());
}
}底层Game类(用于自定义实现):
dart
class MyGame extends Game {
void render(Canvas canvas) { /* 自定义绘制 */ }
void update(double dt) { /* 自定义逻辑 */ }
}Essential Mixins
必备Mixin
| Mixin | Purpose |
|---|---|
| Enables collision system |
| Allows keyboard input handling |
| Provides access to game instance |
| Mixin | 用途 |
|---|---|
| 启用碰撞系统 |
| 支持键盘输入处理 |
| 提供对游戏实例的访问 |
Component Hierarchy
组件层级
Component
├── PositionComponent
│ ├── SpriteComponent
│ ├── SpriteAnimationComponent
│ ├── TextComponent
│ └── ShapeComponent
├── ParticleSystemComponent
└── ParallaxComponentComponent
├── PositionComponent
│ ├── SpriteComponent
│ ├── SpriteAnimationComponent
│ ├── TextComponent
│ └── ShapeComponent
├── ParticleSystemComponent
└── ParallaxComponentComponent Lifecycle
组件生命周期
dart
class MyComponent extends PositionComponent {
Future<void> onLoad() async {
// Called once when added to game
// Load assets, initialize state
}
void update(double dt) {
// Called every frame
// dt = delta time in seconds
}
void render(Canvas canvas) {
// Called every frame for drawing
}
void onRemove() {
// Cleanup before removal
}
}dart
class MyComponent extends PositionComponent {
Future<void> onLoad() async {
// 添加到游戏时调用一次
// 加载资源,初始化状态
}
void update(double dt) {
// 每帧调用
// dt = 以秒为单位的增量时间
}
void render(Canvas canvas) {
// 每帧调用用于绘制
}
void onRemove() {
// 移除前清理资源
}
}Working with Sprites
精灵使用
Static Sprite
静态精灵
dart
class Player extends SpriteComponent {
Player() : super(size: Vector2(64, 64), position: Vector2(100, 100));
Future<void> onLoad() async {
sprite = await Sprite.load('player.png');
}
}dart
class Player extends SpriteComponent {
Player() : super(size: Vector2(64, 64), position: Vector2(100, 100));
Future<void> onLoad() async {
sprite = await Sprite.load('player.png');
}
}Sprite from SpriteSheet
从精灵表创建精灵
dart
class Enemy extends SpriteComponent {
Future<void> onLoad() async {
final spriteSheet = await SpriteSheet.load(
'enemies.png',
srcSize: Vector2(32, 32),
);
sprite = spriteSheet.getSprite(0, 0); // row, column
}
}dart
class Enemy extends SpriteComponent {
Future<void> onLoad() async {
final spriteSheet = await SpriteSheet.load(
'enemies.png',
srcSize: Vector2(32, 32),
);
sprite = spriteSheet.getSprite(0, 0); // 行, 列
}
}PositionComponent (Custom Drawing)
PositionComponent(自定义绘制)
dart
class Circle extends PositionComponent {
final Paint paint = Paint()..color = Colors.red;
Circle() : super(size: Vector2.all(50));
void render(Canvas canvas) {
canvas.drawCircle(
size.toOffset() / 2,
size.x / 2,
paint,
);
}
}dart
class Circle extends PositionComponent {
final Paint paint = Paint()..color = Colors.red;
Circle() : super(size: Vector2.all(50));
void render(Canvas canvas) {
canvas.drawCircle(
size.toOffset() / 2,
size.x / 2,
paint,
);
}
}Sprite Animation
精灵动画
Basic Animation
基础动画
dart
class AnimatedPlayer extends SpriteAnimationComponent {
AnimatedPlayer() : super(size: Vector2(64, 64));
Future<void> onLoad() async {
final spriteSheet = await SpriteSheet.load(
'player_run.png',
srcSize: Vector2(64, 64),
);
animation = spriteSheet.createAnimation(
row: 0,
stepTime: 0.1, // seconds per frame
to: 8, // number of frames
);
}
}dart
class AnimatedPlayer extends SpriteAnimationComponent {
AnimatedPlayer() : super(size: Vector2(64, 64));
Future<void> onLoad() async {
final spriteSheet = await SpriteSheet.load(
'player_run.png',
srcSize: Vector2(64, 64),
);
animation = spriteSheet.createAnimation(
row: 0,
stepTime: 0.1, // 每帧秒数
to: 8, // 帧数
);
}
}Multiple Animations
多动画切换
dart
class Character extends SpriteAnimationComponent with HasGameReference {
late final SpriteAnimation runAnimation;
late final SpriteAnimation idleAnimation;
late final SpriteAnimation jumpAnimation;
Future<void> onLoad() async {
final spritesheet = await game.images.load('character.png');
runAnimation = _createAnimation(spritesheet, row: 0, frames: 8);
idleAnimation = _createAnimation(spritesheet, row: 1, frames: 4);
jumpAnimation = _createAnimation(spritesheet, row: 2, frames: 6);
animation = idleAnimation;
}
SpriteAnimation _createAnimation(
Image image, {
required int row,
required int frames,
}) {
return SpriteAnimation.fromFrameData(
image,
SpriteAnimationData.sequenced(
amount: frames,
amountPerRow: frames,
textureSize: Vector2(64, 64),
texturePosition: Vector2(0, row * 64),
stepTime: 0.1,
),
);
}
void run() => animation = runAnimation;
void idle() => animation = idleAnimation;
void jump() => animation = jumpAnimation;
}dart
class Character extends SpriteAnimationComponent with HasGameReference {
late final SpriteAnimation runAnimation;
late final SpriteAnimation idleAnimation;
late final SpriteAnimation jumpAnimation;
Future<void> onLoad() async {
final spritesheet = await game.images.load('character.png');
runAnimation = _createAnimation(spritesheet, row: 0, frames: 8);
idleAnimation = _createAnimation(spritesheet, row: 1, frames: 4);
jumpAnimation = _createAnimation(spritesheet, row: 2, frames: 6);
animation = idleAnimation;
}
SpriteAnimation _createAnimation(
Image image, {
required int row,
required int frames,
}) {
return SpriteAnimation.fromFrameData(
image,
SpriteAnimationData.sequenced(
amount: frames,
amountPerRow: frames,
textureSize: Vector2(64, 64),
texturePosition: Vector2(0, row * 64),
stepTime: 0.1,
),
);
}
void run() => animation = runAnimation;
void idle() => animation = idleAnimation;
void jump() => animation = jumpAnimation;
}Collision Detection
碰撞检测
Basic Setup
基础设置
dart
// Game level
class MyGame extends FlameGame with HasCollisionDetection {
Future<void> onLoad() async {
add(Player());
add(Enemy());
add(ScreenHitbox()); // Boundary collision
}
}
// Component level
class Player extends PositionComponent with CollisionCallbacks {
Future<void> onLoad() async {
add(CircleHitbox(radius: 20));
// OR
add(RectangleHitbox(size: Vector2(40, 40)));
// OR
add(PolygonHitbox([
Vector2(0, 0),
Vector2(20, 10),
Vector2(0, 20),
]));
}
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
if (other is Enemy) {
takeDamage();
}
}
void onCollisionStart(
Set<Vector2> intersectionPoints,
PositionComponent other,
) {
// Called once when collision begins
}
void onCollisionEnd(PositionComponent other) {
// Called once when collision ends
}
}dart
// 游戏层级
class MyGame extends FlameGame with HasCollisionDetection {
Future<void> onLoad() async {
add(Player());
add(Enemy());
add(ScreenHitbox()); // 边界碰撞
}
}
// 组件层级
class Player extends PositionComponent with CollisionCallbacks {
Future<void> onLoad() async {
add(CircleHitbox(radius: 20));
// 或者
add(RectangleHitbox(size: Vector2(40, 40)));
// 或者
add(PolygonHitbox([
Vector2(0, 0),
Vector2(20, 10),
Vector2(0, 20),
]));
}
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
if (other is Enemy) {
takeDamage();
}
}
void onCollisionStart(
Set<Vector2> intersectionPoints,
PositionComponent other,
) {
// 碰撞开始时调用一次
}
void onCollisionEnd(PositionComponent other) {
// 碰撞结束时调用一次
}
}Collision Types
碰撞类型
dart
// Customize collision behavior
class MyHitbox extends CircleHitbox {
MyHitbox() : super(radius: 20) {
collisionType = CollisionType.passive; // Won't trigger collisions actively
// Options: active, passive, inactive
}
}dart
// 自定义碰撞行为
class MyHitbox extends CircleHitbox {
MyHitbox() : super(radius: 20) {
collisionType = CollisionType.passive; // 不会主动触发碰撞
// 选项:active, passive, inactive
}
}Input Handling
输入处理
Keyboard Input
键盘输入
dart
class Player extends SpriteComponent with KeyboardHandler {
Vector2 velocity = Vector2.zero();
double speed = 200;
bool onKeyEvent(KeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
velocity = Vector2.zero();
if (keysPressed.contains(LogicalKeyboardKey.keyA)) {
velocity.x = -1;
}
if (keysPressed.contains(LogicalKeyboardKey.keyD)) {
velocity.x = 1;
}
if (keysPressed.contains(LogicalKeyboardKey.keyW)) {
velocity.y = -1;
}
if (keysPressed.contains(LogicalKeyboardKey.keyS)) {
velocity.y = 1;
}
return true;
}
void update(double dt) {
position += velocity.normalized() * speed * dt;
}
}dart
class Player extends SpriteComponent with KeyboardHandler {
Vector2 velocity = Vector2.zero();
double speed = 200;
bool onKeyEvent(KeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
velocity = Vector2.zero();
if (keysPressed.contains(LogicalKeyboardKey.keyA)) {
velocity.x = -1;
}
if (keysPressed.contains(LogicalKeyboardKey.keyD)) {
velocity.x = 1;
}
if (keysPressed.contains(LogicalKeyboardKey.keyW)) {
velocity.y = -1;
}
if (keysPressed.contains(LogicalKeyboardKey.keyS)) {
velocity.y = 1;
}
return true;
}
void update(double dt) {
position += velocity.normalized() * speed * dt;
}
}Touch/Tap Input
触摸/点击输入
dart
class Button extends SpriteComponent with TapCallbacks {
void onTapDown(TapDownEvent event) {
scale = Vector2.all(0.9); // Visual feedback
}
void onTapUp(TapUpEvent event) {
scale = Vector2.all(1.0);
onPressed();
}
void onTapCancel(TapCancelEvent event) {
scale = Vector2.all(1.0);
}
void onPressed() {
// Handle button press
}
}dart
class Button extends SpriteComponent with TapCallbacks {
void onTapDown(TapDownEvent event) {
scale = Vector2.all(0.9); // 视觉反馈
}
void onTapUp(TapUpEvent event) {
scale = Vector2.all(1.0);
onPressed();
}
void onTapCancel(TapCancelEvent event) {
scale = Vector2.all(1.0);
}
void onPressed() {
// 处理按钮点击
}
}Drag Input
拖拽输入
dart
class DraggableObject extends PositionComponent with DragCallbacks {
Vector2? dragStartPosition;
Vector2? objectStartPosition;
void onDragStart(DragStartEvent event) {
dragStartPosition = event.localPosition;
objectStartPosition = position.clone();
}
void onDragUpdate(DragUpdateEvent event) {
if (dragStartPosition != null && objectStartPosition != null) {
position = objectStartPosition! + event.localPosition - dragStartPosition!;
}
}
void onDragEnd(DragEndEvent event) {
dragStartPosition = null;
objectStartPosition = null;
}
}dart
class DraggableObject extends PositionComponent with DragCallbacks {
Vector2? dragStartPosition;
Vector2? objectStartPosition;
void onDragStart(DragStartEvent event) {
dragStartPosition = event.localPosition;
objectStartPosition = position.clone();
}
void onDragUpdate(DragUpdateEvent event) {
if (dragStartPosition != null && objectStartPosition != null) {
position = objectStartPosition! + event.localPosition - dragStartPosition!;
}
}
void onDragEnd(DragEndEvent event) {
dragStartPosition = null;
objectStartPosition = null;
}
}Camera System
相机系统
Camera Following Player
相机跟随玩家
dart
class MyGame extends FlameGame with HasCollisionDetection {
late final Player player;
Future<void> onLoad() async {
final world = World();
player = Player();
await world.add(player);
await add(world);
camera = CameraComponent.withFixedResolution(
world: world,
width: 800,
height: 600,
);
await add(camera);
camera.follow(player);
}
}dart
class MyGame extends FlameGame with HasCollisionDetection {
late final Player player;
Future<void> onLoad() async {
final world = World();
player = Player();
await world.add(player);
await add(world);
camera = CameraComponent.withFixedResolution(
world: world,
width: 800,
height: 600,
);
await add(camera);
camera.follow(player);
}
}Camera with Bounds
带边界的相机
dart
camera.follow(
player,
maxSpeed: 300,
snap: false, // Smooth following
);
// Set world bounds
camera.setBounds(
Rectangle.fromLTRB(0, 0, mapWidth, mapHeight),
);dart
camera.follow(
player,
maxSpeed: 300,
snap: false, // 平滑跟随
);
// 设置世界边界
camera.setBounds(
Rectangle.fromLTRB(0, 0, mapWidth, mapHeight),
);Zoom
缩放
dart
// Zoom in
camera.viewfinder.zoom = 2.0;
// Animated zoom
await camera.viewfinder.zoomTo(2.0, speed: 2.0);dart
// 放大
camera.viewfinder.zoom = 2.0;
// 动画缩放
await camera.viewfinder.zoomTo(2.0, speed: 2.0);Tiled Maps
Tiled地图
Loading Tiled Maps
加载Tiled地图
dart
class MyGame extends FlameGame {
Future<void> onLoad() async {
final mapComponent = await TiledComponent.load(
'map.tmx',
Vector2(32, 32), // tile size
);
await add(mapComponent);
// Access object layer
final objectLayer = mapComponent.tileMap.getLayer<ObjectGroup>('objects');
for (final object in objectLayer?.objects ?? []) {
if (object.name == 'player_spawn') {
add(Player(position: Vector2(object.x, object.y)));
}
}
}
}dart
class MyGame extends FlameGame {
Future<void> onLoad() async {
final mapComponent = await TiledComponent.load(
'map.tmx',
Vector2(32, 32), // 瓦片大小
);
await add(mapComponent);
// 访问对象层
final objectLayer = mapComponent.tileMap.getLayer<ObjectGroup>('objects');
for (final object in objectLayer?.objects ?? []) {
if (object.name == 'player_spawn') {
add(Player(position: Vector2(object.x, object.y)));
}
}
}
}Tile Collisions
瓦片碰撞
dart
// In Tiled editor, add custom property "collision" = true to tiles
// Then in code:
Future<void> onLoad() async {
final map = await TiledComponent.load('map.tmx', Vector2(32, 32));
await add(map);
// Generate collision blocks from tile layer
final collisionLayer = map.tileMap.getLayer<TileLayer>('ground');
final collisionBlocks = <PositionComponent>[];
for (var y = 0; y < collisionLayer!.height; y++) {
for (var x = 0; x < collisionLayer.width; x++) {
final tile = collisionLayer.tileData![y][x];
if (tile.tile > 0) {
collisionBlocks.add(
PositionComponent(
position: Vector2(x * 32.0, y * 32.0),
size: Vector2.all(32),
)..add(RectangleHitbox()),
);
}
}
}
addAll(collisionBlocks);
}dart
// 在Tiled编辑器中,为瓦片添加自定义属性 "collision" = true
// 然后在代码中:
Future<void> onLoad() async {
final map = await TiledComponent.load('map.tmx', Vector2(32, 32));
await add(map);
// 从瓦片层生成碰撞块
final collisionLayer = map.tileMap.getLayer<TileLayer>('ground');
final collisionBlocks = <PositionComponent>[];
for (var y = 0; y < collisionLayer!.height; y++) {
for (var x = 0; x < collisionLayer.width; x++) {
final tile = collisionLayer.tileData![y][x];
if (tile.tile > 0) {
collisionBlocks.add(
PositionComponent(
position: Vector2(x * 32.0, y * 32.0),
size: Vector2.all(32),
)..add(RectangleHitbox()),
);
}
}
}
addAll(collisionBlocks);
}Audio
音频
Background Music
背景音乐
dart
import 'package:flame_audio/flame_audio.dart';
class MyGame extends FlameGame {
Future<void> onLoad() async {
await FlameAudio.audioCache.load('bgm.mp3');
FlameAudio.bgm.initialize();
FlameAudio.bgm.play('bgm.mp3', volume: 0.5);
}
void onRemove() {
FlameAudio.bgm.dispose();
super.onRemove();
}
}dart
import 'package:flame_audio/flame_audio.dart';
class MyGame extends FlameGame {
Future<void> onLoad() async {
await FlameAudio.audioCache.load('bgm.mp3');
FlameAudio.bgm.initialize();
FlameAudio.bgm.play('bgm.mp3', volume: 0.5);
}
void onRemove() {
FlameAudio.bgm.dispose();
super.onRemove();
}
}Sound Effects
音效
dart
class Player extends PositionComponent {
void jump() {
FlameAudio.play('jump.wav', volume: 0.8);
velocity.y = -300;
}
void collectCoin() {
FlameAudio.play('coin.wav');
}
}dart
class Player extends PositionComponent {
void jump() {
FlameAudio.play('jump.wav', volume: 0.8);
velocity.y = -300;
}
void collectCoin() {
FlameAudio.play('coin.wav');
}
}Particle Effects
粒子效果
Basic Particle
基础粒子
dart
import 'package:flame/particles.dart';
class Explosion extends ParticleSystemComponent {
Explosion({required Vector2 position})
: super(
position: position,
particle: Particle.generate(
count: 20,
lifespan: 0.5,
generator: (i) => AcceleratedParticle(
acceleration: Vector2.random() * 100,
speed: Vector2.random() * 200,
child: CircleParticle(
radius: 4,
paint: Paint()..color = Colors.orange,
),
),
),
);
}dart
import 'package:flame/particles.dart';
class Explosion extends ParticleSystemComponent {
Explosion({required Vector2 position})
: super(
position: position,
particle: Particle.generate(
count: 20,
lifespan: 0.5,
generator: (i) => AcceleratedParticle(
acceleration: Vector2.random() * 100,
speed: Vector2.random() * 200,
child: CircleParticle(
radius: 4,
paint: Paint()..color = Colors.orange,
),
),
),
);
}Computed Particle (Custom)
自定义计算粒子
dart
Particle.generate(
count: 12,
lifespan: 1.0,
generator: (i) {
final angle = (i / 12) * 2 * pi;
return ComputedParticle(
renderer: (canvas, particle) {
final progress = particle.progress;
final radius = 10 * (1 - progress);
final alpha = (1 - progress) * 255;
canvas.drawCircle(
Offset(cos(angle) * 50 * progress, sin(angle) * 50 * progress),
radius,
Paint()..color = Colors.red.withAlpha(alpha.toInt()),
);
},
);
},
)dart
Particle.generate(
count: 12,
lifespan: 1.0,
generator: (i) {
final angle = (i / 12) * 2 * pi;
return ComputedParticle(
renderer: (canvas, particle) {
final progress = particle.progress;
final radius = 10 * (1 - progress);
final alpha = (1 - progress) * 255;
canvas.drawCircle(
Offset(cos(angle) * 50 * progress, sin(angle) * 50 * progress),
radius,
Paint()..color = Colors.red.withAlpha(alpha.toInt()),
);
},
);
},
)UI Overlays
UI覆盖层
Setup
设置
dart
void main() {
runApp(
GameWidget(
game: MyGame(),
overlayBuilderMap: {
'PauseMenu': (context, game) => PauseMenuOverlay(),
'HUD': (context, game) => HudOverlay(game: game as MyGame),
},
initialActiveOverlays: const ['HUD'],
),
);
}dart
void main() {
runApp(
GameWidget(
game: MyGame(),
overlayBuilderMap: {
'PauseMenu': (context, game) => PauseMenuOverlay(),
'HUD': (context, game) => HudOverlay(game: game as MyGame),
},
initialActiveOverlays: const ['HUD'],
),
);
}Show/Hide Overlays
显示/隐藏覆盖层
dart
class MyGame extends FlameGame {
void showPauseMenu() {
overlays.add('PauseMenu');
pauseEngine();
}
void resumeGame() {
overlays.remove('PauseMenu');
resumeEngine();
}
}dart
class MyGame extends FlameGame {
void showPauseMenu() {
overlays.add('PauseMenu');
pauseEngine();
}
void resumeGame() {
overlays.remove('PauseMenu');
resumeEngine();
}
}HUD Example
HUD示例
dart
class HudOverlay extends StatelessWidget {
final MyGame game;
const HudOverlay({required this.game});
Widget build(BuildContext context) {
return ValueListenableBuilder<int>(
valueListenable: game.score,
builder: (context, score, child) {
return Positioned(
top: 20,
left: 20,
child: Text(
'Score: $score',
style: TextStyle(fontSize: 24, color: Colors.white),
),
);
},
);
}
}dart
class HudOverlay extends StatelessWidget {
final MyGame game;
const HudOverlay({required this.game});
Widget build(BuildContext context) {
return ValueListenableBuilder<int>(
valueListenable: game.score,
builder: (context, score, child) {
return Positioned(
top: 20,
left: 20,
child: Text(
'分数: $score',
style: TextStyle(fontSize: 24, color: Colors.white),
),
);
},
);
}
}Parallax Backgrounds
视差背景
dart
class MyGame extends FlameGame {
Future<void> onLoad() async {
final parallax = await loadParallaxComponent(
[
ParallaxImageData('bg_layer1.png'),
ParallaxImageData('bg_layer2.png'),
ParallaxImageData('bg_layer3.png'),
],
baseVelocity: Vector2(50, 0),
velocityMultiplierDelta: Vector2(1.5, 0),
);
add(parallax);
}
}dart
class MyGame extends FlameGame {
Future<void> onLoad() async {
final parallax = await loadParallaxComponent(
[
ParallaxImageData('bg_layer1.png'),
ParallaxImageData('bg_layer2.png'),
ParallaxImageData('bg_layer3.png'),
],
baseVelocity: Vector2(50, 0),
velocityMultiplierDelta: Vector2(1.5, 0),
);
add(parallax);
}
}Joystick Component
摇杆组件
dart
class MyGame extends FlameGame {
late final JoystickComponent joystick;
late final Player player;
Future<void> onLoad() async {
player = Player();
await add(player);
joystick = JoystickComponent(
knob: CircleComponent(
radius: 20,
paint: Paint()..color = Colors.red.withAlpha(200),
),
background: CircleComponent(
radius: 50,
paint: Paint()..color = Colors.black.withAlpha(100),
),
margin: const EdgeInsets.only(left: 40, bottom: 40),
);
await add(joystick);
}
void update(double dt) {
if (joystick.direction != JoystickDirection.idle) {
player.velocity = joystick.delta * 5;
}
super.update(dt);
}
}dart
class MyGame extends FlameGame {
late final JoystickComponent joystick;
late final Player player;
Future<void> onLoad() async {
player = Player();
await add(player);
joystick = JoystickComponent(
knob: CircleComponent(
radius: 20,
paint: Paint()..color = Colors.red.withAlpha(200),
),
background: CircleComponent(
radius: 50,
paint: Paint()..color = Colors.black.withAlpha(100),
),
margin: const EdgeInsets.only(left: 40, bottom: 40),
);
await add(joystick);
}
void update(double dt) {
if (joystick.direction != JoystickDirection.idle) {
player.velocity = joystick.delta * 5;
}
super.update(dt);
}
}Performance Best Practices
性能最佳实践
Delta Time Usage
增量时间使用
Always multiply movement/animation by for frame-rate independence:
dtdart
void update(double dt) {
// Good - frame-rate independent
position.x += speed * dt;
// Bad - frame-rate dependent
position.x += speed;
}始终将移动/动画乘以以实现帧率无关:
dtdart
void update(double dt) {
// 良好 - 帧率无关
position.x += speed * dt;
// 糟糕 - 依赖帧率
position.x += speed;
}Object Pooling
对象池
Reuse frequently created/destroyed objects:
dart
class BulletPool {
final List<Bullet> _available = [];
final List<Bullet> _inUse = [];
Bullet acquire() {
if (_available.isEmpty) {
return Bullet();
}
final bullet = _available.removeLast();
_inUse.add(bullet);
return bullet;
}
void release(Bullet bullet) {
_inUse.remove(bullet);
_available.add(bullet);
}
}重用频繁创建/销毁的对象:
dart
class BulletPool {
final List<Bullet> _available = [];
final List<Bullet> _inUse = [];
Bullet acquire() {
if (_available.isEmpty) {
return Bullet();
}
final bullet = _available.removeLast();
_inUse.add(bullet);
return bullet;
}
void release(Bullet bullet) {
_inUse.remove(bullet);
_available.add(bullet);
}
}Cleanup
资源清理
Always dispose resources:
dart
class Player extends PositionComponent {
late final SpriteAnimationTicker ticker;
void onRemove() {
ticker.dispose();
super.onRemove();
}
}始终释放资源:
dart
class Player extends PositionComponent {
late final SpriteAnimationTicker ticker;
void onRemove() {
ticker.dispose();
super.onRemove();
}
}References
参考资料
For detailed guides on specific topics:
- Components Deep Dive: See references/components.md
- Collision System: See references/collision-system.md
- Input Handling: See references/input-handling.md
- Camera & Tiled Maps: See references/camera-tiled.md
- Audio & Particles: See references/audio-particles.md
如需特定主题的详细指南:
- 组件深入解析:查看 references/components.md
- 碰撞系统:查看 references/collision-system.md
- 输入处理:查看 references/input-handling.md
- 相机与Tiled地图:查看 references/camera-tiled.md
- 音频与粒子效果:查看 references/audio-particles.md