I’m practicing with writing SpriteKit games.
I’m trying to write merge game using SpriteKit (https://plays.org/merge-the-gems/).

For now I’ve created game scene with board, added gems on board and created moving interaction.
Here is Gem node’s code:
import SpriteKit
class Gem: SKSpriteNode {
private var touchOffset: CGPoint = CGPoint.zero
private var originalPosition: CGPoint = CGPoint.zero
init(size: CGSize) {
super.init(
texture: nil,
color: .red,
size: size
)
let numberLabel: SKLabelNode = .init(text: name)
physicsBody = SKPhysicsBody(rectangleOf: .init(width: size.width - 2, height: size.height))
physicsBody?.affectedByGravity = true
physicsBody?.restitution = 0
physicsBody?.friction = 0
physicsBody?.allowsRotation = false
physicsBody?.categoryBitMask = 0b0001
physicsBody?.collisionBitMask = 0b0001
physicsBody?.contactTestBitMask = 0b0001
physicsBody?.node?.zPosition = 1
physicsBody?.isDynamic = true
isUserInteractionEnabled = true
addChild(numberLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
physicsBody?.affectedByGravity = false
let location = touch.location(in: parent!)
if contains(location) {
touchOffset = CGPoint(x: position.x - location.x, y: position.y - location.y)
originalPosition = position
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: parent!)
var newPosition = CGPoint(x: location.x + touchOffset.x, y: location.y + touchOffset.y)
newPosition.x = max(min(newPosition.x, (scene?.size.width ?? 0) - size.width / 2), size.width / 2)
newPosition.y = max(min(newPosition.y, (scene?.size.height ?? 0) - size.height / 2), size.height / 2)
position = newPosition
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let column: Int = Int(position.x / size.width)
position.x = (CGFloat(column) * size.width) + (size.width / 2)
physicsBody?.affectedByGravity = true
}
}
Here’s scene code:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
override init(size: CGSize) {
super.init(size: size)
setupScene()
createGameBoard()
isUserInteractionEnabled = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupScene() {
physicsWorld.gravity = CGVector(dx: 0, dy: -7)
physicsWorld.contactDelegate = self
let borderBody: SKPhysicsBody = .init(edgeLoopFrom: self.frame)
borderBody.friction = 0
borderBody.restitution = 0
self.physicsBody = borderBody
}
func createGameBoard() {
let gemWidth: CGFloat = size.width / 6.0
let gemSize: CGSize = .init(width: gemWidth, height: gemWidth)
for row in 0..<8 {
for column in 0..<6 {
let positionX: CGFloat = CGFloat(column) * gemWidth + gemWidth / 2
let positionY: CGFloat = size.height - (CGFloat(row) * gemWidth + gemWidth / 2)
let backgroundNode: SKSpriteNode = getBackgroundNode()
backgroundNode.size = gemSize
backgroundNode.position = CGPoint(x: positionX, y: positionY)
addChild(backgroundNode)
if row >= 6 {
let gem: Gem = .init(size: gemSize)
gem.size = gemSize
gem.position = CGPoint(x: positionX, y: positionY)
addChild(gem)
gems.append(gem)
}
}
}
}
func getBackgroundNode() -> SKSpriteNode {
let background: SKSpriteNode = .init(imageNamed: "Diamond/empty")
background.isUserInteractionEnabled = false
return background
}
}
Problem.
The problem is I can’t achieve same behavior as in example, where I can move gem only when there is no other gems on it’s way. Now it’s just pushing other gems. I need exactly the same moving behavior as in example above.
While I’m moving gem with gesture I need other gems to be “walls”, so I can slide along their sides.




