SKSprite.addChild at location - swift

In Swift, I have some code like this:
let wand = SKSpriteNode(imageNamed: "Wand")
let item = SKSpriteNode(imageNamed: "Item")
...
func didBeginContact(contact: SKPhysicsContact) {
contact.bodyB.node!.removeAllActions()
var imageToAdd = contact.bodyB.node?.name
wand.addChild(SKSpriteNode(imageNamed: imageToAdd!))
}
What I want is when a certain type of item touches the wand, I want to attach it to the wand. The bitmap masks are set and all of that works perfectly. But, when addChild adds the image, it's in the center of the wand and I want to control where it gets added.
Additional information. I'm using physicsBody to animate the wand and the items. The wand is controlled by the user touching the screen. I tried a few ways to connect the two objects, SKPhysicsJointFixed.jointWithBodyA and SKPhysicsJointSliding.jointWithBodyA but couldn't get it to work the way I wanted. I think joining the two physics bodies is overkill for what I'm doing, but I could be wrong. My real goal is:
1) to add (or join) one sprite (the wand) to another sprite (an item)
2) allow the wand to continue to be controlled (animated) by the user touch
3) have control over the relative position of the item in relation to some point on the wand.
It's the third item that is tough. Any help is appreciated, let me know if you need to see more code.

Your First Question:
The default position of the SKSpriteNode is (0,0) so if you add it to a parent node, the SKSpriteNode will thus be in the middle of the parent node's coordinate system. If you want to add the image a bit higher on the wand, lets say 100 points up, you just say imageToAdd.position = CGPointMake(0,100). The child node will remain attached at that location. Another option of course is to create a joint, which isn't overkill at all if you want the wand and the attachment to have physics properties.
Your Second Question:
As long as you don't remove the physics body from the wand, it will still be there and could be controlled by the user.
Your Third Question:
The attached item will have a certain position relative to the wand since you created it as a child node. If you need to move the item you would create an SKAction. For example, if you needed to move the item down the wand, you could say:
moveDownWand = SKAction.moveByX(0.0, y: pointsToMoveDownBy, duration: someDuration)
attachedItem.runAction(moveDownWand)
Hope this helps, let me know if you have questions.

Thanks #M321K, you put me on the right track! Here is what I did:
var nodeToAdd = SKSpriteNode(imageNamed: imageToAdd!)
nodeToAdd.position = CGPointMake(0, -40)
wand.addChild(nodeToAdd)
I had to create the sprite and set it's position before adding it to the wand. Huge help, thanks much.

Related

SKEmitterNode tied to a moving SKNode

I am attempting to move a SKEmitterNode to follow a bullet in my game to give it a trailing effect however, no matter which way I attempt to implement this, it doesn't seem to work how I want it to and I'm at a loss for how to make this.
I have attempted to add the emitter to my main scene and manually moved the node a few times per second but it ends up not leaving a trail and keeping all the particles in one place like this:
Next I attempted to set the target node, however when I do this the trail goes for a bit then stops rather than following the bullet like it's supposed to. It also rotates and distorts from the rotation of the projectile like shown here:
For reference of the type of effect I'm looking for:
You should populate the targetNode property of your emitter with a node that is not moving like the scene.
emitterNode.targetNode = self // where self is the current scene

How to add a view to SceneKit?

I am very new to Swift and currently coding a minigolf game.
However, the player can hit the ball n times.
How do I make a simple view which should show how many hits the player has left?
I think that you want something like this:
First of all, i recommend you to create a function to call when the game is restarted, there you can reset the scene, reset the player moves, etc...
You may do this like:
func restart(){
//Everything you need to restart the game
}
To call it later you just have to:
restart()
Ok, let's go to what you asked now:
declare a Int variable that contains the number of moves that the player has:
var playerMoves = 10 //Whatever number you want
Ok, you have a variable that contains the number of moves, now you have to show this on your scene, to do this, create an SKLabelNode:
let moveLabel = SKLabelNode()
Now, set the label text to be equal your playerMoves, set the position, the zPosition, the font, whatever you want:
moveLabel.text = "\(playerMoves)" //add this line to restart function that i mentioned and also set the variable playerMoves to "n" again.
moveLabel.position = ....
moveLabel.fontSize = ....
moveLabel.zPosition = ... //A number higher than any other zPosition
addChild(moveLabel) //Don't forget to add the label to scene!
Thats it! And now you have to update the playerMoves every time the player moves, to do this i'm assuming you are controlling somehow when the player moves (using update function, touches began, created a function, etc....)
so...when the player take one move:
playerMoves -= 1
moveLabel.text = "\(playerMoves)"
THATS IT!!! :) HOPE IT HELPS!
there are several ways to go about it. the most common way would be to overlay a spritekit scene over the scenekit scene. in the spritekit scene you can have a label element that you update
the code might look something like
let overlay = SKScene(fileNamed: "yourspritekitscene.sks")
yourScnView.overlaySKScene = menuOverlay
you can also assign a spritekit scene as a texture on a 3D object in your scene. or you can can make the numbers a texture you swap as you count down.

Giving physics to tiles of SKTileMapNode in Xcode 8

I am learning Swift, and as a project I am working on a tile based 2D game similar to super mario where my character will walk and jump on tiles.
The latest version of Xcode and Sprite Kit give the ability to create a Tile Map directly in Xcode.
In the presentation of the new Xcode and Sprite kit, the guy demonstrates a game similar to what i am working on.
https://developer.apple.com/videos/play/wwdc2016/610/ (around the 20th minute).
He mentions giving Tiles user data properties which i did, and in code we search through all the tiles which have that user data and give them some physics properties so that the character can collide or interact with them (in my case, my character not falling or walking through the tiles).
so basically, the idea is giving those tiles a physics Body, but this can't be done using SKphysicsBody. So there must be another way, and since i am new to Swift i am missing it.
if anyone knows this, i would very much appreciate the help.
If the question is unclear let me know because i am also new to stack overflow.
Apple staff member Bobjt says here that "the right approach" is to add user data to your SKTileDefinition objects and use that to identify and add physics bodies to your tiles.
So you would add a user data value to a tile definition either programmatically or in the editor, like so:
Then in code you would check each tile definition to see if it has the user data value. If so, then you need to calculate the tile's position and add a physics body on a new node, parenting it to your tile map. Here is the code for this which Bobjt referred to as "the correct approach":
self.tileMap = self.childNode(withName: "Tile Map") as? SKTileMapNode
guard let tileMap = self.tileMap else { fatalError("Missing tile map for the level") }
let tileSize = tileMap.tileSize
let halfWidth = CGFloat(tileMap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(tileMap.numberOfRows) / 2.0 * tileSize.height
for col in 0..<tileMap.numberOfColumns {
for row in 0..<tileMap.numberOfRows {
let tileDefinition = tileMap.tileDefinition(atColumn: col, row: row)
let isEdgeTile = tileDefinition?.userData?["edgeTile"] as? Bool
if (isEdgeTile ?? false) {
let x = CGFloat(col) * tileSize.width - halfWidth
let y = CGFloat(row) * tileSize.height - halfHeight
let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)
let tileNode = SKShapeNode(rect: rect)
tileNode.position = CGPoint(x: x, y: y)
tileNode.physicsBody = SKPhysicsBody.init(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0))
tileNode.physicsBody?.isDynamic = false
tileNode.physicsBody?.collisionBitMask = playerCollisionMask | wallCollisionMask
tileNode.physicsBody?.categoryBitMask = wallCollisionMask
tileMap.addChild(tileNode)
}
}
}
Personally, I think this approach is too fussy. I'm going to try a different approach in my game and if it works I'll post it here. What I'd really like is for Apple to enhance the tile map API so that we can add physics bodies directly to individual tiles. Maybe in the process they could optimize the engine so that physics bodies on individual tiles would be automatically merged to form larger, more optimal shapes in order to improve system performance.
Update: I filed a request with Apple about this issue, for whatever good might come of it.
I'm not sure there's a surefire way to do this yet... but here's two ways to think about how to try apply physics to a tilemap.
Option 1: Apply SKNodes to each positions of each tile on your map, and apply an appropriate physicsbody to that SKNode based on the content and state of that tile.
Option 2: Use the position information of each tile to add an array of physicsbodies to the SKTileMapNode, and position each of them accordingly.
I'm imagining a gravity down, Mario style platformer, with this kind of terrain in need of physics bodies for the ground:
Lifted transcript from Apple's WWDC about tiles and physics:
And you'll note that I'm colliding with the tiles here.
And I achieve this by leveraging custom user data that we can put on
each of our tiles.
Here, I'll show you in our tile set.
Select one of the variants here.
And you can see that we have some user data over here.
And I just have a value called edgeTile which is a Boolean, and I set
to 1.
So, in code, I'm going through the tile map in our platform demo here,
and I'm looking for all of these edge tiles.
And whenever I find one, I create some physics data to allow the
player to collide with it.
And since it is just in our tile map here, say I wanted to get over
this large wall here.
When I run the game, you'll see that my guy can't actually jump high
enough to get over it.
And he really wants to because that red button over there looks really
tempting.
I really want to push that thing.
So, since we're just generating our physics data from our tiles and
our user data, all we can do is just going here and erase these tiles
and build and run our game again.
The tiles are now gone and we can now move through there.
And we didn't have to change code or anything.
We just used the data that we pulled from the tile map to set up our
tile.
It's that simple.
Makes it sound very simple, but takes a far greater mind than mine to figure out what is being done, and how it's being done.
Alternatively, you can apply a line sweep algorithm to the tiles you want to give a SKPhysicsBody.
You can do this in four steps;
Iterate through the position of the tiles in your SKTileMap.
Find the tiles that are adjacent to one another.
For each group of adjacent tiles, collect:
a down-left corner coordinate and
an up-right corner coordinate.
Draw a square, and move on to the next group of tiles until you run out of tile coordinates.
Screenshot without visuals for comparison
Screenshot without visuals showing the physicsbodies
See my answer in a similar post on how to implement this.
I spent two days trying different ideas but could find nothing better than what I posted in my other answer. As mentioned there, it is the way recommended by an Apple staff member and as far as I know it's the most efficient way to have SpriteKit automatically add physics bodies to all your tiles. I've tested it and it works. (Although I'm still holding my breath for Apple to add a straightfoward way of putting physics bodies on tiles).
But there are other considerations. If you are having performance issues because there are too many physics bodies in your scene, you might want to try one of these other approaches. However, they are both more time-consuming than the approach described above. The only reason that may justify using one of these more labor-intensive approaches is if you need to reduce the number of physics bodies in your scene due to performance issues. Otherwise, I think the "automatic" approach mentioned above is the best option we have.
So I won't go into detail here because I think the automatic option is the best. These are just ideas for alternate approaches if your game needs to be extra stingy with system resources.
Alternate Approach #1 - Use Fewer Physics Bodies
Create your tile map in the editor. Then keep working in the editor to drag Color Sprites (SKSpriteNodes) over parts of your map that need a physics body. Shape the nodes to make the largest rectangle possible for areas that need physics bodies. This works best for for large, flat surfaces like walls, floors, ceilings, platforms, crates, etc. It's tedious but you end up with far fewer physics bodies in your simulation than if you had used the automatic approach mentioned above.
Alternate Approach #2 - Use No Physics Bodies
This idea would probably require even more work, but you could potentially avoid using physics bodies altogether. First, create your tile map in the editor. Analyze your map to identify which tiles mark a barrier, beyond which the player should not cross. Assign a user data identifier to that type of tile. You would need different categories of identifiers for different types of barriers, and you may also need to design your artwork to fit this approach.
Once your barrier tiles are sufficiently identified, write code which checks the user data value for the tile currently occupied by the player sprite and restrict the sprite's movement accordingly. For example, if the player enters a title that marks an upper boundary, your movement code would not allow the player sprite to move up. Likewise, if the player enters a tile that marks the leftmost boundary, your movement code will not let the player travel left.

SetScale unintentionally affecting SKSpriteNode position

I'm making my first shooter game using Swift and SpriteKit and I've recently been running into problems with setScale(). I have a Laser class whose instances are added as children to a Ship class. I now realize that any down scaling of the parent node will scale its child as well. In my update method in GameScene I check the position of each child named 'Laser' to determine if it should be removed from its parent when offscreen. I recently updated some of my sprites- including their relative sizes, and I noticed that the position of each laser is way off, as they are removed far before they reach the end of the screen and upon debugging their x positions are in fact far larger than where they are displayed onscreen. I also noticed that the starting x of each Laser instance is relative to its original position within the Ship instance. So the ship may be halfway across the screen but the laser x position still starts at 0 and if the ship is able to pass the laser, the laser's position becomes negative--is there a way to grab its position on the screen rather than relative to its start within its parent?
This is the code I'm doing the check with:
self.aShip.gun.enumerateChildNodesWithName("laser",
usingBlock: { node, _ in
if let aLaser = node as? Laser { i
if(aLaser.position.x > self.size.width){
aLaser.removeFromParent()
}
}
}
)
It seems like scaling has a ton more baggage than I would normally assume, so any insight into this problem or how to manage the relation between code and sprites would be awesome!!
EDIT:
I found that adding let positionInScene = self.convertPoint(aLaser.position, fromNode: self.aShip.gun) under if let aLaser = node as? Laser {and consequently using positionInScene rather than the aLaser position works.. however, I still don't fully understand the scaling effect going on with aLaser and it's position and don't know if its efficient to have to convert positions like this at the rate of update (60 times a second).

Create Unlimited Sprite Nodes Sprite kit

If anyone knows how to do this, I have a sprite node that I want to position all around the screen "Like stars" and I want to create a field node that pulls the "stars" to the center of the screen as more stars spawn basically creating a vortex like action.
This is how I created the node in this "addObjects" function, How is it that I can have this "stars" effect? and have them come towards the center of the screen unlimitedly? thanks a bunch
func addObjects() {
for (var i=0 ; i<5 ; i++) {
let PositiveNode = SKSpriteNode(imageNamed: "Enemy1")
PositiveNode.physicsBody = SKPhysicsBody(circleOfRadius: PositiveNode.frame.size.width/2)
PositiveNode.physicsBody?.dynamic = true
self.addChild(PositiveNode)
}
I had a thought that maybe making a for loop could help me achieve my goal , but I'm probably wrong.
To simulate a black hole effect you can use a radialGravityField SKFieldNode. As for an unlimited number of stars, you can create SKSpriteNodes and remove them from the scene once they reach a predefined position. That being the center of radial gravity field.
The important thing here is to removeFromParent as to avoid a memory leak caused by constantly creating new nodes without removing the ones in the center.