I've started implementing an iOS game with swift and SpriteKit.
I have an object called "bubble" which basically is an SKSpriteNode (with zPosition=0, with image) that have a child (which is an SKCropNode of a person image cropped to a circle, with zPozition=1).
That's ok if one bubble covers another bubble as a whole, but somehow it seems like the bubbles are partially covered with the person images.
a demo picture is provided: (my final output should be that bubble1 will be on top of child-of-bubble-2)
Maybe the problem is that SpriteKit ignores child ordering and just set any node with a zPozition to be in it's correct place? That is - all the bubbles are drawn first because they have zPosition=0 and then all the bubble-children are drawn, as they all have zPosition=1?
If this is the case, what can I do to make sure all bubble parts are drawn together (and other bubbles can cover that bubble, I don't care) knowing that I have a dynamic amount of bubbles?
well according to this SO answer, indeed all the zPosition values are calculated before drawing.
I ended up creating a counter for bubbles, adding 1 every time a bubble has been added, and assinging the counter value as its zPosition.
And inside the bubbles, I made sure every child has a zPosition in the range (0, 1)
If you need to draw things in a precise order, then I suggest you to read section Understanding the Drawing Order for a Node Tree of SKNode Apple's documentation. Everything is correctly describe in details.
Related
I have a list of items in a server. These items have scores and the scores correspond to the size of their view (a circle). I would like to depict these items with the largest circle in the center and each successively smaller circle wrapping around it, spiralling smaller and smaller.
Once I achieve this I would like to set it to a scroll. A scroll up will add each successively larger item to the center of the spiral and eventually spiralling down and away and vice versa.
The sizing of these objects I have performed already.
Here's my methodology: Native Swift doesn't have anything to help me layout-wise here to my knowledge. V/HStack won't help and neither will ScrollView. Essentially my understanding is that I should create a path that calculates its spiral based on the sizes of the items and these items should use .offset to position themselves on the path. Using a DragGesture, I can attach a #State variable to determine how far down the path each item is.
This is by far the most complex UI I've attempted so anything to get me down the right path is appreciated.
Edit 1:
Here is the ideal UI:
I'm sure there are various ways to approach this.
Dragging the "top" view down using a UIPanGestureRecognizer - and Scaling it during the drag - is pretty straight-forward. Getting the "already dragged" views to animate around the center is a bit tougher.
One approach is to use a UIViewPropertyAnimator and creating Key Frames for the positions around the arc.
Additional advantages to that include fewer calculations (set scaling and position key frames in .began state) as well as being able to "scrub" through the animation.
I put together a little demo that ended up looking like this:
You can play with the source code here: https://github.com/DonMag/OrbitDrag
I am trying to understand how Unity decides what to draw first in a 2D game. I could just give everything an order in layer, but I have so many objects that it would be so much easier if it was just drawing in the order of the hierarchy. I could write a script that gives every object its index, but I also want to see it in editor.
So the question is, is there an option that I can check so that it uses the order in the hierarchy window as the default sorting order?
From your last screenshot I could see you are using SpriteRenderer.
The short answer to the question "is there an option that I can check so that it uses the order in the hierarchy window as the default sorting order?" would be no, there isn't by default*.
Sprite renderers calculates which object is in front of others in one of two ways:
By using the distance to the camera, this will draw the objects closest to the camera on top of the rest of the objects in that same order in layer, as per the docs:
Sprite Sort Point
This property is only available when the Sprite Renderer’s Draw Mode is set to Simple.
In a 2D project, the Main Camera is set to Orthographic Projection mode by default. In this mode, Unity renders Sprites in the order of their their distance to the camera, along the direction of the Camera’s view.
If you want to keep everything on the same sorting layer/order in layer you can change the order in which the objects appear by moving one of the two objects further away from the camera (this is probably further down the z axis). For example if your Cashew is on z = 0, and you place the walnut on z = 1 then the cashew will be drawn on top of the walnut. If Cashew is on z=0 and the walnut is on z = -1 then the walnut will be draw on top (Since negative is closer to the camera). If both of the objects are on z - 0 they are both equally as far away from the camera, so it becomes a coin toss for which object gets drawn in front, as it does not take into account the hierarchy.
The second way the order can be changed is by creating different sorting layers, and adjusting the order in layer in the sprite renderer component. But you already figured that out.
*However, that doesn't mean it cannot be done, technically...
If you feel adventurous there is nothing stopping you from making an editor script that automates setting the order in layer for you based on the position in the hierarchy. This script would loop through all the objects in your hierarchy, grab the index of the object in the hierarchy, and assign the index to the Order in Layer.
I don't think Unity has such feature (https://docs.unity3d.com/Manual/2DSorting.html).
Usually you shall define some Sorting Layers:
far background
background
foreground
and assign Sprite Renderer of each sprite to one of Sorting Layers
I really want to know how to find the EXACT frame of an SKSpriteNode if it is rotated. Currently, the frame of an SKSpriteNode looks like this:
This frame is the rect.frame.
However, this frame includes a lot of empty space due to its zRotation. I don't want this empty space and instead want the frame of exactly the SKSpriteNode.
This is what I want:
How can I achieve this? If you have any idea how to find this 'exact' frame of an SKSpriteNode, I would really like to know. Please use SWIFT.
Thank you
This can be done.
Put a dummy node at the bottom left of your sprite that you know the exact size of. Probably use a perfect square. Anchor it's bottom left to the bottom left of your parent. The parent is the one you want the exact size definition of.
From there, two ways:
Scale the dummy sprite to the size of the sprite you're curious about, and use those measurements to determine where the sprite is and what size it is at any point in time.
Put another dummy sprite at the top right of the parent. In this case you can use the midpoint of your two dummy objects, you don't need to use their edges perfectly. Now you can find the position of these two dummies, at any time, and figure out the size/shape/outline of your sprite in world space units.
Here's way 1 animated
Context
For the purpose of a MWE we will be using the following image of a stick figure:
with the goal of having a chain of these sprites move, hand-in-hand, across the screen:
It is worthwhile to note that the stick figure image itself is wider than the arm-span of this stick figure. The background is, however, transparent.
Depending on the application, one may make a class that either inherits from SKSpriteNode or encapsulates it, e.g. a class called Person, to store additional information, where there may be an array var people = [Person]().
Questions
1.) Suppose you had two instances of the aforementioned Person class with each sprite taking a stick figure image. How could one position them - programmatically - such that the sprites are touching ''hand in hand'' although the image has a transparent background? Of course one could spend some time fiddling about to get find a spacing parameter to ensure this is achieved, but that parameter would always have to be, via trial-and-error, re-calculated if the sprites were re-scaled.
2.) Given a chain of these sprites, hand in hand, how could one animate them to move across the screen at the same velocity? If one calculates the spacing parameter alluded to in 1.) then an SKAction could be given to each Person such that their end position is offset (but total distance traveled is the same), where the TimeInterval is maintained the same. Is there a way to tell all the sprites to move to the left until off the screen at a rate of $x$ pixels per second?
It looks like you've mostly answered your own questions already, but here are some additional ideas:
Make the spacing value proportional to the size of the sprite.
Yes, there is an SKAction that moves a sprite a given distance over a given period of time (effectively a velocity): let moveAction = SKAction.moveBy(x: 10, y: 0, duration: 2)
I am having an issue with the zPosition undesirably changing when a node is touched.
I can't give a simple code example because currently my scene and rendering is quite complex. Everything is drawn correctly when the scene is presented and for the most part besides as described below it works.
So the issue explained - we have a base image node correctly rendered below a tower node (tower defence game). The tower is circular and the base image is square, so the corners stick out. If you manage to touch the corners, for some reason beyond my knowledge, the zPosition changes to above the tower.
There is no particular code on touches to change any zPositions of anything in the game.
I know this is a shot in the dark, but any insight would be great and much appreciated.
If your view's ignoreSiblingOrder property is set, you should set the zPosition of each node, appropriately, to ensure that they are drawn in the correct order.
From Apple's documentation...
The default value (of SKView's ignoreSiblingOrder property) is NO,
which means that when multiple nodes share the same z position, those
nodes are sorted and rendered in a deterministic order. Parents are
rendered before their children, and siblings are rendered from eldest
to youngest. When this property is set to YES, the position of the
nodes in the tree is ignored when determining the rendering order.
The rendering order of nodes at the same z position is arbitrary and may change every time a new frame is rendered [emphasis added].