How to add SKShapeNode to random position until screen getting full - swift

I want to add SKShapeNode to random position, using Sprite Kit, until the screen getting full.
How can I find the empty space in screen?

This is not an off the shelf kind of function in SK. Your only solution would be to create a hack to do what you want. One possible approach might be to create a very small node and place it in various positions on the screen, using a loop. For example (x,y position), 0,0 -> 4,0 -> 8,0 and so on...
You can use the func intersectsNode(_ node: SKNode) -> Bool to see if this node intersects any other node.
Another approach might be to use the same kind of logic but use func containsPoint(_ p: CGPoint) -> Bool instead.
Both approaches would be processor intensive so you should only run them sparingly.

Related

Is it possible to place objects with hit test without plane detection?

Following Apple's Creating an Immersive AR Experience with Audio
, I thought it would be interesting to experiment and try to place objects anywhere and not just on a vertical and horizontal plane. Is it at all possible to place an object using touch without plane detection? I understand that plane detection would increase the accuracy of hit tests and ARAnchor detection, so would there be any way where one could perform hit tests on any other location in the scene?
If your AR scene already contains any 3D geometry in a current session you can definitely use hit-testing to place a new model there (a placement based on already contained 3D geometry), or you can use feature points for model's placement (if any).
If there's no 3D geometry at all in your AR scene, or there's a extremely sparse point cloud, what do you apply hit-testing method to? Hit-test is a projected 2D point from screen-space onto a 3D surface (remember, detected planes are hidden 3D planes), or onto any appropriate feature point.
So, in AR, plane detection is crucial when developer is using hit-testing.
func hitTest(_ point: CGPoint,
types: ARHitTestResult.ResultType) -> [ARHitTestResult]
Here you can see all the ARHitTestResult.ResultType available.
But pay attention to this, there's a hitTest method returning SCNHitTestResult:
func hitTest(_ point: CGPoint,
options: [SCNHitTestOption : Any]?) -> [SCNHitTestResult]
Usage:
let touchPosition: CGPoint = gesture.location(in: sceneView)
let hitTestResult = sceneView.hitTest(touchPosition,
types: .existingPlaneUsingExtent)
or:
let hitTestResult = sceneView.hitTest(touchPosition,
types: .featurePoint)
Also, hit-testing is actively used in 3D games but it's rather for VR there than for AR.

How to handle mouseEntered: events on an NSView with rounded corners

I'm creating a view that has rounded corners and I want the view to highlight when the mouse hovers over it. The problem is that the NSTrackingArea registers a mouseEntered: event when the mouse is outside of the corners. Is there a way to override how it determines if it's in the view?
I've tried:
Adding .inVisibleRect to the NSTrackingArea.Options
Overriding isMousePoint:in:
If necessary, I can implement mouseEntered: to ignore all events whose points aren't within the rectangle, but I was wondering if there was a more elegant way (i.e where mouseEntered: gets called only when the mouse actually enters the view).
This is the code I use for drawing (it works for me):
...
override func draw(_ dirtyRect: NSRect)
{
let path = NSBezierPath(roundedRect: dirtyRect, xRadius: radius, yRadius: radius)
path.addClip()
backgroundColor.setFill()
dirtyRect.fill()
}
...
override func mouseEntered(with event: NSEvent)
{
print("Mouse entered!")
}
Let me know if I can clarify anything. Thanks for the help!
First of all, if your rounded corners are small enough, I'd say don't worry about it and just stick with the rectangular tracking area. Most users are unlikely to notice, so in my opinion it's probably not worth your time to create.
If you still want to do this, as Victor mentioned, you should be checking using whatever bezier path describes the shape of your view, but it's a little more complicated than just checking that inside mouseEntered() and mouseExited(). You also need to implement mouseMoved() and do the same path checking there in order to respond properly when the cursor moves in and out of the path within the tracking area. Don't forget to configure your tracking area to actually send you those mouseMoved events.
You might also need to start thinking about performance. If you end up implementing mouseMoved, then your testing code will be running every single frame of cursor motion through your tracking area, which could potentially reduce the responsiveness of your UI. There are a number of optimizations you could use if this winds up being an issue, such as using an inner tracking area (inside the rounded edges) that doesn't use mouseMoved or increasing the flatness of the path to reduce the complexity of the testing calculation. As you might imagine, optimizing this gets complicated really quick.
You could possibly save the path and check whether the point of the event is inside or not by using containsPoint(point: CGPoint).
For more see here.

Find the Exact Frame of a rotated SKSpriteNode

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

How do I change a tile to another (or a blank) in SpriteKit?

So I'm trying to program a chess app in Xcode 8.3.3 using a tile map node, with an 8x8 tile map containing either a piece or a blank space per tile (made in GameScene.sks, not with code). What I want to do is change a tile. For example, if someone moves a pawn, I want to change the starting space from a pawn to a blank space and then change the ending square from a blank space to a pawn. The tile groups are in a file called pieces.sks, which I made at File->New->File->SpriteKit Tile Set.
Here's what I have so far in GameScene.swift (I took out things not relating to the question), goal is to change a single square to a white bishop when the code is run:
class GameScene: SKScene {
var pieces : SKTileMapNode?
override func didMove(to view: SKView) {
self.pieces = self.childNode(withName: "//pieces") as? SKTileMapNode
if let pieces = self.pieces {
pieces.setTileGroup(backgroundGroup*, forColumn: 4, row: 4) //arbitrary square I'm using for testing
}
}
}
So pieces.setTileGroup confuses me for a few reasons, such as the "forColumn" instead of a simple "column". I got it off the Apple Developer Documentation, and am not sure it is the right thing to do what I want. The backgroundGroup (starred in the code) argument is a placeholder. I have no idea how to fill in a white bishop there.
Thanks!
I found the answer at https://www.raywenderlich.com/137216/whats-new-spritekit-ios-10-look-tile-maps which was incredibly useful for tile maps in general.
I added:
let tileSet = SKTileSet(named: "Pieces")
pieces.setTileGroup(tileSet?.tileGroups[0], forColumn: 4, row: 4)
Origin is in bottom left, as would a standard Cartesian plane's first quadrant.

Swift SKSpriteNode: Chaining Sprites by Image Feature

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)