How to make a view scrollable to view the full scene with sprites with Swift and SpriteKit? - ios

I have a Scene which now fills in the view:
scene.size = skView.bounds.size
scene.scaleMode = .AspectFill
I fill in the scene with sprites from bottom to top and in my up they need to go further up - outside the view.
So my scene should have the same width as a view but height should be... around 3000 fixed.
Sprite will fill in the screen and i want to scroll up to be able to view the whole scene.
Would you please give me an advice on how to "construct" that?

Here is a quick road map:
In your game scene "didMoveToView" you want to make a "world" node. Make sure the "world" node is a global variable by declaring it outside of the class. Make that "world" node size as large as the full size needed (e.g., 3000).
Also in "didMoveToView" add a pinch gesture that will eventually allow you to "zoom" that world, using UIPinchGestureRecognizer
Make the function to handle the pinch. Ultimately, the "recognizer.scale" should influence the "world.scale"
All your sprites should be added to the "world" node

You should create a node that acts as the World for the scene in your project and simply shift the y coordinate downwards to give the illusion of being able to see the scene that is further up.
For some sample code, you can look here

Related

How do I convert coordinates in SpriteKit?

I'm trying to grasp the conversion thing in SpriteKit but despite having read the documentation and several posts on SO I can't seem to get it right. As far as I understand there are two coordinate systems that work independently of one another, one for the scene and one for the view, which is why I simply can't use the things like UIScreen.main.bounds.maxX to determine screen corners that the node can relate to. Am I getting this right?
Anyway, here's my attempt at converting coordinates:
let mySquare = SKShapeNode(rectOf: CGSize(width: 50, height: 50))
mySquare.fillColor = SKColor.blue
mySquare.lineWidth = 1
let myPoint = CGPoint(x: 200, y: 0)
let newPosition = mySquare.convert(myPoint, from: self)
mySquare.position = newPosition
print(newPosition)
self.addChild(mySquare)
The print returns the exact same position as went in so obviously I'm not doing this right, but I have tried a number of different constellations but with pretty much no result; the coordinates remain the same. I have also tried let myPoint = CGPoint(x: UIScreen.main.bounds.maxX, y: UIScreen.main.bounds.maxY) but same there; no conversion.
What am I missing? In my head I read the conversion above as "convert myPoint from the view coordinate system and use it for my node mySquare.
There are lots of coordinate systems floating around, and so lots of potential sources of confusion:
Scene coordinates: that's your game's world, and what you usually think about when imagining coordinates and how to position things overall.
Node: Nodes have their own coordinate system. Once you start building a hierarchy, that matters. Imagine, e.g., an on-screen joystick that has a background showing a graphic of movement directions and a central "knob" that the player can manipulate. You might represent the joystick as a node with two children. One child is a sprite for the background, and the other is a sprite for the knob. The background sprite would naturally be at position (0,0), meaning the center of the overall joystick. The knob would move around, with (0,0) meaning centered and maybe (0,100) meaning up a bit. The overall joystick might sit at (200,300) in the scene. Then the background sprite would show up at (200,300) in the scene and the knob, when up, would be at (200,300)+(0,100) = (200,400) in the scene. The convert(from:) and convert(to:) are for converting within the node hierarchy. You could ask where the knob is in the overall scene's coordinates by knob.convert(.zero, to: scene) or joystick.convert(knob.position, to: scene). You very rarely need to do that sort of conversion.
View coordinates: The view is a window on the scene, i.e., what's actually being shown. If you've got a full screen game, the view is basically determined by the screen size in points. How view coordinates map to scene coordinates determines what part of the scene you actually see. If you need to go between view coordinates and scene coordinates, you use the scene's convertPoint(fromView:) and convertPoint(toView:) methods.
If you don't do anything special and have the scene size the same as the view size, then the scene-view mapping will have (0,0) in the scene at the lower left corner of the view. Another common convention is to have (0,0) in the scene at the center of the screen by setting the scene's anchorPoint to (0.5,0.5). Or perhaps you've designed the scene so that the world is 2000x2000 in size and there will be a nontrivial scaling and possible letter-boxing or cropping involved (depending on the setting of the scene's scaleMode). Or if your game has a camera node and, e.g., the camera is set to follow the player around, then the view-to-scene mapping will be changing as the player moves.
In your code, calling mySquare.convert(from:) doesn't really even make sense since the square hasn't been added to the scene at the time you're doing the "conversion".
Anyway, if you really want to do something like "put a square in the top-left corner of the screen", then you can take the point in the view's frame and convert it to scene coordinates and set the square's position to that.
override func didMove(to view: SKView) {
...
mySquare.position = convertPoint(fromView: CGPoint(x: view.frame.minX, y: view.frame.minY))
addChild(mySquare)
...
}
Edit: I would encourage you though to think mostly in terms of the overall scene, after some initial consideration of how the game should map to devices with screens of different sizes and aspect ratios. Once you're thinking in terms of the scene, then the scene's frame (rather than the view's frame) becomes the most natural reference when you're imagining "at the left edge" or "near the bottom right".

How does one initialize/move a camera in a SpriteKit program using Swift?

I have a map and it's bigger than the device's screen size, so I want the user to be able to navigate around the map by dragging the screen, similar to Clash of Clans.
How can I create a camera view so I can drag the camera around the map?
Also, how can I customize where the camera's center begins as the scene switches to the map?
theres no camera in swift. the idea is you add your background to a "world node"
// set up stuff you want to add to world node
let bg = SKSpriteNode(... initializer
// make a "worldNode" that you move around
self.worldNode = SKNode()
// add your bg to the worldNode
worldNode.addChild(bg)
// add worldNode to the scene
self.addChild(worldNode)
ok so now you have that.. now you make some method to move the world around depending on a position
func centerViewOn(centerOn: CGPoint) {
self.worldNode.position = CGPoint(x: -x, y: -y)
}
You pass in the players position, and the worldNode will move. That's the general idea.
You probably need to add some conditional stuff in here so that the camera doesn't move too far. With this code, the camera has no limit, and can look past the edge of your game world. You'd want to limit it so it stops following the player when they reach the edge.

Spritekit - Bottom of screen coordinates

What are the coordinates for the bottom of the screen... or how can I create a "floor" at the bottom of the screen in spritekit?
Sorry, but I don't understand screen coordinates that well in spritekit.
You need to understand the Sprite Kit coordinate system as explained in Apple's Documentation here.
Here's how you create a floor at the bottom of the screen in SpriteKit:
SKNode *floor = [SKNode node];
node.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(CGRectGetMidX(self.frame),1.0 , CGRectGetWidth(self.frame), 1)];
[self addChild: floor];
You need some universal approach to get coordinates of corners on the screen.
Using code from that answer you can get CGRect with necessary information.
Example:
let screenRect = getVisibleScreen(
sceneRect: self.scene!.frame,
viewRect: self.view!.frame )
And then you can use it:
screenRect.minX
screenRect.maxX
screenRect.minY
screenRect.maxY
screenRect.width
screenRect.height
This is more then enough to calculate coordinates of "floor" or any other relative positions.
The location of the bottom of the screen will depend on what coordinate system you are using for your scene.
Out of the box, the bottom of the screen will be at y coordinate zero, but there are a few things that can happen that will affect that.
For instance, if you are using the scene editor in xCode, and your scene's anchorPoint property is something other than y=0, then the "origin" of your scene will not be at the bottom of the screen. In the recent xCode beta, they changed the default behavior to have the scene's origin at the center of the scene instead of the lower left corner, so that would explain why you might be seeing things in the center of the screen when you expect them to be at the bottom.
Also, the "bottom of the screen" will be relative to whatever parenting structure you have in your scene. For instance, if you place a background sprite in your scene, and want to attach a floor sprite to that which is at the bottom of the screen, you'll have to do some computing to figure out where to place it because you are going to inherit the translation and rotation of the floor's parent node (and any parents that node has).
To keep things simple, you can just place everything directly on the stage and manage their z-order manually. This will let you, basically, use the same coordinate system for everything. This is often fine; as long as you're not trying to do anything complex with your sprites, you don't need a complicated "tree" of nodes.
But even with this approach, the metrics of your scene are going to have to be handled dynamically. The width and height of your scene are going to depend on how you approach displaying your scene on different devices with different sizes. For instance, the top right of an iPhone 4 is going to be in a different place than the top right of an iPad Pro. A full discussion of how to deal with that is beyond the scope of your question, but generally, you'll probably want to use a "reference width" or a "reference height" for your scene, use .AspectFit or .AspectFill for the scaleMode, and set your scene's size accordingly. (I.e., inspect the view's frame to get the actual aspect ratio of your scene and set your scene size to match your reference metric on one axis and scale the other axis to match the device's aspect ratio.) This will let you use the same metrics for all devices (although one of your two axes will be fluid).

scale around certain point over time SpriteKit

I have a SpriteKit Scene in which I want to have the effect as if a camera zoom and scale. Does anyone know of any libraries or some easy methods of doing this?
It was very easy to do in other 2D engines but does not seem simple.
I was thinking of doing it from the app delegate, and using the window to zoom since my character does stay around the same position.
The desired effect I would like to accomplish is like that of the start of an Angry Bird level when the camera pans into the level and then the launch doc.
http://www.youtube.com/watch?v=_iQbZ3KNGWQ This is an example of the camera zoom and pans I am talking about.
Thanks for the help.
If you add an SKNode to the SKScene, and make your scene content children of that node instead of direct children of the scene, then you can zoom and pan all of the contained content just by adjusting the xScale, yScale and position properties of the added node. (Any content you did not want scrolled e.g. scores or whatever could then be added to a different SKNode or added directly to the scene).
The adjustment could be done by overriding one of update:, didEvaluateActions, or didSimulatePhysics in your SKScene subclass. The choice would depend on if you are just moving your character around by yourself in update:, or if it also gets moved around by running SKActions or by simulated physics.

Best way to add a SKPhysicsJoint to an object

So I have a marionette I am making in Sprite Kit, and placing him in a scene. I have created a Marionette class, which is a subclass of SKNode, and when I create an instance of this class, I add a number of SKSpriteNodes to each other. This should give you an idea of how it works (I've snipped out the unnecessary lines):
[self addChild:self.head];
[self.head addChild:self.chest];
[self.chest addChild:self.leftLeg];
[self.chest addChild:self.rightLeg];
[self.leftLeg addChild:self.leftFoot];
[self.rightLeg addChild:self.rightFoot];
So, after doing this, I have my marionette showing up. I want to add a SKPhysicsJointPin to each of these connections. Code looks like this:
self.chestPin = [SKPhysicsJointPin jointWithBodyA:self.marionette.head.physicsBody bodyB:self.marionette.chest.physicsBody anchor:self.marionette.chest.anchorPoint];
At first I thought I could just set up all the pins inside the Marionette class, but that doesn't work (I get EXC_BAD_ACCESS if I recall). It turns out that I have to add the Marionette to the overall SKScene before I add the joints.
This make sense I guess, but I can't help thinking there should be some way I could just set everything up in the Marionette class, and then stick 2 lines of code in the scene to create a new Marionette, then addChild him to the Scene.
As it is, my SKScene subclass has a bunch of code now to add all the pins, and it doesn't feel like it fits in the Scene. Thoughts?
You don't want to use anchorPoint as anchor. The anchorPoint property is a factor which ranges from 0 to 1 and affects how the texture is offset from the sprite's position, ie the default 0.5/0.5 centers the sprite's texture on the sprite's position. In other words anchorPoint is not a position, and using that will anchor the body & head joint to the lower left corner of the screen.
Instead try using either chest or head's position as anchor, or the point in between the two.

Resources