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

相关推荐
学嵌入式的小杨同学12 小时前
【Linux 封神之路】信号编程全解析:从信号基础到 MP3 播放器实战(含核心 API 与避坑指南)
java·linux·c语言·开发语言·vscode·vim·ux
lang2015092812 小时前
JSR-340 :高性能Web开发新标准
java·前端·servlet
Re.不晚13 小时前
Java入门17——异常
java·开发语言
缘空如是13 小时前
基础工具包之JSON 工厂类
java·json·json切换
追逐梦想的张小年13 小时前
JUC编程04
java·idea
好家伙VCC13 小时前
### WebRTC技术:实时通信的革新与实现####webRTC(Web Real-TimeComm
java·前端·python·webrtc
南极星100514 小时前
蓝桥杯JAVA--启蒙之路(十)class版本 模块
java·开发语言
消失的旧时光-194314 小时前
第十三课:权限系统如何设计?——RBAC 与 Spring Security 架构
java·架构·spring security·rbac
baidu_2474386114 小时前
Android ViewModel定时任务
android·开发语言·javascript
不能隔夜的咖喱14 小时前
牛客网刷题(2)
java·开发语言·算法