Keyboard as game controller, handling multiple keys in Swift - swift

I'm trying to make a spaceship move on the screen with the keyboard. I manage to deal with key events, but I noticed that when multiple keys are kept down at the same time, it won't behave correctly and only one will have priority. I'm using a switch statement because I thought the keyDown function was called once for every key, but even when I explicitly add a fallthrough in the cases, it's not better. Has anyone ever experienced anything like that and is there any better way to use the keyboard as a controller?
override func keyDown(with event: NSEvent) {
switch event.keyCode {
case 0x31:
if let playerShip = self.playerShip {
playerShip.run(SKAction.init(named: "Pulse")!, withKey: "fadeInOut")
}
case 123:
if let playerShip = self.playerShip {
playerShip.run(SKAction.applyAngularImpulse(0.001, duration: 0.1))
}
case 124:
if let playerShip = self.playerShip {
playerShip.run(SKAction.applyAngularImpulse(-0.001, duration: 0.1))
}
case 125:
if let playerShip = self.playerShip {
playerShip.run(SKAction.applyImpulse(CGVector.init(angle: playerShip.zRotation).opposite(), duration: 0.1))
}
case 126:
if let playerShip = self.playerShip {
playerShip.run(SKAction.applyImpulse(CGVector.init(angle: playerShip.zRotation), duration: 0.1))
}
default:
print("keyDown: \(event.characters!) keyCode: \(event.keyCode)")
}
}

My guess is that even if you get the code working exactly the way you describe it will be extremely difficult in practice for anyone playing your game to hit two keys at the exact same time. One key will almost always be hit slightly before the other. So, maybe you could implement it so that you capture one key event, and then look for a second one happening within a short time window after the first one (before the first one is released).

The keyDown method should be called by the OS X every time a key is pressed. If two keys are pressed, it's called twice, etc. Perhaps you're seeing this behavior because only the last key's action overrides the first key's action?

After testing a few things to make sure that my SKActions were not the cause of the problem, I remembered a few things from the days I was coding in Delphi with DelphiX and GLScene. I know it's PC, but it's related.
The thing is that the keyboard event cue will retrigger only the las key that was pressed. So applying force with the up arrow and keeping it pressed to accelerate will work until I press, for example, the left arrow to apply some torque. Then the left arrow key will get retriggered, but the next event to come from the up arrow, even if it's still pressed by now, will be when you actually release it. Therefore, the ship will start rotating but will stop accelerating because the keyDown event won't get retriggered for the up arrow.
This is why you need a way to keep track of key states, so you can check if multiple keys are pressed together at any given moment.
This is also why I'm gonna build my keyboard manager class.

Related

How to get info about completion of one of my animations in Unity?

I have monster named Fungant in my 2D platform game.
It can hide as mushroom and rise to his normal form.
I try to handle it in code, but I don't know how to get information about finished animation (without it, animation of rising and hiding always was skipped).
I think the point there is to get info about complete of the one of two animations - Rise and Hide.
There is current code:
if (
fungantAnimator.GetCurrentAnimatorStateInfo(0).IsName("Fungant Rise")
)
{
fungantAnimator.SetBool("isRising", false);
isRisen = true;
fungantRigidbody.velocity = new Vector2(
walkSpeed * Mathf.Sign(
player.transform.position.x - transform.position.x),
fungantRigidbody.velocity.y
);
}
if (fungantAnimator.GetCurrentAnimatorStateInfo(0).IsName("Fungant Hide"))
{
fungantAnimator.SetBool("isHiding", false);
isRisen = false;
}
I try this two ways:
StateMachineBehaviour
I would like to get StateMachineBehaviour, but how to get it?
No idea how to process this further.
AnimationEvents
Tried to do with animation event but every tutorial have list of functions to choose (looks easy), but in my Unity I can write Function, Float, Int, String or select object (what I should do?).
I decided to write test() function with Debug only inside, and create AnimationEvents with written "test()" in function field, but nothing happens.
Same as above, no more ideas how to process this further.
I personally would use animation events for this. Just add an event to the animation and then the event plays a function after the animation transition is finished.
For more information on how to use animation events you can click here.
This post may also help you to add an event when the animation is finished.
I hope this answer helps you to solve this problem.

RxSwift With UiTextField

Im new to RxSwift and what Im trying is perform the following,
User type is search UiTextField
App must wait 1 sec after user finish typing to make sure not to hit the Api with each letter write or delete button pressed.
App hit my Api for search
I tried the below code as example
class DestinationSearch: UIViewController {
let searchTF = UITextField()
var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
setUpUI()
searchTF.rx.controlEvent(.editingChanged)
.throttle(.milliseconds(1000), scheduler: MainScheduler.instance)
.withLatestFrom(searchTF.rx.text)
.subscribe(onNext:{ query in
print(self.hitSomeApi(query: query ?? "--"))
}).disposed(by: disposeBag)
}
func hitSomeApi(query: String) -> String {
return query + " Response from API"
}
When I run the app and start typing I get the the Response from API message with each letter or backspace button pressed! why the throttle delay is not working? Im I doing something wrong here?
Any help will be much appreciated
Based on your description of the problem, it seems like the debounce operator is more suitable than throttle. debounce only emits an element if a certain time, in which nothing has been emitted, has passed, whereas throttle ensures that elements are emitted at least a certain time interval apart.
I can confirm that your code using throttle is working as I'd expect - if I type very quickly, the "Response from API" messages appear roughly every 1 second. If I type very slowly, slower than 1 keystroke per second, then the messages come whenever I press a key. In other words, whenever there is an editing changed event, throttle checks to see if there was a previous one less than 1 second ago. If there is, ignore this new one.
If you use debounce however (the same code, just replace throttle with debounce), then after each keystroke, it would wait for 1 second to see if the text field is going to change again. It would only emit the editing changed event if it has waited and there is no more editing changed events. So if you keep typing at a pace higher than 1 keystroke per second, no elements will ever be emitted by the observable. This seems to be what you want.
You can compare these two operators on RxMarbles (they are called slightly different names there):
debounceTime
throttleTime

Control SCNAction with slider

I'm looking for a solution for my problem:
I have defined a animation as SCNAction with the action editor in Xcode. Now I want to control it with a slider. So basically manipulating the slider shows a specific time/frame in the animation.
Right now, I can just pause/play the animation or reset it to the beginning. I achieved this with Swift code addressing the SCNAction from the action editor.
As far as I researched, there is no possibility
to animate a SCNAction to a specific time immediately and stop right there. That would work.
I'm also interested in other attempts if this is not achievable with SCNActions. It may be that this is not realizable at all. Thank you.
I found a solution: I loaded a CAAnimation from a .dae file(For this look here:https://stackoverflow.com/a/24924702/9625548, bit outdated but still usable).
With the SCNAnimationPlayer I can address the CAAnimation with a key. With the following
code I can control my animation with a UISlider:
let moment: TimeInterval = TimeInterval(self.animationTimeSlider.value)
guard let animationPlayer: SCNAnimationPlayer = node.animationPlayer(forKey: key) else{
return
}
animationPlayer.animation.timeOffset = moment
animationPlayer.play()
animationPlayer.speed = 0.0
Basically, I read the thumb and convert it to TimerInterval. I'm looking for the SCNAnimationPlayer and aborting if no SCNAnimationPlayer is found for the given key. Then I set the .timeOffset to the moment I want to display. With play(), the animation starts from the beginning + .timeOffset. Finally, set the .speed to zero. the animation is not running but shows the moment.
Attention: If you let your animation running and also manipulating it in that way, you have to reset it. Otherwise it is getting messed up. You can catch that case with a boolean that saves if you let your animation run normally. Then you know you have to reset it before manipulating with the slider. For example like this:
node.removeAllAnimations()
node.addAnimation(animation, forKey: key)
self.sceneView.scene.rootNode.animationPlayer(forKey: key)?.paused = true

Processing Continuous Callbacks on key holding

I want to implement the navigational system in my Processing project. If user (that navigates the object) wants to turn left, he hits the 'a' button. Here is my callback function on keyPressed:
void keyPressed() {
...
if(key == 'a') { ship.plainAngle -= 0.1; }
else if(key == 'd') { ship.plainAngle += 0.1; }
}
What I want to avoid is spamming the 'a' key in order to make 90 degrees turn left. I tried increasing the value that affects it directly (0.1) but it made it look laggy. I heard that there is a possibility in processing to make continuous callbacks while user keeps holding the key. As I found out using callback function keyPressed should work out but it doesn't work for me. The code in callback function executes only once. I don't know how to solve that problem.
Use a boolean variable to keep track of whether the a key (or any other key you care about) is currently held down. Set it to true in the mousePressed() function, and set it to false in the mouseReleased() function. Then in the draw() function, you can check that variable and do something if the key is currently being pressed.
Shameless self-promotion: I wrote a tutorial on keyboard input in Processing available here. Check out the section on handling multiple key presses for the approach I just outlined.

Proper transition between SpriteKit Animations

I have an idle player animation and I want to do a smooth transition between some animations. The idle animation is the default one and from that transition I want to be able to switch to another state(let's say fight) and then back to idle. The code for the idle character animation is currently like :
self.addChild(playerAnimation)
playerAnimation.runAction(SKAction.repeatActionForever(
SKAction.animateWithTextures(playerAnimationManager.idleManAnimation.textureArray, timePerFrame: 0.1)))
Now, this is scheduled to go on forever for now, but I would need to intercept it and add a new animation on top of that (which is the same character, in a new state). I was thinking that I should stop the idle animation, switch to the new one and then back to idle when finished but I am not convinced that this is the best way of chaining animations and I haven't really found a good resource explaining how to go about it.
Any suggestions ? Thanks !
Depending on how short your texture array is, you might be able to do this.
I will try to explain without code seeing how I use objective C and you use Swift
First make a property or variable that can be called by any subroutine in this class file. It should be Boolean and should be set to NO. You could call it idleFlag.
Next make a method that changes the animation to fight mode. This change would be by removing the idle animation and replacing it with the fight animation. This method also set's idleFlag to NO. Let's call the method "beginFightAnim"
And last, in your repeatActionForEver idle animation, right after your animateWithTextures animation, add a runBlock animation. In this block define a static variable (one that will be remembered in the calling of the block over and over) and increment it by +1, add an "if statement" that looks something like this -> if (my_static_var == number_of_frames_in_texture_animations && idleFlag). And in the "if statement" set the static variable to 0 and call "beginFightAnim"
After this all you have to do to change the animation is set idleFlag to YES.
Hope it works!
If you have any problems, please comment below.
I want to do a series of examples to make it easier to understand the matter. Suppose you have your node called playerAnimation, a typical atlasc with a plist where you have
player-idle1.png
player-idle2.png
player-idle3.png
...
If you want to intercept in real-time your animation to know what frame is running in that moment you can do:
override func update(currentTime: CFTimeInterval) {
if String(playerAnimation.texture).rangeOfString("player-idle") != nil {
print(String(playerAnimation.texture))
}
}
At this point, after you have assigned a "key" to your action (withKey:"idleAnimation") you could stop your specific idle action when you preefer to start the other next animation.
Another good thing is to build a structure to know everytime your player status and modify this variable each time you launch a new action or animation:
enum PlayerStatus: Int {
case Idle = 1
case Fight = 2
case Run = 3
case Jump = 4
case Die = 5
...
}
var currentStatus: PlayerStatus!