Swift SpriteKit匹配游戏:双节点触摸追踪与纹理匹配/重置功能实现技术问询
Hey there! Let's tackle your matching game requirements step by step using SpriteKit in Swift. I'll start by breaking down the core logic gaps in your current code, then provide a complete, modified implementation that covers all three features you need.
Core Problem Breakdown
Your current code handles basic touch detection but lacks:
- A way to track the two selected cards for comparison
- State management for each card (original texture, matched texture, whether it's already paired)
- Logic to disable interaction when cards match, or reset textures when they don't
Solution Implementation
1. Create a Custom MatchCard Class
First, let's make a reusable MatchCard subclass of SKSpriteNode to encapsulate each card's state and behavior. This will make your code cleaner and easier to maintain:
import SpriteKit class MatchCard: SKSpriteNode { // Store the original back texture and the front match texture let originalTexture: SKTexture let matchTexture: SKTexture // Track if this card has already been matched (and should ignore touches) var isMatched: Bool = false init(originalImageName: String, matchImageName: String) { self.originalTexture = SKTexture(imageNamed: originalImageName) self.matchTexture = SKTexture(imageNamed: matchImageName) super.init(texture: originalTexture, color: .clear, size: CGSize(width: 150, height: 300)) // Enable user interaction for the card isUserInteractionEnabled = true } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // Flip the card to show its match texture func flipToMatch() { texture = matchTexture } // Reset the card back to its original texture func resetToBack() { texture = originalTexture } }
2. Modified EasyScreen Scene
Now let's update your EasyScreen class to manage selected cards, handle touch logic, and implement the matching/reset behavior:
import Foundation import SpriteKit class EasyScreen: SKScene { // Track the two currently selected (unmatched) cards private var selectedCards: [MatchCard] = [] // Timer counter (made a property so we can manage it easily) private var timerCounter: Int = 90 override func didMove(to view: SKView) { // Enable multi-touch (though we'll handle sequential taps, this ensures no restrictions) view.isMultipleTouchEnabled = true // Background setup let background = SKSpriteNode(imageNamed: "Easy Screen Background") background.position = CGPoint(x: size.width/2, y: size.height/2) background.size = size background.anchorPoint = CGPoint(x: 0.5,y: 0.5) addChild(background) // Timer label setup let timerText = SKLabelNode(fontNamed: "Arial") timerText.fontSize = 40 timerText.fontColor = .white timerText.position = CGPoint(x: 20, y: 400) timerText.zPosition = 1 timerText.text = " Time: \(timerCounter)" // Timer action timerText.run(SKAction.repeatForever(SKAction.sequence([ SKAction.run { [weak self] in guard let self = self else { return } self.timerCounter -= 1 timerText.text = " Time: \(self.timerCounter)" print("\(self.timerCounter)") if self.timerCounter <= 0 { guard let newScene = TryAgainScreen(fileNamed: "Try Again Screen") else { return } newScene.scaleMode = .aspectFill self.view?.presentScene(newScene) } }, SKAction.wait(forDuration: 1) ]))) addChild(timerText) // Create all match cards using our custom class let card1 = MatchCard(originalImageName: "Fruit Match Card", matchImageName: "Apple") card1.name = "FruitMatchCard1" card1.position = CGPoint(x: -125, y: 60) card1.zPosition = 1 let card2 = MatchCard(originalImageName: "Fruit Match Card", matchImageName: "Apple") card2.name = "FruitMatchCard2" card2.position = CGPoint(x: -125, y: -260) card2.zPosition = 1 let card3 = MatchCard(originalImageName: "Fruit Match Card", matchImageName: "Grapes") card3.name = "FruitMatchCard3" card3.position = CGPoint(x: 70, y: 60) card3.zPosition = 1 let card4 = MatchCard(originalImageName: "Fruit Match Card", matchImageName: "Grapes") card4.name = "FruitMatchCard4" card4.position = CGPoint(x: 70 , y: -260) card4.zPosition = 1 // Sound button setup let soundButton = SKSpriteNode(imageNamed: "Sound On") soundButton.name = "Sound" soundButton.size = CGSize(width: 75, height: 75) soundButton.position = CGPoint(x: -180, y: -600) soundButton.zPosition = 3 // Add all nodes to the scene addChild(card1) addChild(card2) addChild(card3) addChild(card4) addChild(soundButton) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let touchLocation = touch.location(in: self) let tappedNode = atPoint(touchLocation) // Handle sound button tap separately if tappedNode.name == "Sound" { print("Sound button tapped - add your toggle logic here!") return } // Make sure we tapped a MatchCard that hasn't been matched yet guard let tappedCard = tappedNode as? MatchCard, !tappedCard.isMatched else { return } // Don't let the user select more than 2 cards at once guard selectedCards.count < 2 else { return } // Flip the card to show its front texture tappedCard.flipToMatch() // Add it to our selected cards list selectedCards.append(tappedCard) // If we have 2 selected cards, check for a match if selectedCards.count == 2 { let firstCard = selectedCards[0] let secondCard = selectedCards[1] // Compare the match textures to see if they're a pair let isMatch = firstCard.matchTexture == secondCard.matchTexture if isMatch { // Match successful: disable interaction for both cards firstCard.isMatched = true secondCard.isMatched = true firstCard.isUserInteractionEnabled = false secondCard.isUserInteractionEnabled = false selectedCards.removeAll() print("Match found! Cards disabled.") } else { // Match failed: wait 1 second, then reset both cards run(SKAction.wait(forDuration: 1.0)) { [weak self] in firstCard.resetToBack() secondCard.resetToBack() self?.selectedCards.removeAll() print("No match! Cards reset.") } } } } }
Key Feature Explanations
- Dual Touch Tracking: The
selectedCardsarray keeps track of up to 2 unmatched cards. We ignore taps once 2 cards are selected until the match check completes. - Disable Interaction on Match: When cards match, we set
isMatchedto true and turn offisUserInteractionEnabled, so the user can't tap those cards again. - Reset Textures on No Match: We add a 1-second delay (using
SKAction.wait) to let the user see both cards, then reset them back to their original texture and clear the selected list.
Bonus Optimization Tips
- Add flip animations: Use
SKAction.scaleX(to: 0, duration: 0.2)before switching textures, then scale back to 1 for a smooth flip effect. - Add sound effects: Play a success sound when cards match, and a different sound when they don't.
- Handle edge cases: Make sure the user can't tap the same card twice (add a check in
touchesBeganto see iftappedCardis already inselectedCards).
内容的提问来源于stack exchange,提问作者Developer0113
相关产品推荐
相关产品推荐

