Post

Replies

Boosts

Views

Activity

How do I stop a node as it is moving around a UIBezierPath and then restart the moving from where it stopped?
I have an oval UIBezierPath with a moving SKSpriteNode, I stop its motion and record the stopped position. I then restart this motion and want it to restart where it initially stopped. Works great if motion is not stopped. Movement is great around entire oval Path. Also works great as long as this stop-restart sequence occurs along the top half of the oval UIBezierPath. However, I have problems along the bottom half of this Path -- it stops okay, but the restart position is not where it previously stopped. My method to create this oval UIBezierePath is as follows: func createTrainPath() { trainRect = CGRect(x: tracksPosX - tracksWidth/2, y: tracksPosY - tracksHeight/2, width: tracksWidth, height: tracksHeight) // these methods come from @DonMag trainPoints = generatePoints(inRect: trainRect, withNumberOfPoints: nbrPathPoints) trainPath = generatePathFromPoints(trainPoints!, startingAtIDX: savedTrainIndex) } // createTrainPath My method to stop this motion is as follows: func stopFollowTrainPath() { guard (myTrain != nil) else { return } myTrain.isPaused = true savedTrainPosition = myTrain.position // also from @DonMag savedTrainIndex = closestIndexInPath( trainPath, toPoint: savedTrainPosition) ?? 0 } // stopFollowTrainPath Finally, I call this to re-start this motion: func startFollowTrainPath() { var trainAction = SKAction.follow(trainPath.cgPath, asOffset: false, orientToPath: true, speed: thisSpeed) trainAction = SKAction.repeatForever(trainAction) myTrain.run(trainAction, withKey: runTrainKey) myTrain.isPaused = false } // startFollowTrainPath Again, great if motion is not stopped. Movement is great around entire oval Path. Again, no problem for stopping and then restarting along top half of oval .. the ohoh occurs along bottom half. Is there something I need to do within GameScene's update method that I am missing? For example, do I need to reconstruct my UIBezierPath? every time my node moves between the top half and the bottom half and therein account for the fact that the node is traveling in the opposite direction from the top half?
2
0
194
1w
Using Swift, how do I resize individual SKSpriteNodes for various iPad device sizes?
Using Swift, how do I resize individual SKSpriteNodes for various iPad device sizes? Currently, I use theScene.scaleMode = .resizeFill for scaling the entire SKScene and it works as advertised. Please note that .aspectFill does not solve the challenge described below. My challenge is to resize individual SKSpriteNodes (that are components of the overall SKScene) based on size of the iOS device; for example, iPad Mini <--> iPad Pro Right now, I hard code the sizes of these Nodes. But I realize this is not the optimum approach. Again, hard coding is frowned upon. So I am looking for a more dynamic method that functions based on device size.
0
0
159
Oct ’24
How do I set the *static* position of a `SKSpriteNode` so that it tilts toward the `UIBezierPath` as if it were following this Path?
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?
0
0
416
Aug ’24
How do I correctly show a PDF document?
How do I correctly show a PDF document? iPad and Xcode 15.4 Within my GameViewController, I have: func presentScene(_ theScene: SKScene) { theScene.scaleMode = .resizeFill if let skView = self.view as? SKView { skView.ignoresSiblingOrder = true skView.showsFPS = true skView.showsNodeCount = true #if os(iOS) let theTransition = SKTransition.doorway(withDuration: 2.0) skView.presentScene(theScene, transition: theTransition) #elseif os(tvOS) skView.presentScene(theScene) #endif } } // presentScene I believe presentScene(theScene) goes to the sceneDidLoad() func of theScene which adds various SKSpriteNodes to the scene via a call to addChild(theNode). So far so good ... Until I have a SKScene wherein I wish to display a PDF. I use this snippet to display this PDF with this call within the SKScene's sceneDisLoad(): displayPDF("ABOUT_OLD_WEST_LOCOMOTIVE") func displayPDF(_ itsName:String) { let pdfView = PDFView() guard let path = Bundle.main.path(forResource: itsName, ofType: "pdf") else { return } print("path") guard let pdfDocument = PDFDocument(url: URL(fileURLWithPath: path)) else { return } print("pdfDocument") pdfView.displayMode = .singlePageContinuous pdfView.autoScales = true pdfView.displayDirection = .vertical pdfView.document = pdfDocument } // displayPDF The 2 print statements show, yet the SKScene does not display the PDF via pdfView.document = pdfDocument? Anyone have a clue what errors I have committed? Appreciate it.
4
0
556
Jul ’24
Why is Add Emitter to Node not happening immediately?
Why is Add Emitter to Node not happening immediately? Within an extension to GameViewController I have: func addEmitterToNode(_ particleName: String, _ theNode:SKSpriteNode) { if let emitter = SKEmitterNode(fileNamed: particleName) { emitter.name = "emitter" emitter.position = CGPoint(x: 0, y: 0) // = at center theNode.addChild(emitter) } } // addEmitterToNode Here is the func (extension to GameViewController) wherein I call the above. The problem is the explosion sound plays but the smoke particle emitter is not immediately added. func increaseSpeed() { if thisSpeed > maxSpeed { pauseGame() // sets myTrain.isPaused = true playSound(theSoundName: "explosion") addEmitterToNode("smokeParticle.sks", myTrain) trainHasCrashed = true } } // increaseSpeed What I do not understand is that the following within GameScene's sceneDidLoad, the smoke particle emitter is added = override func sceneDidLoad() { if itsGameViewController.trainHasCrashed { itsGameViewController.addEmitterToNode("smokeParticle.sks", myTrain) } }
4
0
475
Jul ’24
How to ensure current SKScene has fully loaded before engaging it with the GamePad Controller?
How to ensure current SKScene has fully loaded before engaging it with the GamePad Controller? The problem is this = when stopping sound is involved when I do switch SKScenes, if I press the buttons of the GamePad Controller (which cycle thru these other SKScenes) too fast, the movement of the Game Pieces fails to resume when I return to the Game Scene after the above cycling. This problem occurs only with the os(tvOS) version, but not with the iPad version. And the reason for this distinction is that each SKScene for the iPad has to fully load due to the fact that the button I press to switch SKScenes is at the top-left corner of the iPad -- so, by definition, by the time I have access to this button, the current SKScene has fully loaded. By definition, there is no such button for os(tvOS). Given this button’s absence, I believe I need the Swift version of jQuery’s $(document).ready (function() {. Any help will be appreciated to the rafters ...
2
0
545
Jul ’24
How to ensure current SKScene has fully loaded before engaging it with the GamePad Controller?
How to ensure current SKScene has fully loaded before engaging it with the GamePad Controller? MAJOR REWRITE FOR THE SAKE OF HOPEFULLY (?) INCREASED CLARITY The problem is this = when stopping sound is involved when I do switch SKScenes, if I press the buttons of the GamePad Controller (which cycle thru these other SKScenes) too fast, the movement of the Game Pieces fails to resume when I return to the Game Scene after the above cycling. This problem occurs only with the os(tvOS) version, but not with the iPad version. And the reason for this distinction is that each SKScene for the iPad has to fully load due to the fact that the button I press to switch SKScenes is at the top-left corner of the iPad -- so, by definition, by the time I have access to this button, the current SKScene has fully loaded. By definition, there is no such button for os(iOS). Given this button’s absence, I need the Swift version of jQuery’s $(document).ready (function() {. Any help will be appreciated to the rafters ...
1
0
502
Jul ’24
Calling SKAction.follow(..) causes my SKSpriteNode to rotate 90 degrees CW and not stay horizontal as it follows my UIBezierPath?
Calling SKAction.follow(..) causes my SKSpriteNode to rotate 90 degrees CW and not stay horizontal as it follows my UIBezierPath? I have this code (within my GameViewController Class) which implements the following of a SKSpriteNode along a UIBezierPath. ===== Please note that a brilliant contributor solved the above challenge by creating a new Class, e.g., class NewClass: NSObject. Nevertheless, I need the solution to appear in an extension of my GameViewController ===== func createTrainPath() { trackRect = CGRect(x: tracksPosX - tracksWidth/2, y: tracksPosY, width: tracksWidth, height: tracksHeight) trainPath = UIBezierPath(ovalIn: trackRect) } // createTrainPath func startFollowTrainPath() { var trainAction = SKAction.follow( trainPath.cgPath, asOffset: false, orientToPath: true, speed: theSpeed) trainAction = SKAction.repeatForever(trainAction) myTrain.run(trainAction, withKey: runTrainKey) } // startFollowTrainPath func stopFollowTrainPath() { guard myTrain == nil else { myTrain.removeAction(forKey: runTrainKey) savedTrainPosition = getPositionFor(myTrain, orPath: trainPath) return } } // stopFollowTrainPath
2
0
828
May ’24
UIBezierPath’s orientToPath
Here is a link to a super long conversation with a true genius at StackOverflow https://stackoverflow.com/questions/78343630/what-algorithm-is-available-to-correlate-a-skspritenodes-position-with-the-uibe/78379892#78379892 He has done an extraordinary amount of work to make up for what I consider is the non-working of UIBezierPath’s orientToPath = true. He’s done multiple hours work to make up for orientToPath = true not working. What are we missing?
0
0
506
Apr ’24
Apple TV won’t connect to my external surround sound speakers
I know that this is not a coding question and I have posted on the regular Apple Forum and have been working with the 800 Tech Support folk for 4 months. I am in a world of hurt. Have ATV + Samsung TV + external soundbar. All software versions are current NO Cable box (direct input). 4 months ago I had Cable box = zero problems. With Samsung on and ATV off, stereo = Dolby 5.1 sounds great. With ATV also on, no stereo. I can use Siri remote to activate stereo until I change to a new movie. ATV then reverts to mono = PCM One Tech Support lady said to just not watch multiple movie. ?$@!*+^? I have spoken with engineers at Samsung and Sonos (soundbar). Both say my cabling is correct = 1 cable from soundbar to HDMI Arc on TV + 1 cable from ATV to another HDMI port on TV. Another Tech Support folk said to connect ATV to soundbar. Soundbars don’t have gobs of HDMI ports, the TV does. ?!$@*^+%# I have spoken with Apple Tech Support with zero help for 3-4 months. I think I have been a good soldier. But it is time for real results. Please help
1
0
816
Mar ’24
Detecting touching a SKSpriteNode within a touchesBegan event?
Detecting touching a SKSpriteNode within a touchesBegan event? My experience to date has focused on using GamepadControllers with Apps, not a touch-activated iOS App. Here are some short code snippets: Note: the error I am trying to correct is noted in the very first snippet = touchesBegan within the comment <== shows "horse" Yes, there is a "horse", but it is no where near the "creditsInfo" SKSpriteNode within my .sksfile. Please note that this "creditsInfo" SKSpriteNode is programmatically generated by my addCreditsButton(..) and will be placed very near the top-left of my GameScene. override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if let ourScene = GameScene(fileNamed: "GameScene") { if let touch:UITouch = touches.first { let location = touch.location(in: view) let node:SKNode = ourScene.atPoint(location) print("node.name = \(node.name!)") // <== shows "horse" if (node.name == "creditsInfo") { showCredits() } } } // if let ourScene } // touchesBegan The above touchesBegan function is an extension GameViewController which according to the docs is okay, namely, touchesBegan is a UIView method besides being a UIViewController method. Within my primary showScene() function, I have: if let ourScene = GameScene(fileNamed: "GameScene") { #if os(iOS) addCreditsButton(toScene: ourScene) #endif } with: func addCreditsButton(toScene: SKScene) { if thisSceneName == "GameScene" { itsCreditsNode.name = "creditsInfo" itsCreditsNode.anchorPoint = CGPoint(x: 0.5, y: 0.5) itsCreditsNode.size = CGSize(width: 2*creditsCircleRadius, height: 2*creditsCircleRadius) itsCreditsNode.zPosition = 3 creditsCirclePosY = roomHeight/2 - creditsCircleRadius - creditsCircleOffsetY creditsCirclePosX = -roomWidth/2 + creditsCircleRadius + creditsCircleOffsetX itsCreditsNode.position = CGPoint(x: creditsCirclePosX, y: creditsCirclePosY) toScene.addChild(itsCreditsNode) } // if thisSceneName } // addCreditsButton To finish, I repeat what I stated at the very top: The error I am trying to correct is noted in the very first snippet = touchesBegan within the comment <== shows "horse"
5
0
1k
Feb ’24
How to detect the location of a mouseDown event using Swift?
How to detect the location of a mouseDown event using Swift? With the following code snippet, I get the error "Cannot find type 'NSEvent' in scope"? import SpriteKit func mouseDown(with event: NSEvent) { if let ourScene = GameScene(fileNamed: "GameScene") { let location = event.location(in: view) let node:SKNode = ourScene.atPoint(location) if (node.name == "creditsInfo") { showCredits() } } // if let ourScene } // mouseDown Anybody have a floodlight to shine on this very basic error?
5
0
951
Feb ’24
How to integrate UIDevice rotation and creating a new UIBezierPath after rotation?
How to integrate UIDevice rotation and creating a new UIBezierPath after rotation? My challenge here is to successfully integrate UIDevice rotation and creating a new UIBezierPath every time the UIDevice is rotated. (Please accept my apologies for this Post’s length .. but I can’t seem to avoid it) As a preamble, I have bounced back and forth between NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: UIDevice.orientationDidChangeNotification, object: nil) called within my viewDidLoad() together with @objc func rotated() { } and override func viewWillLayoutSubviews() { // please see code below } My success was much better when I implemented viewWillLayoutSubviews(), versus rotated() .. so let me provide detailed code just for viewWillLayoutSubviews(). I have concluded that every time I rotate the UIDevice, a new UIBezierPath needs to be generated because positions and sizes of my various SKSprieNodes change. I am definitely not saying that I have to create a new UIBezierPath with every rotation .. just saying I think I have to. Start of Code // declared at the top of my `GameViewController`: var myTrain: SKSpriteNode! var savedTrainPosition: CGPoint? var trackOffset = 60.0 var trackRect: CGRect! var trainPath: UIBezierPath! My UIBezierPath creation and SKAction.follow code is as follows: // called with my setTrackPaths() – see way below func createTrainPath() { // savedTrainPosition initially set within setTrackPaths() // and later reset when stopping + resuming moving myTrain // via stopFollowTrainPath() trackRect = CGRect(x: savedTrainPosition!.x, y: savedTrainPosition!.y, width: tracksWidth, height: tracksHeight) trainPath = UIBezierPath(ovalIn: trackRect) trainPath = trainPath.reversing() // makes myTrain move CW } // createTrainPath func startFollowTrainPath() { let theSpeed = Double(5*thisSpeed) var trainAction = SKAction.follow( trainPath.cgPath, asOffset: false, orientToPath: true, speed: theSpeed) trainAction = SKAction.repeatForever(trainAction) createPivotNodeFor(myTrain) myTrain.run(trainAction, withKey: runTrainKey) } // startFollowTrainPath func stopFollowTrainPath() { guard myTrain == nil else { myTrain.removeAction(forKey: runTrainKey) savedTrainPosition = myTrain.position return } } // stopFollowTrainPath Here is the detailed viewWillLayoutSubviews I promised earlier: override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() if (thisSceneName == "GameScene") { // code to pause moving game pieces setGamePieceParms() // for GamePieces, e.g., trainWidth setTrackPaths() // for trainPath reSizeAndPositionNodes() // code to resume moving game pieces } // if (thisSceneName == "GameScene") } // viewWillLayoutSubviews func setGamePieceParms() { if (thisSceneName == "GameScene") { roomScale = 1.0 let roomRect = UIScreen.main.bounds roomWidth = roomRect.width roomHeight = roomRect.height roomPosX = 0.0 roomPosY = 0.0 tracksScale = 1.0 tracksWidth = roomWidth - 4*trackOffset // inset from screen edge #if os(iOS) if UIDevice.current.orientation.isLandscape { tracksHeight = 0.30*roomHeight } else { tracksHeight = 0.38*roomHeight } #endif // center horizontally tracksPosX = roomPosX // flush with bottom of UIScreen let temp = roomPosY - roomHeight/2 tracksPosY = temp + trackOffset + tracksHeight/2 trainScale = 2.8 trainWidth = 96.0*trainScale // original size = 96 x 110 trainHeight = 110.0*trainScale trainPosX = roomPosX #if os(iOS) if UIDevice.current.orientation.isLandscape { trainPosY = temp + trackOffset + tracksHeight + 0.30*trainHeight } else { trainPosY = temp + trackOffset + tracksHeight + 0.20*trainHeight } #endif } // setGamePieceParms // a work in progress func setTrackPaths() { if (thisSceneName == "GameScene") { if (savedTrainPosition == nil) { savedTrainPosition = CGPoint(x: tracksPosX - tracksWidth/2, y: tracksPosY) } else { savedTrainPosition = CGPoint(x: tracksPosX - tracksWidth/2, y: tracksPosY) } createTrainPath() } // if (thisSceneName == "GameScene") } // setTrackPaths func reSizeAndPositionNodes() { myTracks.size = CGSize(width: tracksWidth, height: tracksHeight) myTracks.position = CGPoint(x: tracksPosX, y: tracksPosY) // more Nodes here .. } End of Code My theory says when I call setTrackPaths() with every UIDevice rotation, createTrainPath() is called. Nothing happens of significance visually as far as the UIBezierPath is concerned .. until I call startFollowTrainPath(). Bottom Line It is then that I see for sure that a new UIBezierPath has not been created as it should have been when I called createTrainPath() when I rotated the UIDevice. The new UIBezierPath is not new, but the old one. If you’ve made it this far through my long code, the question is what do I need to do to make a new UIBezierPath that fits the resized and repositioned SKSpriteNode?
8
0
1.1k
Feb ’24