Flutter for OpenHarmony 万能游戏库App实战 - 蜘蛛纸牌游戏实现

蜘蛛纸牌是一个经典的单人纸牌游戏。这篇文章我们来实现一个完整的蜘蛛纸牌游戏,包括游戏逻辑、拖拽交互、难度选择、以及得分系统。通过这个功能,我们能展示如何构建一个复杂的交互式游戏

游戏的核心逻辑

PlayingCard类代表一张牌:

dart 复制代码
class PlayingCard {
  final int rank;
  final Suit suit;
  bool faceUp;

  PlayingCard({required this.rank, required this.suit, this.faceUp = false});

  String get rankString {
    switch (rank) {
      case 1: return 'A';
      case 11: return 'J';
      case 12: return 'Q';
      case 13: return 'K';
      default: return rank.toString();
    }
  }

  String get suitSymbol {
    switch (suit) {
      case Suit.spades: return '♠';
      case Suit.hearts: return '♥';
      case Suit.diamonds: return '♦';
      case Suit.clubs: return '♣';
    }
  }

  Color get suitColor {
    return (suit == Suit.hearts || suit == Suit.diamonds) ? Colors.red : Colors.black;
  }
}

每张牌有rank(1-13)、suit(花色)和faceUp(是否翻开)。
rankString和suitSymbol提供了牌的显示信息。

游戏状态管理

SpiderSolitaireScreen管理游戏的整体状态:

dart 复制代码
class _SpiderSolitaireScreenState extends State<SpiderSolitaireScreen> {
  List<List<PlayingCard>> columns = List.generate(10, (_) => []);
  List<PlayingCard> stock = [];
  List<List<PlayingCard>> completed = [];
  int moves = 0;
  int score = 500;
  Difficulty difficulty = Difficulty.easy;
  bool gameStarted = false;
  bool gameWon = false;

columns存储10列的牌。
stock存储剩余的牌。
completed存储已完成的牌组。
score从500开始,每次移动减1,完成一组加100。

游戏初始化

initGame方法初始化游戏:

dart 复制代码
  void initGame() {
    List<Suit> suits;
    switch (difficulty) {
      case Difficulty.easy:
        suits = [Suit.spades];
        break;
      case Difficulty.medium:
        suits = [Suit.spades, Suit.hearts];
        break;
      case Difficulty.hard:
        suits = Suit.values;
        break;
    }

    List<PlayingCard> deck = [];
    int decksPerSuit = 8 ~/ suits.length;
    for (var suit in suits) {
      for (int d = 0; d < decksPerSuit; d++) {
        for (int rank = 1; rank <= 13; rank++) {
          deck.add(PlayingCard(rank: rank, suit: suit));
        }
      }
    }
    deck.shuffle(Random());

根据难度选择花色数量。
简单模式只有黑桃,中等模式有黑桃和红心,困难模式有所有花色。
创建牌组并打乱。

牌的移动逻辑

getMovableCards方法获取可移动的牌组:

dart 复制代码
  List<PlayingCard>? getMovableCards(int col, int startIdx) {
    if (startIdx >= columns[col].length) return null;
    var cards = columns[col].sublist(startIdx);
    if (cards.isEmpty || !cards.first.faceUp) return null;
    
    // 检查是否是同花色递减序列
    for (int i = 0; i < cards.length - 1; i++) {
      if (cards[i].suit != cards[i + 1].suit || 
          cards[i].rank != cards[i + 1].rank + 1) {
        return null;
      }
    }
    return cards;
  }

只有同花色递减序列才能整体移动。

canPlace方法检查是否可以放置牌:

dart 复制代码
  bool canPlace(int toCol, PlayingCard card) {
    if (columns[toCol].isEmpty) return true;
    var top = columns[toCol].last;
    // 只要求数值递减1,不要求花色
    return top.rank == card.rank + 1;
  }

可以在任何数值比当前牌小1的牌上放置。

完成检查

checkComplete方法检查是否有完成的牌组:

dart 复制代码
  void checkComplete() {
    for (int col = 0; col < columns.length; col++) {
      if (columns[col].length < 13) continue;
      
      int start = columns[col].length - 13;
      bool valid = true;
      Suit? s;
      
      for (int i = 0; i < 13; i++) {
        var c = columns[col][start + i];
        if (!c.faceUp) { valid = false; break; }
        if (i == 0) {
          if (c.rank != 13) { valid = false; break; }
          s = c.suit;
        } else {
          if (c.rank != 13 - i || c.suit != s) { valid = false; break; }
        }
      }
      
      if (valid) {
        setState(() {
          var removed = columns[col].sublist(start);
          columns[col].removeRange(start, columns[col].length);
          completed.add(removed);
          score += 100;
        });
      }
    }
  }

检查每列是否有K到A的同花色序列。
如果有就移除并加分。

拖拽交互

buildCol方法实现拖拽交互:

dart 复制代码
  Widget buildCol(int colIdx, double cardW, double cardH) {
    var col = columns[colIdx];
    
    return DragTarget<Map<String, dynamic>>(
      onWillAcceptWithDetails: (details) {
        var data = details.data;
        int fromCol = data['col'];
        List<PlayingCard> cards = data['cards'];
        if (fromCol == colIdx) return false;
        return canPlace(colIdx, cards.first);
      },
      onAcceptWithDetails: (details) {
        var data = details.data;
        moveCards(data['col'], data['idx'], colIdx);
      },

使用DragTarget接收拖拽的牌。
在onWillAcceptWithDetails中检查是否可以放置。
在onAcceptWithDetails中执行移动。

牌的显示

buildCard方法显示牌:

dart 复制代码
  Widget buildCard(PlayingCard card, double w, double h, {bool dragging = false}) {
    if (!card.faceUp) {
      return Container(
        width: w,
        height: h,
        decoration: BoxDecoration(
          gradient: const LinearGradient(
            colors: [Color(0xFF1565C0), Color(0xFF0D47A1)],
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
          ),
          borderRadius: BorderRadius.circular(4),
          border: Border.all(color: Colors.white30),
        ),
        child: Center(
          child: Text('🕷️', style: TextStyle(fontSize: w * 0.4)),
        ),
      );
    }

    return Container(
      width: w,
      height: h,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(4),
        border: Border.all(color: Colors.grey.shade400),
      ),
      child: Stack(
        children: [
          // 左上角
          Positioned(
            left: 2,
            top: 1,
            child: Column(
              children: [
                Text(card.rankString, style: TextStyle(color: card.suitColor, fontWeight: FontWeight.bold, fontSize: w * 0.32, height: 1)),
                Text(card.suitSymbol, style: TextStyle(color: card.suitColor, fontSize: w * 0.24, height: 1)),
              ],
            ),
          ),
          // 中间大花色
          Center(
            child: Text(card.suitSymbol, style: TextStyle(color: card.suitColor, fontSize: w * 0.55)),
          ),
          // 右下角(倒置)
          Positioned(
            right: 2,
            bottom: 1,
            child: Transform.rotate(
              angle: 3.14159,
              child: Column(
                children: [
                  Text(card.rankString, style: TextStyle(color: card.suitColor, fontWeight: FontWeight.bold, fontSize: w * 0.32, height: 1)),
                  Text(card.suitSymbol, style: TextStyle(color: card.suitColor, fontSize: w * 0.24, height: 1)),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

翻开的牌显示为白色,背面显示为蓝色。
牌的四个角显示牌的信息。

总结

这篇文章我们实现了一个完整的蜘蛛纸牌游戏。涉及到的知识点包括:

  • 游戏逻辑 - 实现复杂的游戏规则和检查
  • 拖拽交互 - 使用DragTarget和Draggable实现拖拽
  • 状态管理 - 管理游戏的多个状态
  • 难度系统 - 支持多个难度级别
  • 得分系统 - 实现得分计算和显示
  • UI渲染 - 使用Stack和Transform创建逼真的牌显示

蜘蛛纸牌游戏展示了如何构建一个复杂的交互式游戏 。通过完善的游戏逻辑、流畅的交互、以及精心的UI设计,我们能为用户提供一个有趣的游戏体验


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
IT陈图图2 小时前
基于 Flutter × OpenHarmony 开发的文本处理工具箱首页
flutter·华为·openharmony
打工的小王2 小时前
java并发编程(三)CAS
java·开发语言
小白阿龙2 小时前
鸿蒙+Flutter 跨平台开发——一款“随机宝盒“的开发流程
flutter·华为·harmonyos·鸿蒙
飞Link2 小时前
【Django】Django的静态文件相关配置与操作
后端·python·django
csj502 小时前
安卓基础之《(18)—内容提供者(4)在应用之间共享文件》
android
尤老师FPGA2 小时前
使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第四十五讲)
android·java·ui
爱吃大芒果2 小时前
Flutter for OpenHarmony前置知识:Dart 语法核心知识点总结(下)
开发语言·flutter·dart
Ulyanov3 小时前
从桌面到云端:构建Web三维战场指挥系统
开发语言·前端·python·tkinter·pyvista·gui开发
星火开发设计3 小时前
C++ 函数定义与调用:程序模块化的第一步
java·开发语言·c++·学习·函数·知识