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
相关产品推荐
相关产品推荐

