Moving a Camera in SpriteKit advanced - swift

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

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]

How to find the speed of SKSpriteNode from 5 frames ago in 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?

TouchesMoved is lagged in swift

I am writing the code in Xcode 6 Beta 6 using Swift in SpriteKit. In the code I need a picture to follow the finger when the user moves it. touchesMoved works but with glitches. If I move the finger slowly everything is fine. If I the move the finger fast going right to left then everything is fine. If I move the finger fast going left to right, then the picture follows the finger only for a fraction of a second. If I tap and hold the picture in its current position for about half a second then everything is fine when I move it fast going both from right to left or left to right. In summary I cannot move the picture fast going left to right unless I tap and hold the picture for about half a second. Anybody has a clue why this is happening? Thanks for your time. Below is the code. I am moving SKSPriteNode follow2
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let angle_area_location = touch.locationInNode(self)
if self.nodeAtPoint(angle_area_location) == self.angle_area {
if (angle_area_location.x <= 21) {
angle = 1.55681122463001
distance12 = sqrt((angle_area_location.y - 30) * (angle_area_location.y - 30) + 1)
}
if (angle_area_location.y <= 31) {
angle = 0.0102037274939542
distance12 = sqrt((31 - 30) * (31 - 30) + (angle_area_location.x - 20) * (angle_area_location.x - 20))
}
if (angle_area_location.x > 21) && (angle_area_location.y > 31) {
angle = atan((angle_area_location.y - 30) / (angle_area_location.x - 20))
distance12 = sqrt((angle_area_location.y - 30) * (angle_area_location.y - 30) + (angle_area_location.x - 20) * (angle_area_location.x - 20))
}
if (distance12 <= maxFollow2) && (distance12 >= minFollow2) {
self.cannon.zRotation = angle
self.arc.zRotation = angle
if (angle_area_location.x > 21) || (angle_area_location.y > 31) {
follow2.position = CGPointMake(angle_area_location.x , angle_area_location.y)
}
if(angle_area_location.x <= 21) {
follow2.position = CGPointMake(21 , angle_area_location.y)
}
if (angle_area_location.y <= 31) {
follow2.position = CGPointMake(angle_area_location.x , 31)
}
}
if(distance12 > maxFollow2) {
self.cannon.zRotation = angle
self.arc.zRotation = angle
delta = 290/3
arc.size = CGSizeMake(160 * (1 + delta/20) , 35)
arc.position = CGPointMake(20 - 3 * (delta) * cos(angle) , 30 - 3 * (delta) * sin(angle))
followdist = 360
follow2.position = CGPointMake(angle_area_location.x , angle_area_location.y)
velocity = vmin + (followdist - minFollow2) * (300/(maxFollow2 - minFollow2))
}
if (distance12 < minFollow2) {
self.cannon.zRotation = angle
self.arc.zRotation = angle
arc.size = CGSizeMake(160 , 6.8)
arc.position = CGPointMake(20 , 30)
follow2.position = CGPointMake( minFollow2 * cos(angle) + 20 , minFollow2 * sin(angle) + 30)
followdist = sqrt((follow2.position.y - 30) * (follow2.position.y - 30) + (follow2.position.x - 20) * (follow2.position.x - 20))
velocity = vmin + (followdist - minFollow2) * (300/(maxFollow2 - minFollow2))
}
}
}
}
Ok I figured the glitch. I had a UISwipeGestureRecognizer that calls a method when I right swipe. I deactivated that and everything works fine. I guess swiping right and moving left to right on touchesMoved are interfering with each other.
A common trick when working with distances is to avoid taking the square root and only compare the squared values. This saves quite a bit of processor resources.
Example:
let maxfollow2sqr = maxFollow2 * maxFollow2
distance12 = (angle_area_location.y - 30) * (angle_area_location.y - 30) + (angle_area_location.x - 20) * (angle_area_location.x - 20)
if (distance12 <= maxFollow2sqr) {
// do something here
}
Since all you care about is if the calculated distance is between the min and the max you can deal just with the squares. This may speed up the function quite a bit but there are probably other optimizations that can be done.

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
}