How do I check if CGRect intersects with any SpriteNode? - swift

A little of background: I have function spawnBubbles(), which uses output of another function determineSpawnPoint().
determineSpawnPoint() returns random CGPoint. There is also action, which spawns SpriteNodes once 0.5 second in the random X coordinate CGPoint.
The problem: as determineSpawnPoint() is random, sometimes 2 or 3 SpriteNodes in a row created nearby, so they intersect with each over.
What do I want to achieve: create a function
func checkForFreeSpace(spawnPoint:CGPoint) -> Bool{
//some code
}
which returns true if there is free space around certain point.
So, basically, when I get new random CGPoint, I want to implement a CGRect around it, and check if this rectangle intersects with some SpriteNodes (speaking in common sense, if there is free space around it)

You can create two CGRects from the point and nodes and use CGRectIntersectsRect function to check whether they intersect. The function returns true if they intersect.
if (CGRectIntersectsRect(rect1, rect2))
{
println("They intersect")
}

Related

How to convert the position in CGDisplayBounds() to global coordinate ( like NSEvent.mouseLocation) in Swift

I'm a newbie in Swift and MacOS.
I gonna find a method to get the exact display coordinate
NSEvent.mouseLocation
I have found method in CoreGraphic :
func CGDisplayBounds(_ display: CGDirectDisplayID) -> CGRect
but the coordinate is different.
I can workaround to apply a method to mathematically method to convert point Y.
But is there any method to get or convert the position programmatically?
I expect to get the same coordinate with NSEvent.mouseLocation.
Thank for your attention.
It returns to the same coordinate with mouse location.
As you noted, CoreGraphics has what Apple calls ‘flipped’ geometry, with the origin at the top left and the y coordinates increasing toward the bottom of the screen. This is the geometry used by most computer graphics systems.
AppKit prefers what Apple calls ‘non-flipped’, with the origin at the bottom left and the y coordinates increasing toward the top of the screen. This is the geometry normally used in mathematics.
The origin (0, 0) of the CoreGraphics global geometry is always at the top-left of the ‘main’ display (identified by CGMainDisplayID()). The origin of the AppKit global geometry is always at the bottom-left of the main display. To convert between the two geometries, subtract your y coordinate from the height of the main display.
That is:
extension CGPoint {
func convertedToAppKit() -> CGPoint {
return .init(
x: x,
y: CGDisplayBounds(CGMainDisplayID()).height - y
)
}
func convertedToCoreGraphics() -> CGPoint {
return .init(
x: x,
y: CGDisplayBounds(CGMainDisplayID()).height - y
)
}
}
You may notice that these two functions have the same implementation. You don't really need two functions; you can just use one. It converts in both directions.
Calling CGDisplayBounds(CGMainDisplayID()) might also be inefficient. You might want to cache the value or batch your transformations if you're going to be doing a lot of them. But if you cache the value, you'll want to subscribe to NSApplication.didChangeScreenParametersNotification so you can update the cached value if it needs to change.

How to move a rotated SCNNode in SceneKit, on its "own" axis?

The image below shows a rotated box that should be moved horizontally on the X and Z axes. Y should stay unaffected to simplify the scenario. The box could also be the SCNNode of the camera, so I guess a projection does not make sense at this point.
So lets say we want to move the box in the direction of the red arrow. How to achieve this using SceneKit?
The red arrow indicates -Z direction of the box. It also shows us it is not parallel to the camera's projection or to the global axes that are shown as dark grey lines of the grid.
My last approach is the product of a translation matrix and a rotation matrix that results in a new transformation matrix. Do I have to add the current transform to the new transform then?
If yes, where is the SceneKit function for the addition of matrices like SCNMatrix4Mult for multiplication or do I have to write it myself using Metal?
If no, what I'm missing out with the matrix calculations?
I don't want to make use of GLKit.
So my understanding is that you want to move the Box Node along its own X axis (not it's parents X axis). And because the Box Node is rotated, its X axis is not aligned with its parent's one, so you have the problem to convert the translation between the two coordinate systems.
The node hierarchy is
parentNode
|
|----boxNode // rotated around Y (vertical) axis
Using Transformation Matrices
To move boxNode along its own X axis
// First let's get the current boxNode transformation matrix
SCNMatrix4 boxTransform = boxNode.transform;
// Let's make a new matrix for translation +2 along X axis
SCNMatrix4 xTranslation = SCNMatrix4MakeTranslation(2, 0, 0);
// Combine the two matrices, THE ORDER MATTERS !
// if you swap the parameters you will move it in parent's coord system
SCNMatrix4 newTransform = SCNMatrix4Mult(xTranslation, boxTransform);
// Allply the newly generated transform
boxNode.transform = newTransform;
Please Note: The order matters when multiplying matrices
Another option:
Using SCNNode coordinate conversion functions, looks more straight forward to me
// Get the boxNode current position in parent's coord system
SCNVector3 positionInParent = boxNode.position;
// Convert that coordinate to boxNode's own coord system
SCNVector3 positionInSelf = [boxNode convertPosition:positionInParent fromNode:parentNode];
// Translate along own X axis by +2 points
positionInSelf = SCNVector3Make(positionInSelf.x + 2,
positionInSelf.y,
positionInSelf.z);
// Convert that back to parent's coord system
positionInParent = [parentNode convertPosition: positionInSelf fromNode:boxNode];
// Apply the new position
boxNode.position = positionInParent;
Building on #Sulevus's correct answer, here's an extension to SCNNode that simplifies things by using the convertVector rather than the convertPosition transformation, in Swift.
I've done it as a var returning a unit vector, and supplied an SCNVector3 overload of multiply so you can say things like
let action = SCNAction.move(by: 2 * cameraNode.leftUnitVectorInParent, duration: 1)
public extension SCNNode {
var leftUnitVectorInParent: SCNVector3 {
let vectorInSelf = SCNVector3(x: 1, y: 0, z: 0)
guard let parent = self.parent else { return vectorInSelf }
// Convert to parent's coord system
return parent.convertVector(vectorInSelf, from: self)
}
var forwardUnitVectorInParent: SCNVector3 {
let vectorInSelf = SCNVector3(x: 0, y: 0, z: 1)
guard let parent = self.parent else { return vectorInSelf }
// Convert to parent's coord system
return parent.convertVector(vectorInSelf, from: self)
}
func *(lhs: SCNVector3, rhs: CGFloat) -> SCNVector3 {
return SCNVector3(x: lhs.x * rhs, y: lhs.y * rhs, z: lhs.z * rhs)
}
func *(lhs: CGFloat, rhs: SCNVector3) -> SCNVector3 {
return SCNVector3(x: lhs * rhs.x, y: lhs * rhs.y, z: lhs * rhs.z)
}
}
The far easier way this is usually done:
The usual, normal, and extremely easy way to do this in any game engine or 3D engine is:
You simply have a wrapper node, which, holds the node in question.
This is indeed the entire point of transforms, they enable you to abstract out a certain motion.
That's the whole point of 3D engines - the GPU just multiplies out all the quaternions on the way down to the object; it's wholly pointless to (A) figure out in your head the math and (B) do it manually (indeed in the CPU).
In Unity it's "game objects", in scene kit it's "nodes" and so on.
In all 3D engines, including scene kit, almost everything has one or more "holders" around it.
To repeat, the reasons for this are (A) it's the entire raison d'etre of a game engine, to achieve performance in multiplying out the quaternions of every vertex and (B) sheer convenience and code solidity.
One of a million examples ...
Of course you can trivially do it in code,
cameraHolder.addChildNode(camera)
In the OP's example. It looks like you would use cameraHolder only to rotate the camera. And then for the motion the OP is asking about, simply move camera.
It's perfectly normal to have a chain of a number of nodes to get to an object.
This is often used for "effects". Say you have an object, which sometimes has to "vibrate up and down". You can have one node which only does that movement. Note that then, all the animations etc for that movement only have to be on that node. And critically, they can run independently of any other animations or movements. (And indeed you can just use the node elsewhere to jiggle something else.)

How to prevent spawning overlap in swift sprite kit?

If I'm trying to spawn in enemies in 2 defined areas of the screen (top and bottom with a middle section where they can't spawn in), how do I prevent them from spawning on top of or too near each other .
My sprites are relatively quite small to the screen, and the only suggestion I've found on here is to create an array of possible positions and every time you use one of those positions take it off the list, but first of all I don't know how that would even look, second of all I've got SO many possible positions because I'm working with 5px high sprites, and I want for them to be able to respawn once that area is clear.
My method of choosing top or bottom is just picking a random number 1 or 2, and depending I've got 2 functions that make them either on top or on bottom.
I just need for no 2 objects to spawn in a ball's diameter from each other. Any ideas how to incorporate that into my spawning?
edit:
//Create your array and populate it with potential starting points
var posArray = Array<CGPoint>()
posArray.append((CGPoint(x: 1.0, y: 1.0))
posArray.append((CGPoint(x: 1.0, y: 2.0))
posArray.append((CGPoint(x: 1.0, y: 3.0))
//Generate an enemy by rolling the dice and
//remove its start position from our queue
let randPos = Int(arc4random()) % posArray.count
posArray[randPos]
posArray.removeAtIndex(randPos)
...
//Play game and wait for enemy to die
//Then repopulate the array with that enemy's start position
posArray.append(enemyDude.startPosition)
This is the recommendation I found, but this gives errors "expected separator" that I don't really know how to fix.
And so really, I'd have to make a HUGE array of possible positions going across the X and Y covering all areas, or is there some better way to do this?
Not spawning a node on top of another is simply enough by using intersectsNode(_ node: SKNode) -> Bool.
As for not spawning too close, that's another story. The only way you can do that is too have all your current nodes in an array, enumerate the array and check each node's position to that of the spawning node. Dependent on your parameters, you either spawn or not spawn.
I am not versed in Swift so you will have to translate the code yourself.
-(void)testMethod {
// an array with all your current nodes
NSMutableArray *myArray = [NSMutableArray new];
// the potential new spawn node with the proposed spawn position
SKSpriteNode *mySpawnNode = [SKSpriteNode new];
BOOL tooClose = NO;
// enumerate your node array
for(SKSpriteNode *object in myArray) {
// get the absoulte x and y position distance differences of the spawn node
// and the current node in the array
// using absolute so you can check both positive and negative values later
// in the IF statement
float xPos = fabs(object.position.x - mySpawnNode.position.x);
float yPos = fabs(object.position.y - mySpawnNode.position.y);
// check if the spawn position is less than 10 for the x or y in relation
// to the current node in the array
if((xPos < 10) || (yPos < 10))
tooClose = YES;
}
if(tooClose == NO) {
// spawn node
}
}
Note that the array should be a property and not declared in the scope I have it in the example.

Understanding function algorithm

During study tutorial, i using function to show visible rect, containing all of annotations (on mapView) as it shown:
// 1
let rectToDisplay = self.treasures.reduce(MKMapRectNull) { (mapRect: MKMapRect, treasure: Treasure) -> MKMapRect in
// 2
let treasurePointRect =
MKMapRect(origin: treasure.location.mapPoint,
size: MKMapSize(width: 0, height: 0))
return MKMapRectUnion(mapRect, treasurePointRect)
// 3
}
// 4
self.mapView.setVisibleMapRect(rectToDisplay, edgePadding: UIEdgeInsetsMake(74, 10, 10, 10), animated: false)
Everything work, but I'm not understanding how exactly it working.
On third lane, we are doing following:
return MKMapRectUnion(mapRect, treasurePointRect)
Before that, we declare
mapRect: MKMapRect
So, mapRect does not have any initial value and suppose to not contain any values. Am i right?
How exactly MKMapRectUnion calculated, if mapRect have zero values? Is there any way i could look at every step of function using some kind of NSLog statement?
If you be so kind, please, explain me in details how that function work. As i understand, function try to make map Rect combining "zero" value map point and other mapPoint, with correct values.
you have understood everything correctly. as for your question
How exactly MKMapRectUnion calculated, if mapRect have zero values?
apple docs says:
If either rectangle is null, this method returns the other rectangle.
The origin point of the returned rectangle is set to the smaller of
the x and y values for the two rectangles. Similarly, the size and
width of the rectangle are computed by taking the maximum x and y
values and subtracting the x and y values for the new origin point.
how exactly that is being calculated, you can ask apple engineers or reverse engineer the mapkit. if the function does work fine then you don't have go to such length and divert from your real assignment.

object stop falling when reach a specific point in unity3d

I am making a game in which the balls are falling down on a plane one ball fall over the other ball and they make a line, which goes from bottom to top. I want that at certain point on y axis the ball stop falling. Don't know how to do it the code I used until now for balls to fall down is:
function calling(){
functionsRandom.Range(0, functions.Length);
}
function sphereA() {
var go = Instantiate(sphere,new Vector3(Random.Range(-3, 3),Random.Range(-3,3),-12.78451),Quaternion.identity);
go.renderer.material.color = Color(Random.value, Random.value, Random.value);
}
function sphereB() {
var go = Instantiate(sphere1,new Vector3(Random.Range(-3, 3),Random.Range(-3,3),-12.78451),Quaternion.identity);
go.renderer.material.color = Color(Random.value, Random.value, Random.value);
}
I used random.range, so that the ball falls from points between it both in x and y, for x it is working, but it is not working for y.
Add all the instantiated spheres to an array go[] instead of go. which are in line. Now at certain point where you want to stop generating other spheres, take the count of the sphere in the array. If the count is equal to the max limit. Then DON'T instantiate the spheres. If any doubts, come to unity3d chatroom.