Sprite Kit change anchorpoint but keep physicsbody centered - objective-c

I have a lot of different sprite nodes with a physicsbody the same size as the object. For positioning I need to change the anchorpoint of the node, but this changes the position of the physicsbody as well. Is there a way to keep the anchorpoint for the physicsbody centered? Using a path for the physicsbody is no option because I have so many different objects.

The anchorPoint is a purely visual property, it defines how the texture is drawn relative to the node's position. The physics body remains unaffected by changing the anchorPoint, it remains centered on the node's position.
So in a sense, the physics body does remain centered on the sprite's position already. By changing the anchorPoint you merely changed where the sprite's texture is displayed, and I believe you assumed the physics body would center on the sprite's anchorPoint. It does not, for one every node can have a physics body but only few nodes (sprite, scene, video) have an anchorPoint property.
The best way to fix this is to create your sprite images so that the physics body is always assumed to be centered on the image. Leave transparent borders around the image to ensure the image size is always the same and the position of the body properly centered.
You can also use SKPhysicsBody bodyWithRectangleOfSize:center: initializer to define the center point of the body and to match it up with the sprite's anchorPoint. But this is tricky and counterproductive, as you'll have to constantly realign body and sprite anchorPoint if you make even the smallest change.
Other than that it's best to leave the anchorPoint alone, especially with physics.

An old thread but still pops up when I google searched... Here was how I solved the problem for myself.
I essentially just stacked my SKSpriteNodes. The highest level parent node would receive the SKPhysicsBody so that all the objects would stay together. If you add the SKPhysicsBody object as a child to another node, the physicsBody can essentially fall out of the other nodes (images would be left floating in my case) when gravity or other forces are applied and can return some wonky results if you aren't careful.
To further clarify:
Parent node with no image/texture associated with it, just a size and then the physicsBody. (This node had the modified anchor point)
I then added a child node that had my "body" texture. (position had to be adjusted to make up for the parent's anchor point)
Finally I added two more child nodes to the "body" node to make up the eyes.
This specifically allows you to use a texture based SKPhysicsBody or something more complicated that does not have a centering option.
The physicsBody worked just as I needed it to while still being able to manipulate SKActions in the way in which a modified anchor point allows.
Hope this helps someone.

Related

Sprite Kit equivalent of clipsToBounds:

I would like to add moving child nodes on a Sprite node, but want them to be clipped if they move outside the bounds, hence giving an impression of a 'window'. I know there is a UIView property called clipsToBounds that does the same. Is there a similar property for SKSpriteNode, and if not, how to achieve the same?
You could add the sprite supposed to be clipped to a SKCropNode. Then set the crop node's maskNode to a sprite node using an image that defines the crop region.

Draw a line with finger and make an UIView follow it

Ok, I have a View, and first, I want that as soon as the user touches the View (a rectangle in this case) and starts dragging around a line should be drawn, following the path of the finger. Later, when I call a specific method, I want the View to follow the line and of course the line to disappear.
My thought for drawing the line:
Add a UIPanGestureRecognizer to the view, and then AddLineToContext, then draw it.
Use the touchesBegan, etc. methods. But later, I have multiple Views in there, and I need to find out which one the user touched. (have fun, there will be between 1 and 15...)
And I still have no idea about the other thing.
For the purpose of drawing the path, you are on the right track. I would use the gesture recognizer out of the two options you have.
To make the rectangle follow the path the easiest method I can think of is to keep an array of x and y for each point you are passing trough (don't forget to remove the consecutive duplicates).
So, now that you have an array of points that describe the shape of the path you can start a timer, or better use CADisplayLink, that will set the position of the rectangle to each of the points in the array. This will make the rectangle follow the path.
If you want the rectangle to follow the orientation as well, you will need to use vectors to describe the direction in which the rectangle should head.
First you need calculate the distance between the rectangle's position and the point in which it should go next using:
then, when you know the distance you can use arcsine to get you the direction angle. Then simply rotate the rect by that value.
Be careful at angle representation (pi vs degrees) and at the coordinates system.

How to keep a CALayer at the same position while resizing the parent NSView?

Imagine a small red box (CALayer instance) drawn in the lower left corner of its parent layer (which is the root layer of a layer hosting NSView).
When the frame of the parent view changes, the red box should remain at the same position on the screen. I do this by adjusting it's position relative to the lower left corner of the parent view .
The problem is that in some cases there is flickering and I can see the red box layer being drawn in the lower left corner of the extended frame before it is shown at the correct position.
I assumed that wrapping the frame and position change into one CATransaction would make both changes together, but that doesn't always work (the docs say that by using a transaction the animations will start at the same time, but there still seems to be a race condition at times).
How can I adjust the frame of the parent NSView while keeping the child layer at its perceived position?
Example and code:
My own ideas:
Hide red box layer, update the position, show it again
Use constraints to bind it to the right corner. Problem is that this offset could also change and I would have to update the constraint which could lead to the same flickering issue.
Try deleting the code you have already made to have the layer move with the view and put this where you are creating the layer.
boxLayer.autoresizingMask = kCALayerMaxXMargin | kCALayerMinYMargin;

Programmatically reveal a UIView

I am attempting to reveal (through animation) a UIView. Specifically I want to show the center portion of the view and then slowly reveal the outer edges of it (sort of like pulling back a curtain).
My first attempt was to simply set the bounds rect to be smaller and animate it to be the full size of the view's frame, but this did not have the desired effect since by changing the bounds I was also changing the frame.
If what I am trying to do does not sound possible (at least not in a simple manner), at least I would like to be able to have is some way to make the subviews of the main view stationary relative to the screen, NOT their parent view, as the parent resizes (this would give a similar effect).
Any ideas?
Thank you,
-Matt
It definitely is possible. What you need to do is
For the view you're animating, setAutoresizesSubviews:NO and setClipsToBounds:YES.
Set the view's bounds (NOT the frame) to a rect with zero size and origin at the center point of the rect you want the view to occupy when it is fully revealed (in the view's own coordinate system). In other words, startBounds.origin.x should equal half of endBounds.size.width and similarly for y.
Position the view by setting its center (in the parent view's coordinate system).
In an animation block, change the view's bounds to zero origin and full size.
In the animation's completion block, you probably want to setAutoresizesSubviews:YES again.
You may also need to set the view's autoresizing mask to be fully flexible (springs but no struts), depending on what other layout gets triggered as you resize.
Sounds like you want to change its clipping. A cheap (code-wise) way to do that would be to insert the view into a parent view (with autoresizing set to center it), set the parent to clip its children and then animate the parent's frame.

How should I wrap a custom NSView in an NSScrollView?

Cocoa's NSScrollView is horribly under-explained. I hope someone here knows what it's all about and can spare me a few seconds.
So I have a custom NSView. I implement -drawRect: for it to draw something, fill itself with colour, whatever. Then I have an NSScrollView wrapping it (set up through the interface Builder).
Now the inner, custom, view must have a size larger than that which fits in the outer scroll view—for it to scroll. That much I realise. I have incidentally configured it so that the scroll view adjusts to the surrounding window’s size, but that shouldn’t matter.
I override my inner view’s -frame method to return a frame sized at least 1000x1000.
- (NSRect)frame {
CGFloat w = 1000;
CGFloat h = 1000;
if (self.superview.bounds.size.width > w)
w = self.superview.bounds.size.width;
if (self.superview.bounds.size.height > h)
h = self.superview.bounds.size.height;
return NSMakeRect(0, 0, w, h);
}
Here’s the outcome, which I have trouble interpreting:
I can scroll when the scroll view encloses an area smaller than 1000x1000
BUT
The only area filled with colour (i.e. that my -drawRect: method has any effect on) is
as large as the scroll view’s bounds
located at (0,0. I use flipped, so that’s top left, and it ends up being outside the visible area after scrolling.
The visible area that lies outside this irrelevant rectangle is not painted at all.
I don’t know anything beyond this point. It seems like the rect for drawing is clipped to the scroll view’s position in the window, and size, or something—but it does not take the scrolled "location" into account.
It should be noted that I don't really expect anything else to happen. I feel I am missing a piece, but can't find which. Sorry for the wall of text, but I can’t explain better right now. I hope it is easier to answer than it is to ask.
Regards and hope,
Not Rick Astley
It's a very very very bad idea to overwrite -frame. There is so much that depends on the actual instance variable having a correct value. Instead try to set the frame to the one you want using setFrame:, that might fix all your problems if you're lucky...
I agree with Max's warning that you shouldn't override -frame. If you want to constrain the set frame, override its setter ( -setFrame: ) and the designated initializer ( -initWithFrame: ) and adjust the proposed frame as desired.
Regarding your overall problem, I wonder if your problem is conceptual. The argument for -drawRect: (the dirty rectangle you're asked to redraw) is useful if you're drawing something that you can redraw incrementally in parts (like a grid - any grid blocks intersecting dirtyRect can be redrawn and the rest can be ignored). If you're doing something that has to be completely redrawn, you should use [self bounds] and not the dirty rect passed at drawRect.
For example, if you have just a standard gradient background, it's difficult to tell from dirtyRect which part of the gradient to redraw and infinitely easier just to redraw the whole view with the gradient, ignoring dirtyRect altogether.
You're right in assuming that only the area of your view exposed by the scroll view's clip rect will normally be asked to redraw when scrolling. There're also interactions with the scroll view's -copiesOnScroll to consider.
I hope this helps.
Use of the NSScroller really relies on a solid understanding of the MVC paradigm. Apple's docs really focus on showing a photo and a set of text, but not much else. The use of NSScrollView is something that I've struggled with in the past.
First off, do not override frame. Use setFrame to tell the scrollView how large the working area is, and then just simply draw in the area the frame encompasses. As I understand it, a custom NSView and the encompassing NSScrollView takes care of the rest, such as what to draw where when. In other words, ignore the bounds of the rect passed into drawRect and instead draw within the bounds of the frame you sent to scrollView; don't worry about what is visible and what isn't because that is the job of the framework.
Here is where the MVC paradigm comes in: setFrame should be used when your Model is updated. So, if an object falls outside of the current bounds of the frame, then use setFrame to set the newly expanded bounds, and then draw within that area.