How to use GCVirtualController directly with SKScene?

GCVirtualController isn't displaying when used with SKScene class. The Virtual controllers appear but then it seems that they are obscured by the SKScene itself!? The documentation says that calling connect() will display the virtual controllers but I seem to be missing how to add the controllers to the SKScene?

class GameScene: SKScene {
    private var _virtualController: Any?
@available(iOS 15.0, *)
    public var virtualController: GCVirtualController? {
        get { return self._virtualController as? GCVirtualController }
        set { self._virtualController = newValue }
    }
    override func didMove(to view: SKView) {
    let background = SKSpriteNode(imageNamed: ".jpg")
        background.zPosition = -1
        addChild(background)

        let virtualConfig = GCVirtualController.Configuration()
        virtualConfig.elements = [GCInputLeftThumbstick, GCInputRightThumbstick, GCInputButtonA, GCInputButtonB]
        virtualController = GCVirtualController(configuration: virtualConfig)
        virtualController?.connect()
    }
}

I've also tried adding the virtual controllers in the UIViewController but this doesn't work either.

Add a Comment

Replies

covaticMyth, I hope you've already found your answer but I wanted to post the code that worked for me so anyone one else might come across it. Also, thank you for posting your question, it helped me.

In the Game Scene, I declared it similar to you:

private var _virtualController: Any?
@available(iOS 15.0, *)
        public var virtualController: GCVirtualController? {
            get {return self._virtualController as? GCVirtualController}
            set {self._virtualController = newValue}
        }

In the Game Scene I also created the following function, which I called in the didMove function:

func configVirtualController(){
        if #available(iOS 15.0, *) {
            let virtualConfig = GCVirtualController.Configuration()
            virtualConfig.elements = [GCInputLeftThumbstick, GCInputButtonA, GCInputButtonB]
            virtualController = GCVirtualController(configuration: virtualConfig)
            virtualController?.connect()
        } else {
            // Fallback on earlier versions
        }

To add my two cents, if you're using the AppDelegate to register the physical MFi controller, aka a physical game controller, you need to write exception logic to make sure that the Virtual Controller isn't included. Otherwise it will create bugs and send the button event twice.

A virtual controller has a vendorName of "Apple Touch Controller".

// MARK: - Controller -

@objc private func controllerWasConnected(_ notification: Notification) {
    
    let controller: GCController = notification.object as! GCController

    let status = "MFi Controller: \(String(describing: controller.vendorName)) is connected"

    print(status)
    
    // make sure it's not virtual

    if controller.vendorName != "Apple Touch Controller" {
     
        gcSharedInstance.reactToInput(controller: controller)
        
    }

}

@objc private func controllerWasDisconnected(_ notification: Notification) {
    
    let controller : GCController = notification.object as! GCController
    
    let status = "MFi Controller: \(String(describing: controller.vendorName)) is disconnected"

    print(status)
    
    // make sure it's not virtual

    if controller.vendorName != "Apple Touch Controller" {
        
        gcSharedInstance.deallocController(controller: controller)
        
    }
    
}