Using Swift, how do I continuously test for a GamePad being on/connected?
When you build a Browser-based Canvas + Javascript Game, you do the above via:
function listenForGamepadConnected() {
window.addEventListener("gamepadconnected", (event) => {
// this is the BIGEE that is always testing
});
} // listenForGamepadConnected
And it definitely works!
I cannot find its equivalent in the Xcode/Swift world?
I do see the following that’s built into a boilerplate Game that you initially create using Xcode - but it seems that it just looks once, versus continuously:
func ObserveForGameControllers() {
NotificationCenter.default.addObserver(
self,
selector: #selector(connectControllers),
name: NSNotification.Name.GCControllerDidConnect,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(disconnectControllers),
name: NSNotification.Name.GCControllerDidDisconnect,
object: nil)
} // ObserveForGameControllers
@objc func connectControllers() {
// Unpause the Game if it is currently paused
self.isPaused = false
// Used to register the Nimbus Controllers to a specific Player Number
var indexNumber = 0
// Run through each controller currently connected to the system
for controller in GCController.controllers() {
// Check to see whether it is an extended Game Controller (Such as a Nimbus)
if controller.extendedGamepad != nil {
print("CONNECTED - Extended Gamepad #\(indexNumber)")
controller.playerIndex = GCControllerPlayerIndex.init(rawValue: indexNumber)!
indexNumber += 1
setupControllerControls(controller: controller)
}
else {
print("CONNECTED - but, NOT an Extended Gamepad #\(indexNumber)")
}
}
} // connectControllers
@objc func disconnectControllers() {
print("DIS-CONNECTED")
// Pause the Game if a controller is disconnected ~ This is mandated by Apple
self.isPaused = true
} // disconnectControllers
I try to call it in a Timer loop, but still does not work:
@objc func testForGamepadIsConnected() {
ObserveForGameControllers
var gamepadOn = !self.isPaused // does not work
} // testForGamepadIsConnected
func startTestForGamepad() {
guard (gamepadTimer != nil) else {
gamepadTimer = Timer.scheduledTimer(
timeInterval: 1.0,
target: self,
selector:#selector(testForGamepadIsConnected),
userInfo: nil,
repeats: true)
return
}
} // startTestForGamepad
func stopTestForGamepad() {
guard (gamepadTimer == nil) else {
gamepadTimer!.invalidate()
// .invalidate() removes Timer() from gamepadTimer, so reinitialize it.
gamepadTimer = Timer()
return
}
} // stopTestForGamepad
Scoured the Google world, but I’ve come up empty.
Would appreciate some genius out there providing what I’m missing.
Thanks loads.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Someone somewhere in Apple Development needs to do something. This just cannot be allowed to continue. This has been reported by many others before me .. but nothing changes.
What their Reviewers do borders on Assault which is a Crime.
How much longer?
The very capable folk such as Quinn are tarnished by working along side some of the Reviewers. I truly feel sorry for Quinn.
Submitted my Monster Paddle Pong App that operates on Apple TV and iPad with a Game Controller.
SO the Tester uploads my App to their iPad and starts poking their finger all over the screen .. and NOTHING happens.
Maybe their reading skills aren’t ample because that Game Controller requirement is delineated in sentence #1.
Promotional Text:
Monster Paddle Pong is FUN! to play on a iPad + Apple TV with a Extended Game Controller. Match wits with your skill to react quickly. See the Description for more info.
Description Text:
Monster Paddle Pong is FUN! to play on a iPad or Apple TV with a Game Controller. It mischievously matches wits with your skill to react quickly.
It's 100% free, so give it a GO!
Pressing the Right Shoulder Button starts the Game and causes the Dinossaur Ball to start moving .. and unless the Ball is moving the Monster Paddle will not move.
As a matter of fact, until this Right Shoulder Button is first pressed, only the Left Shoulder Button works (described below).
This makes sense because after all what’s so hard in hitting a stationary target? Right?
Use your Game Controller’s A, B, X, Y buttons & the Joysticks to move the Monster Paddle and hit the moving Dinossaur Ball.
If you’re successful then your Score in the upper right corner advances. If instead your Monster Paddle hits one of the 4 Walls, your score decreases by 1.
All this is graphically depicted in the About Scene which can be accessed by pressing your Controller’s Left Shoulder Button once.
BTW, press this Left Shoulder a 2nd time, and you will see my Credits Scene.
I don’t want to get too detailed here because a big part of FUN! is the thrill of Discovering.
So, feel free to DISCOVER, most notably pressing all the buttons on your Controller to see the MAGIC! each of them makes happen.
Too Complex to Compile?
Ventura 13.4.1
New error after update to Xcode 14.3.1 from 14.3.0
// ... <= -(roomHeight/2 - kFudge) too complex to compile ??
if (playerPosY - playerHeight/2) <= (kFudge - roomHeight/2)
{
// ... this variation works, the previous one doesn't
}
kFudge and the other parms are declared Double! within AppDelegate.
I don't understand what is suddenly wrong after Xcode update.
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?
After many former OS and Xcode updates, my Game Controller Swift code generates a "DIS-CONNECTED" MESSAGE.
Mac Sequoia 15.2
Xcode 16.2
Tried to update PlayStation controller firmware on my Mac.
Still no luck with Xcode and its use of a game controller with tvOS.
Have received no feedback on:
Case-ID: 1777202
March 10th
Got automated reply
Sent added data as they requested in this reply (seems to be a standard component in such replies).
Anyway, 17 days ago.
Can anyone check on this ID for me
Thanks.
Just submitted version update to my existing app. Exactly how long before App Connect shows Waiting for Review
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?
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"
Can you set isPaused = true of a SKSpriteNode and keep its SKEmitterNode still moving?
Topic:
Graphics & Games
SubTopic:
SpriteKit
Moving a SKSpriteNode image in response to a change in its position is not working.
Based on the code below, does anyone have an idea why?
N.B. I change the position of the image within my class GameScene.
Then, I call, e.g., this function:
func drawBall() {
let positionInSceneCoordinates = CGPoint(x: ballPosX, y: ballPosY)
myBall!.removeAction(forKey: "moveTO")
let moveTO = SKAction.move(to: positionInSceneCoordinates, duration: TimeInterval(1))
myBall!.run(moveTO, withKey: "moveTO)")
}
Selective placement of print statements prove that the ballPosX, ballPosY are changing as they should, but I am observing zero movement on the Xcode Simulator via the call to myBall!run(..).
I am at a loss here on how to proceed.
Thanks bunches!
Nimbus+ game controller not working with Ventura 13.3 and comparable tvOS
Prior to updating these OS' yesterday, my Computer App in the App Store worked.
After updating, not working.
I have contacted SteelSeries, the mgr of Nimbus+, and they refuse to admit it's their firmware.
And I have definitely started up their SteelSeries CG application and looked for a firmware update -- none.
Replacement of hardware is not the solution.
Any contrary ideas you folk may have would be extremely helpful.
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)
}
}
Topic:
Community
SubTopic:
Apple Developers
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.
Cannot add a iOS Distribution Certificate to my Project or Target?
During the Submission for Review process, it states I should choose a Build (for Distribution) and I need to do the above add before I can do that.
Here is a screen shot of my Certificates set in my Developer Account:
Here is the screen shot when I double-click on the iOS Distribution Certificate above:
Here is a screen shot of that Certificate in the Finder:
Yet in spite of all the above evidence, here is a screen shot of my Target for both my iOS and tvOS Project:
So, please provide some hints because this is the only step left before I finally submit my App to the App store.