You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI开发卡牌游戏:求助实现卡牌翻面显示功能

SwiftUI卡牌游戏全局状态管理与卡牌翻面实现

问题描述

我正在开发一款卡牌游戏,初始时庄家和玩家各有8张牌背面朝上。当玩家或庄家要牌/发牌时,希望将对应的卡牌翻面显示正面。

尝试用SwiftUI的@ObservableObject和.environmentObject全局传递实例,之前实现过基础版本但忘了具体方法。我是SwiftUI新手,以下是我的代码:

@State var dealerHand: [String]
@State var playerHand: [String]
@State var dealerHasCard:[Bool]
@State var playerHasCard:[Bool]
    var body: some View {
        HStack(spacing: -30.0) {
            Spacer()
            Image(dealerHasCard[0] ? "\($dealerHand[0])" : faceDown)
            Image(dealerHasCard[1] ? "\($dealerHand[1])" : faceDown)
            Image(dealerHasCard[2] ? "\($dealerHand[2])" : faceDown)
            Image(dealerHasCard[3] ? "\($dealerHand[3])" : faceDown)
            Image(dealerHasCard[4] ? "\($dealerHand[4])" : faceDown)
            Image(dealerHasCard[5] ? "\($dealerHand[5])" : faceDown)
            Image(dealerHasCard[6] ? "\($dealerHand[6])" : faceDown)
            Image(dealerHasCard[7] ? "\($dealerHand[7])" : faceDown)
            Spacer()
        }
        Text("You")
            .font(.title)
            .foregroundColor(.blue)

        HStack(spacing: -30.0) {
            Spacer()
            Image(playerHasCard[0] ? "\($playerHand[0])" : faceDown)
            Image(playerHasCard[1] ? "\($playerHand[1])" : faceDown)
            Image(playerHasCard[2] ? "\($playerHand[2])" : faceDown)
            Image(playerHasCard[3] ? "\($playerHand[3])" : faceDown)
            Image(playerHasCard[4] ? "\($playerHand[4])" : faceDown)
            Image(playerHasCard[5] ? "\($playerHand[5])" : faceDown)
            Image(playerHasCard[6] ? "\($playerHand[6])" : faceDown)
            Image(playerHasCard[7] ? "\($playerHand[7])" : faceDown)
            Spacer()
        }
    }
}
func showCards(dealerHand: [String], playerHand: [String], dealerHasCards: [Bool], playerHasCards: [Bool]) -> ([String], [String], [Bool], [Bool]) {

    var dealerCards:[String] = ["","","","","","","",""]
    var playerCards:[String] = ["","","","","","","",""]
    var dealerHasCards:[Bool] = [false,false,false,false,false,false,false,false]
    var playerHasCards:[Bool] = [false,false,false,false,false,false,false,false]
    
    for i in 0..<dealerHand.count {
        if dealerHand[i] != "" {
            dealerCards[i] = dealerHand[i]
            dealerHasCards[i] = true
        } else {
            dealerCards[i] = faceDown
            dealerHasCards[i] = false
        }
     }

    for i in 0..<playerHand.count {
        if playerHand[i] != "" {
            playerCards[i] = playerHand[i]
            playerHasCards[i] = true
        } else {
            playerCards[i] = faceDown
            playerHasCards[i] = false
        }
     }
   
    if gameOver {
        message = checkResults(playerScore: playerScore, dealerScore: dealerScore)
    }
    
    return (dealerCards, playerCards, dealerHasCards, playerHasCards)
}

解决方案

1. 封装全局游戏状态管理类

创建一个遵循ObservableObject的类,把所有游戏状态和逻辑集中在这里,方便跨视图共享:

import SwiftUI

class GameManager: ObservableObject {
    // 庄家和玩家的卡牌数组(空字符串表示未发牌)
    @Published var dealerHand: [String] = Array(repeating: "", count: 8)
    @Published var playerHand: [String] = Array(repeating: "", count: 8)
    
    // 标记每张牌是否翻面显示正面
    @Published var dealerHasCard: [Bool] = Array(repeating: false, count: 8)
    @Published var playerHasCard: [Bool] = Array(repeating: false, count: 8)
    
    // 游戏状态相关
    @Published var gameOver = false
    @Published var resultMessage = ""
    private(set) var playerScore = 0
    private(set) var dealerScore = 0
    
    // 给玩家发牌:指定位置设置卡牌并翻面
    func dealPlayerCard(at index: Int, cardName: String) {
        guard index < playerHand.count, playerHand[index].isEmpty else { return }
        playerHand[index] = cardName
        playerHasCard[index] = true
    }
    
    // 给庄家发牌
    func dealDealerCard(at index: Int, cardName: String) {
        guard index < dealerHand.count, dealerHand[index].isEmpty else { return }
        dealerHand[index] = cardName
        dealerHasCard[index] = true
    }
    
    // 结算游戏结果
    func calculateResult() {
        // 这里替换成你的实际分数计算逻辑,示例按卡牌数量算分
        playerScore = playerHand.filter { !$0.isEmpty }.count
        dealerScore = dealerHand.filter { !$0.isEmpty }.count
        
        resultMessage = playerScore > dealerScore ? "你赢了!" : playerScore < dealerScore ? "庄家赢了!" : "平局!"
        gameOver = true
    }
}

2. 在根视图注入环境对象

在App入口处初始化GameManager,并通过.environmentObject注入到整个视图环境中:

@main
struct CardGameApp: App {
    // @StateObject用于管理ObservableObject实例的生命周期
    @StateObject private var gameManager = GameManager()
    
    var body: some Scene {
        WindowGroup {
            GameView()
                .environmentObject(gameManager)
        }
    }
}

3. 重构游戏视图

在视图中通过@EnvironmentObject获取全局状态,用循环生成卡牌避免重复代码,同时添加操作按钮:

struct GameView: View {
    // 从环境中获取游戏管理器实例
    @EnvironmentObject var gameManager: GameManager
    // 替换成你的背面卡牌图片名称
    let faceDownCard = "card_back"
    
    var body: some View {
        VStack(spacing: 25) {
            // 庄家卡牌区域
            Text("庄家")
                .font(.title)
                .foregroundColor(.red)
            
            HStack(spacing: -30) {
                Spacer()
                ForEach(0..<8, id: \.self) { index in
                    Image(gameManager.dealerHasCard[index] ? gameManager.dealerHand[index] : faceDownCard)
                        .resizable()
                        .frame(width: 100, height: 150)
                }
                Spacer()
            }
            
            // 玩家卡牌区域
            Text("你")
                .font(.title)
                .foregroundColor(.blue)
            
            HStack(spacing: -30) {
                Spacer()
                ForEach(0..<8, id: \.self) { index in
                    Image(gameManager.playerHasCard[index] ? gameManager.playerHand[index] : faceDownCard)
                        .resizable()
                        .frame(width: 100, height: 150)
                }
                Spacer()
            }
            
            // 操作按钮组
            HStack(spacing: 20) {
                Button("要牌") {
                    // 找到第一个未发牌的位置给玩家发牌
                    if let emptyIndex = gameManager.playerHand.firstIndex(where: { $0.isEmpty }) {
                        // 这里替换成实际的卡牌名称生成逻辑
                        gameManager.dealPlayerCard(at: emptyIndex, cardName: "player_card_\(emptyIndex)")
                    }
                }
                .buttonStyle(.borderedProminent)
                
                Button("停牌") {
                    // 模拟庄家自动发牌逻辑(比如发4张)
                    for index in 0..<4 {
                        if gameManager.dealerHand[index].isEmpty {
                            gameManager.dealDealerCard(at: index, cardName: "dealer_card_\(index)")
                        }
                    }
                    // 结算结果
                    gameManager.calculateResult()
                }
                .buttonStyle(.bordered)
            }
            
            // 显示游戏结果
            if gameManager.gameOver {
                Text(gameManager.resultMessage)
                    .font(.largeTitle)
                    .foregroundColor(.green)
                    .padding(.top, 10)
            }
        }
        .padding()
    }
}

核心要点说明

  • @ObservableObject + @Published:@Published标记的属性变化时,会自动通知所有依赖的视图刷新
  • .environmentObject:一次性注入全局状态,所有子视图无需手动传递参数即可获取
  • 用ForEach循环生成卡牌,避免重复编写8个Image组件,代码更简洁易维护
  • 游戏逻辑和视图展示分离,符合MVVM架构,后续扩展功能更方便

内容的提问来源于stack exchange,提问作者user3487784

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 01:12:43