How to find the speed of SKSpriteNode from 5 frames ago in Swift - swift

So I have this Swift code using SpriteKit:
let ballX = ball.position.x
let ballY = ball.position.y
let ballR = ball.size.width / 2
let sceneW = self.size.width
let sceneH = self.size.height
if ballY >= sceneH - ballR - 3 {
framesOnTop += 1
} else {
framesOnTop = 0
}
if framesOnTop > 2 {
// Some code here
}
I have the ball node (SKSpriteNode) that can hit the ceiling and bounce back (scene has no gravity) but sometimes it can hit the ceiling at a very steep angle and get stuck in an infinite loop, left right left right left right. In the if statement that says framesOnTop > 2 I am successfully detecting when it gets stuck, and I want to make it bounce back down at the right angle with physicsBody.applyImpulse. But to do this I need the velocity from before it gets stuck (~5 frames before). Is this possible or is there a better way to fix it?

Related

SKNodes follow in a consistent speed and stop them from spazzing?

Currently doing a SoloProject for class and decided to study SpriteKit on my own. I decided to make a top-down zombie shooter and I have a lot of questions but so far these are the two main ones I can't seem to fix or find solution for.
Problem 1
Zombies slow down the closer they get to the target, If I increase the speed they just speed in from off the screen and still slowdown as they get closer (I've read somewhere putting that function in the update is bad but I still did it...)
I want to make it where they spawn with the speed of 3 and when the player moves closer or further away it stays at 3. (Currently using an analog stick code I found that was on Youtube to move my character around)
func zombieAttack() {
let location = player.position
for node in enemies {
let followPlayer = SKAction.move(to: player.position, duration: 3)
node.run(followPlayer)
//Aim
let dx = (location.x) - node.position.x
let dy = (location.y) - node.position.y
let angle = atan2(dy, dx)
node.zRotation = angle
//Seek
let velocityX = cos(angle) * 1
let velocityY = sin(angle) * 1
node.position.x += velocityX
node.position.y += velocityY
}
}
override func update(_ currentTime: TimeInterval) {
zombieAttack()
}
Problem 2
Also when multiple zombies get close to the players (function above) they start to spazz so I allowed them to overlap on top of each other to stop the spazzing.
I want to make it where they are more solid? if that is the right way to describe it. Basically I want them to huddle up around the player**.
If I add enemy to the collision it will spazz trying to get into the same position.
private func spawnZombie() {
let xPos = randomPosition(spriteSize: gameSpace.size)
let zombie = SKSpriteNode(imageNamed: "skeleton-idle_0")
zombie.position = CGPoint(x: -1 * xPos.x, y: -1 * xPos.y)
zombie.name = "Zombie\(zombieCounter)"
zombie.zPosition = NodesZPosition.enemy.rawValue
let presetTexture = SKTexture(imageNamed: "skeleton-idle_0.png")
zombie.physicsBody = SKPhysicsBody(texture: presetTexture, size: presetTexture.size())
zombie.physicsBody?.isDynamic = true
zombie.physicsBody?.affectedByGravity = false
zombie.physicsBody?.categoryBitMask = BodyType.enemy.rawValue
zombie.physicsBody?.contactTestBitMask = BodyType.bullet.rawValue
zombie.physicsBody?.collisionBitMask = BodyType.player.rawValue
zombie.zRotation = 1.5
zombie.setScale(0.2)
enemies.append(zombie)
zombieCounter += 1
run(SKAction.playSoundFileNamed("ZombieSpawn", waitForCompletion: false))
keepEnemiesSeperated() // ADDED FROM UPDATED EDIT*
addChild(zombie)
}
Let me know if I need to post more code or explain it better, I'm a five months in on learning Swift and have only a week and a half of SpriteKit experience and first time posting on StackOverFlow. Thanks all in advance!
EDIT: I am using a code I found from having a node follow at a constant speed but I don't think I'm doing it right since it is not working. I added the following code:
private func keepEnemiesSeparated() {
// Assign constrain
for enemy in enemies {
enemy.constraints = []
let distanceBetween = CGFloat(60)
let constraint = SKConstraint.distance(SKRange(lowerLimit: distanceBetween), to: enemy)
enemy.constraints!.append(constraint)
}
}
Problem 1, your zombie is moving based on time, not at a set speed. According to your code, he will always reach the player in 3 seconds. This means if he is 1 foot away, he takes 3 seconds. If he is 100 miles away, he takes 3 seconds. You need to use a dynamic duration if you are planning to use the moveTo SKAction based on the speed of the zombie. So if your zombie moves 10 points per second, you want to calculate the distance from zombie to player, and then divide by 10. Basically, your duration formula should be distance / speed
Problem 2, if you want the zombies to form a line, you are going to have to determine who the leading zombie is, and have each zombie follow the next leading zombie as opposed to all zombies following the player. Otherwise your other option is to not allow zombies to overlap, but again, you will still end up with more of a mosh pit then a line.

How to have a sprite follow another "within bounds"

I want to create an eyeball that follows the user. In this sense, the eye should follow the position of the user but should only be able to move within certain bounds (The eye socket). The code I wrote below works, but its very choppy.
if(dist <= socketRadius - self.aEye.size.width/2.2){
lastPosition = self.aEye.position
self.aEye.physicsBody?.velocity = CGVector(dx:theVec.dx * eyeMoveSpeed, dy:theVec.dy * eyeMoveSpeed)
}
else{
let toCenterVector = normalizeVector( CGVector(dx:(self.socket.position.x - self.aEye.position.x), dy:(self.socket.position.y - self.aEye.position.y)*3 ))
self.aEye.physicsBody?.velocity = toCenterVector
print(toCenterVector)
print("Out")
}
(dist is the distance from the eyeball to the eyesocket center)
Is there a way to have a smooth flow of the eyeball around its socket to follow the user's position?
You can use SKConstraints to achieve this. Something like:
let rangeToCenterSprite = SKRange(lowerLimit: 80, upperLimit: 90)
var distanceConstraint: SKConstraint
distanceConstraint = SKConstraint.distance(rangeToCenterSprite, toNode: SpriteNodeInEyeCenter)
EyeBallSprite.constraints = [distanceConstraint]

Moving a Camera in SpriteKit advanced

I want to make a camera like in the video bellow
// Please watch the video first :)
https://www.youtube.com/watch?v=nryuCql2k9A
I try this
// moveWorld is true when the game lunch
if(moveWorld){
moveWorld = false
rightHeroBorder = hero.position.x + (hero.frame.size.width * 2)
leftHeroBorder = hero.position.x - (hero.frame.size.width * 2)
}
// move world to the right
if(hero.position.x > rightHeroBorder){
let diff = hero.position.x - rightHeroBorder
rightHeroBorder = hero.position.x + 1
leftHeroBorder = hero.position.x - 1
self.world.position.x -= diff
}
// move world to the left
if(hero.position.x < leftHeroBorder){
let diff = leftHeroBorder - hero.position.x
leftHeroBorder = hero.position.x - 1
rightHeroBorder = hero.position.x + 1
self.world.position.x += diff
}
The result is not what I want
in the beginning the world don't move
but when the node passes rightHeroBorder or leftHeroBorder it starts to move
If the node move a little, the world will move (doesnt detects the borders)
if the node continue to move to the right, less of the right part of screen is visible (this should be opposite)
This is hard, but I don't want to center the node and the world.
Hopefully people with more experience will help
I'am asking about x coordinate system, but you can help with the y as well

Why does SKLabelNode increment too much on Contact? (Swift)

I am trying to have my Label to increment +1 every time a sprite makes contact with a Contact node, but it increments by large numbers like +23. I think what is happening is that its taking into account every millisecond that the sprite is touching the node, but i don't know how to fix it.
Here is my Label node code within DidMoveToView:
score = 0
scoreLabelNode = SKLabelNode(fontNamed:"[z] Arista Light")
scoreLabelNode.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/4)
scoreLabelNode.zPosition = 100
scoreLabelNode.fontSize = 500
scoreLabelNode.alpha = 0.03
scoreLabelNode.text = String(score)
self.addChild(scoreLabelNode)
here is my contact node:
var contactNode = SKNode()
contactNode.position = CGPoint(x: self.frame.size.width + asteroidTexture.size().height / 15 + rocket.size.width, y: 0.0)
contactNode.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake( asteroid.size.width/16, self.frame.size.height*2 ))
contactNode.physicsBody?.dynamic = false
contactNode.physicsBody?.categoryBitMask = scoreCategory
contactNode.physicsBody?.contactTestBitMask = rocketCategory
contactNode.runAction(asteroidsMoveAndRemove)
moving.addChild(contactNode)
and here is my code where when my rocket sprite makes contact with the contactNode it increments:
func didBeginContact(contact: SKPhysicsContact) {
if moving.speed > 0 {
if ( contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory || ( contact.bodyB.categoryBitMask & scoreCategory ) == scoreCategory {
// Rocket has contact with score entity
score++
scoreLabelNode.text = String(score)
println("HIT")
}
else{
gameOver()
}
}
'moving' is when my asteroid sprites are moving, the contact nodes move with it
It's probably not every millisecond, but instead every frame, and that's why you likely end up with numbers like +23 (The collision is taking place for a 1/3 of a second and if it's running at 60 FPS that is about 20 or so frames).
One thing you could do is subclass SKNode into an actual ContactNode class that has a property hasBeenStruck that is initially set to false or something to that extent.
Then when you check contact, you first check to see if hasBeenStruck is false. If it is, register the hit and update the score and then set that ContactNode's hasBeenStruck property to true. This way, on the next 20 or so checks to see if contact has happened, it won't keep updating the label because the if condition has failed.
However, if your game allows a user to hit the same ContactNode twice and get score both times, this isn't a complete solution. If that is the case, you could use a timer or "invincibility" period for the given ContactNode.

Square collision detection iPhone

I have this game where i need to know if the ball has hit a wall on the side (to bounce back on the x-axis) or on the top (to bounce back on the y-axis, like a bounce on the ground). They work fine individually, but when I uncomment both of them, it dosen't work. (I think this is because the code is 'overlapping'?). Anyway, here is the code, and any help is fantastic:
if (CGRectIntersectsRect(guy.frame, wall_01.frame)) {
if (guy.frame.origin.y+guy.frame.size.height >= wall_01.frame.origin.y && guy.frame.origin.y <= wall_01.frame.origin.y+wall_01.frame.size.height) {
iJump *= kRestitution;
}
if (guy.frame.origin.x+guy.frame.size.width >= wall_01.frame.origin.x && guy.frame.origin.x <= wall_01.frame.origin.x+wall_01.frame.size.width) {
jJump *= kRestitution;
}
}
assuming wall is on the left side and the y increases from top to bottom
CGFloat leftWall = someXPosition;
CGFloat ground = someYPosition;
CGFloat ballLeft = CGRectGetMinX(guy.frame);
CGFloat ballRight = CGRectGetMaxX(guy.frame);
CGFloat ballBottom = CGRectGetMaxY(guy.frame);
if (ballLeft <= leftwall && ballBot >= ground){
//ball hit corner ?
} else if (ballLeft <= leftWall){
//passed or touched wall
} else if (ballBot >= ground){
//passed or touched ground
}