Skip to content

Instantly share code, notes, and snippets.

@AchrafKassioui
Last active October 29, 2024 19:56
Show Gist options
  • Save AchrafKassioui/8f8661641d3ec779debc5745f8673f29 to your computer and use it in GitHub Desktop.
Save AchrafKassioui/8f8661641d3ec779debc5745f8673f29 to your computer and use it in GitHub Desktop.
Testing GameplayKit's GKSKNodeComponent with SpriteKit.
/**
# Testing GKSKNodeComponent
Apple documentation: https://developer.apple.com/documentation/gameplaykit/gksknodecomponent
A GKSKNodeComponent is a special class of component that contains a node by default.
When a GKSKNodeComponent is added to an entity, the `node.entity` property of the node is automatically assigned.
Whereas if we use a regular GKComponent with a node inside it, we must assign that property manually inside `didAddToEntity` and `willRemoveFromEntity`.
Note that in both cases, the SpriteKit scene must hold a strong reference to the entity in order to retrieve it.
Achraf Kassioui
Created 28 October 2024
updated 29 October 2024
*/
import SwiftUI
import SpriteKit
import GameplayKit
// MARK: SwiftUI
struct GKSKView: View {
var body: some View {
SpriteView(scene: GKSKScene())
.ignoresSafeArea()
}
}
#Preview {
GKSKView()
}
class GKSKScene: SKScene {
// MARK: Scene Setup
/// An entity must be stored as a class property, not inside a closure, so that SpriteKit maintains a strong reference to it
let myEntity = GKEntity()
override func didMove(to view: SKView) {
size = view.bounds.size
anchorPoint = CGPoint(x: 0.5, y: 0.5)
backgroundColor = .gray
view.isMultipleTouchEnabled = true
createEntities()
/// An entity created inside this closure won't be retrievable outside the closure
let entity = GKEntity()
entity.addComponent(GKSKRenderingComponent(color: .systemRed, size: CGSize(width: 100, height: 100)))
if let component = entity.component(ofType: GKSKRenderingComponent.self) {
component.node.position.y = 100
addChild(component.node)
if let entity = component.node.entity {
/// This works here, but not outside `didMove`
print("Entity inside a closure: \(entity)")
}
}
}
func createEntities() {
let component = GKSKRenderingComponent(color: .systemYellow, size: CGSize(width: 100, height: 100))
/// We use the entity initialized with the class
myEntity.addComponent(component)
if let renderingComponent = myEntity.component(ofType: GKSKRenderingComponent.self) {
addChild(renderingComponent.node)
}
print("Entity created with a strong reference: \(myEntity)")
}
// MARK: Touch
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchedNode = atPoint(touch.location(in: self))
/// Only the strongly referenced entity will be found here
print(touchedNode.entity ?? "No entity found on this node")
}
}
}
// MARK: Rendering Components
class GKSKRenderingComponent: GKSKNodeComponent {
init(texture: SKTexture? = nil, color: SKColor, size: CGSize) {
let sprite = SKSpriteNode(texture: texture, color: color, size: size)
/// The passed in node will automatically manage its entity property
super.init(node: sprite)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment