I am making a game for iOS. I inputed two images, one will be used as the player, and the other which is an object. I want to code that if the two objects intersect, then it runs the method EndGame.
-(void)Collision{
if (CGRectIntersectsRect(Ball.frame, Platform1.frame)) {
[self EndGame];
}
}
Although, The images are not a square shape, but the UIImage is a square. Therefore when the two objects intersect it Ends the game, even if the two images look like they came close it still ends the game because of the incorrect collision detection. Do you have suggestions?
Can I change the image shape on XCODE or can I make it so if the image collides with a certain point on the players image?
Thanks.
create a custom view and in draw rect create a bezier path. Something like this
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:ball.center radius:ballRadius startAngle:0.0 endAngle:M_PI*2.0 clockwise:YES];
This will draw a circle view
EDIT: heres a sample - I'm not sure exactly what all the details are, so this is pretty generic
#interface ballView : UIView//create subclass of UIView
...
#implementation ...
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPath];//create bezier path
[path addArcWithCenter:self.center radius:5.0 startAngle:0.0 endAngle:M_PI*2.0 clockwise:YES];//draw circle. You can change around the parameters to the way you like
[[UIColor orangeColor]setStroke];
[path stroke];//draw line
Related
I've got a "SLATE2" tablet that allows me to write on a tablet with a special pen and interact with my own app. I'm having some trouble though, and I don't think it's a problem with hardware.
- (void)touchesBegan: (CGPoint)point
{
[path moveToPoint:point];
}
- (void)touchesMoved: (CGPoint)point
{
[path addLineToPoint: point]; // (4)
[Newpath appendPath: path];
pathTwo = [UIBezierPath bezierPath];
pathTwo = [UIBezierPath bezierPathWithCGPath:Newpath.CGPath];
pathTwo = [pathTwo fitInto: self.bounds];
[self setNeedsDisplay];
}
- (void)touchesEnded: (CGPoint)point
{
pathTwo = [UIBezierPath bezierPath];
pathTwo = [UIBezierPath bezierPathWithCGPath:Newpath.CGPath];
pathTwo = [pathTwo fitInto: self.bounds];
[path removeAllPoints];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[[UIColor whiteColor] setStroke];
[pathTwo stroke];
return;
}
The drawing to the UIView screen is far too slow. I'm looking at the console and the events being raised by the actual tablet are at almost lightspeed. If there is a better way to draw on this UIView faster please show me.
I've dscovered the issue. fitInto is called so frequently and pathTwo is re-drawn every time. I need fitInto to be called everytime theres a new part of the pad that's drawn on to scale how much is shown.
I tried turning off anti-aliasing but that didn't work.
fitInto works like so...
imagine the screen of the iphone
and if you were to draw on a tablet, a square tablet, at the bottom, say a circle, if you were to draw a circle, it would just zoom in on the iphone screen as its the only drawing on the tablet. if you draw at the top of the tablet, the circle at the bottom is now shown to scale of the entire tablet because of the two opposing ends. i don't think this function could be made any faster... but let's try!
func fit(into:CGRect) -> Self {
let bounds = self.cgPath.boundingBox
let sw = into.size.width/bounds.width
let sh = into.size.height/bounds.height
let factor = min (5, min(sw, max(sh, 0.0)))
return scale(x: factor, y: factor, into: into)
}
and here is scale
func scale(x:CGFloat, y:CGFloat, into: CGRect? = nil) -> Self{
var transform = CGAffineTransform(scaleX: x, y: y)
if into != nil {
transform = transform.concatenating(CGAffineTransform(translationX: into!.midX - self.cgPath.boundingBox.midX, y: into!.midY - self.cgPath.boundingBox.midY))
}
let _ = applyCentered(transform: transform)
return self
}
Have you checked how many points are actually in your path? I don't think you want to draw your CGPath pixel by pixel. Instead you should only add a segment when the distance exceeds a certain threshold. You can also simplify the path by drawing curves through the points instead of drawing line segments. See this primer on bezier curves.
EDIT: The Ray Wenderlich sample app looks like its actually doing the same thing, adding points in TouchedMoved. However in touchesEnded they rasterize the curve into a bitmap. I'm not so sure that that will help your case though because it looks like you are deleting all of the points in touchesEnded. I would still look at their code and see if you have any other points that could be a bottle neck.
What effect are you after? Are you trying to let the user trace lines and curves with the pen, or dots if they tap?
The simple fact is that iOS is not fast enough to track the user's pen/finger strokes point-by-point. Instead, you want to collect a series of points and create a UIBezierPath that connects those points with line segments. That will give you something pretty close to what you want. However, if the user traces fast, that will lag behind as well.
I want to make custom drawing so that i could convert it to image.
i have heard of UIBezierPath but donot know much about it, my purpose is to change color of it on basis of user's selection of color.
Create a CGGraphcisContext and get an image like this:
UIGraphicsBeginImageContextWithOptions(bounds.size, NO , [[UIScreen mainScreen] scale]);
// set the fill color (UIColor *)
[userSelectedColor setFill];
//create your path
UIBezierPath *path = ...
//fill the path with your color
[path fill];
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
You might have to combine multiple paths to get your desired shape. First create the 'drop' with bezier paths. The path might look something like this:
//Create the top half of the circle
UIBezierPath *drop = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetWidth(bounds)*0.5f, CGRectGetWidth(bounds)*0.5f)
radius:CGRectGetWidth(bounds)*0.5f
startAngle:0
endAngle:DEGREES_TO_RADIANS(180)
clockwise:NO];
//Add the first half of the bottom part
[drop addCurveToPoint:CGPointMake(CGRectGetWidth(bounds)*0.5f,CGRectGetHeight(bounds))
controlPoint1:CGPointMake(CGRectGetWidth(bounds),CGRectGetWidth(bounds)*0.5f+CGRectGetHeight(bounds)*0.1f)]
controlPoint2:CGPointMake(CGRectGetWidth(bounds)*0.6f,CGRectGetHeight(bounds)*0.8f)];
//Add the second half of the bottom part beginning from the sharp corner
[drop addCurveToPoint:CGPointMake(0,CGRectGetWidth(bounds)*0.5f)
controlPoint1:CGPointMake(CGRectGetWidth(bounds)*0.4f,CGRectGetHeight(bounds)*0.8f)
controlPoint2:CGPointMake(0,CGRectGetWidth(bounds)*0.5f+CGRectGetHeight(bounds)*0.1f)];
[drop closePath];
Not entirely sure if this works since I couldn't test it right now. You might have to play with the controls points a bit. It could be that I made some error with the orientation.
In iOS 9 Apple introduced the collisionBoundsType to UIKit-Dynamics.
I have no issue when setting this UIDynamicItemCollisionBoundsTypeRectangle or when I set this to UIDynamicItemCollisionBoundsTypeEllipse.
The screenshot below is from a game I am making where the collisionBoundsType of the player is set to rectangle and the ball is set to ellipse:
However, when I set the player's collisionBoundsType to path I get weird behavior as seen here:
The view appears higher than it should and the collision body is to the right of where it should be.
Currently I have collisionBoundingPath set to this:
- (UIBezierPath *)collisionBoundingPath
{
maskPath = [[UIBezierPath alloc] init];
[maskPath addArcWithCenter:CGPointMake(SLIME_SIZE, SLIME_SIZE) radius:SLIME_SIZE startAngle:0*M_PI endAngle:M_PI clockwise:NO];
return maskPath;
}
Additionally, my drawRect function looks like this:
- (void) drawRect:(CGRect)rect
{
if (!_color){
[self returnDefualtColor];
}
if (!maskPath) maskPath = [[UIBezierPath alloc] init];
[maskPath addArcWithCenter:CGPointMake(SLIME_SIZE, SLIME_SIZE) radius:SLIME_SIZE startAngle:0*M_PI endAngle:M_PI clockwise:NO];
[_color setFill];
[maskPath fill];
}
Why is this happening? How do I set the path of the collision body to be the same as the drawing in the view?
Additionally, the red is just the background of the view (i.e. view.backgroundColor = [UIColor redColor];).
From the documentation on the UIDynamicItem here, the following statement about the coordinate system for paths seems to represent what is wrong:
The path object you create must represent a convex polygon with
counter-clockwise or clockwise winding, and the path must not
intersect itself. The (0, 0) point of the path must be located at the
center point of the corresponding dynamic item. If the center point
does not match the path’s origin, collision behaviors may not work as
expected.
Here it states that the (0,0) for the path MUST be the center point.
I would think that the center of your arc path should be (0,0) and not (SLIME_SIZE/2,SLIME_SIZE/2). Have you perhaps set the width and height of the UIView frame to SLIME_SIZE rather than SLIME_SIZE*2?
SLIME_SIZE really seems to define the radius, so the frame width should be SLIME_SIZE*2. If it is set as SLIME_SIZE, then that would explain why you need to translate by SLIME_SIZE/2 as a correction.
I was able to answer this by changing:
- (UIBezierPath *)collisionBoundingPath
{
maskPath = [[UIBezierPath alloc] init];
[maskPath addArcWithCenter:CGPointMake(SLIME_SIZE, SLIME_SIZE) radius:SLIME_SIZE startAngle:0*M_PI endAngle:M_PI clockwise:NO];
return maskPath;
}
to:
- (UIBezierPath *)collisionBoundingPath
{
maskPath = [[UIBezierPath alloc] init];
[maskPath addArcWithCenter:CGPointMake(SLIME_SIZE / 2, SLIME_SIZE / 2) radius:SLIME_SIZE startAngle:0*M_PI endAngle:M_PI clockwise:NO];
return maskPath;
}
The key difference is that I modified the center of the arc by dividing the x and y values by 2.
Debugging physics is a thing. It's probably not something that iOS users have tended to think a lot about as they've generally done very simple things with UIKit Dynamics. This is a bit of a shame, as it's one of the best aspects of the recent editions of iOS, and offers a truly fun way to make compelling user experiences.
So... how to debug physics?
One way is to mentally imagine what's going on, and then correlate that with what's going on, and find the dissonance between the imagined and the real, and then problem solve via a blend of processes of elimination, mental or real trial & error and deduction, until the problem is determined and solved.
Another is to have a visual depiction of all that's created and interacting presenting sufficient feedback to more rapidly determine the nature and extents of elements, their relationships and incidents/events, and resolve issues with literal sight.
To this end, various visual debuggers and builders of physics simulations have been created since their introduction.
Unfortunately iOS does not have such a screen based editor or "scene editor" for UIKit Dynamics, and what is available for this sort of visual debugging in Sprite Kit and Scene Kit is rudimentary, at best.
However there's CALayers, which are present in all UIKit Views, into which CAShapeLayers can be manually created and drawn to accurately represent any and all physical elements, their bounds and their anchors and relationships.
CAShapeLayers are a "container" for CGPaths, and can have different colours for outline and fill, and more than one CGPath element within a single CAShapeLayer.
And, to quote the great Rob:
"If you add a CAShapeLayer as a layer to a view, you don't have to
implement any drawing code yourself. Just add the CAShapeLayer and
you're done. You can even later change the path, for example, and it
will automatically redraw it for you. CAShapeLayer gets you out of the
weeds of writing your own drawRect or drawLayer routines."
If you have an enormous number of interacting elements and want to debug them, CAShapeLayer's performance issues might come into play, at which point you can use shouldRasterize to convert each to a bitmap, and get a significant performance improvement when hitting limits created by the "dynamic" capabilities of CAShapeLayers.
Further, for representing things like constraints and joints, there's a simple process of created dashed lines on CAShapeLayers, by simply setting properties. Here's the basics of setting up a CAShapeLayer's properties, and the way to use an array to create a 5-5-5 dashed outline with a block stroke, width of 3, no fill.
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setBounds:self.bounds];
[shapeLayer setPosition:self.center];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
[shapeLayer setLineWidth:3.0f];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setLineDashPattern:
[NSArray arrayWithObjects:[NSNumber numberWithInt:10],
[NSNumber numberWithInt:5],nil]];
I'm trying to figure out how to use UIKit Dynamics to successfully collide two UIViews which have custom boundary shapes.
The most basic example I can think of to explain my question is to have two circles collide (taking in to account their round corners) instead of their square boundary.
I'm sure I've seen this somewhere but I can't find any documentation or discussion on the subject from any official source.
I'd like to do this too, but I don't think you can do it under the current UIKit Dynamics for iOS 7. Items added to the animator must adopt the UIDynamicItem protocol (UIView does). The protocol only specifies their boundaries to be rectangular, via the bounds property, which is a CGRect. There's no custom hit test.
However, you can add a fixed Bezier path to the collision set, and it could be circular or any shape you can make with a path, but it would act like a curved wall that other rectangular objects bounce off of. You can modify the DynamicsCatalog sample code in Xcode to see the use of a curved boundary which doesn't move.
Create a new view file called BumperView, subclass of UIView.
In BumperView.m, use this drawRect:
#define LINE_WIDTH 2.0
- (void)drawRect:(CGRect)rect
{
UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(self.bounds, LINE_WIDTH/2, LINE_WIDTH/2)];
[[UIColor blueColor] setStroke];
[[UIColor lightGrayColor] setFill];
ovalPath.lineWidth = LINE_WIDTH;
[ovalPath stroke];
[ovalPath fill];
}
In the storyboard for the Item Properties page, add a View somewhere below the boxes and change its class to BumperView, and change its background color to clear.
Create an outlet named bumper to it in APLItemPropertiesViewController.m, but give it class BumperView.
Add the following in the viewDidAppear function, after collisionBehavior has been created:
UIBezierPath *bumperPath = [UIBezierPath bezierPathWithOvalInRect:self.bumper.frame];
[collisionBehavior addBoundaryWithIdentifier:#"Bumper" forPath:bumperPath];
Run it and go to the Item Properties page to see the rectangles bounce off the oval.
need some help with quartz 2d, it is completely new for me.
Basically my app needs to follow the touch, draw that line starting from center multiple times. The issue is that it has to be dynamic and the lines have to be on equally spread( kind of like octopus starting from center). The way I have it on android is that I remember the shape paths in array, than draw it multiple times with rotating the coordinate system, but I cannot figure out how to do it on iOS.
My rotate function
- (void) rotateContext:(int)angle
{
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), self.center.x, self.center.y);
CGContextRotateCTM(UIGraphicsGetCurrentContext(), radians(angle));
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -self.center.x, -self.center.y);
}
It only works if I try do do it in drawRect(), and it rotates all the paths with it.
Can you please suggest me a good way to solve the problem?
Thanks
This can lead you to a solution:
(maybe it even compiles)
/* setup the context */
UIBezierPath *bpath = [UIBezierPath bezierPath];
UIBezierPath *subpath =
[UIBezierPath bezierPathWithOvalInRect:<#some rect#>];
[bpath appendPath:subpath];
/* add more stuff to the path as you wish */
bezierPath.lineWidth = 2;
/* draw the same path rotated multiple times */
for(NSInteger i = 0; i < 4; i++) {
[bpath applyTransform:CGAffineTransformMakeRotation(M_PI_2 * i)];
[bpath stroke];
}
/* teardown the context */
Rotating a bezier is tricky, you'll need to apply a more complex transformation depending on the results you expect.
Those bezier paths objects can be stored in an array or whatever you need.