I'm trying to create a simplified Dr. Mario like game where I have a pill shaped object, each half with their own color.
The pills can be stacked on top of each other and I need to find out via collision detection if two similar colors have collided.
What I tried
I created an SKSpriteNode subclass where I generated half of the pill with its color and spawned two in my scene. I positioned them near each other and created a fixed joint between them. The problem is that I couldn't rotate them as if they were a single Node (there's an open question regardin this issue)
I created two SKSpriteNode subclasses. The first one is just like the one above, returning half a pill, but instead of getting called from the scene, these get called from the second subclass, which returns the pill as a single node into the scene. In this scenario, the rotation works, but I can't figure out how to create a joint between them and add that joint to the physicsWorld from outside the scene.
Either way, neither of my solutions seem okay. How would you go about creating the pill object?
I would just create an SKNode subclass with two SKSpriteNode references to the left and the right half of the pill and add these two sprite nodes as children.
Something like this.
class Pill: SKNode {
var left: SKSpriteNode?
var right: SKSpriteNode?
func setup() {
left = SKSpriteNode()
/* Setup left half of the pil */
addChild(left!)
right = SKSpriteNode()
/* Setup right half of the pil */
addChild(right!)
}
}
I would not bother having two nodes for the object. The colour is just the texture after all. I would create one SKSpriteNode subclass, one physicsBody and store the rest as attributes. Then on collision you do the calculation on sides and colorus and rotaion.
enum PillColor {
case red
case blue
case yellow
}
class Pill: SKSpriteNode {
var rightSide: PillColor?
var leftSide: PillColor?
var rotation: Int
}
In the SKPhysicsContactDelegate's didBegin(_ contact) method you can use the contactPoint property of the SKPhysicsContact to know where the two actually met.
Related
I want to be able to improve my game's performance by somehow "flattening" my background nodes.
This youtube video demonstrates how I build up my background graphics.
But, I have my background graphics set up like this.
I use two textures like stamps and repeat then.
In this case... one mountain texture with a snowy top...
One mountain texture without the snow.
I alter the zPosition of these stamps for a "layering" effect.
I then use "fills" which are just SKSpriteNodes of solid grey to layer over parts that need a grey fill.
E.g. Before fill nodes are added
A colour screen with alpha is then added on top to give the mountain a faded looked.
E.g. Before
E.g. After
The game sometimes freezes up... but it doesn't freeze when I remove these graphics.
Is there a way to improve performance by merging or flattening my background graphics nodes based on zPosition as a scene is loading up?
How would I do this?
Would this improve performance?
Or what would be the best way to improve performance in my case?
I think this question is similar...
Merge all SKSpriteNode children into a single SKSpriteNode
But, how do I do this in Swift and take zPosition and alpha into account so I don't lose the layering effects?
Somethings you may want to do:
Separate your foreground and background into separate SKS files, and load them
into your main SKS file via SKReferenceNode
Convert these SKS files into textures instead of SKReferenceNodes via code using view!.texture(from: node)
Ensure your atlas is not being broken up in a way that adds to the draw count
Design your code in a way that not all nodes are on the scene, the more nodes the slower your code becomes.
If you have SKPhysicsBody's, try to use as few as possible. This can be done my merging multiple bodies together with SKPhysicBody(bodies:) or creating 1 body with a polygon that goes around multple objects.
Also, if your bodies are not moving via physics (Not by SKActions) then make sure that isDynamic is set to false
If you are running SKActions, make sure you use as few of these as possible. For example, if you have 4 different nodes travelling left at 10 points per second, you can put these 4 nodes into a parent node, and run the action on the parent node.
Ok so here is an example of what you have now.. all your nodes here like this:
How to execute the bitblit easily is to move all these under a new empty node called "background"
Now head to your swift file, and set up an placeholder variable for the new node we will create in code
class GameScene: SKScene {
var blitBackground = SKSpriteNode()
override func didMove(to view: SKView) {
}
}
Now , add this handy function that you can use in all of your projects if desired:
func blit(from node: SKNode) -> SKSpriteNode {
return SKSpriteNode(texture: SKView().texture(from: node))
}
and here is the method we will use to initialize our bitblit background, and remove the laggy one from the game:
func blitTheBackground() { // use in didMove
let laggyBackground = childNode(withName: "background")!
blitBackground = blit(from: laggyBackground)
laggyBackground.removeFromParent()
addChild(blitBackground)
}
Here is the completed scene:
class GameScene: SKScene {
var blitBackground = SKSpriteNode()
func blit(from node: SKNode) -> SKSpriteNode {
return SKSpriteNode(texture: SKView().texture(from: node))
}
func blitTheBackground() {
let laggyBackground = childNode(withName: "background")!
blitBackground = blit(from: laggyBackground)
laggyBackground.removeFromParent()
addChild(blitBackground)
}
override func didMove(to view: SKView) {
blitTheBackground()
}
}
and it reduced the node count from 10 to 4... hopefully, for you it will help with your draw count too!!
NOTE, this method is EXTREMELY effective with shapenodes!!
If you need to do this in a loop, be sure to place it in an autorelease pool to be sure that the memory is freed up after it is used. Creating textures from the view uses a lot of memory, so you can expect to get errors if you are not careful with it.
for i in 0..<5
{
autoreleasepool{
let laggyBackground = childNode(withName: "background\(i)")!
blitBackground = blit(from: laggyBackground)
laggyBackground.removeFromParent()
addChild(blitBackground)
}
}
I need to create a road(one simple horizontal line) using SpriteKit, so i have four obstacles to put anywhere position on the road. These obstacles will be in a "shelf" and I have to drag them to the road.
How can i do that using SpriteKit and the new Swift2 ?
PS: I'm beginner on spriteKit.
So you meant to make some SKSpriteNode (road) as you say and put another SKSpriteNode onto that road. (Further I assume that you already have sprite nodes of the road and one obstacle)
define the property of your gameScene:
let roadAndObstaclesLayer: SKNode!
then in didMoveToView you have to add the road SKSpriteNode and obstacle SKSpriteNode to that layer. Your have to make either spawn obstacle method or you have to make position of your obstacles by yourself.
In general it would be like:
roadAndObstaclesLayer = SKNode()
roadAndObstaclesLayer.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetHeight(frame) / 4) // position of your layer
roadAndObstaclesLayer.addChild(road) //child road is added
roadAndObstaclesLayer.zPosition = 0 //zPosition of the layer in the scene
road.zPosition = 0 //zPosition of the child (road) in the layer
roadAndObstaclesLayer.addChild(obstacle) //child obstacle is added
obstacle.zPosition = 1 //zPosition of the child (obstacle) in the layer above child rode
addChild(roadAndObstaclesLayer) //child of the scene is added
As you understand this is an example of single obstacle that is on the road. Your obstacle and road would be placed at the same place but if you want to make some difference of the positions so you have to describe the position of obstacle separately.
As well you can place road and obstacle in different layers or in scene itself separately so their position would be independent according to each other. So in other words it depends on what do you want see at the end.
Hope it helps.
Creating a game like Doodle Jump where my player is constantly being moved up by bouncing off of obstacles. Ive tried every trick in the book but nothing seems to be working/doing exactly what I want. Can anyone give me some tips?
iOS 9 introduced the Camera Node. Use SKCameraNode, which is a subclass of SKNode, and can be translated and rotated in same way.
So, instead of moving all of your background elements in the opposite direction of your hero/player, you can simply attach your scene's camera node to your hero/player and the rest is taken care of.
PS. You can also do cool stuff like scaling the camera size.
EDIT.
Happy to include an example.
First, make a camera constant in your scene.
import SpriteKit
class myFirstScene: SKScene {
let myCamera: SKCameraNode = SKCameraNode()
...
}
Then in your didMoveToView() function, assign the scene's built-in camera variable to the camera constant we made earlier.
override func didMoveToView( view: SKView ) {
camera = myCamera
...
}
Now, there are a few different ways to "attach" your camera to your hero/player. The first is to attach your camera node to your hero.
hero.addChild( myCamera )
I don't even know if it works that easily because my game uses something different, a simpler version is below.
update(){
camera!.zRotation = hero.zRotation
camera!.position = hero.position
}
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.
I am creating a game where user controls a cube (50x50px) and can collect more cubes while exploring and collected cubes added to a random side of the users current cubes.
User starting cube
http://s7.postimg.org/id95wms7r/cube.png
After user collected some more cubes
http://s13.postimg.org/oo9v4c9bb/morecubes.png
As you can already see from the second image, my problem is when I generate the body from texture, it has some offset with the original picture. Cube adding is random so you can get really weird shapes and those offsets are always different. My question is, is there a way to align that body with the parent sprite? Also I don't want to move the sprite because sprite is in the right place, I actually want to move the body.
This is how my code looks like. Both player and cubes are at CGPointZero in their own coordinate systems.
Player: SKNode
-- Cubes: SKSpriteNode
physicsBody = SKPhysicsBody(texture: cubes.texture, size: accumulatedSize)
physicsBody?.dynamic = true
physicsBody?.mass = 2.0
physicsBody?.allowsRotation = false
Instead of creating the SKPhysicsBody from the texture, create an SKPhysicsBody for each individual cube, then put all those individual bodies into an array, and then create the SKPhysicsBody from the bodies in that array using:
(SKPhysicsBody *)bodyWithBodies:(NSArray *)bodies
That will get you a more accurate body with the sprite.
"The shapes of the physics bodies passed into this method are used to create a new physics body whose covered area is the union of the areas of its children" (from the SKPhysicsBody Class Reference).