使用Flutter Flame快速轻松地创建2D游戏
Flutter 可以用一套代码为 Android、iOS、桌面和网页等平台开发应用程序。作为跨平台 UI 工具包,Flutter 团队致力于让所有类型的开发者都能快速构建和发布应用程序。例如,游戏开发者现在可以轻松构建美观的游戏应用,无需担心性能、加载时间和应用大小等问题。
本教程将为您介绍Flutter Flame游戏引擎。您将学习如何设置和构建Flutter Flame游戏、加载精灵以及添加动画。
本教程假定您已经掌握了Dart和Flutter的基本知识。
Flame 引擎
Flame是一个运行在Flutter上的2D游戏开发框架。Flame引擎可以轻松实现游戏循环以及动画、碰撞检测、反弹检测和视差滚动等其他必要功能。
Flame是模块化的,它提供了可以独立使用的包,用于扩展其功能,例如:
flame_audio: 提供音频功能
flame_forge2d: 提供物理引擎功能
flame_tiled: 为Flame游戏添加对Tiled的地图支持。
Flutter Flame设置
要开始使用 Flame,您需要安装该包。在您的 pubspec.yaml
文件中,按照以下方式添加依赖项:
dependencies:
flame: ^1.1.1
为了运行游戏,您需要使用 GameWidget
组件。在 main.dart
文件中添加下面的代码片段可以渲染一个Flame游戏,但目前游戏界面是一片空白。
void main() {
final game = FlameGame();
runApp(
GameWidget(
game: game,
),
);
}
现在你可以开始为游戏添加图形了。
加载精灵
要生成静态图像,您需要使用 SpriteComponent
类。将需要的游戏图像添加到 assets/images
文件夹中,并更新 pubspec.yaml
文件以加载资产。所需的图片可以在这里找到。
你需要在 lib
文件夹中创建并更新以下三个文件:
dino_player.dart
将加载并定位我们的玩家角色:
import 'package:flame/components.dart';
class DinoPlayer extends SpriteComponent with HasGameRef {
DinoPlayer() : super(size: Vector2.all(100.0));
@override
Future<void> onLoad() async {
super.onLoad();
sprite = await gameRef.loadSprite('idle.png');
position = gameRef.size / 2;
}
}
dino_world.dart
它将加载我们的游戏背景:
import 'package:flame/components.dart';
class DinoWorld extends SpriteComponent with HasGameRef {
@override
Future<void> onLoad() async {
super.onLoad();
sprite = await gameRef.loadSprite('background.png');
size = sprite!.originalSize;
}
}
dino_game.dart
,它将管理我们所有的游戏组件。它添加我们的游戏玩家和背景并确定他们的位置。
import 'dart:ui';
import 'package:flame/game.dart';
import 'dino_player.dart';
import 'dino_world.dart';
class DinoGame extends FlameGame{
DinoPlayer _dinoPlayer = DinoPlayer();
DinoWorld _dinoWorld = DinoWorld();
@override
Future<void> onLoad() async {
super.onLoad();
await add(_dinoWorld);
await add(_dinoPlayer);
_dinoPlayer.position = _dinoWorld.size / 1.5;
camera.followComponent(_dinoPlayer,
worldBounds: Rect.fromLTRB(0, 0, _dinoWorld.size.x, _dinoWorld.size.y));
}
}
camera.followComponent
将游戏视窗设置为跟随玩家移动。这个函数是必需的,因为我们将在游戏中为玩家添加移动效果。
更新你的 main.dart
文件加载 DinoGame
,就像下面展示的那样:
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'dino_game.dart';
void main() {
final game = DinoGame();
runApp(
GameWidget(game: game),
);
}
运行你的应用程序应该会显示你的玩家和背景。
移动精灵
要移动玩家,您需要检测并响应您所选择的方向。在这节教程中,您将使用游戏的箭头键为玩家添加移动功能。
首先,创建一个名为helpers
的文件夹,并将以下文件放入其中,并按照以下方式更新它们:
directions.dart
枚举包含以下方向:
enum Direction { up, down, left, right, none }
navigation_keys.dart
包含了导航键的UI和逻辑。
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'directions.dart';
class NavigationKeys extends StatefulWidget {
final ValueChanged<Direction>? onDirectionChanged;
const NavigationKeys({Key? key, required this.onDirectionChanged})
: super(key: key);
@override
State<NavigationKeys> createState() => _NavigationKeysState();
}
class _NavigationKeysState extends State<NavigationKeys> {
Direction direction = Direction.none;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 200,
width: 120,
child: Column(
children: [
ArrowKey(
icons: Icons.keyboard_arrow_up,
onTapDown: (det) {
updateDirection(Direction.up);
},
onTapUp: (dets) {
updateDirection(Direction.none);
},
onLongPressDown: () {
updateDirection(Direction.up);
},
onLongPressEnd: (dets) {
updateDirection(Direction.none);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ArrowKey(
icons: Icons.keyboard_arrow_left,
onTapDown: (det) {
updateDirection(Direction.left);
},
onTapUp: (dets) {
updateDirection(Direction.none);
},
onLongPressDown: () {
updateDirection(Direction.left);
},
onLongPressEnd: (dets) {
updateDirection(Direction.none);
},
),
ArrowKey(
icons: Icons.keyboard_arrow_right,
onTapDown: (det) {
updateDirection(Direction.right);
},
onTapUp: (dets) {
updateDirection(Direction.none);
},
onLongPressDown: () {
updateDirection(Direction.right);
},
onLongPressEnd: (dets) {
updateDirection(Direction.none);
},
),
],
),
ArrowKey(
icons: Icons.keyboard_arrow_down,
onTapDown: (det) {
updateDirection(Direction.down);
},
onTapUp: (dets) {
updateDirection(Direction.none);
},
onLongPressDown: () {
updateDirection(Direction.down);
},
onLongPressEnd: (dets) {
updateDirection(Direction.none);
},
),
],
),
);
}
void updateDirection(Direction newDirection) {
direction = newDirection;
widget.onDirectionChanged!(direction);
}
}
class ArrowKey extends StatelessWidget {
const ArrowKey({
Key? key,
required this.icons,
required this.onTapDown,
required this.onTapUp,
required this.onLongPressDown,
required this.onLongPressEnd,
}) : super(key: key);
final IconData icons;
final Function(TapDownDetails) onTapDown;
final Function(TapUpDetails) onTapUp;
final Function() onLongPressDown;
final Function(LongPressEndDetails) onLongPressEnd;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: onTapDown,
onTapUp: onTapUp,
onLongPress: onLongPressDown,
onLongPressEnd: onLongPressEnd,
child: Container(
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0x88ffffff),
borderRadius: BorderRadius.circular(60),
),
child: Icon(
icons,
size: 42,
),
),
);
}
}
然后,更新 main.dart
文件,以显示您的游戏和按键,如下所示:
void main() {
final game = DinoGame();
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Stack(
children: [
GameWidget(
game: game,
),
Align(
alignment: Alignment.bottomRight,
child: NavigationKeys(onDirectionChanged: game.onArrowKeyChanged,),
),
],
),
),
),
);
}
在 dino_game.dart
文件中添加以下代码以实现玩家的移动功能:
onArrowKeyChanged(Direction direction){
_dinoPlayer.direction = direction;
}
最后,更新 dino_player.dart
文件,通过包含以下代码片段来更新玩家的位置:
Direction direction = Direction.none;
@override
void update(double dt) {
super.update(dt);
updatePosition(dt);
}
updatePosition(double dt) {
switch (direction) {
case Direction.up:
position.y --;
break;
case Direction.down:
position.y ++;
break;
case Direction.left:
position.x --;
break;
case Direction.right:
position.x ++;
break;
case Direction.none:
break;
}
}
运行你的程序,按下任意一个箭头方向应该会更新玩家的位置。
Sprite animations 精灵动画
现在,你的玩家按预期移动,但现在还没有动画效果,看起来很不自然。为了让你的玩家产生动画,你需要使用精灵表sprite sheet。
精灵表是一组排列成行和列的精灵图像的集合。与单独的精灵图相比,加载速度更快。Flame引擎可以只加载和渲染精灵表的一部分。下面是一张恐龙玩家的精灵表。
精灵表包含了不同的玩家帧,可以被动画化以展示诸如向右或向左行走等动作。精灵表被添加到 assets/images
文件夹中。
在 dino_player.dart
文件中,按照以下步骤来使玩家动起来:
用
SpriteAnimationComponent
替代SpriteComponent
。初始化你的动画和动画速度。在这节教程中,我们将专注于左右行走的动画。
late final SpriteAnimation _walkingRightAnimation;
late final SpriteAnimation _walkingLeftAnimation;
late final SpriteAnimation _idleAnimation;
final double _animationSpeed = .15;
从精灵表中加载精灵。精灵的加载取决于它们在表上的位置。你可以通过指定每个精灵的宽度和列数,或者根据其行和列的位置来加载精灵。
Future<void> _loadAnimations() async {
final spriteSheet = SpriteSheet.fromColumnsAndRows(
image: await gameRef.images.load('spritesheet.png'),
columns: 30,
rows: 1);
_idleAnimation = spriteSheet.createAnimation(
row: 0, stepTime: _animationSpeed, from: 0, to: 9);
_walkingRightAnimation = spriteSheet.createAnimation(
row: 0, stepTime: _animationSpeed, from: 10, to: 19);
_walkingLeftAnimation = spriteSheet.createAnimation(row: 0, stepTime: _animationSpeed, from: 20, to: 29);
}
spriteSheet.createAnimation
函数根据row
、from
和to
属性选择了动画精灵序列,并让它们动起来。
更新玩家以加载选定的动画.
首先, 重写 onLoad
方法加载 _idleAnimation
.
@override
Future<void> onLoad() async {
super.onLoad();
await _loadAnimations().then((_) => {animation = _idleAnimation});
}
然后更新 updatePosition
函数,根据玩家所面对的方向加载不同的动画。本教程提供了静止状态、向右移动和向左移动的精灵。
updatePosition(double dt) {
switch (direction) {
case Direction.up:
position.y --;
break;
case Direction.down:
position.y ++;
break;
case Direction.left:
animation = _walkingLeftAnimation;
position.x --;
break;
case Direction.right:
animation = _walkingRightAnimation;
position.x ++;
break;
case Direction.none:
animation = _idleAnimation;
break;
}
}
运行你的应用程序并向左或向右移动,会更新玩家的移动状态,现在看起来更加真实了。
恭喜,你刚刚用Flame制作了你的第一个简单的游戏!
你可以使用flame_tiled 包在应用程序中加载带有碰撞层的自定义地图或贴图,以改进游戏。要设计地图和贴图,你需要知道如何使用Tiled创建它们。
你还可以使用flame_audio包在游戏中添加音频。
总结
Flame是一个基于Flutter的轻量级游戏引擎,可帮助开发人员快速创建2D游戏。
在本教程中,您学习了如何安装和使用 Flame。我们还介绍了如何使用 Flutter Flame 引擎来加载精灵、添加精灵移动和动画。您还了解了可以增强游戏的多种独立包。
希望您喜欢本教程!
- 1
- 0
-
赞助
微信支付宝 -
分享