日期:
来源:code小生收集编辑:Flutter社区
以下内容来自公众号code小生,关注每日干货及时送达
文/ Very Good Ventures 团队,5 月 11 日发表于 Flutter 官方博客
游戏开发要点
游戏循环
Game
类包含了游戏组件以及其逻辑的实现,然后被交给 widget 树中的 GameWidget
。在 I/O 弹球游戏中,游戏循环反映了弹球在游戏场的位置以及状态,然后如果球与物体碰撞或跌出比赛则需要给出必要的反馈。@override
void update(double dt) {
super.update(dt);
final direction = -parent.body.linearVelocity.normalized();
angle = math.atan2(direction.x, -direction.y);
size = (_textureSize / 45) *
parent.body.fixtures.first.shape.radius;
}
使用 2D 组件渲染 3D 空间
/// Scales the ball's body and sprite according to its position on the board.
class BallScalingBehavior extends Component with ParentIsA<Ball> {
@override
void update(double dt) {
super.update(dt);
final boardHeight = BoardDimensions.bounds.height;
const maxShrinkValue = BoardDimensions.perspectiveShrinkFactor;
final standardizedYPosition = parent.body.position.y + (boardHeight / 2);
final scaleFactor = maxShrinkValue +
((standardizedYPosition / boardHeight) * (1 - maxShrinkValue));
parent.body.fixtures.first.shape.radius = (Ball.size.x / 2) * scaleFactor;
final ballSprite = parent.descendants().whereType<SpriteComponent>();
if (ballSprite.isNotEmpty) {
ballSprite.single.scale.setValues(
scaleFactor,
scaleFactor,
);
}
}
}
Forge 2D 的物理引擎
forge2d
增强游戏中的物理特性,例如物体(夹板)在游戏场上的之间的碰撞检测。使用 forge2D
能够我们监听夹板发生碰撞的时机。我们就可以在这里向夹板添加交互的回调,当两个物体发生碰撞的时候我们就能收到通知。例如,弹球(它是圆形的)与弹簧(它是椭圆形的)接触时,我们就会增加它的得分。在这些回调中,我们可以清楚地设置接触开始和结束的位置,以便当两个物体相互接触时,会发生碰撞。@override
Body createBody() {
final shape = CircleShape()..radius = size.x / 2;
final bodyDef = BodyDef(
position: initialPosition,
type: BodyType.dynamic,
userData: this,
);
return world.createBody(bodyDef)
..createFixtureFromShape(shape, 1);
}
Sprite sheet 动画
SpriteAnimationComponent
。对于每个元素,我们都有一个文件,其中包含不同方向的图像、文件中的帧数以及帧之间的时间。使用这些数据,Flame 中的 SpriteAnimationComponent
能够在一个循环中将所有图像编在一起,使元素看起来在运动。final spriteSheet = gameRef.images.fromCache(
Assets.images.android.spaceship.animatronic.keyName,
);
const amountPerRow = 18;
const amountPerColumn = 4;
final textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,
);
size = textureSize / 10;
animation = SpriteAnimation.fromFrameData(
spriteSheet,
SpriteAnimationData.sequenced(
amount: amountPerRow * amountPerColumn,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: textureSize,
),
);
来自 Firebase 的实时积分排行榜
/// Acquires top 10 [LeaderboardEntryData]s.
Future<List<LeaderboardEntryData>> fetchTop10Leaderboard() async {
try {
final querySnapshot = await _firebaseFirestore
.collection(_leaderboardCollectionName)
.orderBy(_scoreFieldName, descending: true)
.limit(_leaderboardLimit)
.get();
final documents = querySnapshot.docs;
return documents.toLeaderboard();
} on LeaderboardDeserializationException {
rethrow;
} on Exception catch (error, stackTrace) {
throw FetchTop10LeaderboardException(error, stackTrace);
}
}
构建 Web 应用
代码架构
CharacterThemeCubit
类控制的。根据角色的选择,球的颜色、背景和其他元素都会更新。/// {@template character_theme}
/// Base class for creating character themes.
///
/// Character specific game components should have a getter specified here to
/// load their corresponding assets for the game.
/// {@endtemplate}
abstract class CharacterTheme extends Equatable {
/// {@macro character_theme}
const CharacterTheme();
/// Name of character.
String get name;
/// Asset for the ball.
AssetGenImage get ball;
/// Asset for the background.
AssetGenImage get background;
/// Icon asset.
AssetGenImage get icon;
/// Icon asset for the leaderboard.
AssetGenImage get leaderboardIcon;
/// Asset for the the idle character animation.
AssetGenImage get animation;
@override
List<Object> get props => [
name,
ball,
background,
icon,
leaderboardIcon,
animation,
];
}
flame_bloc
来记录剩余的游戏回合数、游戏中获得的奖励以及当前的游戏分数。另外,在 wdget 树顶层有一个 widget,它包含加载页面的逻辑以及玩游戏的说明。我们还遵循 行为型模式[8] 来封装和隔离基于组件的游戏功能元素。例如,保险杠在被球击中时会发出声音,所以我们实现了 BumperNoiseBehavior
类来处理这个问题。class BumperNoiseBehavior extends ContactBehavior {
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
readProvider<PinballPlayer>().play(PinballAudio.bumper);
}
}
组件沙盒
接下来
原文链接: https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d 本地化: CFUG 团队 中文链接: https://flutter.cn/posts/i-o-pinball
文内链接
I/O 弹球游戏主页: https://pinball.flutter.dev/
[2]Flame 开发文档主页: https://docs.flame-engine.org/
[3]Flame 团队维护的 package: forge2d: https://pub.flutter-io.cn/packages/forge2d
[4]Box2D 物理引擎官网: https://box2d.org/
[5]Firebase Cloud Firestore 文档: https://firebase.google.cn/docs/firestore
[6]Firebase Cloud Function 文档: https://firebase.google.cn/docs/functions
[7]Flutter package: flam_bloc 页面: https://pub.flutter-io.cn/packages/flame_bloc
[8]百度百科: 设计模式行为型模式: http://baike.baidu.com/l/i4znnfCN
[9]Flutter package: flame_test 页面: https://pub.flutter-io.cn/packages/flame_test
[10]Flutter Gallery 网页版: https://gallery.flutter.cn/#/
[11]I/O Pinball 弹球游戏: https://pinball.flutter.dev/
[12]弹球游戏源代码仓库: https://github.com/flutter/pinball
我是 code小生
,喜欢可以随手点个在看
、转发给你的朋友,谢谢~
觉得不错,请点个在看