用Cursor开发Flutter游戏:AI编辑器提升编程效率
最近体验了Cursor这款编辑器,用它实现了一个"汉斯打怪兽"的Flutter小游戏。整个开发过程比较顺畅,今天分享一下使用心得,以及与团队去年引入的Copilot相比的一些体验差异。
Cursor是什么
Cursor是一个集成了AI功能的代码编辑器。它基于VS Code,内置了AI助手,可以帮你写代码、解决问题、回答问题。与普通编辑器的主要区别是,你可以用自然语言与它对话,不需要记一堆命令。
Flutter游戏开发体验
从零创建项目结构
开始新Flutter项目时,通常需要花时间思考项目结构,然后一个个手动创建文件夹和文件。这个过程不仅繁琐,还容易遗漏重要的模块。
用Cursor时,我只需要输入:
"帮我创建一个Flutter飞机射击游戏的项目结构"
它会给出一个目录结构建议,我最终实现的项目结构如下:
css
lib/
├── main.dart
├── screens/
│ ├── home_page.dart
│ ├── game_screen.dart
│ └── splash_screen.dart
└── game/
├── models/
│ ├── player.dart
│ └── monster.dart
├── utils/
│ └── collision_detector.dart
└── widgets/
├── starry_background.dart
└── game_controls.dart
assets/
└── images/
├── app_logo.svg
├── player_ship.svg
└── monster.svg
这个结构分离了UI、游戏逻辑和资源。点击确认后,Cursor会创建这些文件和文件夹,省去了手动操作的时间。将游戏相关的代码放在单独的game
目录下,使项目结构更清晰,便于维护。
项目架构设计
在开发过程中,Cursor帮助我设计了游戏的架构。下面是项目架构图:
这个架构图展示了游戏的组件层次和数据流向。从主应用入口到各个屏幕,再到游戏核心逻辑,每个部分都有明确的职责和关系。
文件结构也可以用Mermaid图表展示:
根据描述生成SVG图标
Flutter游戏开发中,素材往往是个问题。要么去网上找现成的(可能有版权问题),要么需要设计工具自己画(需要设计技能)。
用Cursor时,我可以描述需要的图标:
"帮我创建一个太空飞船的SVG图标,简洁风格,有点未来感"
它会生成SVG代码:
xml
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<path d="M50 20C35 20 25 35 25 50C25 65 35 75 50 75C65 75 75 65 75 50C75 35 65 20 50 20Z" fill="#3498db"/>
<path d="M50 30C40 30 35 40 35 50C35 60 40 65 50 65C60 65 65 60 65 50C65 40 60 30 50 30Z" fill="#2980b9"/>
<rect x="45" y="10" width="10" height="15" rx="2" fill="#95a5a6"/>
<rect x="35" y="75" width="10" height="15" rx="2" fill="#e74c3c"/>
<rect x="55" y="75" width="10" height="15" rx="2" fill="#e74c3c"/>
</svg>
保存为.svg文件后就能在Flutter中使用。这比在网上搜索素材或自己设计要快一些,且可以根据需求调整。
核心组件实现
在Cursor的帮助下,我实现了游戏的核心组件。下面是一些实际代码片段:
1. 主应用入口 (main.dart)
dart
import 'package:flutter/material.dart';
import 'screens/splash_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '汉斯打怪兽',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
fontFamily: 'Microsoft YaHei', // 使用微软雅黑字体
),
home: const SplashScreen(), // 使用启动页面作为首页
debugShowCheckedModeBanner: false, // 移除调试标签
);
}
}
这段代码是应用入口,设置了应用主题、字体和初始页面。我描述"我想要一个中文游戏,使用微软雅黑字体,并且有启动页面",Cursor就生成了这样的代码。
2. 星空背景组件 (starry_background.dart)
dart
import 'dart:math';
import 'package:flutter/material.dart';
class Star {
final double x;
final double y;
final double size;
final double opacity;
Star({required this.x, required this.y, required this.size, required this.opacity});
static Star random() {
final random = Random();
return Star(
x: random.nextDouble() * 400,
y: random.nextDouble() * 800,
size: 1 + random.nextDouble() * 2,
opacity: 0.3 + random.nextDouble() * 0.7,
);
}
}
class StarryBackground extends StatefulWidget {
const StarryBackground({Key? key}) : super(key: key);
@override
_StarryBackgroundState createState() => _StarryBackgroundState();
}
class _StarryBackgroundState extends State<StarryBackground> with TickerProviderStateMixin {
late List<Star> stars;
late AnimationController _controller;
@override
void initState() {
super.initState();
// 生成随机星星
stars = List.generate(100, (_) => Star.random());
// 设置动画控制器
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 5000),
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return CustomPaint(
painter: StarPainter(stars, _controller.value),
child: child,
);
},
child: Container(),
);
}
}
class StarPainter extends CustomPainter {
final List<Star> stars;
final double animationValue;
StarPainter(this.stars, this.animationValue);
@override
void paint(Canvas canvas, Size size) {
for (var star in stars) {
// 计算闪烁效果
final flicker = sin((animationValue * 10) + (star.x + star.y)) * 0.3 + 0.7;
final paint = Paint()
..color = Colors.white.withOpacity(star.opacity * flicker);
canvas.drawCircle(
Offset(star.x % size.width, star.y % size.height),
star.size,
paint,
);
}
}
@override
bool shouldRepaint(StarPainter oldDelegate) => true;
}
这段代码实现了一个动态的星空背景,包含随机生成的星星和闪烁效果。当我向Cursor描述"我想要一个有星星闪烁的太空背景"时,它生成了基本的Widget结构,并考虑了性能优化和动画效果。
3. 玩家飞船模型 (player.dart)
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Player {
Rect rect;
double speed;
int lives;
List<Rect> bullets;
DateTime lastShootTime;
Player({
required this.rect,
this.speed = 5.0,
this.lives = 3,
}) :
bullets = [],
lastShootTime = DateTime.now();
void move(double dx) {
rect = rect.translate(dx * speed, 0);
}
void shoot() {
final now = DateTime.now();
// 限制射击频率
if (now.difference(lastShootTime).inMilliseconds > 300) {
bullets.add(
Rect.fromCenter(
center: Offset(rect.center.dx, rect.top),
width: 5,
height: 10,
),
);
lastShootTime = now;
// 添加振动反馈
HapticFeedback.lightImpact();
}
}
void updateBullets() {
// 移动子弹
for (int i = bullets.length - 1; i >= 0; i--) {
bullets[i] = bullets[i].translate(0, -10);
// 移除超出屏幕的子弹
if (bullets[i].bottom < 0) {
bullets.removeAt(i);
}
}
}
void render(Canvas canvas) {
// 绘制飞船
final paint = Paint()
..color = Colors.blue;
canvas.drawRect(rect, paint);
// 绘制子弹
final bulletPaint = Paint()
..color = Colors.red;
for (var bullet in bullets) {
canvas.drawRect(bullet, bulletPaint);
}
}
}
这段代码定义了玩家飞船的数据模型,包含位置、移动、射击和渲染逻辑。Cursor还添加了振动反馈功能,这是我在描述需求时提到的增强游戏体验的细节。
4. 碰撞检测工具 (collision_detector.dart)
dart
import 'dart:math';
import 'package:flutter/material.dart';
import '../models/player.dart';
import '../models/monster.dart';
class CollisionDetector {
static bool checkCollision(Rect rect1, Rect rect2) {
return rect1.overlaps(rect2);
}
static void detectCollisions(Player player, List<Monster> monsters, Function(int) onScoreChanged) {
int score = 0;
// 检测子弹与怪兽的碰撞
for (int i = player.bullets.length - 1; i >= 0; i--) {
for (int j = monsters.length - 1; j >= 0; j--) {
if (checkCollision(player.bullets[i], monsters[j].rect)) {
// 处理碰撞逻辑
player.bullets.removeAt(i);
monsters[j].health--;
if (monsters[j].health <= 0) {
monsters.removeAt(j);
score += 10;
// 提供振动反馈
HapticFeedback.mediumImpact();
}
// 更新分数
onScoreChanged(score);
break;
}
}
}
// 检测玩家与怪兽的碰撞
for (int i = monsters.length - 1; i >= 0; i--) {
if (checkCollision(player.rect, monsters[i].rect)) {
player.lives--;
monsters.removeAt(i);
// 提供强烈振动反馈
HapticFeedback.heavyImpact();
break;
}
}
}
}
这段代码实现了游戏中的碰撞检测逻辑,包括子弹与怪兽的碰撞和玩家与怪兽的碰撞。Cursor实现了基本的碰撞检测算法,并添加了不同强度的振动反馈,以区分不同类型的碰撞。
5. 游戏控制组件 (game_controls.dart)
dart
import 'package:flutter/material.dart';
class GameControls extends StatelessWidget {
final Function(double) onMove;
final VoidCallback onShoot;
const GameControls({
Key? key,
required this.onMove,
required this.onShoot,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
// 左右移动控制区
Positioned.fill(
child: GestureDetector(
onPanUpdate: (details) {
onMove(details.delta.dx);
},
),
),
// 射击按钮
Positioned(
right: 20,
bottom: 20,
child: GestureDetector(
onTap: onShoot,
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.5),
shape: BoxShape.circle,
),
child: const Icon(
Icons.flash_on,
color: Colors.white,
size: 30,
),
),
),
),
],
);
}
}
这段代码实现了游戏的控制界面,包括左右移动和射击按钮。Cursor考虑到了移动设备的触摸特性,使用GestureDetector
来捕获用户的滑动和点击操作。
数据流向
游戏的数据流向如下:
这种数据流向在代码中的体现是:
dart
// GameScreen中的一部分代码
void _updateGame() {
// 更新玩家状态
_player.updateBullets();
// 更新怪兽状态
for (var monster in _monsters) {
monster.update();
}
// 检测碰撞
CollisionDetector.detectCollisions(
_player,
_monsters,
(score) {
setState(() {
_score += score;
});
}
);
// 生成新怪兽
_generateMonsters();
// 检查游戏结束条件
if (_player.lives <= 0) {
_gameOver();
}
// 触发重绘
setState(() {});
}
用户输入通过GameControls传递给Player模型,触发游戏状态更新,然后通过碰撞检测影响Monster模型,最终通过setState触发重绘,反映到屏幕显示上。
最终实现效果
Cursor与Copilot的对比
去年团队引入了GitHub Copilot,它提高了编码效率,但Cursor在某些方面提供了不同的体验:
交互方式
Copilot:主要是行内代码补全和注释驱动的代码生成。你写一个Widget名或注释,它会尝试补全剩余代码。
Cursor:提供对话界面,可以进行多轮交流。你可以描述一个Flutter UI需求,它会生成解决方案,然后你可以继续提问或要求修改。
项目理解能力
Copilot:擅长理解当前文件和上下文,对整个项目结构的理解有限。
Cursor:可以理解项目结构,处理多个文件,帮助创建项目框架。
代码生成范围
Copilot:通常一次生成几行到几十行代码,适合单个Widget实现。
Cursor:可以生成完整的类或文件,包括相关的辅助类和控制器。
Flutter开发技巧
1. 描述UI需求
不需要特定格式,直接描述:"我想实现一个Flutter计分板,可以记录玩家得分"。
2. 遇到报错直接粘贴
编译报错时,把错误信息粘贴给Cursor,它通常能理解问题并给出解决方案。
3. Widget重构
当Widget变得复杂时,可以让Cursor帮忙重构:"这个StatefulWidget太长了,能帮我拆分成更小的组件吗?"
4. 学习Flutter特性
想了解Flutter中某个widget怎么用?直接问Cursor:"如何使用AnimatedBuilder实现动画?",它会给出示例代码和解释。
优缺点
Cursor在Flutter开发中的优点
- 项目结构建议:可以提供Flutter项目结构建议,减少手动创建的工作
- 从UI设计到代码的转化:描述UI需求可以得到Flutter代码实现
- 素材生成:能根据描述生成SVG图标,减少对设计工具的依赖
- 跨平台问题解决:帮助处理Flutter在不同平台上的兼容性问题
Cursor在Flutter开发中的缺点
- 有时会生成错误代码:特别是复杂Widget,需要自己验证
- 依赖网络:断网就变成普通编辑器
- 对大型项目理解有限:随着项目变大,可能需要多次解释上下文
- 编辑器功能:某些Flutter特定功能不如Flutter专用插件成熟
总结
Cursor和Copilot在Flutter开发中各有优势。Copilot像是一个随时准备补全代码的助手,而Cursor更像是一个可以讨论项目的合作伙伴。
对于"汉斯打怪兽"这样的新Flutter项目,Cursor帮助我搭建了项目结构,生成了游戏素材,实现了核心功能。在日常维护和小Widget开发中,Copilot的即时补全可能更方便。
理想的Flutter开发工作流可能是结合两者:用Cursor进行项目规划和复杂UI实现,用Copilot处理日常编码和小改动。
你用过Cursor或Copilot开发Flutter应用吗?对这两种AI编程助手有什么看法?欢迎分享你的体验!