Touching my fixture fails - ios

I'm trying to tap a falling object in my Cocos2d/Box2d app.
Here's my touchesBegan code:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
b2Vec2 bPos = b->GetPosition();
for (b2Fixture *f = b->GetFixtureList(); f; f=f->GetNext()) {
if (f->TestPoint(locationWorld)) {
NSLog(#"hit it!");
}
}
}
I have 2 objects in my world, the ground and the falling object. I have gravity really slow so touching it shoudn't be a problem. What is odd is that my positions seem off by a lot!
In the last run locationWorld (where I tapped) was:
Printing description of locationWorld:
(b2Vec2) locationWorld = {
x = 7.90625
y = 9.875
}
I went through the outer loop twice, once for each body, and got:
Printing description of bPos:
(b2Vec2) bPos = {
x = 3.03125
y = 19.6643
}
Printing description of bPos:
(b2Vec2) bPos = {
x = 8.59375
y = 17.4969
}
What is odd is that the 2nd body, the one reported at (8.59, 17.49) should be the falling object, but the Y coordinate is way off.
This is my first cocos2d/box2d app so I'm sure I'm missing something obvious. I've been googling for awhile and this seems like it should work.
Thanks for any help.
SEE MY ANSWER BELOW, MY CODE HERE IS CORRECT, I'M SETTING UP MY FIXTURES INCORRECTLY.

ok, this is sorta working now but I'm sure I'll have another question from it.
When I ran this above I had created a fairly small dynamic box so the items would pile up on the ground, overlapping each other.
dynamicBox.SetAsBox(.25f, .25f)
This was the cause. When I open this up to something much larger, say:
dynamicBox.SetAsBox((spriteSize.width/PTM_RATIO)/4, (spriteSize.height/PTM_RATIO)/4);
Then it becomes possible with the above code to touch a body. (So my question needs to be remformulated since I do want the objects piling up, overlapping each other.
I also have no idea why I needed to divide the size by 4 to get the items close to each other, that's just plain bothering me.

Related

UIButton exceeds the limitation of boundaries when dragging very fast

I'm trying to implement drag controls with boundary limitation on UIButton.
And I wrote following code for that.
- (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event{
UITouch *touch = [[event touchesForView:btn] anyObject];
CGPoint prevPos = [touch previousLocationInView:btn];
CGPoint pos = [touch locationInView:btn];
float dX = pos.x-prevPos.x;
if (btn.frame.origin.x >= buttonOffPosition && btn.frame.origin.x <= buttonOnPosition) {
btn.center=CGPointMake(btn.center.x+dX, btn.center.y);
NSLog(#"buttonOffPos: %f", buttonOffPosition);
NSLog(#"btn.center.x+dX: %f", btn.center.x+dX);
NSLog(#"buttonOnPos: %f", buttonOnPosition);
}
}
This works almost properly. But only when the button is dragged very fast, it exceeds the limitation buttonOffPosition and buttonOnPosition.
This is the problem I want to resolve. Is there a good way to solve this problem?
Your thoughts and help will be hugely appreciated.
If you want the code to limit your button's location to between your two values you need to check what the final resting point of your button will be after this touch event is processed, not check where it currently is. If it's currently in bounds to be moved, then you move it by say 1000, it will no longer be in bounds at the end but you allow it to go there because you didn't check the end point.
You can do this several ways. The simplest one that comes to my mind is:
- (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event{
UITouch *touch = [[event touchesForView:btn] anyObject];
CGPoint prevPos = [touch previousLocationInView:btn];
CGPoint pos = [touch locationInView:btn];
float dX = pos.x-prevPos.x;
//Get the new origin after this motion
float newXOrigin = btn.frame.origin.x + dX;
//Make sure it's within your two bounds
newXOrigin = MIN(newXOrigin,buttonOnPosition);
newXOrigin = MAX(newXOrigin,buttonOffPosition);
//Now get the new dX value staying in bounds
dX = newXOrigin - btn.frame.origin.x;
btn.center=CGPointMake(btn.center.x+dX, btn.center.y);
}
This method brings up a problem with your finger no longer being inside the button as you are dragging it from one direction to the other, but I will leave that problem to your next question.
EDIT:
Here is how I would make it more readable. This is only my opinion and has no bearing on the way the code works. Outside of just modifying your code, I would create a cached starting point for the button object and do all motion events from that. That way if your button stops moving even though your finger continues, as your finger comes back the button stays with your finger. This current solution, as soon as your finger changes direction the button will start moving again even though your finger is far off the button now. But to do that would be a large code change for you.
- (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event{
//This code can go awry if there is more than one finger on the screen, careful
UITouch *touch = [[event touchesForView:btn] anyObject];
CGPoint prevPos = [touch previousLocationInView:btn];
CGPoint pos = [touch locationInView:btn];
float dX = pos.x-prevPos.x;
//Get the original position of the button
CGRect buttonFrame = btn.frame;
buttonFrame.origin.x += dX;
//Make sure it's within your two bounds
buttonFrame.origin.x = MIN(buttonFrame.origin.x,buttonOnPosition);
buttonFrame.origin.x = MAX(buttonFrame.origin.x,buttonOffPosition);
//Set the button's new frame if we need to
if(buttonFrame.origin.x != btn.frame.origin.x)
btn.frame = buttonFrame
}

Unpinch custom gesture recognizer with three fingers in iOS

I want to make a custom gesture recognizer with three fingers. Which is similar to unpinch gesture recognizer.
All I need is an idea about how to recognize it.
My gesture needs to recognize three fingers with three directions. For example:
I hope images makes sense. I need to make it flexible for any three opposite directions. Thanks in advance. Any help would be appreciated.
I am aware about the subclass methods and I've created custom gestures already with single finger like semicircle, full circle. I need a coding idea about how to handle that.
You need to create a UIGestureRecognizer subclass of your own (let's call it DRThreeFingerPinchGestureRecognizer) and in it to implement:
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
These methods are called when touches are accepted by the system and possibly before they are sent to the view itself (depending on how you setup the gesture recognizer). Each of these methods will give you a set of touches, for which you can check the current location in your view and previous location. Since pinch gesture is relatively very simple, this information is enough for you to test if the user is performing a pinch, and fail the test (UIGestureRecognizerStateFailed). If state was not failed by – touchesEnded:withEvent:, you can recognize the gesture.
I say pinch gestures are simple, because you can easily track each touch and see how it moves compared to other touches and itself. If a threshold of an angle is passed and broken, you fail the test, otherwise you allow it to continue. If touches do not move in separate angles to each other, you fail the test. You will have to play with what angles of the vectors are acceptable, because 120 degrees are not optimal for the three most common fingers (thumb + index + middle fingers). You may just want to check that the vectors are not colliding.
Make sure to read the UIGestureRecognizer documentation for an in-depth look at the various methods, as well as subclassing notes.
Quick note for future readers: the way you do an unpinch/pinch with three fingers is add the distances ab,bc,ac.
However if your graphics package just happens to have on hand "area of a triangle" - simply use that. ("It saves one whole line of code!")
Hope it helps.
All you need to do is track:
the distance between the three fingers!
Simply add up "every" permutation
(Well, there's three .. ab, ac and cb. Just add those; that's all there is to it!)
When that value, say, triples from the start value, that's an "outwards triple unpinch".
... amazingly it's that simple.
Angles are irrelevant.
Footnote if you want to be a smartass: this applies to any pinch/unpinch gesture, 2, 3 fingers, whatever:
track the derivative of the sum-distance (I mean to say, the velocity) rather than the distance. (Bizarrely this is often EASIER TO DO! because it is stateless! you need only look at the previous frame!!!!)
So in other words, the gesture is trigger when the expansion/contraction VELOCITY of the fingers reaches a certain value, rather than a multiple of the start value.
More interesting footnote!
However there is a subtle problem here: whenever you do anything like this (any platform) you have to be careful to measure "on the glass".
IF You are just doing distance (ie, my first solution above) of course everything cancels out and you can just say "if it doubles" (in pixels -- points -- whatever the hell). BUT if you are doing velocity as part of the calculation in any gesture, then somewhat surprisingly, you have to literally find the velocity in meters per second in the real world, which sounds weird at first! Of course you can't do this exactly (particularly with android) coz glass sizes vary somewhat, but you have to get close to it. Here is a long post discussing this problem http://answers.unity3d.com/questions/292333/how-to-calculate-swipe-speed-on-ios.html In practice you usually have to make do with "screen-widths-per-second" which is pretty good. (But this may be vastly different on phones, large tablets, and these days "surface" type things. on your whole iMac screen, 0.1 screenwidthspersecond may be fast, but on an iPhone that is nothing, not a gesture.)
Final footnote! I simply don't know if Apple use "distance multiple" or "glass velocity" in their gesture recognition, or also likely is some subtle mix. I've never read an article from them commenting on it.
Another footnote! -- if for whatever reason you do want to find the "center" of the triangle (I mean the center of the three fingers). This is a well-travelled problem for game programmers because, after all, all 3D mesh is triangles.
Fortunately it's trivial to find the center of three points, just add the three vectors and divide by three! (Confusingly this even works in higher dimensions!!)
You can see endless posts on this issue...
http://answers.unity3d.com/questions/445442/calculate-uv-at-center-of-triangle.html
http://answers.unity3d.com/questions/424950/mid-point-of-a-triangle.html
Conceivably, if you were incredibly anal, you would want the "barycenter" which is more like the center of mass, just google if you want that.
I think track angles is leading you down the wrong path. I think it's likely a more flexible and intuitive gesture if you don't constrain it based on the angles between the fingers. It'll be less error prone if you just deal with it as a three-fingered pinch regardless of how the fingers move relative to each other. This is what I'd do:
if(presses != 3) {
state = UIGestureRecognizerStateCancelled;
return;
}
// After three fingers are detected, begin tracking the gesture.
state = UIGestureRecognizerStateBegan;
central_point_x = (point1.x + point2.x + point3.x) / 3;
central_point_y = (point1.y + point2.y + point3.y) / 3;
// Record the central point and the average finger distance from it.
central_point = make_point(central_point_x, central_point_y);
initial_pinch_amount = (distance_between(point1, central_point) + distance_between(point2, central_point) + distance_between(point3, central_point)) / 3;
Then on each update for touches moved:
if(presses != 3) {
state = UIGestureRecognizerStateEnded;
return;
}
// Get the new central point
central_point_x = (point1.x + point2.x + point3.x) / 3;
central_point_y = (point1.y + point2.y + point3.y) / 3;
central_point = make_point(central_point_x, central_point_y);
// Find the new average distance
pinch_amount = (distance_between(point1, central_point) + distance_between(point2, central_point) + distance_between(point3, central_point)) / 3;
// Determine the multiplicative factor between them.
difference_factor = pinch_amount / initial_pinch_amount
Then you can do whatever you want with the difference_factor. If it's greater than 1, then the pinch has moved away from the center. If it's less than one, it's moved towards the center. This will also give the user the ability to hold two fingers stationary and only move a third to perform your gesture. This will address certain accessibility issues that your users may encounter.
Also, you could always track the incremental change between touch move events, but they won't be equally spaced in time and I suspect you'll have more troubles dealing with it.
I also apologize for the pseudo-code. If something isn't clear I can look at doing up a real example.
Simple subclass of UIGestureRecognizer. It calculates the relative triangular center of 3 points, and then calculates the average distance from that center, angle is not important. You then check the average distance in your Gesture Handler.
.h
#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface UnPinchGestureRecognizer : UIGestureRecognizer
#property CGFloat averageDistanceFromCenter;
#end
.m
#import "UnPinchGestureRecognizer.h"
#implementation UnPinchGestureRecognizer
-(CGPoint)centerOf:(CGPoint)pnt1 pnt2:(CGPoint)pnt2 pnt3:(CGPoint)pnt3
{
CGPoint center;
center.x = (pnt1.x + pnt2.x + pnt3.x) / 3;
center.y = (pnt1.y + pnt2.y + pnt3.y) / 3;
return center;
}
-(CGFloat)averageDistanceFromCenter:(CGPoint)center pnt1:(CGPoint)pnt1 pnt2:(CGPoint)pnt2 pnt3:(CGPoint)pnt3
{
CGFloat distance;
distance = (sqrt(fabs(pnt1.x-center.x)*fabs(pnt1.x-center.x)+fabs(pnt1.y-center.y)*fabs(pnt1.y-center.y))+
sqrt(fabs(pnt2.x-center.x)*fabs(pnt2.x-center.x)+fabs(pnt2.y-center.y)*fabs(pnt2.y-center.y))+
sqrt(fabs(pnt3.x-center.x)*fabs(pnt3.x-center.x)+fabs(pnt3.y-center.y)*fabs(pnt3.y-center.y)))/3;
return distance;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([touches count] == 3) {
[super touchesBegan:touches withEvent:event];
NSArray *touchObjects = [touches allObjects];
CGPoint pnt1 = [[touchObjects objectAtIndex:0] locationInView:self.view];
CGPoint pnt2 = [[touchObjects objectAtIndex:1] locationInView:self.view];
CGPoint pnt3 = [[touchObjects objectAtIndex:2] locationInView:self.view];
CGPoint center = [self centerOf:pnt1 pnt2:pnt2 pnt3:pnt3];
self.averageDistanceFromCenter = [self averageDistanceFromCenter:center pnt1:pnt1 pnt2:pnt2 pnt3:pnt3];
self.state = UIGestureRecognizerStateBegan;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if ([touches count] == 3)
{
NSArray *touchObjects = [touches allObjects];
CGPoint pnt1 = [[touchObjects objectAtIndex:0] locationInView:self.view];
CGPoint pnt2 = [[touchObjects objectAtIndex:1] locationInView:self.view];
CGPoint pnt3 = [[touchObjects objectAtIndex:2] locationInView:self.view];
CGPoint center = [self centerOf:pnt1 pnt2:pnt2 pnt3:pnt3];
self.averageDistanceFromCenter = [self averageDistanceFromCenter:center pnt1:pnt1 pnt2:pnt2 pnt3:pnt3];
self.state = UIGestureRecognizerStateChanged;
return;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
self.state = UIGestureRecognizerStateEnded;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
self.state = UIGestureRecognizerStateFailed;
}
#end
implementation of Gesture, I have a max avg distance set to start, and then a minimum to end, you can also check during changed as well:
-(IBAction)handleUnPinch:(UnPinchGestureRecognizer *)sender
{
switch (sender.state) {
case UIGestureRecognizerStateBegan:
//If you want a maximum starting distance
self.validPinch = (sender.averageDistanceFromCenter<75);
break;
case UIGestureRecognizerStateEnded:
//Minimum distance from relative center
if (self.validPinch && sender.averageDistanceFromCenter >=150) {
NSLog(#"successful unpinch");
}
break;
default:
break;
}
}

Converting Points to Node in Sprite-Kit

I've been working on this problem for a few days now and I just can't seem to figure it out. I've done a ton of searching for an answer, and I've seen hints that maybe the problem is with Sprite-Kit itself, so I am debating moving to Cocos2D and starting over. But, I hope that someone here can help me.
I have a basic camera node called _world that I am using to pan around the world and to zoom. The panning and zooming works fine, but I've been trying to get the world node to move to the center of where the pinch occurs. It sort of works, but converting the point to a position in the world node seems to be the problem. Here is my code:
I use this code to convert a scene point to a world point elsewhere in the code and it works fine:
CGPoint positionInScene = [touch locationInNode:self];
CGPoint locationInWorld = [self.scene convertPoint:positionInScene toNode:_world];
But later I try to do this using the middle point of the two points found in the pinch, and it doesn't seem to convert properly:
originalLocationOne = [sender locationOfTouch:0 inView:self.view];
originalLocationTwo = [sender locationOfTouch:1 inView:self.view];
//I do this because SpriteKit doesn't have a ccpAdd method
originalAddedMidPoint = CGPointMake(originalLocationOne.x + originalLocationTwo.x, originalLocationOne.y + originalLocationTwo.y);
//same thing here but for ccpMidPoint
originalMidPoint = CGPointMake(originalAddedMidPoint.x * 0.5f, originalAddedMidPoint.y * 0.5f);
_newWorldPos = [self convertPoint:originalMidPoint toNode:_world];
I would really appreciate it if someone can point me in the right direction! Thank you so much!
EDIT: I've been working on this problem some more, and certainly something weird is happening but I still can't tell what.
Here is my complete touchesbegan method:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
_myTouches = [[NSMutableArray alloc]init];
for (UITouch *touch in touches) {
[_myTouches addObject:touch];
}
if (_myTouches.count > 1) {
UITouch *touch1 = [_myTouches objectAtIndex:0];
UITouch *touch2 = [_myTouches objectAtIndex:1];
CGPoint touch1Location = [touch1 locationInView:self.view];
CGPoint touch2Location = [touch2 locationInView:self.view];
NSLog(#"tpoint1 = %#, tpoint2 = %#", NSStringFromCGPoint(touch1Location), NSStringFromCGPoint(touch2Location));
CGPoint touchAddedPoint = CGPointMake(touch1Location.x + touch2Location.x, touch1Location.y + touch2Location.y);
CGPoint touchMidPoint = CGPointMake(touchAddedPoint.x * 0.5f, touchAddedPoint.y * 0.5f);
NSLog(#"touch mid point = %#", NSStringFromCGPoint(touchMidPoint));
CGPoint newWorldPoint = [self convertTouchPointToWorld:touchMidPoint];
//camera needs to be offset to work properly
CGPoint alteredWorldPoint = CGPointMake(-newWorldPoint.x * 0.75f, -newWorldPoint.y * 0.75f);
_firstTouch = alteredWorldPoint;
_tempWorldLocation = _firstTouch;
_worldMovedForUpdate = YES;
}
}
Here is the method that I extracted to convert the position to the world node:
-(CGPoint) convertTouchPointToWorld:(CGPoint)touchLocation {
CGPoint firstLocationInWorld = [self.scene convertPoint:touchLocation toNode:_world];
NSLog(#"inside converting method %#", NSStringFromCGPoint(firstLocationInWorld));
return firstLocationInWorld;
}
Here is the entire method that places a building in the game map based on its position in the world map:
-(void)selectNodeForTouch:(CGPoint)touchLocation {
SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:touchLocation];
//NSLog(#"node name is = %#", touchedNode.name);
_selectedNode = touchedNode;
if ([[touchedNode name] isEqualToString:#"hudswitch1"]) {
if([self.theGame getMovesLeft] == 0){
_hudNeedsUpdate = YES;
_turnNeedsUpdating = YES;
}
}
if ([self.theGame getMovesLeft] > 0) {
[self handleButtonsForTouch:touchedNode];
}
//this inserts the tile at the location in world node
NSLog(#"touchLocation.x = %f, touchlocation.y = %f", touchLocation.x, touchLocation.y);
if ([[touchedNode name] isEqualToString:#"tile"] && _selectedBuildingType != 0) {
//CGPoint locationInWorld = [self.scene convertPoint:touchLocation toNode:_world];
CGPoint locationInWorld = [self convertTouchPointToWorld:touchLocation];
CGPoint gameLocation = [self convertWorldPointToGamePoint:locationInWorld];
NSLog(#"locationWorld.x = %f, locationWorld.y = %f", locationInWorld.x, locationInWorld.y);
if(![self.theGame isBuildingThere:gameLocation] && [self.theGame isValidLocation:gameLocation]) {
[self updateActiveTilePos:locationInWorld];
}
}
}
Inserting a tile into the map works fine. It gets the world location and then divides it by the tile size to figure out the position to place the tile in the world map. It is always inserting a tile in the proper place.
However when I use the middle point of the two touches in a pinch and convert it to a world point, it returns a value but the value is off by a large amount, and different amounts depending on the distances from the center...
Maybe I just need to figure out how to offset the camera properly? Thanks again for any help with this problem, it is driving me crazy!
I had the same problem.
It is counterintuitive but point conversion is not smart.
It does not convert from one node to another node.
It can convert to scene coordinates and to node coordinates from scene coordinates.
It does not work from one node to another node.
What you need to do is convert point from node and then convert point to node. After two these calls coordinates will be in the node you need.

Cocos2d ccDrawLine performance issue

I use cocos2d 2.0 and Xcode 4.5. I am trying to learn how to draw a line. I can draw a line but after I drew few lines a serious performance issue occurs on Simulator.
Simulator starts to freeze, draws lines very very slowly and worst of all ,I guess because of -(void)draw is called every frame, the label on the screen becomes bold
before lines :
after lines;
I use following code :
.m
-(id) init
{
if( (self=[super init])) {
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Simple Line Demo" fontName:#"Marker Felt" fontSize:32];
label.position = ccp( 240, 300 );
[self addChild: label];
_naughtytoucharray =[[NSMutableArray alloc ] init];
self.isTouchEnabled = YES;
}
return self;
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
BOOL isTouching;
// determine if it's a touch you want, then return the result
return isTouching;
}
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [ touches anyObject];
CGPoint new_location = [touch locationInView: [touch view]];
new_location = [[CCDirector sharedDirector] convertToGL:new_location];
CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
// add my touches to the naughty touch array
[_naughtytoucharray addObject:NSStringFromCGPoint(new_location)];
[_naughtytoucharray addObject:NSStringFromCGPoint(oldTouchLocation)];
}
-(void)draw
{
[super draw];
ccDrawColor4F(1.0f, 0.0f, 0.0f, 100.0f);
for(int i = 0; i < [_naughtytoucharray count]; i+=2)
{
CGPoint start = CGPointFromString([_naughtytoucharray objectAtIndex:i]);
CGPoint end = CGPointFromString([_naughtytoucharray objectAtIndex:i+1]);
ccDrawLine(start, end);
}
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
ManageTraffic *line = [ManageTraffic node];
[self addChild: line z:99 tag:999];
}
I saw few Air Traffic Control games such as Flight Control, ATC Mania works really well.
Does this performance issue occur because of CCDrawLine/UITouch *touch or it is a common issue?
What Flight Control, ATC Mania might be using for line drawing?
Thanks in advance.
EDIT::::
OK I guess problem is not ccDrawLine, problem is I call ManageTraffic *line = [ManageTraffic node]; every time touch ends it calls init of node so it overrides scene
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
ManageTraffic *line = [ManageTraffic node];
[self addChild: line z:99 tag:999];
}
There's three things going on:
You assess performance on the Simulator. Test it on a device as Ben says.
You store points as strings and convert strings back to CGPoint. That is terribly inefficient.
ccDrawLine is not exactly efficient. For a couple dozen line segments it's ok. In your case maybe not (see below).
For #2, create a point class with only a CGPoint property and use that to store points in the array. Removes the string conversion or packing into NSData.
For #3 make sure that new points are only added if the new point is at least n points away from the previous point. For example a distance of 10 should reduce the number of points while still allowing for relatively fine line details.
Also regarding #3, I notice you add both current and previous point to the array. Why? You only need to add the new point, and then draw points from index 0 to 1, from 1 to 2, and so on. You only have to test for the case where there is only 1 point. The previous touch event's location is always the next touch event's previousLocation. So you're storing twice as many points as you need to.

RayCasting in Box2D?

i am creating a project in which i have random Box2d bodies. Now i am drawing a line on basis of TouchesMoved by user in the DRAW method. i need to use the RayCasting method of Box2d to check for intersection between that line and the Box2D bodies.
i am using the following code for it in my Draw method
for(int i = 0; i < [pointTouches count]; i+=2)
{
CGPoint startPoint = CGPointFromString([pointTouches objectAtIndex:i]);
CGPoint endPoint = CGPointFromString([pointTouches objectAtIndex:i+1]);
ccDrawLine(startPoint, endPoint);
b2Vec2 start=[self toMeters:startPoint];
b2Vec2 end=[self toMeters:endPoint];
[self checkIntersectionbtw:start:end];
}
-(void)checkIntersectionbtw:(b2Vec2)point1:(b2Vec2)point2
{
RaysCastCallback callback;
world->RayCast(&callback, point1,point2);
if (callback.m_fixture)
{
NSLog(#"intersected");
checkPoint = true;
}
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint currentTouchArea = [myTouch locationInView:[myTouch view]];
CGPoint lastTouchArea = [myTouch previousLocationInView:[myTouch view]];
currentTouchArea = [[CCDirector sharedDirector] convertToGL:currentTouchArea];
lastTouchArea = [[CCDirector sharedDirector] convertToGL:lastTouchArea];
[pointTouches addObject:NSStringFromCGPoint(currentTouchArea)];
[pointTouches addObject:NSStringFromCGPoint(lastTouchArea)];
}
but the callback only tells the intersection when the line drawn completely passes the bodies. when user starts from some point outside and leaves the point inside the box2d body the callback doesn't say the line intersected. what am i possibly doing wrong??
Are you drawing a line or a curve? For a line, I assume that you only use the first point and the last point detected. For a curve, you use all the points detected to form the curve that you are drawing.
If you are drawing a curve then think I understand the problem. You are calculating the intersection line using the consecutive points detected by the touch system. These consecutive points are very close to each other which makes very small ray casts, and what might be happening is that you might be casting the line with a starting and ending point inside the balls, which might issue a negative collision.
If the objective is to detect if you are touching the balls, I suggest using a sensor maintained under the touch, and then checking a collision with this code in your update method:
for (b2ContactEdge* ce = sensorbody->GetContactList(); ce; ce = ce->next)
{
b2Contact* c = ce->contact;
if(c->IsTouching())
{
const b2Body* bodyA = c->GetFixtureA()->GetBody();
const b2Body* bodyB = c->GetFixtureB()->GetBody();
const b2Body* ballBody = (bodyA == sensorbody)?bodyB:bodyA;
...
}
}
If you really want to use a raycast, then I suggest saving a few consecutive points and make a vector with them, to avoid the small ray cast.
edit: Sorry, The example I wrote is in C++, but you should be able to find an equivalent for Objective-C.
Box2D's raycasts ignore any 'back' facing edges they hit, so if the ray starts inside a fixture, that fixture will be ignored. Simplest thing to do is cast the same ray in both directions.

Resources