If the user is using their mouse left-handed, are .leftMouseDown and .rightMouseDown swapped? - mouse

I want to know that the system handles this for me. I really hope it does.
If the user is left-handed, and so the left and right mouse buttons' meanings are swapped, then do all NSEvent things related to these two buttons also swap?
For an in-code example, I want to know if this is left-handed-user-friendly:
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
performPrimaryEvent()
}
override func rightMouseDown(with event: NSEvent) {
super.rightMouseDown(with: event)
performSecondaryEvent()
}
The official documentation says nothing and Google searches are futile...

I plugged in a mouse just to test this. It turns out the answer is yes! All these values and callbacks are automatically flipped.

Related

Game Center Turn Timeout for Multiplayer GAmes

I have created a turn based multiplayer board game using Swift and Game Center that works pretty well. One of the last items I would like to add is a way to keep a player from abandoning a game near the end if they know they are going to lose. It seems like the turnTimeout portion of the endTurn function is built in especially for this purpose, but I cannot get it to work. My endTurn function is below:
func endTurn(_ model: GameModel, completion: #escaping CompletionBlock) {
guard let match = currentMatch else {
completion(GameCenterHelperError.matchNotFound)
return
}
do {
let currenParticipantIndex: Int = (match.participants.firstIndex(of: match.currentParticipant!))!
let nextPerson = [match.participants[(currenParticipantIndex+1) % match.participants.count]]
print("end turn, next participant \(String(describing: nextPerson[0].player?.alias))")
match.endTurn(
withNextParticipants: nextPerson,
turnTimeout: 15,
match: try JSONEncoder().encode(model),
completionHandler: completion
)
} catch {completion(error)}
}
This function takes into account the advice from Anton in the comment of the answer to this question:
Trying to set a time limit on my Game Center game
to update the array of nextParticipant players so that the end of the array is never reached. I've also tried to account for this in my testing by having both player 1 and player 2 delay the end of their turn to see if it would fire (The game is a 2 player game only)
This should also answer this question:
Game Center turn timeouts
The documentation says:
timeoutDate: The time at which the player must act before forfeiting a turn. Your game decides what happens when a turn is forfeited. For some games, a forfeited turn might end the match. For other games, you might choose a reasonable set of default actions for the player, or simply do nothing.
https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/GameKit_Guide/ImplementingaTurn-BasedMatch/ImplementingaTurn-BasedMatch.html
Unfortunately I am unable to get the turnTimeout function to fire at all. I have done a fair amount of research and I found no definitive answer to what function is actually called when it fires (i.e. the player takes longer than the allotted time to take their turn). I would expect that the same function is called for a timeout as a regular call of the endTurn function and the below player listener is called:
func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch, didBecomeActive: Bool) {
if let vc = currentMatchmakerVC {
currentMatchmakerVC = nil
vc.dismiss(animated: true)
}
print("received turn event")
if !didBecomeActive {
print("\n\n\n player listener did become active")
print("match did change")
NotificationCenter.default.post(name: .matchDidChange, object: match)
} else if didBecomeActive {
print("present game")
NotificationCenter.default.post(name: .presentGame, object: match)
}
}
I am able to get the player listener (received turn event) to fire when endTurn is specifically called from the game, but I do not see anything that is called when the turnTimeout event triggers. If it was the player listener I would see the print statements in the console as well as the notification on the next player's device.
The GKTurnTimeoutDefault is 604,800 and is a Time Interval which I did some research on and arrived at the conclusion that it is in seconds, which is 7 days. I changed it to 0.00001, 15, 2000 and a few values in between but I wasn't able to get it to fire.
I also found the below, but the first has no answer and the second only says the turn timeouts probably warrants its own full answer:
Game Center Turnbased Game turn timeout
How to detect when Game Center turn based match has ended in iOS9?
I am thinking that my mistake is probably that I am unable to find the function that is called when the turn timeout fires, although I might be mistaken on the Time Interval values that I'm putting in there as well.
Thank you for taking the time to review my question :)

Is there a way to receive macOS events while users hold down a mouse button?

Having a SwiftUI slider.
The user keeps on dragging it around.
While dragging, the user also presses the Option key.
I want to change the user interface based on the keyboard modifier flag change (e.g. the Option key). However, it seems the main event loop is blocked while dragging the slider or even when having a mouse button pressed.
When using the NSEvent.addLocalMonitorForEvents(mathing:handler) to get notified of the Option keypress, the handler does not even get called while the user drags the slider.
Is there any way I can achieve that?
I would also love to understand why the problem exists in the first place.
Eventually, I ended up using a timer block checking and publishing changes of the keyboard modifier flags.
import Cocoa
import Combine
/**
Publishers for keyboard events.
The modifiers are checked regularly by a Timer running on the Main `RunLoop` in `.common` mode
to ensure the modifiers are detected even when having mouse button pressed down
(which normally blocks the run loops on the main thread which are not in `common` mode).
The `NSEvent.addLocalMonitorForEvents` cannot be used because the handler is not executed
for nested-tracking loops, such as control tracking (e.g. when a mouse button is down).
*/
public class KeyboardEvents {
/// Broadcasts keyboard flag changes (e.g. keys like Option, Command, Control, Shift, etc.)
public let modifierFlagsPublisher: AnyPublisher<NSEvent.ModifierFlags?, Never>
private let modifierFlagsSubject = PassthroughSubject<NSEvent.ModifierFlags?, Never>()
private var timer: Timer!
public init() {
modifierFlagsPublisher = modifierFlagsSubject.removeDuplicates().eraseToAnyPublisher()
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [unowned self] _ in
modifierFlagsSubject.send(NSApp.currentEvent?.modifierFlags)
}
RunLoop.current.add(timer, forMode: .common)
}
deinit {
timer.invalidate()
}
}

Smoother Sprite-Kit objects movement

I'm currently programming a little pong-game for Mac, but I can't find a way to make the paddles move smoothly. I use the keyDown-function to detect when a key (for example W) is pushed. This executes a run-command:
override func keyDown(with event: NSEvent) {
if event.keyCode == 126 {
pR.run(SKAction.move(by: CGVector(dx: 0, dy: 15), duration: 0))
}
}
The thing is, when I push the arrow up-button (keyCode 126), my sprite/paddle (pR) moves one time, and if I keep holding the button down, it starts moving continuously.
Also, if I have two if-instructions (for example one for both pong players), it seems like there can't be made two inputs at the same time, that is, both players can't push a button and expect a response.
How can I solve these issues? All help is appreciated.
Question 1: Why isn't my paddle moving smoothly?
Why are you running a movement SKAction with a duration of 0?
This is the exact same as changing it's y position
You could making the duration longer like 0.1
If you want to do this, you should remove p1's SKAction every time you click the W key.
Why? You don't want it to run 2 SKActions at the same time.
(I'm assuming pR is your player 1 paddle. Stands for Paddle Right I guess.)
pR.removeAllActions()
pR.run(SKAction.move(by: CGVector(dx: 0, dy: 15), duration: 0.1))
Or, if you want it to be immediate, you could do this instead
(Although your paddle would move quite quickly depending on your frame rate)
pR.position.y += 15
I recommend the first option.
Or, if you like the second option because it's cleaner, you replace the 15 to 10 or 5.
Question 2: There can't be made two inputs at the same time
• 1: It's impossible for you to click 2 buttons at the 'exact' same time.
• 2: What does this mean?
• 3: Swift runs keyDown with the first key you pressed.
• 4: Swift then runs keyDown with the second key you pressed.
(Even if you think you pressed two keys at the same time, you didn't. One was probably pressed a microsecond before the second. It's all about timing. This is the same for SpriteKit on iOS, you cannot have two 'touchesBegan' inputs at the same time. It's quite rare.)
How to fix it:
You should be able to add a second 'if' statement for another key for player 2.
Example code (Except I used a switch to save space)
override func keyDown(with event: NSEvent) {
switch Int(event.keyCode) {
// W Key
case 13: // (Player 1 moves up)
// S Key
case 1: // (Player 1 moves down)
// I Key
case 34: // (Player 2 moves up)
// K Key
case 40: // (Player 2 moves down)
// Someone clicked the wrong key
default: return
}
}
Guess what! This code ^^^ almost works. I haven't answered your third question.
Question 3: Paddle Moves Once, Stops, Then Starts Moving Continuously
This is how the keyDown function works apparently.
Here is what I recommend:
• Keep track in a variable which keys have been pressed
• Use the 'keyUp' function to remove those stored values.
How does this work?
var keysPressed: Set<Int> = []
override func keyDown(with event: NSEvent) {
keysPressed.insert(event.keyCode)
}
override func keyUp(with event: NSEvent) {
keysPressed.remove(event.keyCode)
}
override func update(_ currentTime: TimeInterval) {
for i in keysPressed {
// Put my Movement Switch Statement In Here
}
}
I hope this helps! If you have any questions, please feel free to reply.
Snap, I just realized that this question was answered in the tiny comment section above. Oh well.

How to control the speed of nodes in SpriteKit

I have a bunch of objects (SKNode) starting from the top of the screen 'falling' to the bottom via SKAction.move(to:duration:) and node.run(moveAction). In addition I have node in the center of the screen with its own physics body that can be dragged side to side with touch input. I can detect collisions fine, but I was wondering if there was a paradigm for 'pausing' all the objects while the center node is contact with any of the objects. Additionally, I want to able to move the center node while the other objects are 'paused' so I can move it out of the way and then let the objects resume their motion. I figured I could probably iterate through all the existing objects and set their isPaused property, but I am unsure how the app would know when the center node is no longer 'colliding' so I can toggle the property back.
To pause things, you will have to detect the contact in didBegin() , tofill some array with nodes that should be paused and finally to pause the nodes. Actual pausing could be done in didSimulatePhysics() for example. To pause all the nodes you could use
self.enumerateChildNodesWithName("aName") {
node, stop in
// do something with node or stop
}
or use children property of a node and loop through it (eg. to loop through the container of your nodes that should be paused).
Also you can pause certain action with:
if let action = square.actionForKey("aKey") {
action.speed = 0
}
and unpause it with action.speed = 1, or make it slow-mo with action.speed = 0.5
To slow down physics simulation, there is a property called physicsWorld.speed (determines the rate at which the simulation runs).
Oh boy, things sure do get complicated. Every thing that Whirlwind says is correct, but to iterate through every node on the scene can become burdensome.
Instead, I recommend creating a special subclass for SKNode. Now this special class is needed due to a bug caused by apples framework
class UserPausableNode : SKNode
{
public var userPaused = false
{
didSet
{
super.isPaused = userPaused
}
}
override var isPaused : Bool
{
get
{
return userPaused
}
set
{
//Yes, do nothing here
}
}
}
The reason why we need to do this is because whenever isPaused is called, it iterates to all children and sets the child isPaused property.
This becomes a problem when scene pauses and unpauses, as it will change the pause state of the children, which we do not want to be doing.
This class prevents the isPaused variable from changing.
Now that this class exists, what we want to do is add it to the scene, and add all moving nodes to this new node.
Since all of the nodes are on this new node, all we need to do is set the userPaused to true, and this will stop all nodes.
Now make sure the node you are moving is not a part of this branch, it should be outside so that you could move it.
Example
class GameScene : SKScene
{
var userBranch = UserPausableNode()
func didMove(to view:SKView)
{
self.addChild(userBranch)
//generetedChildren are nodes that you create that you plan on pausing as a group
for generatedChild in generatedChildren
{
userBranch.addChild(node)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//this will pause and unpause based on opposite of previous state
userBranch.userPaused = !userBranch.userPaused
}
}

How to control AVAudioPlayer using earPods remote control?

I'm using AVAudioPlayer to play music
I want to control playing from remote controls which are part of using headphones.
I've already this:
override func remoteControlReceivedWithEvent(event: UIEvent?) {
let rc = event!.subtype
print(rc.rawValue)
//rc.rawValue: 101 = pause, 100 = play
switch rc {
case .RemoteControlPlay:
playButtonClicked("")
case .RemoteControlPause:
pauseButtonClicked("")
default:break
}
}
It's working great from this menu
But clicks on headphones are ignored. How can I fix it?
I believe that the headphones send the TogglePlayPauseCommand event:
https://developer.apple.com/library/ios/documentation/MediaPlayer/Reference/MPRemoteCommandCenter_Ref/index.html#//apple_ref/occ/instp/MPRemoteCommandCenter/togglePlayPauseCommand
Because I see you're using Xamarin, RemoteControlTogglePlayPause should be of interest
https://developer.xamarin.com/api/type/MonoTouch.UIKit.UIEventSubtype/