AudioKit: MIDICallbackInstrument not triggering a callback when triggered from Sequencer - audiokit

I'm trying to get a callback when a midi note is triggered so I can display a graphical representation of the current beat in the bar. Nothing I do seems to callback. This is the simplest version of the code that I have tried - audio plays back fine but the callback is never triggered. I have enabled Audio in Background Modes.
Here is the code:
class ViewMain: UIViewController {
let engine = AudioEngine()
let kicks = Sampler()
let claps = Sampler()
let sequencer = Sequencer()
let mixer = Mixer()
let midiCallbackInstrument = MIDICallbackInstrument()
override func viewDidLoad() {
super.viewDidLoad()
midiCallbackInstrument.callback = myCallBack
mixer.addInput(midiCallbackInstrument)
mixer.addInput(kicks)
mixer.addInput(claps)
engine.output = mixer
do {
try engine.start()
} catch {
Log("AudioKit did not start! \(error)")
}
kicks.unloadAllSamples()
claps.unloadAllSamples()
guard let url = Bundle.main.resourceURL?.appendingPathComponent("SamplesSFZ/sfz/909A.sfz") else { return }
kicks.loadSFZ(url: url)
guard let url = Bundle.main.resourceURL?.appendingPathComponent("SamplesSFZ/sfz/909B.sfz") else { return }
claps.loadSFZ(url: url)
sequencer.addTrack(for: midiCallbackInstrument)
sequencer.tracks[0].add(noteNumber: 36, velocity: 120, channel: 0, position: 0.0, duration: 1.0)
sequencer.tracks[0].add(noteNumber: 37, velocity: 120, channel: 1, position: 0.5, duration: 1.0)
sequencer.tracks[0].add(noteNumber: 38, velocity: 120, channel: 2, position: 1.0, duration: 1.0)
sequencer.tracks[0].add(noteNumber: 39, velocity: 120, channel: 3, position: 0.5, duration: 1.0)
sequencer.tracks[0].add(noteNumber: 40, velocity: 120, channel: 4, position: 2.0, duration: 1.0)
sequencer.tracks[0].add(noteNumber: 41, velocity: 120, channel: 5, position: 0.5, duration: 1.0)
sequencer.tracks[0].add(noteNumber: 42, velocity: 120, channel: 6, position: 3.0, duration: 1.0)
sequencer.tracks[0].add(noteNumber: 43, velocity: 120, channel: 7, position: 0.5, duration: 1.0)
sequencer.addTrack(for: kicks)
sequencer.tracks[1].add(noteNumber: 24, velocity: 120, channel: 0, position: 0.0, duration: 2.0)
sequencer.addTrack(for: claps)
sequencer.tracks[2].add(noteNumber: 25, velocity: 120, channel: 0, position: 1.0, duration: 0.1)
sequencer.tracks[2].add(noteNumber: 25, velocity: 120, channel: 0, position: 3.0, duration: 0.1)
sequencer.loopEnabled = true
sequencer.tempo = 120.0
sequencer.length = 4.0
sequencer.play()
}
func myCallBack(a: UInt8, b:MIDINoteNumber, c:MIDIVelocity) -> () {
// THIS IS NEVER CALLED
print("*** callback invoked")
}
}
Thanks in advance - I really appreciate any help here.

Use sequencer.setGlobalMIDIOutput(midiCallbackInstrument.midiIn) or sequencer.tracks[0].setMIDIOutput(midiCallbackInstrument.midiIn) to connect sequencer and midiCallbackInstrument.

I have had good results with the newer AudioKit CallbackInstrument, which is intended as a replacement for MIDICallbackInstrument.

To get CallbackInstrument working with your code, you only need to make two changes:
// replace let midiCallbackInstrument = MIDICallbackInstrument() with
var midiCallbackInstrument = CallbackInstrument()
// replace midiCallbackInstrument.callback = myCallBack with
midiCallbackInstrument = CallbackInstrument(midiCallback: { [self] (event, beat, velocity) in
myCallBack(a: event, b: beat, c: velocity) } )
CallbackInstrument is documented here:
https://github.com/AudioKit/AudioKit/wiki/CallbackInstrument
and the Shaker Metronome recipe in the AudioKit Cookbook is an example of how it is used.
https://github.com/AudioKit/Cookbook

Related

iOS 13 breaks UIViewPropertyAnimator transition

We have a broken transition in WeatherKit only reproducible on iOS 13 beta. We're unsure if this is an UIKit bug or we're doing something awfully wrong.
With an array of UIViewPropertyAnimator working before iOS 13, ever since iOS 13 (through all of the betas) the animation frame is not updating correctly. For example, I have an UIViewPropertyAnimator called labelAnimator which animates a label to some specific CGRect, that CGRect is not respected and the label animates somewhere else as shown in the video.
Curious enough, if I mess around with the order of the transitions in the array, the bottom sheet works fine and the only one that animates wrong is the temperature label.
Here's the code that animates that whole view:
class MainView: UIViewController {
var panGesture = UIPanGestureRecognizer()
var tapGesture = UITapGestureRecognizer()
let animationDuration: TimeInterval = 0.75
var diff: CGFloat = 150
#IBOutlet weak var gradientView: GradientView!
#IBOutlet weak var detailedViewContainer: UIView!
#IBOutlet weak var blurView: UIVisualEffectView!
override func viewDidLoad() {
self.panGesture.addTarget(self, action: #selector(MainView.handlePanGesture(gesture:)))
self.detailedViewContainer.addGestureRecognizer(self.panGesture)
self.tapGesture.addTarget(self, action: #selector(MainView.handleTapGesture(gesture:)))
self.detailedViewContainer.addGestureRecognizer(self.tapGesture)
}
enum PanelState {
case expanded
case collapsed
}
var nextState: PanelState {
return panelIsVisible ? .collapsed : .expanded
}
var panelIsVisible: Bool = false
var runningAnimations = [UIViewPropertyAnimator]()
var animationProgressWhenInterrupted: CGFloat = 0.0
#objc func handleTapGesture(gesture: UITapGestureRecognizer) {
switch gesture.state {
case .ended:
tapAnimation()
default: break
}
}
#objc func tapAnimation(){
self.panGesture.isEnabled = false
self.tapGesture.isEnabled = false
startInteractiveTransition(state: nextState, duration: animationDuration)
updateInteractiveTransition(fractionComplete: 0)
let linearTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.8, y: -0.16), controlPoint2: CGPoint(x: 0.22, y: 1.18))
continueInteractiveTransition(timingParameters: linearTiming){
self.panGesture.isEnabled = true
self.tapGesture.isEnabled = true
}
}
#objc func handlePanGesture(gesture: UIPanGestureRecognizer) {
switch gesture.state {
case .began:
if !panelIsVisible ? gesture.velocity(in: nil).y < 0 : gesture.velocity(in: nil).y > 0 {
startInteractiveTransition(state: nextState, duration: animationDuration)
}
case .changed:
let translation = gesture.translation(in: self.detailedViewContainer)
var fractionComplete = (translation.y / view.bounds.height * 2)
fractionComplete = !panelIsVisible ? -fractionComplete : fractionComplete
updateInteractiveTransition(fractionComplete: fractionComplete)
case .ended:
let linearTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.8, y: -0.16), controlPoint2: CGPoint(x: 0.22, y: 1.18))
continueInteractiveTransition(timingParameters: linearTiming) {
self.panGesture.isEnabled = true
self.tapGesture.isEnabled = true
}
NotificationCenter.default.post(name: .resetHeaders, object: nil)
NotificationCenter.default.post(name: .disableScrolling, object: nil, userInfo: ["isDisabled": nextState == .collapsed])
default:
break
}
}
// MARK: - Animations
func animateTransitionIfNeeded(state: PanelState, duration: TimeInterval) {
if runningAnimations.isEmpty {
// MARK: Frame
var linearTiming = UICubicTimingParameters(animationCurve: .easeOut)
linearTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.1, y: 0.1), controlPoint2: CGPoint(x: 0.1, y: 0.1))
let frameAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: linearTiming)
frameAnimator.addAnimations {
switch state {
case .expanded:
self.detailedViewContainer.frame = CGRect(x: 0, y: self.diff, width: self.view.bounds.width, height: self.view.bounds.height - self.diff)
case .collapsed:
self.detailedViewContainer.frame = CGRect(x: 0, y: self.view.bounds.height - self.view.safeAreaInsets.bottom - 165, width: self.view.bounds.width, height: 200)
}
}
// MARK: Arrow
let arrowAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: linearTiming)
arrowAnimator.addAnimations {
switch state {
case .expanded:
self.leftArrowPath.transform = CGAffineTransform(rotationAngle: 15 * CGFloat.pi / 180)
self.rightArrowPath.transform = CGAffineTransform(rotationAngle: 15 * -CGFloat.pi / 180)
case .collapsed:
self.leftArrowPath.transform = CGAffineTransform(rotationAngle: 15 * -CGFloat.pi / 180)
self.rightArrowPath.transform = CGAffineTransform(rotationAngle: 15 * CGFloat.pi / 180)
}
self.leftArrowPath.center.y = self.detailedViewContainer.frame.origin.y + 15
self.rightArrowPath.center.y = self.detailedViewContainer.frame.origin.y + 15
}
// MARK: Scale
let radiusAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: linearTiming)
radiusAnimator.addAnimations{
switch state {
case .expanded:
self.gradientView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
self.gradientView.layer.maskedCorners = [.layerMaxXMinYCorner,.layerMinXMinYCorner]
self.gradientView.layer.cornerRadius = dataS.hasTopNotch ? 20 : 14
case .collapsed:
self.gradientView.transform = CGAffineTransform.identity
self.gradientView.layer.maskedCorners = [.layerMaxXMinYCorner,.layerMinXMinYCorner]
self.gradientView.layer.cornerRadius = 0
}
}
// MARK: Blur
let blurTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.5, y: 0.25), controlPoint2: CGPoint(x: 0.5, y: 0.75))
let blurAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: blurTiming)
blurAnimator.addAnimations {
switch state {
case .expanded:
self.blurView.effect = UIBlurEffect(style: .dark)
case .collapsed:
self.blurView.effect = nil
}
}
// MARK: Text
let textAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: linearTiming)
textAnimator.addAnimations({
switch state{
case .expanded:
self.tempLabel.transform = CGAffineTransform(scaleX: 0.6, y: 0.6)
self.tempLabel.frame = CGRect(origin: CGPoint(x: 15, y: self.diff / 2 - self.tempLabel.frame.height / 2), size: self.tempLabel.frame.size)
self.descriptionLabel.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
self.descriptionLabel.alpha = 0
self.descriptionLabel.transform = CGAffineTransform(translationX: 0, y: -100)
self.summaryLabel.frame = CGRect(origin: CGPoint(x: self.blurView.contentView.center.x, y: 10), size: self.summaryLabel.frame.size)
case .collapsed:
self.descriptionLabel.transform = CGAffineTransform.identity
self.descriptionLabel.alpha = 1
self.tempLabel.transform = CGAffineTransform.identity
self.tempLabel.frame = CGRect(origin: CGPoint(x: 15, y: self.view.frame.height / 2 - self.tempLabel.frame.height / 2 - 30), size: self.tempLabel.frame.size)
self.summaryLabel.frame = CGRect(origin: CGPoint(x: self.blurView.contentView.center.x, y: self.tempLabel.center.y - self.summaryLabel.frame.height / 2), size: self.summaryLabel.frame.size)
}
}, delayFactor: 0.0)
let summaryLabelTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.05, y: 0.95), controlPoint2: CGPoint(x: 0.15, y: 0.95))
let summaryLabelTimingReverse = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.95, y: 0.5), controlPoint2: CGPoint(x: 0.85, y: 0.05))
// MARK: Summary Label
let summaryLabelAnimator = UIViewPropertyAnimator(duration: duration, timingParameters: state == .collapsed ? summaryLabelTiming : summaryLabelTimingReverse)
summaryLabelAnimator.addAnimations {
switch state{
case .expanded:
self.summaryLabel.alpha = 1
case .collapsed:
self.summaryLabel.alpha = 0
}
}
radiusAnimator.startAnimation()
runningAnimations.append(radiusAnimator)
blurAnimator.scrubsLinearly = false
blurAnimator.startAnimation()
runningAnimations.append(blurAnimator)
summaryLabelAnimator.scrubsLinearly = false
summaryLabelAnimator.startAnimation()
runningAnimations.append(summaryLabelAnimator)
frameAnimator.startAnimation()
runningAnimations.append(frameAnimator)
textAnimator.startAnimation()
textAnimator.pauseAnimation()
runningAnimations.append(textAnimator)
arrowAnimator.startAnimation()
runningAnimations.append(arrowAnimator)
// Clear animations when completed
runningAnimations.last?.addCompletion { _ in
self.runningAnimations.removeAll()
self.panelIsVisible = !self.panelIsVisible
textAnimator.startAnimation()
}
}
}
/// Called on pan .began
func startInteractiveTransition(state: PanelState, duration: TimeInterval) {
if runningAnimations.isEmpty {
animateTransitionIfNeeded(state: state, duration: duration)
for animator in runningAnimations {
animator.pauseAnimation()
animationProgressWhenInterrupted = animator.fractionComplete
}
}
let hapticSelection = SelectionFeedbackGenerator()
hapticSelection.prepare()
hapticSelection.selectionChanged()
}
/// Called on pan .changed
func updateInteractiveTransition(fractionComplete: CGFloat) {
for animator in runningAnimations {
animator.fractionComplete = fractionComplete + animationProgressWhenInterrupted
}
}
/// Called on pan .ended
func continueInteractiveTransition(timingParameters: UICubicTimingParameters? = nil, durationFactor: CGFloat = 0, completion: #escaping ()->()) {
for animator in runningAnimations {
animator.continueAnimation(withTimingParameters: timingParameters, durationFactor: durationFactor)
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + animationDuration) {
completion()
}
}
}
And here's a video of the issue in iOS 13 and how it currently works in iOS 12.
I have same issue, for me UIViewPropertyAnimator continueAnimation durationFactor parameter is the issue, whenever it is not 0, after few animation the table view goes crazy.

How to render and export audios in AudioKit?

I'm building a project about music and I am using the AudioKit framework.
AudioKit is being very useful, but I need now to export sounds created in the app.
I'm using AKSequencer to play notes in sequence and I'm also applying some filters on the sound, like reverb.
I found a example code here that exports an audio from another audio file, but it is not what I need. Actually, I need to render the notes sequence, including the filters, and export it to the user.
My code:
class Sequencer {
let oscBank = AKOscillatorBank(waveform: AKTable(AKTableType.positiveReverseSawtooth))//1
let sequencer = AKSequencer()
init() {
setup()
}
func setup() {
// I instantiate a MIDI node with the oscillator bank to put it in the track output.
// It makes the oscillator's filters are recognized in the track output.
let midiNode = AKMIDINode(node: oscBank)
_ = sequencer.newTrack()
sequencer.tracks[0].setMIDIOutput(midiNode.midiIn)
generateSequence()
// Here I'm applying some filter, a reverb in this case.
let reverb = AKReverb(oscBank)
reverb.loadFactoryPreset(.plate)
// The Audiokit's output may be the last filter applyed. It is working to play.
AudioKit.output = AKMixer(reverb)
try? AudioKit.start()
}
func play() {
sequencer.play()
}
func stop() {
sequencer.stop()
}
/// Generates some melody (Sweet Child of Mine)
func generateSequence() {
for _ in 0..<2 {
sequencer.tracks[0].add(noteNumber: 62, velocity: 127, position: AKDuration(beats: 0), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 74, velocity: 127, position: AKDuration(beats: 0.5), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 69, velocity: 127, position: AKDuration(beats: 1), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 67, velocity: 127, position: AKDuration(beats: 1.5), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 79, velocity: 127, position: AKDuration(beats: 2), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 69, velocity: 127, position: AKDuration(beats: 2.5), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 78, velocity: 127, position: AKDuration(beats: 3), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 69, velocity: 127, position: AKDuration(beats: 3.5), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 62, velocity: 127, position: AKDuration(beats: 4), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 74, velocity: 127, position: AKDuration(beats: 4.5), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 69, velocity: 127, position: AKDuration(beats: 5), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 67, velocity: 127, position: AKDuration(beats: 5.5), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 79, velocity: 127, position: AKDuration(beats: 6), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 69, velocity: 127, position: AKDuration(beats: 6.5), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 78, velocity: 127, position: AKDuration(beats: 7), duration: AKDuration(beats: 0.5))
sequencer.tracks[0].add(noteNumber: 69, velocity: 127, position: AKDuration(beats: 7.5), duration: AKDuration(beats: 0.5))
}
}
// Here is my problem. I want to get the song and export it with the .wav extension exactly as it was played.
// This code is just one of my many attempts and it also does not works.
func saveFile() {
guard let auxiliarPlayer = try? AKAudioPlayer(file: AKAudioFile(readFileName: "bass.wav")) else { return }
guard let outputURL = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("audio_file.m4a") else { return }
guard let output = try? AKAudioFile(forWriting: outputURL, settings: auxiliarPlayer.audioFile.fileFormat.settings) else { return }
try? AudioKit.renderToFile(output, seconds: sequencer.tracks[0].length, prerender: {
self.sequencer.play()
})
}
}
Can someone please help?
As far as I know, offline rendering does not work with MIDI due to timing issues. Basically, only things that process audio can be rendered, not things that rely on MIDI. I believe it has to do with the MIDI clock remaining constant and not being able to be virtually "sped up" to match the audio rendering speed.

create a custom view programmatically then animate it

I'm creating a custom view programmatically, which is working fine, then I'm trying to animate the view. For some strange reason the view is animating at all, I'm not sure why. Here is the code:
override func viewDidLoad() {
super.viewDidLoad()
let customView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
customView.backgroundColor = UIColor.blue
customView.layer.cornerRadius = 25
customView.layer.borderWidth = 8
customView.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(customView)
UIView.animate(withDuration: 4, animations: {
customView.transform.translatedBy(x: 40, y: 60)
customView.transform.rotated(by: CGFloat.pi/2)
customView.transform.scaledBy(x: 0, y: 0.5)
})
}
you better call this animation block in viewDidAppear, with some delay.
import UIKit
class ViewController: UIViewController {
var customView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
customView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
customView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
customView.backgroundColor = UIColor.blue
customView.layer.cornerRadius = 25
customView.layer.borderWidth = 8
customView.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(customView)
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 5, delay: 1, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.customView.transform = CGAffineTransform(scaleX: 1, y: 0.5)
self.customView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
self.customView.transform = CGAffineTransform(translationX: 40, y: 60)
}, completion: nil)
}
func handleTap() {
UIView.animate(withDuration: 5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.customView.transform = CGAffineTransform(scaleX: 1, y: 0.5)
self.customView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
self.customView.transform = CGAffineTransform(translationX: 40, y: 60)
}, completion: nil)
}
}
Your question is not correct. your view is not animating.
Why?
You are using a return function and your customView needs to catch the modified version like this.
customView.transform = customView.transform.translatedBy(x: 40, y: 60)
So your code would be like this.
let customView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
customView.backgroundColor = UIColor.blue
customView.layer.cornerRadius = 25
customView.layer.borderWidth = 8
customView.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(customView)
UIView.animate(withDuration: 4, animations: {
customView.transform = customView.transform.translatedBy(x: 40, y: 60)
//customView.transform = customView.transform.rotated(by: CGFloat.pi/2)
//customView.transform = customView.transform.scaledBy(x: 0, y: 0.5)
})
There are lots of online resources how to group animation.Just google it.:)

Repeat while loop doesn't work - why?

I want an imageView in my viewController to "pulsate", that means become bigger and then tinier again and repeat that until the "start"-button was pressed. The problem is, that every time I call that function in the viewDidLoad() with a repeat-while-loop, it has got a breakpoint for whatever reason.
That's the part of my viewDidLoad():
repeat {
pulsate()
} while hasStarted == false
When I press the start button, hasStarted becomes true and it should stop pulsating. But it doesn't even start the function, it immediately calls an error. Where's the problem?
Here's my pulsate()-function
func pulsate()
{
frameOR = imageView.frame
frameNext = CGRect(x: imageView.frame.midX - 35, y: imageView.frame.midY - 35, width: 80, height: 80)
let frameVDL = CGRect(x: imageView.frame.midX - 150, y: imageView.frame.midY - 150, width: 300, height: 300)
if start == false
{
UIView.animate(withDuration: 2, animations: {
self.imageView.frame = frameVDL
}) { (finished) in
UIView.animate(withDuration: 2, animations: {
self.imageView.frame = self.frameOR
}, completion: nil)
}
}
}
Thank your for your help! Have a great day!
You can use UIView.animate's built-in options to really simplify what you're trying to do, and avoid locking up the thread.
Give this a try:
let frameOR = imageView.frame
let frameVDL = CGRect(x: imageView.frame.midX - 150, y: imageView.frame.midY - 150, width: 300, height: 300)
UIView.animate(withDuration: 2.0,
delay: 0.0,
options: [UIViewAnimationOptions.autoreverse, UIViewAnimationOptions.repeat],
animations: {
imageView.frame = frameVDL
},
completion: nil)
Your imageView should now be in a full "pulsing" loop, without needing any additional code. When you're ready to stop the animation, just call:
imageView.layer.removeAllAnimations()

UIBezier Path, Node Jump

I'm working on a Mario Clone for Mac and would like to make the player jump on a curved line when the right and up arrow are pressed.I've heard about UIBezierPaths being the solution to this, but sadly I have no clue how to use them.Can someone please explain what code I would write in order to make a UIBezierPath as well as make Mario follow the path in a jump?Thank you.
Somewhat of the way I would like my player to jump:
!http://projects.haskell.org/diagrams/doc/images/d8527a0188182ef5.png
My Player Movements So far:
func jump(){
// 250 , 120
let jumpUp = SKAction.moveTo(y: 250, duration: 0.25)
let jumpDown = SKAction.moveTo(y: 120, duration: 0.25)
let jumpSequence = SKAction.sequence([jumpUp,jumpDown])
player.run(jumpSequence)
}
func userMoveRight(){
player.xScale = 0.23
level.run(SKAction.sequence([SKAction.moveBy(x: -20, y: 0, duration: 0.2)]))
background.run(SKAction.sequence([SKAction.moveBy(x: -20, y: 0, duration: 0.2)]))
question.run(SKAction.sequence([SKAction.moveBy(x: -20, y: 0, duration: 0.2)]))
}
func userMoveLeft(){
player.xScale = -0.23
background.run(SKAction.sequence([SKAction.moveBy(x: 20, y: 0, duration: 0.2)]))
level.run(SKAction.sequence([SKAction.moveBy(x: 20, y: 0, duration: 0.2)]))
question.run(SKAction.sequence([SKAction.moveBy(x: 20, y: 0, duration: 0.2)]))
}
override func keyDown(with theEvent: NSEvent) {
let keyCode = theEvent.keyCode
//Moving Right
if keyCode == 124 {
userMoveRight()
}
//Moving Left
if player.position.x <= 490, background.position.x <= 1513, keyCode == 123 {
userMoveLeft()
}
//Jump
if keyCode == 126 , player.position.y == 120 {
jump()
}
}

Resources