Last active
October 29, 2024 19:56
-
-
Save AchrafKassioui/8f8661641d3ec779debc5745f8673f29 to your computer and use it in GitHub Desktop.
Testing GameplayKit's GKSKNodeComponent with SpriteKit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
# 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