SKSpriteNodes spawning in the wrong place - is the game scene itself moving? - swift

[Long post ahead.]
I'm really confused as to why this is happening.
I have a game in which sprites (SKSpriteNodes) spawn at regular-ish intervals off the right side of the screen and move to the left.
The spawning sprites are children of a 'world' SKSpriteNode, which is a child of the game scene. The world is declared here (in didMove(to: view)):
world = self.childNode(withName: "world") as! SKSpriteNode
The spawns are added to the 'world' like this:
nowSpawn.position = CGPoint(x: 350, y: 0)
world.addChild(nowSpawn)
nowSpawn.run(SKAction.moveBy(x: -1000, y: 0, duration: 10))
This spawn-adding is inside the function addSpawn() which is called in another function called spawnDelays() that calculates the slight variations in intervals at which to add the spawns. spawnDelays() is called in didMove(to : view):
func spawnDelays () {
let randDuration = Double(arc4random_uniform(UInt32(2))) + (universalDuration/8)
let rerunSpawnDelays = SKAction.run{spawnDelays()}
run(SKAction.sequence([
SKAction.run(addSpawn),
SKAction.wait(forDuration: Double(randDuration)),
rerunSpawnDelays,
]), withKey: "spawning"
)
}
spawnDelays()
However, something weird is happening. For about the first 10-15 spawns it works as expected, with the spawns emerging from the right of the screen and moving to the left. However, soon after, the spawns start appearing further to the left (i.e. on screen), and keep appearing further to the left until they are completely off the left side of the screen.
To try and see what's going on, I've been printing the .position of each spawn and also of the world sprite every time a new spawn appears. However, even if the spawn is spawning in the wrong place (e.g. in the middle of the screen, whose position would be near (0,0)), the console still prints its position as being (350, 0) - i.e. the position where I add the spawns.
To check that the world sprite isn't moving, I've also been printing its position every time a spawn is spawned - and it is consistently (0.0000.., 0.0....).
My only possible explanation is that the world's parent - which is the game scene - is inching leftwards, and therefore the relative position of all the children sprites is shifted to the left. But that makes no sense.

Related

Is there a way to add a fading trail or stroke to a moving SKSpriteNode?

Still new, but slowly building my first app/game, and am slowly getting there.
I'd like to be able to add a fading streak or trail as one of my SKSpriteNodes moves. Whether it is moving due to touchesMoved or being sent back to its original spot by code. I just want to add a nice visual effect.
The only thing I can think of is calculating the distance, breaking it down into x movements, then gradually move the Sprite a little, with some fade options, and then repeat in a loop till it gets back to home base, using a lot of nested SKActions and sequences.
I know that has to be wrong because it's just so ugly.
When I look at the Sprite Kit's Particle File, it has so few options. And I'm not really sure that's what I should be looking at. I have also looked at SKAction's options, and if there's an option there I'm missing it.
Surely in Swift's huge animation library there has to be something?
Let's create a basic sprite and an emitter, and make the emitter a child of the sprite so that it follows it:
let sprite = SKSpriteNode(color: .white, size: CGSize(width: 20, height: 10))
let emitter = SKEmitterNode() // better to use the visual designer in Xcode...
emitter.particleLifetime = 1.0
emitter.particleBirthRate = 50.0
emitter.particleSpeed = 100.0
emitter.emissionAngleRange = .pi / 5
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleScale = 0.1
emitter.particleAlphaSpeed = -1.0
emitter.emissionAngle = -.pi
sprite.addChild(emitter) // attach it to the sprite
emitter.position.x = -15 // but not to the center
scene.addChild(sprite)
sprite.run(SKAction.group([ // let's run some actions to test it:
SKAction.sequence([
SKAction.move(to: CGPoint(x: 200, y: 200), duration: 5),
SKAction.move(to: CGPoint(x: 50, y: 50), duration: 5),
]),
SKAction.rotate(byAngle: .pi * 2.0, duration: 10)
]))
(Click to open animated GIF if it doesn't display correctly:)
To the casual observer, it looks fineā€¦ Except that, after some scrutiny, you'll realize what's off: the particles emitted live in the universe of the parent sprite, moving and rotating with it, even long after they were emitted! That's not right!
That's because the targetNode of the emitter is its parent, and it should be the scene!
So let's insert this line somewhere:
emitter.targetNode = scene // otherwise the particles would keep moving with the sprite
(Click to open animated GIF if it doesn't display correctly:)
Okay, this is a no-go: the particles now stay in the "universe" of the scene, but apparently their emission angle fails to follow that of the parent (which looks like a bug to me).
Luckily, we can attach a custom action to the emitter which keeps aligning this angle to the parent sprite:
emitter.run(SKAction.repeatForever(
SKAction.customAction(withDuration: 1) {
($0 as! SKEmitterNode).emissionAngle = sprite.zRotation + .pi
_ = $1
}))
(Click to open animated GIF if it doesn't display correctly:)
Okay, now new particles are launched in the correct direction, and keep moving that way even if the sprite moves or rotates in the meantime. This seems to be the most realistic simulation so far, though there may still be ways to improve it by modifying the behavior of the emitter on the fly.
Sorry for the jaggy GIFs, apparently my Mac is too slow to render and capture at the same time. The animations themselves run just fine.

How do I randomly spawn enemies(Kinematicbod) in Vector3

I am making my first 3d fps in Godot and I don't understand how to spawn enemies in a general area. If someone could show me a tutorial or something that would be great.
Make a scene with only your enemy character, Give it control scripts as needed (movement, etc), and save it as a scene (Ex: myEnemy.tscn).
In your main script (or wherever you're calling it from), load the enemy scene and store it as a variable by writing:
onready var loadedEnemy = preload("res://myEnemy.tscn")
Then in your _process() or _ready() function (depending on what you need it for):
Instance the enemy by writing
var enemy = loadedEnemy.instance()
Add the instance to the scene with
add_child(enemy)
3.Specify the location of the enemy placement. For a random placement somewhere in a 10 x 10 area on the ground level (Y=0) by writing
enemy.transform.origin = Vector3( rand_range(0,10), 0, rand_range(0,10) )
You can also specify rotation with
enemy.transform.basis = Vector3(90deg, 0, 0) (example)
You can add more enemies by repeating these steps beginning from var enemy = loadedEnemy.instance() (Ex: The next enemy would be var enemy2 = loadedEnemy.instance())
If you need them to appear at different times, add them in the on_timer_timeout() function of a different Timer nodes.
Good Luck

Force the SKAction.move to be completed - swift

I'm creating a Snake Game using swift and the library SpriteKit.
To control the direction of the snake, the user has to swipe on the screen. To check which was the direction of the swipe, I use UISwipeGestureRecognizer .
Now, I have to pass the information (direction of the swipe) from the GameViewController.swift file to the GameScene.swift file. To to that, I declare a 4 items array called movesConoller:
movesController[false, false, false, false]
If the user swipes up, the first element of the array turns true, if he scrolls down, the second element turns true and the first one false... etc. etc.
Now, in the GameScene.swift file I have to say what the Snake has to do if the player moves up.
For this reason, I use 2 more variables:
var playerX:CGFloat = 0
var playerY:CGFloat = 0
and then I created this code
if movesController[0] == true {
playerY += 56
}
if movesController[1] == true {
playerY -= 56
}
if movesController[2] == true {
playerX += 56
}
if movesController[3] == true {
playerX -= 56
}
Now I create the movement
let startPoint = CGPoint(x: player.position.x, y: player.position.y )
let endPoint = CGPoint(x: playerX + player.position.x, y: playerY + player.position.y)
let moveIt = SKAction.move(to: endPoint, duration:0.1)
player.run(moveIt)
So, every time the program is executed, the playerX and playerYvariables become equal to 0. Then, based on the direction of the swipe, they are added or subtracted 56.
To move the snake I just say to the head to move from his startPointto his endPoint in 0.1 seconds.
Another thing I added is the tail. To create it I use two arrays, snakeX and snakeY.
To these two empty arrays (of type CGFloat) I add the previous position of the snake and then, if the score remains the same, the last element of each array is deleted. Else, the last element isn't deleted and remains in his array.
Whit this method, I make the tail growing when the snake eats an apple.
BUT the head moves 56 in 0.1seconds. This means that he makes a movement of 8 pixels at each execution. Because of that, I have to add to snakeX and snakeY the X and Y values every 7 execution of the program.
This is the problem. If the player swipes on the screen just after 7 execution of the program, the movement of the tail will be perfect. But if the player swipes before the 7 execution, there will be a problem. Let's suppose the snake is moving right and the player swipes up when the program is at its 4th execution.
//snakeX and snakeY values before the swipe
snakeX[168, 112, 56, 0] //all this numbers are multiple of 56
snakeY[224, 224, 224, 224] //the snake is moving right, the Y value doesn't change.
//snakeX and snakeY values after the swipe
snakeX[168 ,112 ,56 , 0]
snakeY[248, 224, 224, 224] //look at the first element
248 is not a multiple of 56. The snake moved Up at the 4th execution, after 3 execution his position will be added to the arrays. But in 3 execution he had moved of 24pixel.
Because of that, I'll get this bug
as you can see, the tail doesn't make a perfect corner.
The snake head doesn't complete his movement of 56 pixels. When I swipe, it leaves his movement and starts another one. Is there a way I can tell the head to always complete his movement before doing another one?
You're sort of trying to use a pixel-based approach to what's basically a grid-based game, but maybe something like this will work...
Make a variable controlling whether movement is allowed:
var moveAllowed = true
Guard your movement code with that variable:
if moveAllowed {
if movesController[0] == true {
playerY += 56
}
...
}
When you schedule the movement action, toggle moveAllowed, and add a completion handler to toggle it back after the action finishes:
...
moveAllowed = false
let moveIt = SKAction.move(to: endPoint, duration:0.1)
player.run(moveIt) { self.moveAllowed = true }
(The self.moveAllowed might be just moveAllowed depending on how you have things structured, but I can't tell from your fragments.)
Edit: I just realized that maybe you're setting snakeX and snakeY based on player.position. It's not really clear. In any case, then you'd use the same idea but copying from player.position only when moveAllowed is true. Basically that variable is telling you if the action is still running or not.

Boundaries Function not Working? Swift SpriteKit

So first off, what I'm trying to do but clearly failing to is prevent a sprite "the player" from moving off the edge of the map "400x400px img".
The controls on the game are buttons for moving right, left, up and down.
I have it so when the buttons are pressed it runs a function that checks if the player's destination is off the map's area.
****Note That the Map is a 5x5 grid map, each square of the grid is 80x80 inside map image****
Here's the function that checks if the player's destination is off the map:
func checkBordersDownPlayer() {
if player.position.y - 80 == map.position.y - 80 {
print("Node cannot move. Map borders are stopping the Node from moving past it's boundaries.")
}
else {
player.runAction(moveDown)
print("Player was able to move!")
}
}
Now what troubles me is that the player automatically ignores the print() functions and the if function but still runs the SKAction to move it.
What I could asses from your post is that perhaps you are not clear about the position concept of sprite node.
Unlike UIKit in which position of a frame is at the top left corner of the Rectangle, in Spritekit a node's position is at its center by default unless you will play with its anchor point.
Again taking your situation into consideration, you should use the tools provided by Spritekit for efficiently handling such tasks, You should use Physics body around the map image and make your player another physics body assigning them different physics categories.
In your case you don't need to assign any contact test bit mask or collision bit mask untill you are not interested in getting informed when the player touches the boundary set by you. You can refer to this tutorial for more https://www.raywenderlich.com/123393/how-to-create-a-breakout-game-with-sprite-kit-and-swift
Hope this helps.
Without knowing more of how you calculate movement the best I can do is guess this is your problem.
player.position.y - 80 == map.position.y - 80
Instead you probably want something like this...
player.position.y - 80 <= map.position.y - 80
Hopefully that helps
edit
One step further would be to reset the players position too...
func checkBordersDownPlayer() {
if player.position.y - 80 <= map.position.y - 80 {
player.position.y = map.position.y - 80
print("Node cannot move. Map borders are stopping the Node from moving past it's boundaries.")
}
else {
player.runAction(moveDown)
print("Player was able to move!")
}
}

Flipping a child sprite's xscale in Spritekit using Swift

Alright I'm offically stumped. I've been able to figure out my problems myself in the past but I'm lost on this one.
I'm trying to flip a child sprite's xscale. I want the flip to occur when the child's x position is negative. With the code below, the sprite does flip, every time the sprite reaches negative position, it loops back and forth flipping until reaching positive again.
I've tried numerous variations, but this is the code in it's simplest form. Any help or alternatives would be appreciative.
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
let pin = childNodeWithName(Pin1) as SKSpriteNode!
let guyPoint = guy.convertPoint(guy.position, fromNode: pin)
if guyPoint.x <= 0 {
guy.xScale = -1
}
else {
guy.xScale = 1
}
}
So if it is constantly flipping it's probably an issue of coordinate space. So check to make sure your convertPoint method is accurately returning the point you want. I would use println() to see what's going on there.
Probably what's happening is you want to flip the xScale when the child node is negative in the scene space or something (i.e. when the child is off the screen) but instead guyPoint might be the position of the child in its parent's coordinate space (so relative to the parent).
Also try with and without the else{} part to see if that changes anything.