Flutter for OpenHarmony 万能游戏库App实战 - 21点游戏实现

21点是一个经典的赌场纸牌游戏。这篇文章我们来实现一个完整的21点游戏,包括游戏逻辑、玩家和庄家的操作、以及结果判定。通过这个功能,我们能展示如何构建一个对抗性的游戏

页面的基本结构

BlackjackScreen是21点游戏的主页面:

dart 复制代码
class BlackjackScreen extends StatefulWidget {
  const BlackjackScreen({super.key});

  @override
  State<BlackjackScreen> createState() => _BlackjackScreenState();
}

class _BlackjackScreenState extends State<BlackjackScreen> {
  final DeckOfCardsApi _api = DeckOfCardsApi();
  String? _deckId;
  List<dynamic> _playerCards = [];
  List<dynamic> _dealerCards = [];
  bool _isLoading = false;
  bool _gameOver = false;
  String _result = '';

_deckId存储牌组ID。
_playerCards和_dealerCards分别存储玩家和庄家的牌。
_gameOver表示游戏是否结束。

得分计算

_calculateScore方法计算牌的总分:

dart 复制代码
  int _calculateScore(List<dynamic> cards) {
    int score = 0;
    int aces = 0;
    for (var card in cards) {
      final value = card['value'];
      if (value == 'ACE') {
        aces++;
        score += 11;
      } else if (['KING', 'QUEEN', 'JACK'].contains(value)) {
        score += 10;
      } else {
        score += int.tryParse(value) ?? 0;
      }
    }
    while (score > 21 && aces > 0) {
      score -= 10;
      aces--;
    }
    return score;
  }

A可以算作1或11,根据情况自动调整。
10、J、Q、K都算作10。
其他牌按数字计算。

游戏开始

_startGame方法初始化游戏:

dart 复制代码
  Future<void> _startGame() async {
    setState(() => _isLoading = true);
    try {
      final deck = await _api.getNewDeck();
      _deckId = deck['deck_id'];
      final cards = await _api.drawCards(_deckId!, count: 4);
      final allCards = cards['cards'] as List;
      setState(() {
        _playerCards = [allCards[0], allCards[2]];
        _dealerCards = [allCards[1], allCards[3]];
        _gameOver = false;
        _result = '';
        _isLoading = false;
      });
    } catch (e) {
      setState(() => _isLoading = false);
    }
  }

创建新牌组并发4张牌(玩家2张,庄家2张)。

要牌操作

_hit方法让玩家要牌:

dart 复制代码
  Future<void> _hit() async {
    if (_deckId == null || _gameOver) return;
    setState(() => _isLoading = true);
    try {
      final cards = await _api.drawCards(_deckId!, count: 1);
      final newCard = (cards['cards'] as List).first;
      setState(() {
        _playerCards.add(newCard);
        _isLoading = false;
      });
      if (_calculateScore(_playerCards) > 21) {
        _endGame('爆牌!你输了');
      }
    } catch (e) {
      setState(() => _isLoading = false);
    }
  }

给玩家增加一张牌。
如果超过21就立即结束游戏。

停牌操作

_stand方法让玩家停牌,庄家继续要牌:

dart 复制代码
  Future<void> _stand() async {
    if (_deckId == null || _gameOver) return;
    setState(() => _isLoading = true);
    try {
      while (_calculateScore(_dealerCards) < 17) {
        final cards = await _api.drawCards(_deckId!, count: 1);
        final newCard = (cards['cards'] as List).first;
        _dealerCards.add(newCard);
      }
      setState(() => _isLoading = false);
      _checkWinner();
    } catch (e) {
      setState(() => _isLoading = false);
    }
  }

庄家要牌直到达到17或以上。
然后检查赢家。

赢家判定

_checkWinner方法判定赢家:

dart 复制代码
  void _checkWinner() {
    final playerScore = _calculateScore(_playerCards);
    final dealerScore = _calculateScore(_dealerCards);
    if (dealerScore > 21) {
      _endGame('庄家爆牌!你赢了!');
    } else if (playerScore > dealerScore) {
      _endGame('你赢了!');
    } else if (playerScore < dealerScore) {
      _endGame('庄家赢了');
    } else {
      _endGame('平局');
    }
  }

比较玩家和庄家的得分。

游戏UI

游戏页面显示庄家的牌、玩家的牌和操作按钮:

dart 复制代码
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('21点')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            _buildHandSection('庄家', _dealerCards, !_gameOver),
            const Spacer(),
            if (_result.isNotEmpty)
              Card(
                color: _result.contains('赢') ? Colors.green : (_result.contains('输') || _result.contains('庄家赢') ? Colors.red : Colors.orange),
                child: Padding(
                  padding: const EdgeInsets.all(16),
                  child: Text(_result, style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold)),
                ),
              ),
            const Spacer(),
            _buildHandSection('你的手牌', _playerCards, false),

上面显示庄家的牌,下面显示玩家的牌。
中间显示游戏结果。

手牌显示

_buildHandSection方法显示手牌:

dart 复制代码
  Widget _buildHandSection(String title, List<dynamic> cards, bool hideSecond) {
    final score = hideSecond && cards.length > 1 ? '?' : _calculateScore(cards).toString();
    return Column(
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
            Text('点数: $score', style: const TextStyle(fontWeight: FontWeight.bold)),
          ],
        ),
        const SizedBox(height: 12),
        SizedBox(
          height: 120,
          child: cards.isEmpty
              ? const Center(child: Text('等待发牌...', style: TextStyle(color: Colors.grey)))
              : ListView.builder(
                  scrollDirection: Axis.horizontal,
                  itemCount: cards.length,
                  itemBuilder: (context, index) {
                    if (hideSecond && index == 1) {
                      return Container(
                        width: 80,
                        margin: const EdgeInsets.only(right: 8),
                        decoration: BoxDecoration(
                          color: Colors.blue[900],
                          borderRadius: BorderRadius.circular(8),
                        ),
                        child: const Center(child: Icon(Icons.question_mark, color: Colors.white, size: 32)),
                      );
                    }
                    return Container(
                      width: 80,
                      margin: const EdgeInsets.only(right: 8),
                      child: AppNetworkImage(imageUrl: cards[index]['image'] ?? '', fit: BoxFit.contain),
                    );
                  },
                ),
        ),
      ],
    );
  }

游戏进行中,庄家的第二张牌隐藏。
使用AppNetworkImage显示牌的图片。

操作按钮

游戏进行中显示"要牌"和"停牌"按钮:

dart 复制代码
            if (_playerCards.isEmpty)
              ElevatedButton.icon(
                onPressed: _isLoading ? null : _startGame,
                icon: const Icon(Icons.play_arrow),
                label: const Text('开始游戏'),
                style: ElevatedButton.styleFrom(minimumSize: const Size(double.infinity, 52)),
              )
            else if (!_gameOver)
              Row(
                children: [
                  Expanded(
                    child: ElevatedButton.icon(
                      onPressed: _isLoading ? null : _hit,
                      icon: const Icon(Icons.add),
                      label: const Text('要牌'),
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: OutlinedButton.icon(
                      onPressed: _isLoading ? null : _stand,
                      icon: const Icon(Icons.stop),
                      label: const Text('停牌'),
                    ),
                  ),
                ],
              )
            else
              ElevatedButton.icon(
                onPressed: _isLoading ? null : _startGame,
                icon: const Icon(Icons.replay),
                label: const Text('再来一局'),
                style: ElevatedButton.styleFrom(minimumSize: const Size(double.infinity, 52)),
              ),

游戏开始前显示"开始游戏"按钮。
游戏进行中显示"要牌"和"停牌"按钮。
游戏结束后显示"再来一局"按钮。

总结

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

  • 游戏逻辑 - 实现21点的规则和得分计算
  • API集成 - 使用Deck of Cards API获取牌
  • 状态管理 - 管理游戏的多个状态
  • 对抗性游戏 - 实现玩家和庄家的对抗
  • 结果判定 - 实现赢家判定逻辑
  • UI设计 - 清晰地展示游戏状态

21点游戏展示了如何构建一个对抗性的游戏 。通过完善的游戏逻辑、流畅的交互、以及清晰的UI设计,我们能为用户提供一个有趣的游戏体验


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

相关推荐
Duang007_2 小时前
【万字学习总结】API设计与接口开发实战指南
开发语言·javascript·人工智能·python·学习
Alex老夫子2 小时前
android room数据库增加字段注意事项
android·数据库
2501_915918412 小时前
iOS App 测试方法,Xcode、TestFlight与克魔(KeyMob)等工具组合使用
android·macos·ios·小程序·uni-app·iphone·xcode
每天吃饭的羊2 小时前
hash结构
开发语言·前端·javascript
吃吃喝喝小朋友2 小时前
JavaScript异步编程
前端·javascript
木斯佳2 小时前
HarmonyOS实战(源码教学篇)— 深入浅出鸿蒙特性【跨端迁移-应用接续】
华为·harmonyos
哈哈你是真的厉害2 小时前
小白基础入门 React Native 鸿蒙跨平台开发:AnimatedSpring 弹簧动画
react native·react.js·harmonyos
冬奇Lab2 小时前
Android 15 显示子系统深度解析(一):显示框架总览与SurfaceFlinger核心机制
android·性能优化
哈哈你是真的厉害3 小时前
基础入门 React Native 鸿蒙跨平台开发:颜色选择器工具
react native·react.js·harmonyos