Posts

Post not yet marked as solved
5 Replies
2.6k Views
Hi.I am trying to add background with gradient color to my SKScene:class GameScene: SKScene { override func didMove(to view: SKView) { let gradientView = UIView(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)) let image = UIImage.gradientBackgroundImage(bounds: CGRect(x: 0, y: 0, width: frame.width, height: frame.height), colors: [UIColor.yellow.cgColor, UIColor.blue.cgColor]) let background = SKSpriteNode(color: UIColor(patternImage: image), size: frame.size) addChild(background) } } extension UIImage { /** http://www.riptutorial.com/ios/example/14328/gradient-image-with-colors */ static func gradientBackgroundImage(bounds: CGRect, colors: [CGColor]) -> UIImage { let gradientLayer = CAGradientLayer() gradientLayer.frame = bounds gradientLayer.colors = colors UIGraphicsBeginImageContext(gradientLayer.bounds.size) gradientLayer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image! } }This is not working. Is there a better way to add gradient background to the SKScene?Thanks a lot!EDIT:If I do this in didMove(to view:):self.view?.backgroundColor = UIColor.blue let backgroundDep = IGABackgroundLayer().gradientMetar() // My method to create CAGradientLayer backgroundDep.frame = self.view!.bounds self.view!.layer.insertSublayer(backgroundDep, at: 0)...background is created nicely but added SKNodes are not seen on top of it.
Posted
by igorland.
Last updated
.
Post not yet marked as solved
0 Replies
374 Views
Hi. Are there best practices in terms of how I should handle a disconnected player in a real-time match? In my app, once a player goes to background, he/she gets disconnected and the player is marked as temporarily unavailable on other players' devices. Is there any way that the disconnected player can return back to the same game when he/she becomes active again? It sounds a bit counterproductive to stop the whole match if one player has to disconnect quickly, for example to answer a call. Thank you!
Posted
by igorland.
Last updated
.
Post not yet marked as solved
2 Replies
430 Views
I am trying to implement a real-time match in GameCenter using GameKit and SpriteKit. This is my scene: class IntroScene: SKScene, GameCenterManagerDelegate { &#9;var gameCenterManager: GameCenterManager? &#9;override func didMove(to view: SKView) { &#9;&#9;GameCenterManager.manager.delegate = self &#9;&#9;<other code> &#9;} &#9;override func didChangeSize(_ oldSize: CGSize) { &#9;&#9;super.didChangeSize(oldSize) &#9;&#9;GameCenterManager.manager.authenticatePlayer() &#9;} &#9;func didChangeAuthStatus(isAuthenticated: Bool) { &#9;&#9;...Enabling the Button to play in GameCenter &#9;} &#9;func presentGameCenterAuth(viewController: UIViewController?) { &#9;&#9;guard let vc = viewController else {return} &#9;&#9;self.view!.window?.rootViewController?.present(vc, animated: true) &#9;} &#9;func presentMatchmaking(viewController: UIViewController?) { &#9;&#9;guard let vc = viewController else {return} &#9;&#9;self.view!.window?.rootViewController?.present(vc, animated: true) &#9;} &#9;->> Problem here. Never gets called &#9;func presentGame(match: GKMatch) { &#9;&#9;print("....INTROSCENE: DELEGATE PRESENT_GAME CALLED......") &#9;&#9;... Presenting a new scene &#9;} } This is my GameCenterManager class: final class GameCenterManager : NSObject, GKLocalPlayerListener {   static let manager = GameCenterManager()   weak var delegate: GameCenterManagerDelegate?   var maxPlayers = 2   var match: GKMatch?   static var isAuthenticated: Bool {     return GKLocalPlayer.local.isAuthenticated   }       override init() {     super.init()   }           func authenticatePlayer() {     GKLocalPlayer.local.authenticateHandler = { gcAuthVC, error in       self.delegate?.didChangeAuthStatus(isAuthenticated: GKLocalPlayer.local.isAuthenticated)       if GKLocalPlayer.local.isAuthenticated {         GKLocalPlayer.local.register(self)       }       // If the User needs to sign to the Game Center       else if let vc = gcAuthVC {         self.delegate?.presentGameCenterAuth(viewController: vc)       }       else {         print(">>>>> Error authenticating the Player! \(error?.localizedDescription ?? "none") <<<<<")       }     }   }       func presentMatchmaker() {     guard GKLocalPlayer.local.isAuthenticated else { return }     let request = GKMatchRequest()     request.minPlayers = 2     request.maxPlayers = 6     request.inviteMessage = "Would you like to play?"     guard let vc = GKMatchmakerViewController(matchRequest: request) else { return }     vc.matchmakerDelegate = self     delegate?.presentMatchmaking(viewController: vc)   }     &#9;// THIS IS WORKING...   func player(_ player: GKPlayer, didAccept invite: GKInvite) {           print("-----player -- did accept invite-------\(player.displayName)")           guard let vc = GKMatchmakerViewController(invite: invite) else { return }     vc.matchmakerDelegate = self           self.gameCenterViewController?.present(vc, animated: true, completion: nil)   }       func player(_ player: GKPlayer, didRequestMatchWithRecipients recipientPlayers: [GKPlayer]) {     print("didRequestMatchWithRecipients")   }           func player(_ player: GKPlayer, matchEnded match: GKTurnBasedMatch) {     print("match ended")   }       func player(_ player: GKPlayer, wantsToQuitMatch match: GKTurnBasedMatch) {     print("wants to quit match")   } } extension GameCenterManager: GKMatchmakerViewControllerDelegate {     &#9;// THIS IS NOT WORKING -(   func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) {           print("-----matchmakerVC did find match-------")           viewController.dismiss(animated: true)           match.delegate = self           delegate?.presentGame(match: match)   }       func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) {     print("matchmakerVC was cancelled")     viewController.dismiss(animated: true)     delegate?.matchmakingCancelled()   }       func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) {     viewController.dismiss(animated: true)     delegate?.matchmakingError(error: error)   } } extension GameCenterManager: GKMatchDelegate {       func match(_ match: GKMatch, didReceive data: Data, forRecipient recipient: GKPlayer, fromRemotePlayer player: GKPlayer) {     // HANDLE DATA   }       func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) {     //HANDLE DATA   }           func match(_ match: GKMatch, player: GKPlayer, didChange state: GKPlayerConnectionState) {     print("-----match did change state")     guard match == self.match else { return }     switch state {     case .connected where self.match != nil :       print("-----MATCH DID CHANGE STATE CONNECTED-----")     case .disconnected:       print("-----MATCH DID CHANGE STATE DISCONNECTED-----")     default:       break     }   }       func match(_ match: GKMatch, didFailWithError error: Error?) {     print("-----match did fail with error")   } } So, basically this is what happens. Player 1 starts the game. The player gets successfully authenticated. I invite Player 2 to join the game. The invitation is sent to Player 2's device. Player 2 accepts the game (printing player did accept invite). However, after that nothing happens. There is continuing spinning wheel saying "Sending" on Player 1's device, and the game scene never launches. I never receive notification that the match has been found, i.e. matchmakerViewController(_ viewController:,match:) is never called. Any help would be greatly appreciated!
Posted
by igorland.
Last updated
.
Post not yet marked as solved
2 Replies
433 Views
Hi. Since updating my Xcode to version 12 and release of iOS14 Simulator, I am no longer able to connect my app to Apple servers to check for in-app purchases or GameKit authentication. Everything works fine on actual devices running iOS 14 or on Simulator with iOS 13.5. Disabling firewall did not help. Anyone experienced the same problem? Thanks!
Posted
by igorland.
Last updated
.
Post marked as solved
1 Replies
627 Views
Hi. I have created several sandbox accounts to test in-app purchases for my application. However, every time I try to make a purchase on my iPhone I get the following error: Verification Failed. Your Apple ID or password is incorrect. This is wrong, I am pretty sure that my credentials are fine. I used to have a similar issue on iPad. What I did was reinstalling the application on my device after I had logged in as a sandbox user and cleaning all application data before installing the application. Somehow it worked. But not on iPhone; no matter what I do, the problem persists. Your help would be much appreciated! I do not want to go to production without a successful test on an iPhone. Thank you!
Posted
by igorland.
Last updated
.
Post not yet marked as solved
3 Replies
542 Views
Hi.I am adding a UITextfield to my SKScene:txtName = UITextField() txtName.borderStyle = UITextField.BorderStyle.roundedRect txtName.textColor = SKColor.black txtName.backgroundColor = SKColor.white txtName.text = appDelegate.playerName_old txtName.delegate = self // Textfield delegate self.view?.addSubview(txtName) txtName.alpha = 1.0After, I am also adding an SKShapeNode to the same Scene:tutorialNotification = TutorialNotification(text: text, position: position, isShiftedByHalf: isShiftedByHalf) tutorialNotification?.alpha = 0 background.addChild(tutorialNotification) tutorialNotification!.zPosition = 2000 tutorialNotification?.run(SKAction.fadeIn(withDuration: 0.25))where TutorialNotification is a subclass of SKShapeNode. The problem I have is that the tutorialNotification node is located on top of other nodes but is always behind the UITextField.Is there any way to place the UITextField behind the SKShapeNode?Thanks a lot!
Posted
by igorland.
Last updated
.
Post not yet marked as solved
0 Replies
377 Views
Hi. I have faced a problem in a seemingly easy situation. I am using SpriteKit to present several boxes (SKShapeNodes) with texts inside (similar to a messaging app). The boxes are created just nice. However, when I rotate the device, the lowest and the second to lowest boxes are placed as expected, then the next box is located higher (by the height of the box), the other one is placed even higher (I wish I could attach screenshots).This is my code.ViewControllerclass GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let scene = GameScene() let skView = self.view as! SKView if skView.bounds.size.height&gt;skView.bounds.size.width { scene.size = CGSize(width: 1080, height: 1920) } else { scene.size = CGSize(width: 1920, height: 1080) } skView.showsFPS = true skView.showsNodeCount = true skView.ignoresSiblingOrder = false scene.scaleMode = .aspectFit skView.presentScene(scene) } override func viewWillLayoutSubviews() { super.viewDidLayoutSubviews() let skView = self.view as! SKView if let scene = skView.scene { if skView.bounds.size.height&gt;skView.bounds.size.width { scene.size = CGSize(width: 1080, height: 1920) } else { scene.size = CGSize(width: 1920, height: 1080) } var size = scene.size let boundWidth = skView.bounds.width let boundHeight = skView.bounds.height let newHeight = boundHeight*size.width/boundWidth let newWidth = boundWidth*size.height/boundHeight if newHeight &gt; size.height { size.height = newHeight scene.size = size } if newWidth &gt; size.width { size.width = newWidth scene.size = size } } } }GameScene:import SpriteKit import GameplayKit struct TextMessage: Equatable { var messageText : SKLabelNode? var messageBox : SKShapeNode? } class GameScene: SKScene { var background : SKSpriteNode! private var label : SKLabelNode? private var spinnyNode : SKShapeNode? var listOfTextMessages = [TextMessage]() var number = 1 override func didMove(to view: SKView) { background = SKSpriteNode() background.color = .green addChild(background) } override func touchesEnded(_ touches: Set, with event: UIEvent?) { switch number { case 1: createTextMessageBox(text:"wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww") case 2: createTextMessageBox(text:"\(number)\(number)\(number)") case 3: createTextMessageBox(text:"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") case 4: createTextMessageBox(text:"rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr") case 5: createTextMessageBox(text:"\(number)\(number)\(number)") default: createTextMessageBox(text:"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy") } number += 1 } func createTextMessageBox(text:String) { // Width of the textbox is fixed and depends on the device position var textboxWidth : CGFloat = 500 if UIDevice.current.orientation.isLandscape { textboxWidth = 800 } else { textboxWidth = 500 } // Create the text message label let notificationText = SKLabelNode(fontNamed: "AppleSDGothicNeo-Regular") notificationText.name = "message_text" notificationText.text = text notificationText.fontSize = 45 notificationText.fontColor = SKColor.white notificationText.alpha = 0 notificationText.numberOfLines = 0 notificationText.horizontalAlignmentMode = .left notificationText.verticalAlignmentMode = .center notificationText.preferredMaxLayoutWidth = textboxWidth-50 // Height of the textbox depends on the size of the text let textboxHeight = notificationText.frame.height + 50 // Notification textbox that contains the text let rightEdgeX = self.convert(CGPoint(x: self.frame.maxX, y: 0), to: background).x let bottomEdgeY = self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y let boxPositionX = rightEdgeX-50-textboxWidth let boxPositionY : CGFloat = bottomEdgeY+100 // Create the Notification Textbox let notificationNode = SKShapeNode(rect: CGRect(x: boxPositionX,y: boxPositionY,width: textboxWidth,height: textboxHeight), cornerRadius: 20) notificationNode.name = "message_box" notificationNode.fillColor = UIColor(red: 120/255, green: 0/255, blue: 0/255, alpha: 0.8) notificationNode.strokeColor = UIColor(red: 128/255, green: 0/255, blue: 0/255, alpha: 1) notificationNode.lineWidth = 2 notificationNode.alpha = 0 //notificationNode.zPosition = 0 // Position text in the middle of the texbox notificationText.position = CGPoint(x: notificationNode.frame.minX+25, y: notificationNode.frame.maxY-textboxHeight/2) // Add nodes to the scene background.addChild(notificationNode) notificationNode.addChild(notificationText) // Add to the list of text messages let currentMessage = TextMessage(messageText: notificationText, messageBox: notificationNode) listOfTextMessages.insert(currentMessage, at: 0) // The first message is shown at the bottom, whereas the older messages are moved on top of it. for (index,textBox) in listOfTextMessages.enumerated() { // The latest message if index == 0 { let actionBoxFadeIn = SKAction.fadeAlpha(to: 0.8, duration: 0.2) // Fade in the textbox let actionTextFadeIn = SKAction.run { textBox.messageText!.run(SKAction.fadeIn(withDuration: 0.2)) } let actionMoveGroup = SKAction.group([actionBoxFadeIn,actionTextFadeIn]) textBox.messageBox!.run(actionMoveGroup) } else { textBox.messageBox!.position.y += listOfTextMessages[0].messageBox!.frame.height } } } override func didChangeSize(_ oldSize: CGSize) { super.didChangeSize(oldSize) var zPosition : CGFloat = 10 if background != nil { background.size = CGSize(width: self.frame.width, height: self.frame.height) background.position = CGPoint(x: self.frame.width/2, y: self.frame.height/2) } if self.view != nil &amp;&amp; !listOfTextMessages.isEmpty { let textboxWidth : CGFloat = UIDevice.current.orientation.isLandscape ? 800 : 500 let textlabelWidth = textboxWidth-50 for (index,textBox) in listOfTextMessages.enumerated() { textBox.messageBox!.zPosition = zPosition zPosition += 1 textBox.messageText!.fontSize = 45 textBox.messageText!.fontColor = SKColor.white textBox.messageText!.numberOfLines = 0 textBox.messageText!.horizontalAlignmentMode = .left textBox.messageText!.verticalAlignmentMode = .center textBox.messageText!.preferredMaxLayoutWidth = textlabelWidth let textboxHeight = textBox.messageText!.frame.height + 50 let rightEdgeX = self.convert(CGPoint(x: self.frame.maxX, y: 0), to: background).x let boxPositionX = rightEdgeX-50-textboxWidth let boxPositionY : CGFloat = { return (index == 0) ? self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y : listOfTextMessages[index-1].messageBox!.frame.minY }() textBox.messageBox!.path = UIBezierPath(roundedRect: CGRect(x: boxPositionX, y: boxPositionY, width: textboxWidth, height: textboxHeight), cornerRadius: 20).cgPath textBox.messageText!.position = CGPoint(x: textBox.messageBox!.frame.minX+25, y: textBox.messageBox!.frame.minY+textboxHeight/2) } } }The text is also shifted upwards.Interesting enough, if I do the following (create each box in the array using the coordinates of the first box), the boxes are placed right on top of each other; however, their position is also skewed when their height varies:let boxPositionY : CGFloat = { return self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y }() textBox.messageText!.position = CGPoint(x: listOfTextMessages[0].messageBox!.frame.minX+25, y: listOfTextMessages[0].messageBox!.frame.minY+textboxHeight/2)Could someone please help me find the culprit?Thanks a lot!
Posted
by igorland.
Last updated
.
Post marked as solved
6 Replies
1.2k Views
Hi. I am trying to figure out how to do a simple thing, but all research that I have done so far has not clarified what the proper way to do it is. I am adding a UITextView that should expand while I am typing upwards and then enable scrolling when the maximum height is reached. Instead, my UITextView is expanding downwards. My code:overridefunc didMove(to view: SKView) { txtMessage = UITextView() txtMessage.font = UIFont.systemFont(ofSize: 16) txtMessage.layer.cornerRadius = 5 txtMessage.autocorrectionType = UITextAutocorrectionType.no txtMessage.keyboardType = UIKeyboardType.default txtMessage.returnKeyType = UIReturnKeyType.done txtMessage.isScrollEnabled = false txtMessage.delegate = self self.view!.addSubview(txtMessage) registerForKeyboardNotifications() } func textViewDidChange(_ textView: UITextView) { let fixedWidth = textView.frame.size.width // Changing height of the message UITextView let newSize = textView.sizeThatFits(CGSize.init(width: fixedWidth, height: CGFloat(MAXFLOAT))) var newFrame = textView.frame newFrame.size = CGSize.init(width: CGFloat(fmaxf(Float(newSize.width), Float(fixedWidth))), height: newSize.height) txtMessage.frame = CGRect(origin: textView.frame.origin, size: CGSize(width: newFrame.width, height: newFrame.height)) } func registerForKeyboardNotifications() { let notificationCenter = NotificationCenter.default notificationCenter.addObserver( self, selector: #selector(GameScene.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil ) notificationCenter.addObserver( self, selector: #selector(GameScene.keyboardWillBeHidden(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) } func unregisterForKeyboardNotifications() { let notificationCenter = NotificationCenter.default notificationCenter.removeObserver( self, name: UIResponder.keyboardWillShowNotification, object: nil) notificationCenter.removeObserver( self, name: UIResponder.keyboardWillHideNotification, object: nil) } @objc func keyboardWillShow(_ notification: Notification) { let txtMessageViewHeight : CGFloat = 41 if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { let duration:TimeInterval = (notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0 let animationCurveRawNSN = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw) keyboardHeight = keyboardFrame.cgRectValue.height let viewHeight = keyboardHeight + txtMessageViewHeight self.txtMessage.frame.origin.y -= viewHeight self.txtMessage.layoutIfNeeded() } }Thank you!
Posted
by igorland.
Last updated
.
Post marked as solved
2 Replies
953 Views
Hi. I had the following code in Swift 3 to send a String message using NSStream.@objcfunc processOutput() { // Create a message and convert it to data let msgToServer = makeMessageToServer() self.outputBuffer = msgToServer.data(using: .utf8, allowLossyConversion: false) if self.outputBuffer != nil { if self.outputBuffer?.count != 0 { let bytesWritten = self.outputBuffer!.withUnsafeBytes { self.outputStream!.write($0, maxLength: self.outputBuffer!.count) } if bytesWritten &lt;= 0 { self.error = self.outputStream?.streamError as NSError? } else { self.outputBuffer?.replaceSubrange(0..&lt;byteswritten, with:="" data())&lt;br=""&gt; } } } }Now, in Swift 5, I get the following warning in Line 12:'withUnsafeBytes' is deprecated: use `withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -&gt; R) rethrows -&gt; R` insteadI have read several posts on the topic but still have not figured out how to deal with the wanring in my case.Your help would be greatly appreciated!
Posted
by igorland.
Last updated
.
Post marked as solved
4 Replies
1.7k Views
Hi. I am stuck trying to figure out how I can perform a series of actions (moving) on a number of nodes one after another. In other words, I want one node to move; once the move is over, the second node will move; etc.Having read a few sources, I ended up with something along these lines:let dispatchGroup = DispatchGroup() numberOfNodes = 6 var i=0 while numberOfNodes &gt; 0 { let positionToMove = CGPoint(x: 400, y: 400) let moveAction = SKAction.move(to: positionToMove, duration: 1.0) let waitAction = SKAction.wait(forDuration: 5) let sequenceAction = SKAction.sequence([waitAction,moveAction]) dispatchGroup.enter() arrayOfNodes[i].run(sequenceAction, completion: { dispatchGroup.leave() }) i+=1 numberOfNodes -= 1 } dispatchGroup.notify(queue: DispatchQueue.main, execute: { print("COMPLETED GROUP") })Still, I have all of my nodes moving at the same time, after which the notification is received. How can I make the nodes move one after another and then get a notification?Thanks a lot!
Posted
by igorland.
Last updated
.
Post not yet marked as solved
2 Replies
1k Views
Hello. I am using MultipeerConnectivity framework in my SpriteKit game.I have two scenarios when a peer leaves the game. First, when the peer leaves temporarily: connection is lost but all the nodes for this peer remain in the scene. And second: when the peer decides to leave for good. S/he selects an option "Exit the game" and sends the message to all other players saying that s/he is leaving. When the message is received by others, this peer's nodes should be removed from the scene.This code is for exiting the game:@objcfunc exitGame() { // Show an alert whether the player wants to exit the game let alert = UIAlertController(title: "", message: "Do you want to leave the game?", preferredStyle: UIAlertController.Style.alert) // If the player wants to leave the game let acceptAction: UIAlertAction = UIAlertAction(title: "Leave", style: UIAlertAction.Style.default) { _ in // Send a message to other players that you are exiting // Create data to send: the current deck is sent to the peers let messageToSend = self.makeMessageExit() // A message is sent to inform others that I left self.appDelegate.connectionManager.sendData(dictionaryWithData: messageToSend, toPeer: self.appDelegate.connectionManager.session.connectedPeers) DispatchQueue.main.async { // Here goes the code to Remove the Menu with the Button Exit if self.btnExitGame != nil &amp;&amp; self.blurEffectView != nil &amp;&amp; self.viewMenu != nil { self.btnExitGame.removeFromSuperview() self.btnExitGame = nil self.blurEffectView.removeFromSuperview() self.blurEffectView = nil self.viewMenu.removeFromSuperview() self.viewMenu = nil } // This closes the game scene self.closeSceneToFindPlayers() // Deinitialize Connection Manager self.appDelegate.connectionManager = nil } } // If the player declines to leave the game let declineAction = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel) { _ in } alert.addAction(acceptAction) alert.addAction(declineAction) OperationQueue.main.addOperation { () -&gt; Void in self.view?.window?.rootViewController?.present(alert, animated: true, completion: nil) } }This code is for losing the peer:func lostPeer(peerID: MCPeerID) { print("I AM LOSING PEER.............") // If the peer temporarily left the game, change the status for (index, player) in self.appDelegate.players.enumerated() { if player.peerID?.displayName == peerID.displayName //&amp;&amp; player.status == "in" TODO: THIS IS REMOVED NEEDED?? { self.appDelegate.players[index].status = "out_temp" } } // Display an alert message that the peer left let alert = UIAlertController(title: "Player Left", message: "\(peerID.displayName) left the game.", preferredStyle: UIAlertController.Style.alert) // Add an action (button) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) OperationQueue.main.addOperation { () -&gt; Void in self.view?.window?.rootViewController?.present(alert, animated: true, completion: nil) } // Then I mark the leaving peer's nodes indicating that s/he left, but the nodes are still in the scene.... drawFields() }Here is the method to deal with the peer when s/he left for good when I receive the exit message from him/her:func updateSceneExit(sender: MCPeerID?) { print("SCENE EXIT UPDATING............") // Remove peer from the list of players for player in appDelegate.players { if player.peerID?.displayName == sender?.displayName { appDelegate.players = appDelegate.players.filter(){$0 != player} } } // Redraw fields drawFields() print("SCENE EXIT UPDATED............",appDelegate.players) }Now. In 80%-90% of cases, everything works fine. However when I have more than 2 players connected, sometimes what happens is that the connection with the peer is lost BEFORE I receive his/her Exit message. This results in the situation when the peer said that s/he had left, but I still have his/her nodes in the scene. Meanwhile, another player would still receive the message in advance resulting in no error.Can someone please help me resolve the problem? Would be greatly appreciated!!!
Posted
by igorland.
Last updated
.
Post marked as solved
12 Replies
4.9k Views
Hi. For the past few days, I have been trying to create an SKShapeNode (a rectangle) that will be used as a menu screen moving down from the outside of the screen when a button is tapped. Everything behind the node should be blurred.Something to this effect: https://stackoverflow.com/questions/49142867/spritekit-blurred-background-sknodeAlternatively, I may have everything in the background blurred with the exception of the SKShapeNode and its children.Using this:let blur = CIFilter(name:"CIGaussianBlur",withInputParameters: ["inputRadius": 10.0]) self.filter = blur self.shouldRasterize = true self.shouldEnableEffects = true menu.run(moveMenuDown)where self is the scene, blurs the background and the SKShapeNode. Besides, it is an awful hit on FPS.I tried the following:let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.light) let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.frame = self.view!.bounds blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] self.view!.addSubview(blurEffectView)This is very fast, but again this blurs everything including the SKShapeNode, which is not intended.Is there anyone who did something like that?Thanks a lot!
Posted
by igorland.
Last updated
.
Post not yet marked as solved
3 Replies
1.1k Views
Hi.I would like to add an UIView in my SKScene with three components in it: a UILabel, a UITextField, and a UIButton. Their layout must change based on the device orientation. In the Landscape mode, they will all be aligned along the Y-axis; in the Portrait mode, they will be located one under another aligned along the X-axis. The height of the view must also change accordingly.For now, I am trying to change the size of the UIView based on the orientation like this: let newView = UIView() newView.backgroundColor = UIColor.red newView.translatesAutoresizingMaskIntoConstraints = false self.view?.addSubview(newView) let newViewLeftAnchorLandscape = newView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20) let newViewRightAnchorLandscape = newView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20) let newViewTopAnchorLandscape = newView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20) let newViewHeightAnchorLandscape = newView.heightAnchor.constraint(equalToConstant: 40) let newViewHeightAnchorPortrait = newView.heightAnchor.constraint(equalToConstant: 150) if UIDevice.current.orientation.isLandscape { newViewLeftAnchorLandscape.isActive = true newViewRightAnchorLandscape.isActive = true newViewTopAnchorLandscape.isActive = true newViewHeightAnchorLandscape.isActive = true newViewHeightAnchorPortrait.isActive = false } else { newViewLeftAnchorLandscape.isActive = true newViewRightAnchorLandscape.isActive = true newViewTopAnchorLandscape.isActive = true newViewHeightAnchorLandscape.isActive = false newViewHeightAnchorPortrait.isActive = true }However, in either orientation the height of the view is 150.Making newViewHeightAnchor a class property and then implementing like this: if UIDevice.current.orientation.isLandscape { newViewLeftAnchorLandscape.isActive = true newViewRightAnchorLandscape.isActive = true newViewTopAnchorLandscape.isActive = true newViewHeightAnchor = newView.heightAnchor.constraint(equalToConstant: 30) newViewHeightAnchor.isActive = true } else { newViewLeftAnchorLandscape.isActive = true newViewRightAnchorLandscape.isActive = true newViewTopAnchorLandscape.isActive = true newViewHeightAnchor = newView.heightAnchor.constraint(equalToConstant: 150) newViewHeightAnchor.isActive = true }helps when opening the scene. However, it does not resize the view when changing the orientation here: overridefunc didChangeSize(_ oldSize: CGSize) { if newView != nil { if UIDevice.current.orientation.isLandscape { newViewHeightAnchor = newView.heightAnchor.constraint(equalToConstant: 30) newViewHeightAnchor.isActive = true } else { newViewHeightAnchor = newView.heightAnchor.constraint(equalToConstant: 150) newViewHeightAnchor.isActive = true } } }Any thoughts?Thanks a lot!
Posted
by igorland.
Last updated
.
Post marked as solved
2 Replies
992 Views
I am a little bit confused about how to decode an SKSpriteNode.I have the following SKSpriteNode class:class Person : SKSpriteNode { var gender : Gender var age : Age var positionX : CGFloat! var positionY : CGFloat! let frontTexture : SKTexture let backTexture : SKTexture required convenience init?(coder aDecoder: NSCoder) { print("INITIALIZED WITH DECODER????") guard let myPerson = aDecoder.decodeObject(forKey: "person") as? Person else { return nil } self.init(coder: aDecoder) } func encodeWithCoder(coder: NSCoder) { coder.encode(self, forKey: "person") } init(gender: Gender, age: Age, positionX: CGFloat, positionY: CGFloat, sceneSize: CGSize) { self.gender = gender self.age = age self.backTexture = SKTexture(imageNamed: "person_back") self.frontTexture = SKTexture(imageNamed: "person_front") var currentTexture = self.frontTexture super.init(texture: currentTexture, color: .clear, size: frontTexture.size()) // Position of the person in the scene self.position = updatePosition(positionX: positionX, positionY: positionY, sceneSize: sceneSize) }In a MultipeerConnection subclass, I use this to send encoded data (data seems to be archived correctly):func sendData(dictionaryWithData dictionary: Dictionary&lt;string, any=""&gt;, toPeer targetPeers: [MCPeerID]) { let dataToSend = NSKeyedArchiver.archivedData(withRootObject: dictionary) do { try session.send(dataToSend, toPeers: targetPeers, with: MCSessionSendDataMode.reliable) } catch { print("ERROR") } }with data made as follows (crowd is an array [Person]):func makeMessageCrowd() -&gt; Dictionary&lt;string, any=""&gt; { var messageToSend = Dictionary&lt;string, any=""&gt;() messageToSend["move"] = "start" messageToSend["action"] = appDelegate.persons return messageToSend }I use the following to receive the data:func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { let message = NSKeyedUnarchiver.unarchiveObject(with: data) as! Dictionary&lt;string, any=""&gt; if let moveType = message["move"] as? String { switch(moveType) { case "start": appDelegate.persons = message["action"] as! [Person] default: print("default") } } }Right now, it starts initializing object Person with the convenience initializer, and then crashes here:let message = NSKeyedUnarchiver.unarchiveObject(with: data) as! Dictionary&lt;string, any=""&gt;saying that it found a nil value. However, data seems to have 15000 bytes, so it was passed well.If instead of a convenience initializer, I use this:required init?(coder aDecoder: NSCoder) { //fatalError("NSCoding not supported") self.gender = Gender.male self.age = Age.young self.frontTexture = SKTexture(imageNamed: "person_front") self.backTexture = SKTexture(imageNamed: "person_back") self.positionX = 0 self.positionY = 0 super.init(coder: aDecoder) }I do not have a crash, however the array [Person] on the peer device is created using the default values initialized as above, and not as passed.The culprit is definitely this as it returns nil in the first case: guard let myPerson = aDecoder.decodeObject(forKey: "person") as? Person else { return nil }How do I do encode and decode a custom class properly?Thanks a lot!
Posted
by igorland.
Last updated
.
Post not yet marked as solved
0 Replies
526 Views
Hi.I have three problems implementing the MultipeerConnectivity framework.1) I am trying to connect three players to one MCSession. Every action that one peer does should be repeated by other peers. If I connect like this:A (iPad device) -&gt; C (Simulator)B (iPhone device) -&gt; Call actions performed by A and B are received by in C. However, when A is advertising and I connect B and C to A, whatever I do on B is not repeated on C (my Simulator). How to ensure that messages are sent all directions (three-way or four way)?2) Whenever one peer leaves the session, I am trying to reconnect the same peer to the same MCSession, but it never does. Why is this?3) If I disconnect peer A that was originally browsing and to which other peers connected, peer B starts crashing with the following message: "message sent to deallocated instance &lt;MCPEERID A&gt;" whenever I do any actions on it. Is there a way to preserve the session and other peers if they joined the peer that left?This is my class ConnectionManager classimport Foundation import MultipeerConnectivity protocol ConnectionManagerDelegate { func foundPeer() func lostPeer(peerID: String) func invitationWasReceived(fromPeer: String) func connectedWithPeer(peerID: MCPeerID) } class ConnectionManager : NSObject, MCSessionDelegate, MCNearbyServiceBrowserDelegate, MCNearbyServiceAdvertiserDelegate, StreamDelegate { var delegate: ConnectionManagerDelegate? private let CONNECTION_SERVICE_TYPE = "play-service" public var session: MCSession! public var peer: MCPeerID! public var browser: MCNearbyServiceBrowser! public var advertiser: MCNearbyServiceAdvertiser! public var foundPeers = [MCPeerID]() public var invitationHandler: ((Bool, MCSession?)-&gt;Void)! var outputStream:OutputStream? override init() { super.init() peer = MCPeerID(displayName: UIDevice.current.name) session = MCSession(peer: peer, securityIdentity: nil, encryptionPreference: MCEncryptionPreference.optional) session.delegate = self browser = MCNearbyServiceBrowser(peer: peer, serviceType: CONNECTION_SERVICE_TYPE) browser.delegate = self advertiser = MCNearbyServiceAdvertiser(peer: peer,discoveryInfo: nil,serviceType: CONNECTION_SERVICE_TYPE) advertiser.delegate = self } deinit { browser.stopBrowsingForPeers() advertiser.stopAdvertisingPeer() } func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) { foundPeers.append(peerID) delegate?.foundPeer() } func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) { for (index, aPeer) in foundPeers.enumerated() { if aPeer == peerID { foundPeers.remove(at: index) break } } delegate?.lostPeer(peerID: peerID.displayName) } func browser(_ browser: MCNearbyServiceBrowser, didNotStartBrowsingForPeers error: Error) { print("DID NOT START BROWSING FOR PEERS",error.localizedDescription) } func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -&gt; Void) { self.invitationHandler = invitationHandler delegate?.invitationWasReceived(fromPeer: peerID.displayName) } func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didNotStartAdvertisingPeer error: Error) { print("DID NOT START ADEVRTISING FOR PEERS",error.localizedDescription) } func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { switch state { case MCSessionState.connected: delegate?.connectedWithPeer(peerID: peerID) startSession(peer: peerID) case MCSessionState.connecting: print("Connecting to session: \(session)") case MCSessionState.notConnected: session.disconnect() print("NOT connected to session: \(session)") default: print("Did not connect to session: \(session)") stopSession() } } func session(_ session: MCSession, didReceiveCertificate certificate: [Any]?, fromPeer peerID: MCPeerID, certificateHandler: @escaping (Bool) -&gt; Void) { certificateHandler(true) } func startSession(peer peerID: MCPeerID) { do { if outputStream == nil { try outputStream = self.session.startStream(withName: "stream", toPeer: peerID) } if let outputStream = outputStream { outputStream.delegate = self outputStream.schedule(in: RunLoop.main, forMode:RunLoopMode.defaultRunLoopMode) outputStream.open() } } catch let error { print("ERROR STARTING STREAM IN SESSION: \(error)") } } func stream(_ aStream: Stream, handle eventCode: Stream.Event) { switch(eventCode) { case Stream.Event.hasBytesAvailable: print("--STREAM EVENT HAS BYTES AVAILABLE--") processInput(from: aStream) break case Stream.Event.hasSpaceAvailable: print("--STREAM EVENT HAS SPACE AVAILABLE--") break case Stream.Event.endEncountered: print("--STREAM EVENT END ENCOUNTERED--") break case Stream.Event.errorOccurred: print("--STREAM EVENT ERROR OCCURRED--") break default: print("--STREAM EVENT DEFAULT--") break } } func stopSession() { closeConnection() } func closeConnection() { if(self.outputStream != nil) { self.outputStream?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode) self.outputStream?.close() self.outputStream = nil; } if(session != nil) { session.disconnect() } } func sendDataToPeer() { if session.connectedPeers.count &gt; 0 { let message = "This is a string test" let data = NSKeyedArchiver.archivedData(withRootObject: message) //this is a NSData instance. Could be any NSData, as a serialized object, for example. if let outputStream = outputStream { _ = data.withUnsafeBytes { outputStream.write($0, maxLength: data.count) } } } } func processInput(from aStream: Stream) { let input = aStream as! InputStream var buffer = [UInt8](repeating: 0, count: 1024) //allocate a buffer. The size of the buffer will depended on the size of the data you are sending. let bytesRead = input.read(&amp;buffer, maxLength:buffer.count) // Read buffer if (bytesRead&gt;0) { let dataString = NSData(bytes: &amp;buffer, length: bytesRead) let message = NSKeyedUnarchiver.unarchiveObject(with: dataString as Data) as! String //deserializing the NSData print("STREAM: ",message) } else if (bytesRead &lt; 0) { print("Input stream has less than 0 bytes") // Close connection //stopSession() TO DO!!! Disabled for now. Should the session continue? } else if bytesRead == 0 { print("Input stream has 0 bytes") } } func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { if streamName == "stream" { stream.delegate = self stream.schedule(in: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode) stream.open() } } }Thank you!
Posted
by igorland.
Last updated
.