Flutter for OpenHarmony 实战:七巧板游戏图案模板与交互系统

欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区

前言

在这里插入图片描述

七巧板游戏不仅需要精确的几何计算,还需要提供直观的图案模板和流畅的交互体验。本文将详细介绍图案模板系统的设计、CustomPainter渲染技术、手势识别机制、状态反馈系统以及性能优化策略。

一、图案模板系统

1.1 模板数据结构

class TangramPattern {
  final String name;
  final List<List<Offset>> outlines;

  const TangramPattern({
    required this.name,
    required this.outlines,
  });
}

每个图案模板包含名称和轮廓线列表,轮廓线用于提示玩家摆放位置。outlines是二维数组,每个子数组表示一个多边形区域。

1.2 正方形模板

static const TangramPattern square = TangramPattern(
  name: '正方形',
  outlines: [
    [Offset(150, 200), Offset(250, 200), Offset(200, 250)],
    [Offset(150, 200), Offset(250, 200), Offset(250, 300), Offset(150, 300)],
  ],
);

正方形是七巧板最经典的拼法,将7块图块完美拼合成一个正方形。两个轮廓分别对应大三角形的位置。

1.3 多种模板支持

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

final List<TangramPattern> patterns = [
  TangramPattern.square,    // 正方形 - 基础
  TangramPattern.cat,       // 猫 - 中等
  TangramPattern.house,     // 房子 - 中等
  TangramPattern.boat,      // 船 - 困难
];

提供4个基础图案模板,按难度递进排列。这种设计让新手从简单的正方形开始,逐步挑战更复杂的图案。

1.4 模板切换机制

void _nextPattern() {
  setState(() {
    currentPatternIndex = (currentPatternIndex! + 1) % patterns.length;
  });
}

使用取模运算实现循环切换,到达最后一个模板后回到第一个。这种无限循环设计让玩家可以反复练习。

二、PatternPainter渲染

2.1 模板轮廓绘制

class PatternPainter extends CustomPainter {
  final TangramPattern pattern;

  
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.grey.shade300
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2
      ..strokeJoin = StrokeJoin.round;

    for (var outline in pattern.outlines) {
      final path = Path();
      path.moveTo(outline[0].dx, outline[0].dy);

      for (int i = 1; i < outline.length; i++) {
        path.lineTo(outline[i].dx, outline[i].dy);
      }

      path.close();
      canvas.drawPath(path, paint);
    }
  }
}

使用灰色虚线绘制模板轮廓,strokeJoin.round使拐角更平滑。每个轮廓使用Path绘制,moveTo移动到起点,lineTo连接各点,close闭合路径。

2.2 模板名称显示

final textPainter = TextPainter(
  text: TextSpan(
    text: pattern.name,
    style: const TextStyle(
      color: Colors.grey,
      fontSize: 24,
      fontWeight: FontWeight.bold,
    ),
  ),
  textDirection: TextDirection.ltr,
);

textPainter.layout();
textPainter.paint(canvas, Offset(20, 20));

在左上角绘制模板名称,提示当前目标图案。使用TextPainter进行文本绘制,这是Flutter中处理文本的标准方式。

2.3 重绘优化


bool shouldRepaint(PatternPainter oldDelegate) {
  return oldDelegate.pattern != pattern;
}

只在模板变化时重绘,避免不必要的性能消耗。

三、TangramPainter渲染

3.1 图块绘制

class TangramPainter extends CustomPainter {
  final List<TangramPiece> pieces;
  final TangramPiece? selectedPiece;

  
  void paint(Canvas canvas, Size size) {
    for (var piece in pieces) {
      final path = Path();
      path.moveTo(piece.vertices[0].dx, piece.vertices[0].dy);

      for (int i = 1; i < piece.vertices.length; i++) {
        path.lineTo(piece.vertices[i].dx, piece.vertices[i].dy);
      }

      path.close();

      final paint = Paint()
        ..color = piece.color
        ..style = PaintingStyle.fill;

      canvas.drawPath(path, paint);
    }
  }
}

遍历所有图块,使用Path绘制多边形并填充颜色。先moveTo到第一个顶点,然后lineTo连接其他顶点,最后close闭合路径。

3.2 边框绘制

final strokePaint = Paint()
  ..color = piece == selectedPiece ? Colors.white : Colors.black
  ..style = PaintingStyle.stroke
  ..strokeWidth = piece == selectedPiece ? 3 : 2;

canvas.drawPath(path, strokePaint);

根据选中状态使用不同颜色和粗细绘制边框。选中:白色3像素,未选中:黑色2像素。

3.3 高光效果

if (piece == selectedPiece) {
  final highlightPaint = Paint()
    ..color = Colors.yellow.withValues(alpha: 0.5)
    ..style = PaintingStyle.stroke
    ..strokeWidth = 6;

  canvas.drawPath(path, highlightPaint);
}

选中图块额外绘制黄色半透明高光,宽度6像素,增强视觉反馈。

四、交互系统设计

4.1 手势识别

GestureDetector(
  onPanStart: _handlePanStart,
  onPanUpdate: _handlePanUpdate,
  onPanEnd: _handlePanEnd,
  child: CustomPaint(...),
)

使用GestureDetector捕获拖拽手势,提供流畅的触摸交互。onPanStart检测选择,onPanUpdate处理移动,onPanEnd结束操作。

4.2 选择流程优化

for (int i = pieces.length - 1; i >= 0; i--) {
  if (_isPointInPiece(localPosition, pieces[i])) {
    setState(() {
      selectedPiece = pieces[i];
      pieces.removeAt(i);
      pieces.add(selectedPiece!);
    });
    break;
  }
}

从上层到底层检测,选择第一个包含点击点的图块,并将其移到数组末尾(最上层)。这种设计确保选中的图块始终显示在最前面。

4.3 移动交互

void _handlePanUpdate(DragUpdateDetails details) {
  if (selectedPiece == null || dragStartPosition == null) return;

  final delta = details.localPosition - dragStartPosition!;

  setState(() {
    for (int i = 0; i < selectedPiece!.vertices.length; i++) {
      selectedPiece!.vertices[i] = selectedPiece!.vertices[i] + delta;
    }
    dragStartPosition = details.localPosition;
  });
}

计算与上一帧的偏移量,更新所有顶点位置,实现整体移动。

五、状态反馈系统

5.1 视觉反馈层次

选中图块有三层视觉反馈:

  1. 边框颜色:白色vs黑色
  2. 边框粗细:3像素vs2像素
  3. 外发光效果:黄色半透明高光

5.2 提示信息

Text(
  selectedPiece != null ? '已选中: ${selectedPiece!.name}' : '点击图块选中,拖拽移动,使用按钮旋转翻转',
  style: const TextStyle(fontSize: 14),
  textAlign: TextAlign.center,
)

底部显示当前选中图块名称或操作提示,帮助玩家了解当前状态。

5.3 层次管理

pieces.removeAt(i);
pieces.add(selectedPiece!);

选中时将图块移到数组末尾,确保绘制在最上层。

六、性能优化

6.1 shouldRepaint实现


bool shouldRepaint(TangramPainter oldDelegate) {
  return oldDelegate.selectedPiece != selectedPiece;
}

只在选中状态变化时重绘,避免频繁的无效重绘。

6.2 快速排斥实验

在碰撞检测中首先排除明显不相交的情况,提高检测效率。

6.3 逆序遍历

for (int i = pieces.length - 1; i >= 0; i--) {

从最上层开始检测,选中第一个符合条件的图块,避免遍历所有图块。

总结

本文详细介绍了七巧板游戏的图案模板和交互系统。从模板设计到渲染技术,从手势识别到状态反馈,每个技术点都为创造直观易用的游戏体验做出了贡献。通过这些技术的综合应用,实现了功能完整且交互流畅的七巧板游戏。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐