I am trying to only allow the SKSpritenode to jump one time but having issue with velocity - swift

I am currently using the WizardCharacter.physicsbody.velocity.dy, and checking if it is equal to 0. I do not want the character to be able to jump in air. The issue I am facing is, my character is resting and he is not moving and the velocity of y is going from 0 to 1 while not doing any actions to the character. I am not sure why this is happening, I am using SpriteKit and SceneKit. I have provided my code, as well a picture for what the console is printing out.
override func update(_ currentTime: TimeInterval) {
print("\(String(describing: wizardCharacter.physicsBody?.velocity.dy))")
cameraNode.position.x = wizardCharacter.position.x
if wizardCharacter.action(forKey: "wizardRun") == nil {
wizardCharacter.texture = SKTexture(imageNamed: "wizard_1_attack-b_001")
}
if upArrow.contains(pointTouched)
{
if wizardCharacter.physicsBody?.velocity.dy == 0 && isJumping == true {
upArrow.alpha = 0.5
jumpWizard(forTheKey: "jumped")
jumpWizardAnimation(fortheKey: "jumpedAnimation")
}
}

Related

Virtual Joystick moving sprite while changing animations

I'm working on a project where I'll have a virtual joystick on the screen which moves a character around the board. I found a few good examples and the one I'm using currently is located # https://github.com/Ishawn-Gullapalli/SpritekitJoystick/tree/master/SpritekitJoystick
While the player is moved around just like I want, I'm unable to figure out how by using this method I can change the player animation based on the direction he's going. I'm able to determine direction but my usual methods of invoking a SKAction doesn't animate the player, it just moves him to the new location while pausing the idle animation. I'm moving my player in the update statement like below and am looking for tips on how to change animations appropriately.
I'm including my update statement below. I've also tried to pass in a CGPoint value with the same effect. Thanks in advance for looking.
override func update(_ currentTime: TimeInterval) {
if joystick.joyStickTouched == true {
mainHero.position.x += joystick.moveRight(speed: movementSpeed)
// Example of me trying to animate my player
if joystick.moveRight(speed: movementSpeed) > 0 {
walkRight()
}
mainHero.position.x -= joystick.moveLeft(speed: movementSpeed)
mainHero.position.y += joystick.moveUp(speed: movementSpeed)
mainHero.position.y -= joystick.moveDown(speed: movementSpeed)
joystick.update(currentTime)
}
}
func walkRight() {
mainHero.isPaused = false
let idleAnimation = SKAction(named: "MainWalkRight")!
let rightGroup = SKAction.group([idleAnimation])
mainHero.run(rightGroup, withKey: "Right")
}

iOS Sprites Contact Detection

I'm working on a platform game where the character can climb ladders. Since the total length of each ladder varies, I have several ladders stacked on top of each other in SceneEditor. Upon entering a ladder, the contact delegate works fine and my code allows the character to move up the ladder. The problem I'm having is that as soon as the character moves off the first ladder segment, the didEnd method fires even though the character has entered the next ladder segment. I've gotten around it by given the ladder segments different category masks. Is this the only way to do it?
try overlapping the ladders by 1 rung, and then set a count whenever didBegin contact is fired increase the value by 1 whenever didEnd is called decrease the value by 1. at the end of the didEnd func check if onladderCount == 0. if it is, trigger whatever code is supposed to fire when the player is not on a ladder.
this assumes that you have a ladder class, and will have to put a property in the ladder class for onLadder to ensure that the += 1 isn't called multiple times.
var onladderCount: Int = 0
func didBegin(_ contact: SKPhysicsContact) {
let contactAName = contact.bodyA.node?.name
let contactBName = contact.bodyB.node?.name
if (contactAName == "ladder") || (contactBName == "ladder") {
let ladder: Ladder? = (contact.bodyA.categoryBitMask == PhysicsCategory. ladder ? (contact.bodyA.node as? Ladder) : (contact.bodyB.node as? Ladder))
if !ladder.onLadder {
ladder.onLadder = true
onladderCount += 1
}
}
}
func didEnd(_ contact: SKPhysicsContact) {
let contactAName = contact.bodyA.node?.name
let contactBName = contact.bodyB.node?.name
if (contactAName == "ladder") || (contactBName == "ladder") {
let ladder: Ladder? = (contact.bodyA.categoryBitMask == PhysicsCategory. ladder ? (contact.bodyA.node as? Ladder) : (contact.bodyB.node as? Ladder))
if ladder.onLadder {
ladder.onLadder = false
onladderCount -= 1
}
}
if onladderCount == 0 {
//trigger whatever code happens when they leave a ladder
}
}

swift call function multiple times inside update

I create a game via SpriteKit. in the game every few second a ball spawn to the screen (via function) and the player has to blow them up. now, I want to check the player level via his score so if the score is bigger than 10 the spawnBall function will be executed twice (so 2 ball will spawn on the screen) an so on. I tried to to it via the update fun (that will "read" the player score and depends on the score will call the spawnBall function). Unfortunately when I do it the screen is spawn with million (or so) balls in few seconds (as I said I want it to call the function every few seconds and increase the call while the score is X). I really don't have any idea how to do it.
here is my code:
override func update(_ currentTime: CFTimeInterval) {
if (self.score <= 10){
spawnBalls()
}
if (self.score > 10 && self.score <= 20){
spawnBalls()
spawnBalls()
}
if (self.score > 20){
spawnBalls()
spawnBalls()
spawnBalls()
}
if (self.subscore == 3) {
_ = randomBallColorToBlow()
self.subscore = 0
}
}
func spawnBalls() {
let wait = SKAction.wait(forDuration: 1)
let action = SKAction.run {
self.createBall()
}
run(SKAction.repeatForever((SKAction.sequence([wait, action]))))
}
how can I do it without using the update function??
you are calling spawn balls 60 times a second by calling it in your update func.
try just checking if a certain requirement is met to upgrade to a higher spawn rate in your update but keep the calls out of the update func.
private var upgradedToLevel2 = false
private var upgradedToLevel3 = false
//called somewhere probably in a start game func
spawnBalls(duration: 1.0)
override func update(_ currentTime: CFTimeInterval) {
if (self.score > 10 && self.score <= 20) && !upgradedToLevel2 {
//this prevents the if loop from running more than once
upgradedToLevel2 = true
self.removeAction(forKey: "spawn")
spawnBalls(duration: 0.5)
}
if (self.score > 20) && !upgradedToLevel3 {
//this prevents the if loop from running more than once
upgradedToLevel3 = true
spawnBalls(duration: 0.33)
}
}
func spawnBalls(duration: Double) {
let wait = SKAction.wait(forDuration: duration)
let action = SKAction.run { self.createBall() }
let repeater = SKAction.repeatForever(SKAction.sequence([wait, action]))
run(repeater, withKey: "spawn")
}
As stated, you are spawning your balls multiple times and need to break it up. I would recommend keeping track of level using an Int instead of a bool to be able to handle an "infinite" amount of level ups without making an "infinite" amount of boolean variables
private var nextLevel = 0 //Level 0 allows us to spawn a ball on startup, so no need to call spawnBalls anywhere else
override func update(_ currentTime: CFTimeInterval) {
if (self.score > 10 * nextLevel){
self.removeAction(forKey: "spawn") //this prevents the if loop from running more than once
nextLevel += 1
spawnBalls(count:nextLevel,forDuration:1) //You can change the 1 here if you want to spawn balls at a faster speed, I would recommend a variable that uses nextLevel in a forumula
}
}
func spawnBalls(count:Int, forDuration duration:TimeInterval) {
let range = 0..<count
let wait = SKAction.wait(forDuration: duration)
let action = SKAction.run {range.forEach{self.createBall()}}
let repeater = SKAction.repeatForever(SKAction.sequence([wait, action]))
removeAction(forKey:"spawn")
run(repeater, withKey: "spawn")
}

SpriteKit Issue with Double Jump only working some of the time

Code:
func jumpAction(_ JumpPos: CGPoint, _ clickPos: CGPoint) {
if jumpCount < 2 {
var disty = (clickPos.y - JumpPos.y)
var distx = clickPos.x
self.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
self.physicsBody?.applyImpulse(CGVector(dx: distx, dy: disty))
if onGround == false {
jumpCount += 1
}
}
}//
func didBegin(_ other: SKSpriteNode, _ contact: SKPhysicsContact) {
if contact.contactPoint.y < self.frame.minY + 10 {
jumpCount = 1
onGround = true
}
if other.physicsBody?.categoryBitMask == Mask.SpinPlat {
jumpCount = 0
}
}
func didEnd(_ contact: SKPhysicsContact) {
onGround = false
}
I have 3 functions above. The JumpAction is called when I press on the Jump Button on the screen. The didBegin and didEnd functions are called when contact with the top of a platform occurs with the player.
My issue:
So if player is on platform and I jump, the jump action is called before the didEnd function. This is why I have the jumpCount set to 1 when the player contacts the platform. This works fine...
The real issue is when my player lands on the platform, and he bounces slightly off (due to restitution) and then the jump button is pressed.
When this occurs, the onGround flag is flipped to false before the jump action is called and my player can only single jump.
I'm trying to find a simple way to get around this issue without having to implement a bunch of code, haven't found a way yet.
So this is why I am here. I want to be able to double jump even with that slight little bounce I explained earlier.
Thank you in advance!!!
Additional Info:
I use the onGround flag so that if the player is on the ground, he can slide along the ground if they press the jump button in the right location...
Have you tried simply delaying setting the "on ground" flag by a fraction of a second?
let waitAction = SKAction.wait(forDuration: 0.5)
scene.runAction(waitAction, completion: {
self.onGround = false
})
Obviously you could experiment with the exact duration to make sure it gives the "bounce" just enough time to end.

How to end a game when hitting an object from below?

Hey so I am making this project in which the player has to jump platforms all the way to the top. Some monsters spawn randomly throughout the game. So the idea is to lose the game when you hit them from below, but can go on if you jump on them. I already did the part in which the player jumps on it and you destroy the monster but I am still stuck on that part to lose the game when you hit it from below. Any ideas on how I can manage to do this? For this project I followed Ray Wenderlich's tutorial on How To Make a Game Like Mega Jump.
So on my GameScene, I have the didBeginContact method:
func didBeginContact(contact: SKPhysicsContact) {
var updateHUD = false
let whichNode = (contact.bodyA.node != player) ? contact.bodyA.node : contact.bodyB.node
let other = whichNode as GameObjectNode
updateHUD = other.collisionWithPlayer(player)
if updateHUD {
lblStars.text = String(format: "X %d", GameState.sharedInstance.stars)
lblScore.text = String(format: "%d", GameState.sharedInstance.score)
}
}
Which then calls the method from the GameObjectNode Scene.
class MonsterNode: GameObjectNode {
var monsterType: MonsterType!
override func collisionWithPlayer(player: SKNode) -> Bool {
if player.physicsBody?.velocity.dy < 0 {
player.physicsBody?.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: 450.0)
if monsterType == .Normal {
self.removeFromParent()
}
}
When the player jumps on top of the monster, the monster is removed from the parent. I was trying to set that if the player's velocity is greater than 0 when colliding with the monster, then the player is removed from parent. Then when I go back to my GameScene, I could declare something in my update method so that when the player is removed from the parent call the endGame() method.
override func update(currentTime: NSTimeInterval) {
if gameOver {
return
}
if Int(player.position.y) > endLevelY {
endGame()
}
if Int(player.position.y) < maxPlayerY - 500 {
endGame()
}
}
Of course I wasn't able to make that work, and I still can't. So if anyone could help me out on how I can manage to do this, or probably some tutorial which could guide me into doing this I would really appreciate it! Thank you in advance.
First you use the didBeginContact method to establish if a contact between player and monster has been made. Next you compare the y positions of the player and monster. If the monster's y position is greater than than the player's... BANG, player dies.
The code sample assumes you have multiple monsters, each with a unique name, and have them all stored in an array.
- (void)didBeginContact:(SKPhysicsContact *)contact {
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (CategoryMonster | CategoryPlayer)) {
for(SKSpriteNode *object in monsterArray) {
if(([object.name isEqualToString:contact.bodyB.node.name]) || ([object.name isEqualToString:contact.bodyA.node.name])) {
if(object.position.y > player.position.y) {
// moster is above player
}
}
}
}
Couple of notes... If you have more than one monster active at any one time, you will need to have a unique name for each one. Reason being that you will need to know which monster contacted the player and that can only happen if you can differentiate between monsters. Hence the unique name for each one.
The if check for the y position is a simple one and only needs +1 y to be fulfilled. If it is possible for your player to make side contact with a monster and not die, you can change the if condition to be something like if(object.position.y > player.position.y+50) to make sure that the contact was actually from the bottom.
(I am not all too proficient in Swift yet so code sample is in Obj-C.)