Hello, and an early "Merry Christmas" to all,
I'm building a SwiftUI app, and one of my Views is a fullscreen UIViewRepresentable (SpriteView) beneath a SwiftUI interface.
Whenever the user interacts with any SwiftUI element, the UIView registers a hit in touchesBegan(). For example, my UIView has logic for pinching (not implemented via UIGestureRecognizer), so whenever the user holds down a SwiftUI element while touching the UIView, that counts as two touches to the UIView which invokes the pinching logic.
Things I've tried to block SwiftUI from passing the gesture down to the UIView:
Adding opaque elements beneath control elements
Adding gestures to the elements above
Adding gesture masks to the gestures above
Converting eligible elements to Buttons (since those seem immune)
Adding SpriteViews beneath those elements to absorb gestures
So far nothing has worked. As long as the UIView is beneath SwiftUI elements, any interactions with those elements will be registered as a hit.
The obvious solution is to track each SwiftUI element's size and coordinates with respect to the UIView's coordinate space, then use exclusion areas, but this is both a pain and expensive, and I find it hard to believe this is the best fix for such a seemingly basic problem.
I'm probably overlooking something basic, so any suggestions will be greatly appreciated
SpriteKit
RSS for tagDrawing shapes, particles, text, images, and video in two dimensions using SpriteKit.
Posts under SpriteKit tag
49 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
I am making a SpriteKit game and I am trying to change the cursor image from the default pointer to a png image that I have imported into the project, but it’s not really working. when I run the project I can see the cursor image change for a brief second and then return to the default image. Here is my code:
print(NSCursor.current)
if let image = NSImage(named: customImage) {
print("The image exists")
cursor = NSCursor(image: image, hotSpot: .zero)
cursor.push()
print(cursor)
}
print(NSCursor.current)
The above code is all contained in the didMove(:) function in GameScene. From the print statements I can see that the memory address of the NSCursor.current changes to that of cursor. HOWEVER, in the mouseMoved(:) call back function I print out the mouse location and the current cursor. I can see from these print stamens that the cursor memory address has again changed and no longer matches the custom cursor address… so I am not sure what is going on…
Also, fyi the cursor is a global property within game scene so it should persist. Also, image is not nil. This is verified by the print statements I see
Thanks
Hello reader,
I am facing an issue that I am not able to resolve. I have been able to create a demo project that demonstrates the issue, which I hope enables you to have a look as well and hopefully find a way to resolve it.
What is the issue:
I am using SKTileMapNode in order to draw tile maps. Instead of using the tilesets as you can use from within the Xcode editor, I prefer to do it all programmatically using tilesheets (for a plethora of reasons that I will leave out of this equation).
This is the code of the gameScene:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private let tileSize = CGSize(width: 32, height: 32)
override func didMove(to view: SKView) {
super.didMove(to: view)
let tileSet = createTileSet()
let tileMap = SKTileMapNode(tileSet: tileSet,
columns: 100,
rows: 100,
tileSize: tileSize)
for column in 0..<tileMap.numberOfColumns {
for row in 0..<tileMap.numberOfRows {
guard let tileGroup = tileSet.tileGroups.randomElement() else {
fatalError()
}
tileMap.setTileGroup(tileGroup, forColumn: column, row: row)
}
}
addChild(tileMap)
}
private func createTileSet() -> SKTileSet {
let tileSheetTexture = SKTexture(imageNamed: "terrain")
var tileGroups = [SKTileGroup]()
let relativeTileSize = CGSize(width: tileSize.width/tileSheetTexture.size().width,
height: tileSize.height/tileSheetTexture.size().height)
for idx in 0...2 {
for jdx in 0...2 {
let tileTexture = SKTexture(rect: .init(x: CGFloat(idx) * relativeTileSize.width,
y: CGFloat(jdx) * relativeTileSize.height,
width: relativeTileSize.width,
height: relativeTileSize.height),
in: tileSheetTexture)
let tileDefinition = SKTileDefinition(texture: tileTexture,
size: tileSize)
let tileGroup = SKTileGroup(tileDefinition: tileDefinition)
tileGroups.append(tileGroup)
}
}
let tileSet = SKTileSet(tileGroups: tileGroups)
return tileSet
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
presentSceneAgain()
}
func presentSceneAgain() {
if let frame = view?.frame {
view?.presentScene(GameScene(size: frame.size),
transition: .doorsCloseHorizontal(withDuration: 1.0))
}
}
}
This demo project create a tilemapnode of 100 X 100 tiles. Then, it fills these 10.000 tiles with a random tile from the tilesheet named "terrain.png". This tile sheet contains many tiles, but I only take the 9 tiles (3 X 3) from the lower left corner as a random tile option.
Thus, the 10.000 tiles get filled with one of these 9 tiles. So it doesnt look pretty or anything, but that isnt the purpose.
Now, to create these 9 tile textures, I use the SKTexture(rectIn:) method on the source texture being "terrain.png".
I think the code is quite clear in itself, but so far the explanation. When you run it, you should see the map being rendered.
When you tap the scene, the scene will present a new instance of the scene. Not more than that.
Now, when you do this, have a look at the RAM usage of the app. You will see it steadily increases over time, each time you click the scene and a new scene is presented.
I looked deeper into what is happening, and what I see in the memory graph, is that for every present of the scene that is done, there are 3 SKTexture instances being created that are never released. The first time the scene is rendered, there 11 SKTexture instances allocated (I dont know why there are 11 though. I would expect 10: the source texture and the 9 tile textures).
But then as mentioned, after a tap and a new present, I get 14 SKTexture, of which 3 are zombies, see image leak_1.
Moreover, Xcode reports multiple additional leaks from Jet and Metal allocations, see image leak_all.
As far as I know, the code presented is not retaining any references that it should not, and I suspect this leaks are happening somewhere inside SpriteKit. But I am not able to find exactly where, or how to resolve it.
I hope someone can help with this issue.
When running the sample code below, every 3 seconds the middle sprite is replaced by a new one. When this happens, most of the time a flicker is noticeable. When recording the screen and stepping through the recording frame by frame, I noticed that the flicker is caused by a temporary reordering of the nodes’. Below you find two screenshots of two consecutive frames where the reordering is clearly visible.
This only happens for a SpriteKit scene used as an overlay for a SceneKit scene. Commenting out
buttons.zPosition = 1
or avoiding the fade in/out animations solves the issue.
I have created FB15945016.
import SceneKit
import SpriteKit
class GameViewController: NSViewController {
let overlay = SKScene()
var buttons: SKNode!
var previousButton: SKSpriteNode!
var nextButton: SKSpriteNode!
var pageContainer: SKNode!
var pageViews = [SKNode]()
var page = 0
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene(named: "art.scnassets/ship.scn")!
let scnView = self.view as! SCNView
scnView.scene = scene
overlay.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scnView.overlaySKScene = overlay
buttons = SKNode()
buttons.zPosition = 1
overlay.addChild(buttons)
previousButton = SKSpriteNode(systemImage: "arrow.uturn.backward.circle")
previousButton.position = CGPoint(x: -100, y: 0)
buttons.addChild(previousButton)
nextButton = SKSpriteNode(systemImage: "arrow.uturn.forward.circle")
nextButton.position = CGPoint(x: 100, y: 0)
buttons.addChild(nextButton)
pageContainer = SKNode()
pageViews = [SKSpriteNode(systemImage: "square.and.arrow.up"), SKSpriteNode(systemImage: "eraser")]
overlay.addChild(pageContainer)
setPage(0)
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { [self] _ in
setPage((page + 1) % 2)
}
}
func setPage(_ page: Int) {
pageViews[self.page].run(.sequence([
.fadeOut(withDuration: 0.2),
.removeFromParent()
]), withKey: "fade")
self.page = page
let pageView = pageViews[page]
pageView.alpha = 0
pageView.run(.fadeIn(withDuration: 0.2), withKey: "fade")
pageContainer.addChild(pageView)
}
override func viewDidLayout() {
overlay.size = view.frame.size
}
}
extension SKSpriteNode {
public convenience init(systemImage: String) {
self.init()
let width = 100.0
let image = NSImage(systemSymbolName: systemImage, accessibilityDescription: nil)!.withSymbolConfiguration(.init(hierarchicalColor: NSColor.black))!
let scale = NSScreen.main!.backingScaleFactor
image.size = CGSize(width: width * scale, height: width / image.size.width * image.size.height * scale)
texture = SKTexture(image: image)
size = CGSize(width: width, height: width / image.size.width * image.size.height)
}
}
Into a SKScene, I add a SCNSphere as a child of SKShapeNode, as depicted below.
When the sphere hit another node (the fence in the example) the sphere is deformed as it were elastic.
I didn't found any information about elastic properties.
Someone know a way to avoid the deformation?
import SwiftUI
import SpriteKit
import SceneKit
@main struct MyApp: App
{
var body: some Scene
{
WindowGroup {SpriteView(scene: GameSceneSK(size: UIScreen.main.bounds.size))}
}
}
class GameSceneSK: SKScene
{
override func sceneDidLoad() {
var fencePoints = [
CGPoint(x: 300, y: 0), CGPoint(x: 300, y: 400), CGPoint(x: 0, y: 400)
]
let fence = SKShapeNode(points: &fencePoints,
count: fencePoints.count)
fence.physicsBody = SKPhysicsBody(edgeChainFrom: fence.path!)
addChild(fence)
let sphereGeometry = SCNSphere(radius: 20)
let sphereNode = SCNNode(geometry: sphereGeometry)
let sphereScnScene = SCNScene()
sphereScnScene.rootNode.addChildNode(sphereNode)
let ball3D = SK3DNode(viewportSize: CGSize(width: 40,
height: 40))
ball3D.scnScene = sphereScnScene
let ball = SKShapeNode(circleOfRadius: 20)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 20)
ball.addChild(ball3D)
physicsWorld.gravity = CGVector(dx: 0.2, dy: 0.2)
addChild(ball)
}
}
I plan to create a simple motion graphics software for macOS that animates text, basic shapes, and handles audio. I'll use SwiftUI for the UI.
What are the commonly used technologies for rendering animated graphics? Core Animation is suitable for UI animations but not for exporting and controlling UI animations.
Basic requirements:
Timeline user interface
Animation of text and basic shapes
Viewer in SwiftUI GUI with transport control (play, pause, scrub, …)
Export to video file
Is Metal or Core Graphics typically used directly? I want to keep it as simple as possible.
I'm trying to make a magnifying glass that shows up when the user presses a button and follows the user's finger as it's dragged across the screen.
I came across a UIKit-based solution (https://github.com/niczyja/MagnifyingGlass-Swift), but when implemented in my SKScene, only the crosshairs are shown. Through experimentation I've found that magnifiedView?.layer.render(in: context) in:
public override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
context.translateBy(x: radius, y: radius)
context.scaleBy(x: scale, y: scale)
context.translateBy(x: -magnifiedPoint.x, y: -magnifiedPoint.y)
removeFromSuperview()
magnifiedView?.layer.render(in: context)
magnifiedView?.addSubview(self)
}
can be removed without altering the situation, suggesting that line is not working as it should. But this is where I hit a brick wall. The view below is shown but not offset or magnified, and any attempt to add something to context results in a black magnifying glass.
Does anyone know why this is? I don't think it's an issue with the code, so I'm suspecting its something specific to SpriteKit or SKScene, likely related to how CALayers work.
Any pointers would be greatly appreciated.
.
.
.
Full code below:
import UIKit
public class MagnifyingGlassView: UIView {
public weak var magnifiedView: UIView? = nil {
didSet {
removeFromSuperview()
magnifiedView?.addSubview(self)
}
}
public var magnifiedPoint: CGPoint = .zero {
didSet {
center = .init(x: magnifiedPoint.x + offset.x, y: magnifiedPoint.y + offset.y)
}
}
public var offset: CGPoint = .zero
public var radius: CGFloat = 50 {
didSet {
frame = .init(origin: frame.origin, size: .init(width: radius * 2, height: radius * 2))
layer.cornerRadius = radius
crosshair.path = crosshairPath(for: radius)
}
}
public var scale: CGFloat = 2
public var borderColor: UIColor = .lightGray {
didSet {
layer.borderColor = borderColor.cgColor
}
}
public var borderWidth: CGFloat = 3 {
didSet {
layer.borderWidth = borderWidth
}
}
public var showsCrosshair = true {
didSet {
crosshair.isHidden = !showsCrosshair
}
}
public var crosshairColor: UIColor = .lightGray {
didSet {
crosshair.strokeColor = crosshairColor.cgColor
}
}
public var crosshairWidth: CGFloat = 5 {
didSet {
crosshair.lineWidth = crosshairWidth
}
}
private let crosshair: CAShapeLayer = CAShapeLayer()
public convenience init(offset: CGPoint = .zero, radius: CGFloat = 50, scale: CGFloat = 2, borderColor: UIColor = .lightGray, borderWidth: CGFloat = 3, showsCrosshair: Bool = true, crosshairColor: UIColor = .lightGray, crosshairWidth: CGFloat = 0.5) {
self.init(frame: .zero)
layer.masksToBounds = true
layer.addSublayer(crosshair)
defer {
self.offset = offset
self.radius = radius
self.scale = scale
self.borderColor = borderColor
self.borderWidth = borderWidth
self.showsCrosshair = showsCrosshair
self.crosshairColor = crosshairColor
self.crosshairWidth = crosshairWidth
}
}
public func magnify(at point: CGPoint) {
guard magnifiedView != nil else { return }
magnifiedPoint = point
layer.setNeedsDisplay()
}
private func crosshairPath(for radius: CGFloat) -> CGPath {
let path = CGMutablePath()
path.move(to: .init(x: radius, y: 0))
path.addLine(to: .init(x: radius, y: bounds.height))
path.move(to: .init(x: 0, y: radius))
path.addLine(to: .init(x: bounds.width, y: radius))
return path
}
public override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
context.translateBy(x: radius, y: radius)
context.scaleBy(x: scale, y: scale)
context.translateBy(x: -magnifiedPoint.x, y: -magnifiedPoint.y)
removeFromSuperview()
magnifiedView?.layer.render(in: context)
//If above disabled, no change
//Possible that nothing's being rendered into context
//Could it be that SKScene view has no layer?
magnifiedView?.addSubview(self)
}
}
I would like to preload and use some images for both SpriteKit and SceneKit models (my game uses SceneKit with a SpriteKit overlay), and as far as I can see the only efficient way would be to create and preload SKTexture objects which can be supplied to SKSpriteNode(texture:) and SCNMaterial.diffuse.contents.
The problem is that SKTexture are rendered too bright in SceneKit, for some unknown reason. Here a comparison between rendering an image (from URL) and a SKTexture:
And the code that produces it:
let url = Bundle.main.url(forResource: "art.scnassets/texture.png", withExtension: nil)!
let plane1 = SCNPlane(width: 10, height: 10)
plane1.firstMaterial!.diffuse.contents = url.path
let node1 = SCNNode(geometry: plane1)
node1.position.x = -5
scene.rootNode.addChildNode(node1)
let plane2 = SCNPlane(width: 10, height: 10)
plane2.firstMaterial!.diffuse.contents = SKTexture(image: NSImage(byReferencing: url))
let node2 = SCNNode(geometry: plane2)
node2.position.x = 5
scene.rootNode.addChildNode(node2)
This issue was already mentioned in this other post, but since I wasn't notified of the reply from Quinn asking about the feedback number I created at the time, it didn't make any progress.
Hi there,
With a couple of other developers we have been busy with migrating our SpriteKit games and frameworks to Swift 6.
There is one issue we are unable to resolve, and this involves the interaction between SpriteKit and GameplayKit.
There is a very small demo repo created that clearly demonstrates the issue. It can be found here:
https://github.com/AchrafKassioui/GameplayKitExplorer/blob/main/GameplayKitExplorer/Basic.swift
The relevant code also pasted here:
import SwiftUI
import SpriteKit
struct BasicView: View {
var body: some View {
SpriteView(scene: BasicScene())
.ignoresSafeArea()
}
}
#Preview {
BasicView()
}
class BasicScene: SKScene {
override func didMove(to view: SKView) {
size = view.bounds.size
anchorPoint = CGPoint(x: 0.5, y: 0.5)
backgroundColor = .gray
view.isMultipleTouchEnabled = true
let entity = BasicEntity(color: .systemYellow, size: CGSize(width: 100, height: 100))
if let renderComponent = entity.component(ofType: BasicRenderComponent.self) {
addChild(renderComponent.sprite)
}
}
}
@MainActor
class BasicEntity: GKEntity {
init(color: SKColor, size: CGSize) {
super.init()
let renderComponent = BasicRenderComponent(color: color, size: size)
addComponent(renderComponent)
let animationComponent = BasicAnimationComponent()
addComponent(animationComponent)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
@MainActor
class BasicRenderComponent: GKComponent {
let sprite: SKSpriteNode
init(color: SKColor, size: CGSize) {
self.sprite = SKSpriteNode(texture: nil, color: color, size: size)
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class BasicAnimationComponent: GKComponent {
let action1 = SKAction.scale(to: 1.3, duration: 0.07)
let action2 = SKAction.scale(to: 1, duration: 0.15)
override init() {
super.init()
}
override func didAddToEntity() {
if let renderComponent = entity?.component(ofType: BasicRenderComponent.self) {
renderComponent.sprite.run(SKAction.repeatForever(SKAction.sequence([action1, action2])))
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
As SKNode is designed to run on the MainActor, the BasicRenderComponent is attributed with MainActor as well. This is needed as this GKComponent is dedicated to encapsulate the node that is rendered to the scene.
There is also a BasicAnimationComponent, this GKComponent is responsible for animating the rendered node.
Obviously, this is just an example, but when using GameplayKit in combination with SpriteKit it is very common that a GKComponent instance manipulates an SKNode referenced from another GKComponent instance, often done via open func update(deltaTime seconds: TimeInterval) or as in this example, inside didAddToEntity.
Now, the problem is that in the above example (but the same goes foupdate(deltaTime seconds: TimeInterval) the methoddidAddToEntity is not isolated to the MainActor, as GKComponent is not either.
This leads to the error Call to main actor-isolated instance method 'run' in a synchronous nonisolated context, as indeed the compiler can not infer that didAddToEntity is isolated to the MainActor.
Marking BasicAnimationComponent as @MainActor does not help, as this isolation is not propogated back to the superclass inherited methods.
In fact, we tried a plethora of other options, but none resolved this issue.
How should we proceed with this? As of now, this is really holding us back migrating to Swift 6. Hope someone is able to help out here!
I'm trying to display a right-aligned timecode in my game. I had expected that digits would all have the same width, but this doesn't seem to be the case in SpriteKit, even though it seems to be the case in AppKit.
In SpriteKit, with the default font there is a noticeable difference in width between the digit 1 and the rest (1 is thinner), so whenever displaying a number with the least significant digit 1 all preceding digits shift slightly to the right. This happens even when setting a NSAttributedString with a font that has a fixedAdvance attribute.
class GameScene: SKScene {
override func didMove(to view: SKView) {
let label = SKLabelNode(text: "")
view.scene!.addChild(label)
// label.horizontalAlignmentMode = .left
label.horizontalAlignmentMode = .right
var i = 11
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
label.text = "\(i)"
// let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-UltraLight", .fixedAdvance: 20]), size: 30)!
// let paragraphStyle = NSMutableParagraphStyle()
// paragraphStyle.alignment = .right
// label.attributedText = NSAttributedString(string: "\(i)", attributes: [.font: font, .foregroundColor: SKColor.labelColor, .paragraphStyle: paragraphStyle])
i += 5
}
}
}
With AppKit, when using SpriteKit's default font HelveticaNeue-UltraLight, this issue doesn't exist, regardless whether I set the fixedAdvance font attribute.
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-UltraLight"]), size: 30)!
// let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-Light", .fixedAdvance: 20]), size: 30)!
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .right
let textField = NSTextField(labelWithString: "")
textField.font = font
textField.alignment = .right
// textField.alignment = .left
textField.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
view.addSubview(textField)
var i = 11
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
textField.stringValue = "\(i)"
// textField.attributedStringValue = NSAttributedString(string: "\(i)", attributes: [.font: font, .paragraphStyle: paragraphStyle])
i += 5
}
}
}
Is there a solution to this problem?
I filed FB15553700.
Even when the action is run on the main thread, the following code causes a crash on iOS, but not on macOS. The game launches with a simple yellow rectangle, and when it finishes fading out and should be removed from the overlay scene, the app crashes.
The code can be pasted into the file GameController.swift of Xcode's default project for Multiplatform macOS and iOS game.
import SceneKit
import SpriteKit
@MainActor
class GameController: NSObject {
let scene: SCNScene
let sceneRenderer: SCNSceneRenderer
init(sceneRenderer renderer: SCNSceneRenderer) {
sceneRenderer = renderer
scene = SCNScene(named: "Art.scnassets/ship.scn")!
super.init()
sceneRenderer.scene = scene
renderer.overlaySKScene = SKScene(size: CGSize(width: 500, height: 500))
DispatchQueue.main.async {
let node = SKShapeNode(rect: CGRect(x: 100, y: 100, width: 100, height: 100))
node.fillColor = .yellow
node.run(.sequence([
.fadeOut(withDuration: 1),
.removeFromParent()
]))
renderer.overlaySKScene!.addChild(node)
}
}
}
The Xcode console shows this stacktrace:
*** Assertion failure in -[UIApplication _performAfterCATransactionCommitsWithLegacyRunloopObserverBasedTiming:block:], UIApplication.m:3246
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Call must be made on main thread'
*** First throw call stack:
(
0 CoreFoundation 0x00000001804ae0f8 __exceptionPreprocess + 172
1 libobjc.A.dylib 0x0000000180087db4 objc_exception_throw + 56
2 Foundation 0x0000000180d17058 _userInfoForFileAndLine + 0
3 UIKitCore 0x00000001853cf678 -[UIApplication _performAfterCATransactionCommitsWithLegacyRunloopObserverBasedTiming:block:] + 376
4 UIKitCore 0x000000018553f7a0 -[_UIFocusUpdateThrottle scheduleProgrammaticFocusUpdate] + 300
5 UIKitCore 0x0000000184e2e22c -[UIFocusSystem _requestFocusUpdate:] + 548
6 UIKitCore 0x0000000184e2dfa4 -[UIFocusSystem requestFocusUpdateToEnvironment:] + 76
7 UIKitCore 0x0000000184e2e864 -[UIFocusSystem _focusEnvironmentWillDisappear:] + 408
8 SpriteKit 0x00000001a3d472f4 _ZL12_removeChildP6SKNodeS0_P7SKScene + 240
9 SpriteKit 0x00000001a3d473b0 -[SKNode removeChild:] + 80
10 SpriteKit 0x00000001a3d466b8 -[SKNode removeFromParent] + 128
11 SpriteKit 0x00000001a3d1678c -[SKRemove updateWithTarget:forTime:] + 64
12 SpriteKit 0x00000001a3d1b740 _ZN11SKCSequence27cpp_updateWithTargetForTimeEP7SKCNoded + 84
13 SpriteKit 0x00000001a3d20e3c _ZN7SKCNode6updateEdf + 156
14 SpriteKit 0x00000001a3d20f20 _ZN7SKCNode6updateEdf + 384
15 SpriteKit 0x00000001a3d26fb8 -[SKScene _update:] + 464
16 SpriteKit 0x00000001a3cf3168 -[SKSCNRenderer _update:] + 80
17 SceneKit 0x000000019c932bf0 -[SCNMTLRenderContext renderSKSceneWithRenderer:overlay:atTime:] + 60
18 SceneKit 0x000000019c9ebd98 -[SCNRenderer _drawOverlaySceneAtTime:] + 204
19 SceneKit 0x000000019cb1a1c0 _ZN3C3D11OverlayPass7executeERKNS_10RenderArgsE + 60
20 SceneKit 0x000000019c8e05ec _ZN3C3D13__renderSliceEPNS_11RenderGraphEPNS_10RenderPassERtRKNS0_9GraphNodeERPNS0_5StageENS_10RenderArgsEbRPU27objcproto16MTLCommandBuffer11objc_object + 2660
21 SceneKit 0x000000019c8e18ac _ZN3C3D11RenderGraph7executeEv + 3808
22 SceneKit 0x000000019c9ed26c -[SCNRenderer _renderSceneWithEngineContext:sceneTime:] + 756
23 SceneKit 0x000000019c9ed544 -[SCNRenderer _drawSceneWithNewRenderer:] + 208
24 SceneKit 0x000000019c9ed9fc -[SCNRenderer _drawScene:] + 40
25 SceneKit 0x000000019c9edce4 -[SCNRenderer _drawAtTime:] + 500
26 SceneKit 0x000000019ca87950 -[SCNView _drawAtTime:] + 368
27 SceneKit 0x000000019c943b74 __83-[NSObject(SCN_DisplayLinkExtensions) SCN_setupDisplayLinkWithQueue:screen:policy:]_block_invoke + 44
28 SceneKit 0x000000019ca50600 -[SCNDisplayLink _displayLinkCallbackReturningImmediately] + 132
29 libdispatch.dylib 0x000000010239173c _dispatch_client_callout + 16
30 libdispatch.dylib 0x0000000102394c14 _dispatch_continuation_pop + 756
31 libdispatch.dylib 0x00000001023aa4e0 _dispatch_source_invoke + 1736
32 libdispatch.dylib 0x00000001023997f0 _dispatch_lane_serial_drain + 340
33 libdispatch.dylib 0x000000010239a774 _dispatch_lane_invoke + 420
34 libdispatch.dylib 0x00000001023a71a8 _dispatch_root_queue_drain_deferred_wlh + 324
35 libdispatch.dylib 0x00000001023a6604 _dispatch_workloop_worker_thread + 488
36 libsystem_pthread.dylib 0x000000010242bb74 _pthread_wqthread + 284
37 libsystem_pthread.dylib 0x000000010242a934 start_wqthread + 8
)
libc++abi: terminating due to uncaught exception of type NSException
Am I doing something wrong?
Hi all,
I'm new to swift and I've just gotten started by making a simple pong-like game using SpriteKit. I'm trying to use the scroll wheel input to spin an object, however, nothing seems to make this work. From my googling and AI advice the way I've been doing it should, as shown in the snippet below, however debugging suggests the scrollWheel function isn't even being called.
#if os(OSX)
extension GameScene {
override func scrollWheel(with event: NSEvent ) {
print("Scroll detected: \(event.scrollingDeltaY)")
let scrollDelta = event.scrollingDeltaY
self.rotatePaddle(value: scrollDelta)
}
}
#endif
I have changed the build targets in Xcode to Mac os, not the designed-for-ipad version of it, and the app does launch and draw sprites correctly as well as detect other mouse or trackpad inputs like mouseDown and rightMouseDown, so it only seems to be this one specific input not working. I've also tried hardware debugging like restarting Xcode and my Mac, but no luck there either. Maybe I've missed something or am doing it completely wrong, so any help would be much appreciated.
Thanks heaps
What are the specific characteristics that trigger Game Mode in an iOS game? I have several casual SpriteKit games in the App Store but only one of them triggers Game Mode.
What does GCSupportsGameMode do when set to true? Will it trigger Game Mode or will the OS still decide by itself?
Hello,
I'm getting an unknown, never-before-seen error at application launch, when running my iOS SpriteKit game on the iOS 18 arm64 simulator from Xcode 16.0 (16A242d) —
AudioConverterOOP.cpp:847 Failed to prepare AudioConverterService: -302
This is occurs on all iOS 18 simulator devices, between application(_:didFinishLaunchingWithOptions:) and the first applicationDidBecomeActive(_:) — the SKScene object may have been already initialized by SpriteKit, but the scene's didMove(to:) method hasn't been called yet.
Also, note that the error message is being emitted from a secondary (non-main) thread, obviously not created by the app.
After the error occurs, no SKScene is able to play audio — this had never occurred on iOS versions prior to 18, neither on physical devices nor on the simulator.
Has anyone seen anything like this on a physical device running 18?
Unfortunately, at the moment I cannot test myself on an 18 device, only on the simulator...
Thank you,
D.
Hey,
Wondering how other developers have been able to determine the location of a mouse event or tap (ie NSEvent) when using MetalView (MKTView) with SKRenderer with a SpriteKit scene (.sks scene) for a 2D game.
In the original scenario with SpriteKit, we could use SKViews convertPoint(fromView:) to determine where in the scene the user tapped. But with the SKRenderer we can no longer use convertPoint(fromView:) as its reliant on SKView being used and thus its making it difficult to determine.
What I do have is:
locationInWindow: NSPoint for showing me where in the MKTView which was touched
Any ideas, would be great
Many thanks
On macOS, system symbols displays in a SKTexture as expected, with the correct color and aspect ratio.
But on iOS they are always displayed in black, and sometimes with slightly wrong aspect ratio.
Is there a solution to this problem?
import SpriteKit
#if os(macOS)
import AppKit
#else
import UIKit
#endif
class GameScene: SKScene {
override func didMove(to view: SKView) {
let systemImage = "square.and.arrow.up"
let width = 400.0
#if os(macOS)
let image = NSImage(systemSymbolName: systemImage, accessibilityDescription: nil)!.withSymbolConfiguration(.init(hierarchicalColor: .white))!
let scale = NSScreen.main!.backingScaleFactor
image.size = CGSize(width: width * scale, height: width / image.size.width * image.size.height * scale)
#else
let image = UIImage(systemName: systemImage)!.applyingSymbolConfiguration(.init(pointSize: width))!.applyingSymbolConfiguration(.init(hierarchicalColor: .white))!
#endif
let texture = SKTexture(image: image)
print(image.size, texture.size(), image.size.width / image.size.height)
let size = CGSize(width: width, height: width / image.size.width * image.size.height)
addChild(SKSpriteNode(texture: texture, size: size))
}
}
Every now and then my SceneKit game app crashes and I have no idea why. The SCNView has a overlaySKScene, so it might also be SpriteKit's fault.
The stack trace is
#0 0x0000000241c1470c in jet_context::set_fragment_texture(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, jet_texture*) ()
#27 0x000000010572fd40 in _pthread_wqthread ()
Does anyone have an idea where I could start debugging this, without being able to consistently reproduce it?
Good morning everyone,
I'm building a simple game (my first game) using SwiftUI and SpriteKit that contains multiple views. I'm writing my game based on a main scene loaded into the GameView using a SpriteView. From there, using buttons, I move from one scene to another using self.scene?.view?.presentScene(...) and also with some cool transitions (.crossFade(withDuration: 0.5))).
But I'm not sure if this is the best approach. I would need some guidance because I cannot find any material discussing the best way to create a proper navigation with SpriteKit.
Do you have an updated article, tutorial, or reference that I can follow to learn about the best way to implement navigation in a SpriteKit game?
What I'm doing right now is working, but I have limitations, for example, if I want to mix SwiftUI views and SpriteKit scenes. I want to add a Credits scene with some text and images that I want to do in SwiftUI and a Statistic scene with some cool graphics to show the players, but I don't know if I can navigate from an SKScene into a View, or if I need a completely different approach. Can I add UI components directly in a SpriteKit scene instead of using a different navigation system and full SwiftUI views?
I really appreciate any help you can provide. As you can see, I'm a little bit lost 😅
Thanks a lot in advance 🙏
How do I set the static position of a SKSpriteNode so that it tilts toward the UIBezierPath as if it were following this Path?
When I first start my App, these Nodes are all aligned in a straight line
When I call:
var trainAction = SKAction.follow(trainPath.cgPath,
asOffset: false,
orientToPath: true,
speed: thisSpeed)
for moving the train, the train + each car will orient its tilt to hug the trainPath.
But I want the identical tilt to hug the trainPath for its initial static presentation.
How do I do that?
Hi,
I’m looking for a way to keep some custom buttons in SpriteKit the same physical size (inches) accross iOS devices (or only slightly vary their size so they’re not humongous on large screens).
How do I get PPI in Swift? (cannot be library code which doesn’t compile in Swift Playgrounds).
I will use PPI for determining total screen size which I will use to determine how to adjust the button sizes while also respecting some physical desirable dimensions for the buttons.
I'm only asking for handheld (same distance from eyes to screen) use, so I don't care about Apple TV (longer distance).