Get SCNVector3 from CGPoint - objective-c

I am trying to get a SCNVector3 from a CGPoint. I am using a gesture recognizer to get the location of a touch (as a CGPoint).
The problem is that the touch doesn't always hit something when I hit test because there isn't always an object being touched. (Touch an empty space to move your ship to that empty spot).
Other Stack Overflow question that I have found uses the SCNHitTestResult to get the worldCoordinates but that doesn't work for me.
Does anyone know how to find this? Given that I know the z coordinate of course. Ships that move always move with a z position of 1.
I Need worldCoordinates to be able to use actions that move a SCNNode to a touch point which has a CGPoint. Thanks!

So, you want to turn a point in view space into a point in scene space? The catch to that, of course, is that scene space has a third dimension and view space doesn't. You use the SCNView (or other renderer) methods projectPoint and unprojectPoint to convert between scene space, which is 3D, and view space, which is... also 3D? Yep — two dimensions of screen pixelspoints, and one of normalized depth: the z-coordinate is 0 for points on the near clipping plane and 1 for points on the far clipping plane.
Anyhow, you have a useful constraint in that you're looking to map view-space points onto a specific plane (z=1) in scene space. You have an even more useful constraint if your scene space is oriented so that said plane is orthogonal to the view direction — i.e. the camera is pointing directly in the +z or -z direction.
If you want to map a view-space point to a particular scene-space depth, you need to know what the view-space depth for that plane is. Use projectPoint for that:
SCNVector3 projectedPlaneCenter = [view projectPoint:planeNode.position];
float projectedDepth = projectedPlaneCenter.z;
Now, hold onto that and you can make use of it whenever you need to map a touch location onto that plane:
CGPoint vp = [recognizer locationInView:view];
SCNVector3 vpWithDepth = SCNVector3Make(vp.x, vp.y, projectedDepth);
SCNVector3 scenePoint = [view unprojectPoint:vpWithDepth];
If your scene isn't oriented with the z-axis parallel to the camera, it's a bit harder — you have to work out where your z=1 plane is independently for any view-space point you process. In that case, you might find it easier to add an invisible SCNPlane to your scene and use the hitTest/worldCoordinates method to locate points on that plane.

Related

Converting Between Coordinate Planes

I'm currently working with multiple nodes acting as layers and I really need a way to convert the coordinates between them since I have multiple layers with varying children, it's a complicated scene so I don't want to lay it out in text. Any links would be much appreciated as well.
Taken straight from Apple Docs
When working with the node tree, sometimes you need to convert a position from one coordinate space to another. For example, when specifying joints in the physics system, the joint positions are specified in scene coordinates. So, if you have those points in a local coordinate system, you need to convert them to the scene’s coordinate space.
The following method shows how to convert a node’s position into the scene coordinate system. The scene is asked to perform the conversion. Remember that a node’s position is specified in its parent’s coordinate system, so the code passes node.parent as the node to convert from. You could perform the same conversion in reverse by calling the convertPoint:toNode: method
CGPoint positionInScene = [node.scene convertPoint:node.position fromNode:node.parent];
One situation where you need to perform coordinate conversions is when you perform event handling. Mouse and touch events need to be converted from window coordinates to view coordinates, and from there into the scene. To simplify the code you need to write, Sprite Kit adds a few convenience methods:
In iOS, use the locationInNode: and previousLocationInNode: on UITouch objects to convert a touch location into a node’s coordinate system.
In OS X, use the locationInNode: method on NSEvent objects to convert a mouse event into a node’s coordinate system.
If you are having this much trouble with your layers that you are requiring converting points, you may want to just rethink your strategy on how you're laying out the game scene.

Centring a CGAffineTransformScale around a given point

I'm animating objects falling onto a board from above, and I want to animate the board 'falling back' as the objects fall upon it. Objects can fall at any point on the board, and when the board 'falls back' I am scaling the board to a smaller scale.
When using CGAffineTransformScale objects scale based on their anchor point, the centre of the object; I want to scale the board and then line up the transformed board with the object that has fallen on it, so that the object that has fallen appears to stay in the same place relative to the board (or, more correctly, the board stays in the same place relative to the position of the board).
I spent hours, and hours changing the anchor point to the position that the object fell, but this revealed a fundamental misunderstanding on my part of how layer.anchorPoint actually works.
I imagine the solution is deriving a vector from the centre of the board to the given falling object and then somehow adjusting position of the board in the transformation so it's the same place. This is where I need help!
As you'd expect in these situations, an animated gif is required.
CALayer's anchorPoint property is the correct property to use for this, with the one minor annoyance that it works in the unit coordinate space, that is, it goes from 0 to 1, not in pixels:
You specify the value for this property using the unit coordinate space. The default value of this property is (0.5, 0.5), which represents the center of the layer’s bounds rectangle. All geometric manipulations to the view occur about the specified point. For example, applying a rotation transform to a layer with the default anchor point causes the layer to rotate around its center. Changing the anchor point to a different location would cause the layer to rotate around that new point.
Because of this, setting an anchor point in pixels would obviously result in some very strange behaviour. You would need to calculate your new anchor point in the unit coordinate space for it to work properly, so, instead of doing something like this:
board.layer.anchorPoint = CGPointMake(ball.x, ball.y);
you would do this:
board.layer.anchorPoint = CGPointMake(ball.x / board.layer.bounds.size.width,
ball.y / board.layer.bounds.size.height);
UPDATE: When you change the anchorPoint property, the view will move, because the anchorPoint, which is set relative to the layer in the unit coordinate space, is anchored to the layer's position property, which is set in the superview's coordinate space. In this way, when you change the value of the anchorPoint property, the view will move such that the point at the new anchor point is at the same place as the old one. You will need to compensate for this, as described in this answer.

IOS 3d for the experts

I've developed a few simple iOS apps, and I'm looking to develop a simple maze game. What I'm looking to do is really basic, so I'm looking for some guidance on which way to go.
Here is basically what I'm trying to do: simple escape-the-maze game where I read in a 2 dimensional array and that makes my maze.
I want the perspective to be standing in the middle of each square, and when you flick or hit a button, it will advance one square, and you can turn left or right. I'd like to be able to apply textures to the ways based on the numbers in the array, and I'd really like to see the movement of the wall moving towards me then stop.
The walls will always be the same height, and the maze will be simple square blocks, although I do want it to be able to hand rooms that might be more than 2x2 , like maybe 4x4, and be able to handle rendering that.
I'm not sure if trying to do something in OpenGL would be overkill since my requirements are really basic. Like I mentioned, it won't be a free move; it will be advance one square each time you hit a button and turn left or right.
I have read something about ray casting for this sort of thing but am not sure how I would accomplish this in iOS. Also I'd like to have the maze not take up the whole screen, maybe 2/3, while the rest is standard iOS controls like buttons and labels, and a background behind it.
What books or articles should I read to help me? I'd prefer not to use a 3rd party engine since this seems really basic.
You can do this using Core Animation, which is much easier than OpenGL. If you import QuartzCore into your project, you can position any view in 3D as follows:
//set the view's 2D position so that the vanishing point is the middle
//of the screen - use the same centre for every view
view.center = CGPointMake(window.bounds.size.width / 2.0f, window.bounds.size.height / 2.0f);
//create a 3d transform
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1.0f/500.0f; // this sets the 3D perspective
//you can transform the view in 3D relative to the camera
//this rotates the view by 45 degrees about the Y axis
//but you can also scale, translate, etc using equivalent functions
transform = CATransform3DRotate(transform, M_PI_4, 0.0f, 1.0f, 0.0f)
//transform the view in 3D
view.layer.transform = transform;
So if you create your walls as UIImageViews, you can arrange them into a room by transforming each one individually using the logic above.

How to render a 2d side-scroller game

I do not really understand the way I'm suppose to render a side-scroller? How do I know what to render when my character move? What kind of positionning should I use for the characters?
I hope my question is clear
The easiest way i've found to do it is have a characterX and characterY variable [integer or float, whatever you want] Then have a cameraX and cameraY variable. Every object in the scene is drawn at theObjectX-cameraX, theObjectY-cameraY...
CameraX/CameraY are tweened by a similar-to-midpoint formula so eventually they'll reach playerx/playery[Cx = (Cx*99+Px)/100] ... yeah
By doing this, every object moves in the stage's space, and is transformed only on render [saving you from headaches]
Use a matrix to define a camera reference frame.
Use space partitioning to split up your level into screens/windows.
Think of your player sprite as any other entity, like enemies and interactive objects.
Now what you want is the abstraction of a camera. You can define a camera as a 3x3 matrix with this layout:
[rotX_X, rotY_X, 0]
[rotX_Y, rotY_Y, 0]
[transX, transY, 1]
The 2x2 sub-matrix in the top-left corner is a rotation matrix. transX and transY defines the translation part, i.e the origin. You also get scaling for free. Just simply scale the rotation part with a scalar, and you have yourself a zoom.
For this to work properly with rotation, your sprites need to be polygons/primitives, say like triangles or quads; you can't just apply the matrix to the positions of the sprites when drawing. If you don't need rotation, just transforming the center point will work fine.
If you want the camera to follow the player, use the player's position as the camera origin. That is the translation vector [transX, transY]
So how do you apply the matrix to entity positions and model vertices? You do a vector-matrix multiplication.
v' = vM^-1, where v' is the new vector, v is the old vector, and M^-1 is the matrix inverse. A camera needs to be an inverse transform because it defines a local coordinate system. An analogy could be: If you are in front of me and I turn left from my reference frame, I am turning your right. This applies to all affine and linear transformations, like scaling, rotation and translation.
Split up your level into sub-parts so you can cull objects and scenery which does not need to be rendered. Your viewport is of a certain size/resolution. Only render scenery and entities which intersect with your viewport. Instead of checking each and every entity against the viewport bounds, assign each entity to a certain sub-screen and test the bounds of the sub-screen against the viewport and camera bounds. If your divide your levels into parts which are the same size as your viewport, then the maximum number of screens visible
at any particular time is:
2 if your camera only scrolls left and right.
4 if your camera scrolls left, right, up and down.
4 if your camera scrolls in any direction, and additionally can be rotated.
A screen-change is an event you can use to activate entities belonging to that screen. That could be enemies, background animations, doors or whatever you like.
If this is your first foray into writing a side-scroller, I'd suggest considering using an already existing game engine (like Construct or Gamemaker or XNA or whatever fits your experience level) so you don't have to worry about what order to render things and how to make it all work. Mess with that a bit--probably exploring a few of them--to get a feel for how they do things then venture out to your own once you've gotten used to it.
Not that there's anything wrong with baptism by fire but it can get pretty overwhelming in my opinion.

How to recognize the touch of a non regular sprite image?

I have a sprite and if it is touched the touch should be recognized. I used the coordinates to do so. I took the coordinates (min x, min y, max x , max y)of the sprite image. But The sprite image is not a rectangular shape. So, even if I touch the coordinates outside the sprite and inside the rectangular bounds the sprite is recognized.
But for my application I need only the sprite to be recognized. So, I have to take only the coordinates of the sprite, but it is not regular shape. I am using CCSprite in my program.
So, what can I do to for only the sprite to be selected ? Which classes should use for this?
Thank You.
You could try one of the following...
Create a bounding box smaller than the absolute extents of the sprite image. Yes it will be smaller than the sprite. This will eliminate the dead space click detection of the sprite the trade off being parts of your sprite which look selectable won't be
Use a circular bounding area to detect if the user has clicked on your sprite. Again you will have the dead space problem in my first suggestion but the sphere may give you some better coverage area over the sprite giving you better results on touch detection
This is a standard problem in physics collision detection systems which often end up using circles or rectangles as their collision bodies. I would go with the either a circle or rectangle smaller than the size of your sprite as your bounding area. Going finer detail than that you could generate bounding area polygons. This would however introduce a whole bunch of new issues and concerns.
I am building a Cocos2D game right now and what I am doing is first I step through my sprites and see which sprites the touch hit (they overlap in my app)
Then, for each sprite hit I use [sprite convertTouchToNodeSpace] to get an X,Y co-ordinate inside the sprite, which I can use (although the Y axis is flipped) to reference the CGImage I created the sprite with.
If the pixel at the touch point is 'clear' ie alpha 0, then the sprite was not really touched, and I check the next sprite in the z-order to see if it has color where it was touched.
Sometimes I think I should be using a two color mask image to go along with each sprite, not the sprite image. But, I am mr. make it work, then make it fast.
I realise this is not super efficient, but I do not have very many sprites and I do this only for touches.