Circle to circle collision reaction - Equal priority - iphone

I am creating an app where there will be ~10 circles on screen, that can all be pushed and dragged around the screen via touch movements. These circles (which are simply UIView's with a circle drawn in them) also have collision detection on them, and the idea is that they push other circles out of the way when they collide. There is also no acceration or gravity in this app, it's more like pushing coins around, where they are simply pushed out of the way, and when you stop moving, so do they.
I have touch-to-circle interaction working, and the fundamentals of circle-to-circle working. The code however gives Circle01 all the power. IE, it can be pushed into other circles and they will move out of the way as hoped. But when the other circles are pushed into it, they move around Circle01, instead of pushing it out of the way.
I've read dozens of pages about collision detection, but I'm just stumped as to how I'd modify this code to give all the circles equal power.
Relevant code:
MyCircle.m
- (void)drawRect:(CGRect)rect
{
CGPoint viewCenter = CGPointMake(50,50);
bpath = [UIBezierPath bezierPathWithArcCenter:viewCenter radius:50 startAngle:0 endAngle:DEGREES_TO_RADIANS(360) clockwise:YES];
[[UIColor blueColor] setFill];
[bpath fill];
}
CircleVC.m
- (void)viewDidLoad
{
Circle01 = [[MyCircle alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
Circle01.backgroundColor = [UIColor redColor];
Circle01.alpha = 0.5;
[self.view addSubview:Circle01];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// Testing circle collision detection
for (UIView *aView in self.view.subviews)
{
if (Circle01 == anotherView)
continue;
if (CGRectIntersectsRect(Circle01.frame, aView.frame))
{
float xDistance = aView.center.x - Circle01.center.x;
float yDistance = aView.center.y - Circle01.center.y;
float distance = xDistance * xDistance + yDistance * yDistance;
float angle = atan2(yDistance, xDistance);
CGPoint newCenter;
if (distance < 100 * 100)
{
newCenter.x = Circle01.center.x + (Circle01.frame.size.width * cos(angle));
newCenter.y = Circle01.center.y + (Circle01.frame.size.width * sin(angle));
aView.center = newCenter;
}
}
}
}
Thanks for any help.

It may be easier to offload this to a physics engine such as Box2d. Cocos2d is packaged with Box2d, but you will want the stand alone version since you are only using UIView based animations
_
Option 1
Here is a good, though slightly dated tutorial on using Box2d with UIKit.
http://www.cocoanetics.com/2010/05/physics-101-uikit-app-with-box2d-for-gravity/
Some caveats: if you are using ARC you will need to use the god-awful bridging compiler hints that tell the ARC pre-compiler when your C objects are being put into and out of its control.
For example:
bodyDef.userData = (__bridge void *)physicalView;
UIView *oneView = (__bridge UIView*)b -> GetUserData();
_
Option 2
You can use Chipmunk (which would also require the above "god-awful" bridging to ARC statements). Here is a good tutorial: http://www.alexandre-gomes.com/articles/chipmunk/
_
Option 3 - Easiest (Not Cheapest)
Alternatively, you could use the Objective-Chipmunk library that comes with Chipmunk Pro which does work with ARC. Here is a recent and "to-the-point" tutorial on using Obj-Chipmunk with UIImageViews. This would be the same for any UIView based code. http://www.ayarsanimation.com/frankayars/chipmunk
You can buy Obj-Chipmunk costs $89 when you by an Indie version license for Chipmunk Pro.
http://chipmunk-physics.net/chipmunkPro.php
I am not advocating a paid solution, but it may save you time.

It seems pretty simple, unless I'm missing something. Instead of hard-coding Circle01 in your touchesMoved, do your hit detection based on the view that the user touched. That way, whatever view they are dragging will get priority.

Related

Rotate UIView while dragging

Using a UIPanGestureRecognizer, I'm allowing the user to drag a UIView. What I now want to accomplish is getting the view to adjust its rotation around a specified point DURING dragging.
Examples of this can be found in the Tinder App (when dragging a portrait the images rotate slightly) or in the Path App (when dragging a friends popup up or down it rotates to the side grabbed on).
I must admit that I have very little experience working with UIPanGestureRecognizer, but I assume that it won't affect the answer.
Nevertheless one solution could be to create a timer and make it run while the UIView is being dragged, the timer action would then make sure to update the rotation of the UIView properly and as often as possible.
Another solution could be to subclass UIView and overwrite the drag-function (I do not know what it is called) and simply make it self adjust its rotation if any pivot point is given.
Do the rotation in the following method
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
self.yourView.transform = CGAffineTransformMakeRotation(CGFloat angle);
}
Use CATransform3D
Add
#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)
in .pch file
CATransform3D myTransform = CATransform3DIdentity;
myTransform.m34 = 1.0 / -500;
myTransform = CATransform3DRotate(myTransform, DEGREES_TO_RADIANS(90), 0.0f, 0.0f, 1.0f);
myView.layer.transform = myTransform;
you can go on changing the angle here DEGREES_TO_RADIANS(90) Hope this will help you

Random enemy spawning

I have a game that uses the camera view, and has enemies flying around the screen. You then shoot the enemies using your chosen weapon. At the moment I have 4 UIImageView's, in which the enemies are animated. I am wanting to have multiple instances of each enemy (there are 4) on the screen, and with time more spawn. What is the best way to do this?
This is part of the code to move one enemy using the gyro: (enemyCyborg is the UIImageView)
valueX4 = gyroData.rotationRate.y* 30;
valueY4 = gyroData.rotationRate.x* 40;
int newX4 = (int)(enemyCyborg.center.x +valueY4);
int newY4 = (int)(enemyCyborg.center.y -valueX4);
CGPoint newCenter4 = CGPointMake(newX4, newY4);
enemyCyborg.center = newCenter4;
And they are moving randomly within that:
- (void) moveCyborg {
[UIView animateWithDuration:1.0f animations:^{
int fromNumber = -30;
int toNumber = 60;
int randomNumber = (arc4random()%(toNumber-fromNumber))+fromNumber;
//Move the image view to 100, 100 over 10 seconds.
enemyCyborg.frame = CGRectMake((int)(enemyCyborg.center.x -randomNumber), (int)(enemyCyborg.center.y -randomNumber), enemyCyborg.frame.size.width, enemyCyborg.frame.size.height);
}];
}
This shows the UIImageView again, after it has been hidden from shooting it:
- (void) showCyborg {
enemyCyborg.hidden = NO;
enemyCyborg.center = CGPointMake((arc4random()%SCREEN_HEIGHT),(arc4random()%SCREEN_WIDTH));
enemyCyborg.animationImages = cyborganim;
enemyCyborg.animationDuration = 0.6;
enemyCyborg.animationRepeatCount = 0;
[enemyCyborg startAnimating];
}
You'll want to create objects encapsulating the enemy model and spawn those objects using some kind of enemy generator function. This generator function might be a written as a class function.
Here are some general suggestions:
1) You're using the block-based animation methods. Blocks are a good approach but depending on your performance and the number of enemies and other visual elements you plan to add, you may want to look into using Cocos2d which gives you all sorts of features. Ray Wenderlich has published a great intro to cocos2d tutorial that would get you up and running quickly. Link to that here. Many games for iPhone are written using either cocos2d or Unity.
2) Check in https://gamedev.stackexchange.com/ for further basic examples, if you haven't done so already.

iOS: CGContextRef drawRect does not agree with input

Background : I would like to draw blocks when the user touch up somewhere. If the block is there, I want to erase it. I manage the blocks by using NSMutableArrayto keep track of points where the block should go. Every time user touches, it will determine if the touch place already contained a block or not and manage the array accordingly.
Problem : I got a very weird feedback from this. First of all, everything in the array works as I wanted. The problem comes when the user wanted to erase a block. While the array is maintained correctly, the drawing seems to ignore the change in the array. It will not remove anything but the last dot. And even that flashes toggles on and off when the user clicked elsewhere.
Here is the code :
- (void)drawRect:(CGRect)rect
{
NSLog(#"drawrect current array %#",pointArray);
for (NSValue *pointValue in pointArray){
CGPoint point = [pointValue CGPointValue];
[self drawSquareAt:point];
}
}
- (void) drawSquareAt:(CGPoint) point{
float x = point.x * scale;
float y = point.y * scale;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(context, x, y);
CGContextAddLineToPoint(context, x+scale, y);
CGContextAddLineToPoint(context, x+scale, y+scale);
CGContextAddLineToPoint(context, x, y+scale);
CGContextAddLineToPoint(context, x, y);
CGContextSetFillColorWithColor(context, [UIColor darkGrayColor].CGColor);
CGContextFillPath(context);
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *aTouch = [touches anyObject];
CGPoint point = [aTouch locationInView:self];
point = CGPointMake( (int) (point.x/scale), (int) (point.y/scale));
NSLog(#"Touched at %#", [NSArray arrayWithObject: [NSValue valueWithCGPoint:point]]);
NSValue *pointValue = [NSValue valueWithCGPoint:point];
int i = [pointArray indexOfObject:pointValue];
NSLog(#"Index at %i",i);
if (i < [pointArray count]){
[pointArray removeObjectAtIndex:i];
NSLog(#"remove");
}else {
[pointArray addObject:pointValue];
NSLog(#"add");
}
NSLog(#"Current array : %#", pointArray);
[self setNeedsDisplay];
}
scale is defined as 16.
pointArray is a member variable of the view.
To Test : You can drop this into any UIView and add that to the viewController to see the effect.
Question : How do I get the drawing to agree with the array?
Update + Explanation: I am aware of the cost of this approach but it is only created for me to get a quick figure. It will not be used in the real application, thus, please do not get hung up about how expensive it is. I only created this capability to get a value in NSString (#"1,3,5,1,2,6,2,5,5,...") of a figure I draw. This will become more efficient when I am actually using it with no redrawing. please stick to the question asked. Thank you.
I don't see anywhere where you are actually clearing what you drew previously. Unless you explicitly clear (such as by filling with UIRectFill() - which, as an aside, is a more convenient way to draw rectangles than filling an explicit path), Quartz is going to just draw over your old content, which will cause unexpected behavior on attempts at erasure.
So... what happens if you put at the beginning of -drawRect::
[[UIColor whiteColor] setFill]; // Or whatever your background color is
UIRectFill([self bounds]);
(This is of course horrendously inefficient, but per your comment, I am disregarding that fact.)
(As a separate aside, you probably should wrap your drawing code in a CGContextSaveGState()/CGContextRestoreGState() pair to avoid tainting the graphics context of any calling code.)
EDIT: I always forget about this property since I usually want to draw more complex backgrounds anyway, but you can likely achieve similar results by setting clearsContextBeforeDrawing:YES on the UIView.
This approach seems a little weird to me because every time the touchesEnded method is called you need to redraw (which is an expensive operation) and also need keep track of the squares. I suggest you subclass an UIView and implement the drawRect: method, so the view knows how to draw itself and implement the touchesEnded method in your view controller, where you can check if you have touched a squareView then remove it from view controller's view otherwise create a squareView and add it as subview to the view controller's view.

Draw line or rectangle on cocos2d Layer

Can you let me know what is the best way to draw a line or rectangle on a scene layer using Cocos2d ios4 iphone.
So far have tried Texture2d, but it is more like a paint brush and is not so good. Tried drawing a line using draw method, but previous line disappears on drawing another line.
Basically want to draw multiple horizontal ,vertical, oblique beams. Please suggest. Any code would help a lot .
The code to draw using texture is below:
CGPoint start = edge.start;
CGPoint end = edge.end;
// begin drawing to the render texture
[target begin];
// for extra points, we'll draw this smoothly from the last position and vary the sprite's
// scale/rotation/offset
float distance = ccpDistance(start, end);
if (distance > 1)
{
int d = (int)distance;
for (int i = 0; i < d; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
[brush setPosition:ccp(start.x + (difx * delta), start.y + (dify * delta))];
[brush setScale:0.3];
// Call visit to draw the brush, don't call draw..
[brush visit];
}
}
// finish drawing and return context back to the screen
[target end];
The rendering is not good esp. with oblique lines as the scaling affects the quality.
Cheers
You could create a separate layer and call the draw method like this:
-(void) draw
{
CGSize s = [[Director sharedDirector] winSize];
drawCircle( ccp(s.width/2, s.height/2), circleSize, 0, 50, NO);
It's for a circle but the principle is the same. This is from a project I made a while back and it worked then. Don't know if anything has changed since.
You need to add draw method to your layer:
-(void) draw {
// ...
}
Inside it you can use some openGL like functions and cocos2d wrapper methods for openGL.
Hint: other methods can be called inside draw method.
But keep in mind that using other name for method
containing openGL instructions, that's not called inside draw mentioned above simply won't work.
Even when called from update method or other method used by scheduleUpdate selector.
So you will end up with something like this:
-(void) draw {
glEnable(GL_LINE_SMOOTH);
glColor4ub(255, 0, 100, 255);
glLineWidth(4);
CGPoint verts[] = { ccp(0,200), ccp(300,200) };
ccDrawLine(verts[0], verts[1]);
[self drawSomething];
[self drawSomeOtherStuffFrom:ccp(a,b) to:ccp(c,d)];
[someObject doSomeDrawingAsWell];
}
For more information check out cocos2d-iphone programming guide :
http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:draw_update?s[]=schedule#draw

Problem animating a Parallax Layer in Cocos2d-iPhone

I'm trying to implement my own parallax scrolling in Cocos2d since the built in parallax functionality isn't exactly what I'm looking for (it scales the layers, which is not what I want)
So, my own Parallax Layers overwrite the Layers setPosition method like this
- (void) setPosition:(CGPoint)point
{
[super setPosition:CGPointMake(point.x / pRatio, point.y / pRatio)];
}
pRatio is an int that reduces the amount the layer moves by, thus creating the kind of Parallax effect I'm looking for. And it works perfectly like this.
However, on occasion I need to animate a Layer to a specific position using MoveTo, so I created this method which responds to an NSNotiification
- (void) gotoStartPosition:(NSNotification *)notification
{
CGPoint startPos = CGPointFromString([notification object]);
id moveToStart = [MoveTo actionWithDuration:1 position:startPos];
[self runAction:moveToStart];
}
Which works until I set the pRatio to anything above 1. If the pRatio is higher than 1, then gotoStartPosition makes the layer jump to CGPoint(0,0) and animates into position from there.
I can't figure out at what point the Layers position is being set to 0,0, and I don't understand why it's only doing this when pRatio is higher than 1.
I suspect this is going to be something painfully simple, but I think my brain is fried. Any help, greatly appreciated.
UPDATE: I'm a doofus. What's happening is obvious. The Animation first sets the layers position to it's current position. But I've told the layer to divide any setPosition coordinates by pRatio. So it's not going to CGPoint(0,0) but something like CGPoint(0.032, 0). I've fixed it like this:
- (void) setPosition:(CGPoint)point
{
if(!animating){
[super setPosition:CGPointMake(round(point.x / pRatio), round(point.y / pRatio))];
}else{
[super setPosition:point];
}
}
- (void) gotoStartPosition:(NSNotification *)notification
{
animating = YES;
CGPoint startPos = CGPointFromString([notification object]);
CGPoint pStart = CGPointMake(startPos.x / pRatio, startPos.y / pRatio);
id moveToStart = [MoveTo actionWithDuration:1 position:pStart];
id endAnim = [CallFunc actionWithTarget:self selector:#selector(endAnimation)];
[self runAction:[Sequence actions: moveToStart, endAnim, nil]];
}
- (void) endAnimation
{
animating = NO;
}
Which isn't elegant, but it works. I'm closing this question.