尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从零跑通你的第一个 Flame 游戏:会跳、会吃金币的 Flutter 2D 小人

从零跑通你的第一个 Flame 游戏:会跳、会吃金币的 Flutter 2D 小人 从零跑通你的第一个 Flame 游戏会跳、会吃金币的 Flutter 2D 小人【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame你刚把 FlameFlutter 2D 游戏引擎加进项目、跑了一下窗口一片黑——先别怀疑人生这很正常。黑的是画布因为游戏世界里现在一个对象都没有。接下来我们往这块空舞台里填东西一个用键盘控制、会跳的小方块地上撒几个金币碰到就加分再让镜头跟着它走起跳时甩一点小特效最后打包成能发给朋友的游戏。每一步都只加一点东西六章之后它就是你的第一个 Flame 游戏。跑黑屏把 Flame 装进一个空白 Flutter 项目先建一个普通 Flutter 项目再让 pub 帮你装引擎flutter pub add flame这一步把flame写进pubspec.yaml并解析依赖比手改版本号省心得多。接下来把main.dart整个换成入口FlameGame是引擎默认的游戏根节点GameWidget负责把它嵌进 Flutter 的窗口import package:flame/game.dart; import package:flutter/material.dart; void main() { runApp(GameWidget(game: FlameGame())); }跑起来你会看到一个纯黑窗口。对就是空的——画布上没有东西可画背景就是黑的游戏入口文档 里管这个状态叫最小可运行游戏。⚠️如果窗口连黑都看不见先检查GameWidget是不是被塞进了MaterialApp内部——它要当窗口内容的主角而不是普通小部件。让方块动起来理解 Flame 的每帧更新循环黑窗口有了往里放第一个对象一个会自己溜达的方块。Flame 里所有游戏对象都是Component组件生命周期大致是加载资源 → 进游戏onMount()→ 每帧更新update(dt)→ 画出来 → 移除组件文档 有完整说明。其中update()每帧都跑一次dt是上一帧到这一帧过了几秒用位置乘dt算位移画面才会匀速。下面这段先跑通动这件事方块一直往右走碰到屏幕右边就折返position是组件坐标size是宽高单位都是像素class Player extends PositionComponent { Player() { size Vector2(40, 40); position Vector2(200, 400); } override void onMount() gameRef.add(this); override void update(double dt) { position.x 100 * dt; if (position.x size.x gameRef.size.x) { position.x 0; } } }在main.dart里把FlameGame()换成你自己的MyGame()继承FlameGame重写onLoad()时await add(Player())一下方块就自己走起来了。⚠️update()里的系数别拍脑袋直接写position.x 100会把速度锁死在帧率上换成100 * dt60 帧和 144 帧的手机上跑得一样快。让方块听键盘的话接上输入光会自己走不够我们希望按 ← → 平移、按空格跳。键盘输入要挂在游戏类上而不是组件上给MyGame混入KeyboardEvents重写onKeyEvent判断当前按下了哪些键keysPressed是个集合天然支持组合键。这段做的事很简单按方向键就设个移动方向按空格打个想跳的标记剩下的交给下一节的update执行class MyGame extends FlameGame with KeyboardEvents { late final Player player; int moveDir 0; override KeyEventResult onKeyEvent(RawKeyEvent event, SetLogicalKeyboardKey keysPressed) { if (keysPressed.contains(LogicalKeyboardKey.arrowLeft)) moveDir -1; if (keysPressed.contains(LogicalKeyboardKey.arrowRight)) moveDir 1; if (keysPressed.contains(LogicalKeyboardKey.space)) player.wantsJump true; return KeyEventResult.handled; } }Player里加一个bool wantsJump false;然后在update里消费void update(double dt) { isOnGround ground.queryPoint(position size / 2) velocity.y 0; position.x moveDir * 200 * dt; if (wantsJump isOnGround) { velocity.y -500; // 屏幕坐标 y 向下负值就是往上跳 wantsJump false; } }注意输入只打标记、update再消费这个习惯输入事件和帧更新不是同一个节奏分开处理不会丢帧。ground.queryPoint(...)是问引擎这个点是否压在地面上暂时先留着下一节它就名副其实了。⚠️键盘只在桌面端有意义Web 和手机上要换成触摸输入思路一样只是把KeyboardEvents换成组件上的TapCallbacks等回调。接入 FlameForge2D 碰撞回调让金币和角色互相认识手动算谁碰到谁很快就会崩溃这一节引入flame_forge2dFlame 对 Box2D 物理引擎的封装重力、碰撞全交给引擎算。先装依赖flutter pub add flame_forge2dFlameForge2D的关键概念是刚体给组件绑一个物理身体它就被重力、碰撞推着走碰撞检测文档 里有全部形状介绍。绑法就三步建BodyComponent→onLoad里用FixtureDef描述碰撞形状 → 交给引擎。FixtureDef描述一个刚体长什么样形状、密度、摩擦这些属性。我们把Player升级成动态刚体受重力跳跃改为改速度而不是改坐标class Player extends BodyComponent { Player() { size Vector2(40, 40); position Vector2(120, 720); } bool wantsJump false; bool isOnGround false; int moveDir 0; override FutureBody onLoad() async { final fixture FixtureDef(PolygonShape()..setAsBox(12, 12, Vector2(0, 0))); final body world.createBody(BodyDef(type: BodyType.dynamic, bullet: true)); body.createFixture(fixture); body.gravityScale 1; return body; } void jump() { if (isOnGround) body.velocity body.velocity..set(0, -500); } override void update(double dt) { isOnGround ground.queryPoint(position size / 2); body.velocity.x moveDir * 200; if (wantsJump) { jump(); wantsJump false; } } }地面是一根静态长条BodyType.static不受重力影响铺满世界底部class Ground extends BodyComponent { override FutureBody onLoad() async { final fixture FixtureDef(PolygonShape()..setAsBox(800, 20, Vector2(0, 0))); final body world.createBody( BodyDef(type: BodyType.static, position: Vector2(800, 760)), ); body.createFixture(fixture); return body; } }现在isOnGround才真正干活引擎每帧把方块往下压只要它压在地面点上就判定贴地允许起跳。金币碰到角色就加分金币的逻辑放在接触回调里两个刚体第一次碰上的瞬间引擎会通知双方我们给Coin混入ContactCallbacks实现beginContact判断碰到的是不是玩家是的话金币自删、分数加一class Coin extends BodyComponent with ContactCallbacks { override FutureBody onLoad() async { final fixture FixtureDef(PolygonShape()..setAsBox(6, 6, Vector2(0, 0))); final body world.createBody(BodyDef(type: BodyType.static, bullet: true)); body.createFixture(fixture); return body; } override void beginContact(Component other, Contact contact) { if (other is Player) { removeFromParent(); gameRefMyGame().score; } } }把舞台搭起来MyGame.onLoad里依次add地面、玩家、金币位置贴着地面再加一个TextComponent当分数牌class MyGame extends FlameGame with KeyboardEvents { int score 0; late final Ground ground; late final Player player; late final TextComponent hud; override Futurevoid onLoad() async { await add(ground Ground()); await add(player Player()); await add(Coin()..position Vector2(420, 754)); await add(hud TextComponent( text: Score: 0, textRenderer: TextPaint(style: const TextStyle(fontSize: 24, color: Colors.white)), position: Vector2(12, 12), )); } }⚠️别手写金币位置 玩家位置这种数值比较——刚体每一帧都在动回调才是引擎给你的碰撞时机另外组件一removeFromParent()它的物理身体会被引擎自动清掉不用手动再删Body。加上相机跟随和粒子特效让画面活起来角色现在会跳会吃金币但世界只有屏幕那么大。我们把世界放大到 1600 像素宽让Camera相机负责看哪个区域摄像机是画布之外的虚拟镜头决定把世界的哪一块裁进来给你看。Camera.follow每帧把镜头对准玩家worldBounds是世界的硬边界镜头不会越界Camera.follow( player, worldBounds: const Rect.fromLTWH(0, 0, 1600, 800), deadzoneSize: Vector2(30, 30), // 玩家在这个范围内镜头不动避免跟手抖 );起跳那点意思用一个效果effect挂在组件上的短时动画跑完自动结束补上。下面这段把缩放脉冲和一点旋转串在player上起跳瞬间触发小人会抖一下final effect AnchorEffect( with: (a) a ..scale(to: const Vector2(1.2, 0.8), duration: 0.1) ..spin(angle: 0.2, duration: 0.2), anchor: (c) AnchorEffect.center(anchor: player), ); player.add(effect);放在Player.jump()里调用effect.play()即可。到这里你有一个镜头会跟着主角穿行的世界了。处理好加载与帧率别让它卡成 PPT游戏要动最怕的是某一帧突然慢半拍。两个来源资源没提前加载以及每帧都在新建对象。图片类资源一律在onLoad里集中预加载引擎自带缓存之后复用零开销override Futurevoid onLoad() async { await images.loadAll([knight.png, coin.png]); // ...再 add 你的组件 }⚠️千万别在render()或update()里临时await加载图片——那几帧组件会拿着空引用直接炸掉一切加载都挤在onLoad完成。帧率方面记住一条update()里尽量不新建对象。Paint、TextStyle这类画笔声明成字段一次创建别每帧 new。默认 60 fps 下dt约为 0.016逻辑写错时最容易暴露的就是这里。打包跑起来发布你的第一个 Flame 游戏逻辑全通了把它变成一个能装进手机的包flutter build apk --release这条命令产出 release 版安卓安装包iOS、Web、桌面端是同一套思路具体配置见 多平台文档。图标、启动页、应用名都是常规 Flutter 工程配置和写游戏代码互不干扰。回头看这一路黑窗口 → 会动的方块 → 听得见键盘 → 有物理的碰撞 → 跟随的镜头和特效 → 能发布的包。仓库里的 examples 目录 还有 platformer 这类完整示例跑起来对照一遍你就知道每个零件该摆在哪了。把示例 clone 下来git clone https://gitcode.com/GitHub_Trending/fl/flame把MyGame里的方块换成你的角色贴图改两行代码它就是你自己的第一个 Flame 游戏了。【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表