How to keep player from moving off screen without using SKCameraNode - swift

In my game, I can't really use the CameraNode because it'll screw up the way everything looks and moves properly. My player is moving along the x axis and I have this set up in my update() func:
if player.position.x > 50.0 {
foregroundNode.position = CGPointMake(-(player.position.x - 50), 0)
}
However, my player still moves off screen when tapped too much. How do I make it so that it will stay on the screen but the foreground node will still move backwards as my player moves forward? Will post more code if necessary.

you want to do something like
if player.position.x < 50
{
let dx = 50 - player.position.x // if player is at 49, the change is 1
player.position.x = 50
background.position.x += dx // we want to add dx and not subtract because the background moves in the opposite direction of the player
}

Related

Swift how to move sprite?

I would like to know how to move a sprite in spritekit depending on the side of the screen the user is pressing down on. Like if the user taps the right side of the screen the sprite should move to the right and when the user removes his finger the sprites stops moving.
Any suggestions?
thank you so much
use the touchesBegan func and get position of touch with:
for location: AnyObject in touches {
var positionOfTouch = location.locationInNode(self)
//position of touch is of value CGPoint
}
store the value of xposition and yposition
var xPostion = positionOfTouch.position.x
var yPosition = positionOfTouch.position.y
//all in the touchesBegan func
make an if else statement to see if the user touches a side or not
if xPosition < 10 && yPosition > 10 && yPosition < self.size.height-10 {
//left side
let moveToLeftSide = SKAction.moveTo(x: 0, duration: 10)
object.run(moveToLeftSide)
}
continue setting values for the sides, then set the touchesEnded func
inside the touchesEnded func, write:
object.removeAllActions()
hope it helped... if you found this answer helpful remember to set this answer to correct ; )

How to make the camera move more frequently?

I have a Player which is a custom SKShapeNode class object.
I added a camera to follow the player, it is working but I want the camera to follow the player more frequently. now it is just checking if the camera still has player inside its view, if it doesn't it moves the camera.
I tried to check the height of the screen and move it when I get close to the top but it didn't work.
This is the code I have now (runs in update() function):
if (!cameraNode.contains(mPlayer)) {
moveCamera()
}
this is moveCamera():
func moveCamera(){
let moveAction = SKAction.moveTo(y: mPlayer.position.y + 600, duration: 0.1)
cameraNode.run(moveAction)
}
It depends how your scene is set up, but this might work:
let idealDistance: CGFloat = 600.0
let allowedDelta: CGFloat = 150.0
let offset = cameraNode.position.y - (mPlayer.position.y + idealDistance)
if abs(offset) > allowedDelta {
moveCamera()
}
You can experiment with the allowedDelta value to see what works nicely.

How to move enemy towards a moving player?

I am creating a simple sprite kit game that will position a player on the left side of the screen, while enemies approach from the right. Since the player can be moved up and down, I want the enemies to "smartly" adjust their path towards the player.
I tried removing and re-adding the SKAction sequence whenever the player moves, but the below code causes the enemies to not show at all, probably because its just adding and removing each action on every frame update, so they never have a chance to move.
Hoping to get a little feedback about the best practice of creating "smart" enemies that will move towards a player's position at any time.
Here is my code:
func moveEnemy(enemy: Enemy) {
let moveEnemyAction = SKAction.moveTo(CGPoint(x:self.player.position.x, y:self.player.position.y), duration: 1.0)
moveEnemyAction.speed = 0.2
let removeEnemyAction = SKAction.removeFromParent()
enemy.runAction(SKAction.sequence([moveEnemyAction,removeEnemyAction]), withKey: "moveEnemyAction")
}
func updateEnemyPath() {
for enemy in self.enemies {
if let action = enemy.actionForKey("moveEnemyAction") {
enemy.removeAllActions()
self.moveEnemy(enemy)
}
}
}
override func update(currentTime: NSTimeInterval) {
self. updateEnemyPath()
}
You have to update enemy position and zRotation property in each update: method call.
Seeker and a Target
Okay, so lets add some nodes to the scene. We need a seeker and a target. Seeker would be a missile, and target would be a touch location. I said you should do this inside of a update: method, but I will use touchesMoved method to make a better example. Here is how you should setup the scene:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let missile = SKSpriteNode(imageNamed: "seeking_missile")
let missileSpeed:CGFloat = 3.0
override func didMoveToView(view: SKView) {
missile.position = CGPoint(x: frame.midX, y: frame.midY)
addChild(missile)
}
}
Aiming
To implement the aiming you have to calculate the how much you have to rotate a sprite based on its target. In this example I will use a missile and make it point towards the touch location. To accomplish this, you should use atan2 function, like this ( inside touchesMoved: method):
if let touch = touches.first {
let location = touch.locationInNode(self)
//Aim
let dx = location.x - missile.position.x
let dy = location.y - missile.position.y
let angle = atan2(dy, dx)
missile.zRotation = angle
}
Note that atan2 accepts parameters in y,x order, rather than x,y.
So right now, we have an angle in which missile should go. Now lets update its position based on that angle (add this inside touchesMoved: method right below the aiming part):
//Seek
let vx = cos(angle) * missileSpeed
let vy = sin(angle) * missileSpeed
missile.position.x += vx
missile.position.y += vy
And that would be it. Here is the result:
Note that in Sprite-Kit the angle of 0 radians specifies the positive x axis. And the positive angle is in the counterclockwise direction:
Read more here.
This means that you should orient your missile to the right rather than upwards . You can use the upwards oriented image as well, but you will have to do something like this:
missile.zRotation = angle - CGFloat(M_PI_2)

SKSpriteKit - repositioning a moving object when didBeginContact is called

Currently, I have a box(size of the iPhone that has a ball bouncing in it. When it hits the boundary of a wall, it bounces back as expected.
Now what I want to do is when the ball is going to the left and hits the left wall, I want it to appear on the right side and to continue going to the left. Also, if the ball moves right and hits the wall on the right then it should appear on the left still going right. (Like the old Asteroids game)
I thought that I could use didBeginContact and simply change the position. Problem is that changing the position using setPosition: doesn't actually change it. If I use the moveTOx:, which does work, the problem then is that the ball can't be moved to the right edge because of the call to didBeginContact on the right side gets called and it gets moved back to the left side.
The balls should move smoothly from one edge to the next. Perhaps didBeginContact is not the correct place to do this.
Any suggestions? I can't imagine this is a unique problem...
I found the solution (https://github.com/macshome/RockBuster/tree/master) and here it is:
// Create a method to be called from the update:
-(void)update:(NSTimeInterval)currentTime {
/* Called before each frame is rendered */
[self updateSpritePositions];
}
// In your method, iterate through your objects and set the position:
- (void)updateSpritePositions {
[self enumerateChildNodesWithName:#"ball" usingBlock:^(SKNode *node, BOOL *stop) {
// Get the current position
CGPoint nodePosition = CGPointMake(node.position.x, node.position.y);
// If we've gone beyond the edge warp to the other side.
if (nodePosition.x > (CGRectGetMaxX(self.frame) + 20)) {
node.position = CGPointMake((CGRectGetMinX(self.frame) - 10), nodePosition.y);
}
if (nodePosition.x < (CGRectGetMinX(self.frame) - 20)) {
node.position = CGPointMake((CGRectGetMaxX(self.frame) + 10), nodePosition.y);
}
if (nodePosition.y > (CGRectGetMaxY(self.frame) + 20)) {
node.position = CGPointMake(nodePosition.x, (CGRectGetMinY(self.frame) - 10));
}
if (nodePosition.y < (CGRectGetMinY(self.frame) - 20)) {
node.position = CGPointMake(nodePosition.x, (CGRectGetMaxY(self.frame) + 10));
}
}];
}

How to add new SKSprideNodes off the screen and sync their animation with existing objects?

I am trying to animate multiple SKSpriteNode's of the same type using single SKAction.
What I would like to do, is to display four SKSpriteNode's of the same type on the screen.
Then animate them all when the button is pressed and move them to the left.
As soon as the first SKSpriteNode would leave the screen I want to add another SKSpriteNode off the screen position on the right side and add him to this SKAction loop.
Image below shows in detail what I am after.
So far I was able to display four SKSpriteNode's of the same type on the screen and add them to the array.
var squareBox = SKSpriteNode()
func addSquares(size:CGSize){
for var i = 0; i < 4; i++ {
// Create a new sprite node from an image
squareBox = SKSpriteNode(imageNamed: "noBox")
// Square pysics settings
squareBox.physicsBody = SKPhysicsBody(rectangleOfSize: self.squareBox.frame.size)
squareBox.physicsBody!.dynamic = false
squareBox.physicsBody!.categoryBitMask = squareBoxCategory
squareBox.physicsBody!.contactTestBitMask = leftEdgeCategory | edgeCategory
// SquareBox positioning on X
var xPos = size.width/5 + squareBox.size.height/2
var xPosInt = Int(xPos) * (i + 1)
xPos = CGFloat(xPosInt)
var yPos = size.height/2 + (squareBox.size.height/2)
squareBox.position = CGPointMake(xPos - squareBox.size.height/2, yPos)
self.addChild(squareBox)
squareArray.append(squareBox)
}
}
I am not sure if that is correct way to animate four SKSpriteNode's of the same type, but when the button is pressed they all seem to move to the left as desired.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
self.nodeAtPoint(location)
if self.nodeAtPoint(location) == self.button {
let move = SKAction.moveByX(-size.width/5 - squareBox.size.height/2, y: 0, duration: 1)
print("button clicked!")
for box in squareArray {
box.runAction(move)
}
}
}
}
I created an invisible 1px line on the left edge of the screen to define when the first SKSpriteNode leaves the screen.
func addLeftEdge(size:CGSize){
let leftEdge = SKNode()
leftEdge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPointMake(1, 1), toPoint: CGPointMake(1, size.height))
leftEdge.physicsBody!.categoryBitMask = leftEdgeCategory
self.addChild(leftEdge)
}
The problem with this approach is that SKSpriteNode's in the array when I animate them don't respond on didBeginContact when they touch the 1px line.
func didBeginContact(contact: SKPhysicsContact) {
println("contact!")
}
In the end I want to be able to apply the Force to all SKSpriteNode's on the screen for a few seconds to move them to the left and descend the force speed over the time until they all stop. My idea was to create four objects of the same type and add them to the screen + add them to the array var squareArray = [SKSpriteNode]().
Then when the first SKSpriteNode leaves the screen I would remove it from array and add it again at the end and position it off the screen on the right so it will be moved in seamless animation.
I have a feeling that my entire approach is wrong.
Please advise what is the best approach to animate multiple SKSpriteNode's to achieve my goal as described above. Am I on the right track ?
Please help, thank you.
In order to detect contact, one of the SKSpriteNodes must have physicsBody.Dynamic= YES;
Since you want neither to be dynamic, there is a quick fix I have found:
leftEdge.physicsBody.Dynamic=YES; //This allows it to enter onContact
leftEdge.physicsBody.collisionBitMask=0; //Disables edge from being moved by collision
leftEdge.physicsBody.AffectedByGravity=NO; //Prevents any in game gravity from moving the edge
This allows you have an edge that is dynamic and can trigger onContact yet the edge will still act as if it is not dynamic.
I think this solution isn't very clean and requires a lot more code than what is necessary to get this effect. In the update() function since there are only 4 or 5 boxes why don't you simply loop through the objects and check if any of their frame's minX's are less than 0 and if they are reset them to the starting point which I'm assuming is (view.frame.width + box.size.width) because that's completely offscreen. That way there's no array manipulation in that you don't have to remove and append objects to the array. Also you don't need to attach a physicsBody where none is required. And you don't need the leftEdge