CGPointEqualToPoint not working - ios

I am making an app in which I want to have some thing happen If an image moves over another point. such as
first I have an image moving horizontally across the screen,'
self.ball.center = CGPointMake(self.ball.center.x + self.gravity.x / 8.0 * 200.0, 9);
then when it gets to a certain place another image moves down from that spot.
CGPoint a = CGPointMake(9, 9);
if (CGPointEqualToPoint(ball.center,a)) {
balla.hidden = NO;
self.balla.center = CGPointMake(self.balla.center.x , (self.balla.center.y)- (self.gravity.y / 8.0 * 200.0));
}
the first time it works ok but when I put in the next statement to move another image down from another spot nothing happens.
CGPoint b = CGPointMake(86, 9);
if (CGPointEqualToPoint(ball.center,b)) {
ball2a.hidden = NO;
self.ball2a.center = CGPointMake(self.ball2a.center.x , (self.ball2a.center.y)- (self.gravity.y / 8.0 * 200.0));}
Any ideas as to why this isn't working

If you're moving the ball by adding some floating-point value offset, you might be "skipping over" the point b - you may never hit b, but rather appear slightly before it and then slightly after it.
Rather than testing if you're "equal" to the point, you could be better off comparing the distance between the ball and the point and seeing if it is inside some small radius. A simple euclidean distance could work.
CGFloat euclid_dist(CGPoint a, CGPoint b)
{
return sqrt((b.x-a.x)*(b.x-a.x) + (b.y-a.y)*(b.y-a.y));
}
You could then use this to see if you've "hit" the point:
if (euclid_dist(ball.center, b) < 0.1)
{
// React appropriately
}
In general it's problematic to test for equality between floating point numbers.

Related

How to disable multi-touch in drawing using flutter

This question is purely based on GestureDetector flutter.
For Example:
In Application, GestureDetector class is implemented so multitouch is supported by default, now we need to disable the multitouch so what would be the best way to do it?. Otherwise in a drawing app using GestureDetector in flutter cause multi touch issue.
So how to disable multitouch in gesture detector?
I faced the same problem but I solve it by measuring the distance between two points.
The rule of how to measure the distance between two points
// Convert a rule to the code
double distanceBetweenTwoPoints(double x1,double y1 ,double x2, double y2){
double x = x1 - x2;
x = x * x;
double y = y1 - y2;
y = y * y;
double result = x + y;
return sqrt(result);
}
First of all, declare two variables with their values
// These two variables are to save the previous points
var fingerPostionY = 0.0,fingerPostionX = 0.0;
Then inside the onPanUpdate method, I took two points to calculate the distance between them. After that, I made a comparison, if the distance was large (e.g. 50) then there are many fingers, so I ignore it otherwise it will be just one finger on the screen.
onPanUpdate: (details) {
if (fingerPostionY < 1.0){
// assigen for the first time to compare
fingerPostionY = details.globalPosition.dy;
fingerPostionX = details.globalPosition.dx;
}else{
// they use a lot of fingers
double distance = distanceBetweenTwoPoints(details.globalPosition.dx,details.globalPosition.dy,
fingerPostionX,fingerPostionY);
// the distance between two fingers must be above 50
// to disable multi touch
if(distance > 50)
return;
// update to use it in the next comparison
fingerPostionY = details.globalPosition.dy;
fingerPostionX = details.globalPosition.dx;
}
// the code of drawing
setState(() {
RenderBox renderBox = context.findRenderObject();
points.add(TouchPoints(
points: renderBox.globalToLocal(details.globalPosition),
paint: Paint()
..strokeCap = strokeType
..isAntiAlias = true
..color = activeColor.withOpacity(opacity)
..strokeWidth = strokeWidth));
});
},
IMPORTANT NOTES:
Inside onPanEnd method, you must write this line, because it
means the finger is up now
fingerPostionY = 0.0;
Still there is some performance issue not solved yet in the drawing code
EDIT:
I enhanced the performance by using path.
You can see my code on the GitHub:
free painting on flutter

CorePlot allow user drag rotate PieChart

(As for I got the solution now, it is being shared at the bottom)
Fact is I have been struggling a while about this and I believe quite a lot of discussions I found are related to older versions of CorePlot or unanswered.
Firstly, I am using CorePlot 1.5.1.
I am able to plot a PieChart already and now I would like the user to be able to rotate it by dragging on the screen ( doesn't really matter touch directly the pieChart or the host View).
Using these delegates at the moment:
#interface MyChartViewController : UIViewController<CPTPieChartDataSource,CPTPlotSpaceDelegate,CPTPieChartDelegate>
Got a hostView,
#property (strong, nonatomic) IBOutlet CPTGraphHostingView *hostView;
Made a graph, set as, self.hostView.hostedGraph = graph
and made a PieChart, put into the graph, [graph addPlot:self.mainPieChart];
(I set the pieChart with a strong property to let me refer it anytime)
So, here is my first attempt, and fact is, it is responding, (though not in a desirable way)
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *) self.hostView.hostedGraph.defaultPlotSpace;
[plotSpace setDelegate:self];
(only works by setting plotSpace delegate to self, not sure why, i guess it's about finding a way to receive user's interaction, anyway, then I overwrite these two functions)
Using this value:
static float deltaAngle;
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDownEvent:(UIEvent *)event atPoint:(CGPoint)point
{
float dx = point.x - self.mainPieChart.centerAnchor.x;
float dy = point.y - self.mainPieChart.centerAnchor.y;
deltaAngle = atan2(dy,dx);
return YES;
}
This, in order to save the first touching point
Then sense the dragging do use the difference to make the rotation
( at least I wanted so )
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDraggedEvent:(UIEvent *)event atPoint:(CGPoint)point
{
int x = self.mainPieChart.centerAnchor.x;
int y = self.mainPieChart.centerAnchor.y;
float dx = point.x - x;
float dy = point.y - y;
double a = atan2(dx,dy);
float angleDifference = deltaAngle - a;
self.mainPieChart.startAngle = -angleDifference;
return YES;
}
And here is an image about it, though i think I covered most of the details already.
http://postimg.org/image/bey0fosqj/
It is in landscape mode though.
Fact is I think this would be the most appropriate function to call, but somehow I cannot call it out (pretty sure I set self.mainPieChart delegate/ datasource to self already)
-(BOOL)pointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)interactionPoint{
(after further testing)
Interesting, after trying to print out different values, by the shouldHandlePointingDevice function (simply clicking), I think i got some ideas now.
the self.mainPieChart.centerAnchor.x / y values always return 0.5 (both)
However, point x, point y are returning values vary from 1-500+,
it seems more like I am comparing two things, though they are on top of each other, from different perspective.
Likely the PlotSpace set delegate part messed that up.
============================================================
So, as for now I still don't know how to call -(BOOL)pointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)interactionPoint{, I tried to put it into a if loop like
if([self.mainPieChart pointingDeviceDownEvent:event atPoint:self.mainPieChart.centerAnchor] == YES)
under my touched function but nothing happened, never mind.
Back to the point, my current solution works well now, even after applying padding.
float x = (self.hostView.bounds.size.width + self.hostView.hostedGraph.paddingLeft)*self.mainPieChart.centerAnchor.x;
float y = self.hostView.bounds.size.height * self.mainPieChart.centerAnchor.y;
float dx = point.x - x;
float dy = point.y - y;
double a = atan2(dx,dy);
these lines are all same for both press / drag functions, as for drag function,
float angleDifference = deltaAngle - a;
self.mainPieChart.startAngle = angleDifference;
are added before the end
However, the case is slightly different when the Pie Chart is not at the middle, or, in other words, the graph holding the Pie Chart is padded.
( my example somehow is mid centre just to make it easy)
you simply have to mortify the x y float value above, it's easier than I expected.
For example if I have,
graph.paddingLeft = -300.0f;
the value of float x in both press/drag will become
float x = (self.hostView.bounds.size.width + self.hostView.hostedGraph.paddingLeft)*self.mainPieChart.centerAnchor.x;
The pie chart centerAnchor is given as fractions of the width and height. Be sure to multiply the anchor values by the corresponding dimension of the graph before computing dx and dy.

Smooth snake movement

This is language-agnostic question, more about model of my game.
I have a snake game with elements, but I move the elements smoothly, they don't just move 1 block each time, but instead they move some amount of pixels every frame.
I have an update loop that calculates the positions of the element, but I am stuck on correct calculations.
I have heading for each element:
typedef NS_ENUM(int, kElementHeading)
{
kElementHeadingNorth = 1,
kElementHeadingSouth,
kElementHeadingEast,
kElementHeadingWest
};
I also have velocity (x, y) that determines in what direction snake is going. I have problem with snake movement, because my elements are in wrong positions. I managed to localize the thing for 2 elements, but my solution fails on more elements.
First solution I tried is to save point of rotation where the head changes direction. This worked, but due to different circumstances element can move different amount of pixels each turn. Often the element would skip the point. I tried increasing the zone where it should rotate, but it adds up error. I tried fixing this error, but element would still separate from snake (quite often).
On the second try I decided to keep the snake head in center of the screen and move the world around it. It worked good for 2 elements, as I just smoothly move the next element to desired position relatively to head. But this fails badly on more elements. If you make fast turns they start dancing and not following the path.
Third thing that I tried is leaving a path for other elements to follow. But that didn't work because I intend to keep my snake on center of the screen and technically it never moves to create a path.
I'm looking to replicate the movement pattern like in Nimble Quest (or any snake).
How should I implement snake elements moving to have no errors?
Here is my code for the first method, problem with it is that often the elements would fall off. The code is pretty self-explanatory. Rotation points are the places where to change direction.
CFTimeInterval delta = self.lastTime - currentTime;
CGPoint currentPosition = self.playerSnake.head.sprite.position;
CGPoint velocity = self.playerSnake.velocity;
self.playerSnake.head.sprite.position = CGPointMake(currentPosition.x + velocity.x * delta * CONSTANTSPEEDFACTOR , currentPosition.y + velocity.y * delta * CONSTANTSPEEDFACTOR);
for (SnakeElement *element in self.playerSnake.elements) {
CGPoint currentPositionE = element.sprite.position;
CGPoint velocityE = element.velocity;
element.sprite.position = CGPointMake(currentPositionE.x + velocityE.x * delta * CONSTANTSPEEDFACTOR , currentPositionE.y + velocityE.y * delta * CONSTANTSPEEDFACTOR);
}
BOOL markToDelete = NO;
NSDictionary *deleteDictionary;
for (NSDictionary *dict in self.playerSnake.rotationPoints) {
CGPoint positionCoordinate = CGPointFromString(dict[#"position"]);
CGPoint velocityNew = CGPointFromString(dict[#"velocity"]);
double newAngle = [dict[#"angle"] doubleValue];
for (SnakeElement *element in self.playerSnake.elements) {
int xDifference = element.sprite.position.x - positionCoordinate.x;
int yDifference = element.sprite.position.y - positionCoordinate.y;
if ((xDifference > -2 && xDifference < 2) && (yDifference > -2 && yDifference < 2) ) {
element.velocity = velocityNew;
element.sprite.position = CGPointMake(element.sprite.position.x + xDifference, element.sprite.position.y + yDifference);
SKAction *action = [SKAction rotateToAngle:newAngle duration:0.2 shortestUnitArc:YES];
[element.sprite runAction:action];
if ([element isEqual:[self.playerSnake.elements lastObject]]) {
markToDelete = YES;
deleteDictionary = dict;
}
}
}
}
[self.playerSnake.rotationPoints removeObject:deleteDictionary];
If I try increase the catch zone for the turning point, the elements tend to fall off more often then when it is 1 or 2 pixels wide. I'm not sure why this happens.
This is what I was suggesting you do in the comments in terms of handling your turning on points :
1.. calculate the distance that the element should move that frame based on speed and your elapsed time since last frame. (delta)
2.. calculate distance from element's current position to the turn point. This is the beforeDistance I spoke of in the comments
3.. calculate the distance the element should move towards the NEW target turning point AFTER the turn
afterDistance = distanceToMoveThisFrame - beforeDistance
4.. Calculate the new position for your element, starting at the current turning point towards the next target turning point of the element using afterDistance
If you follow this logic, you will NEVER overshoot or undershoot the turning point.

CCMoveTo causing jumps

I am making a game using Cocos2D and Kobold2D. In my game, I have a ship that I want to move to where the player taps, using this code:
KKInput * input = [KKInput sharedInput];
CGPoint tap = [input locationOfAnyTouchInPhase:KKTouchPhaseBegan];
if (tap.x != 0 && tap.y != 0)
{
[ship stopAllActions]; // Nullifies previous actions
int addedx = tap.x - ship.position.x;
int addedy = tap.y - ship.position.y;
int squaredx = pow(addedx, 2);
int squaredy = pow(addedy, 2);
int addedSquares = squaredx + squaredy;
int distance = pow(addedSquares, 0.5);
[ship runAction: [CCMoveTo actionWithDuration:distance/100 position:tap]];//makes ship move at a constant speed
}
The ship generally moves as I expect it to. However, if I tap near the ship, instead of smoothly moving to the tap location, it jumps to that location. How do I fix this?
Your logic is right but your calculation is lack precision.
Especially when you calc the fly time as distance/100. That means when distance < 100 the result is 0, so the ship is jumping to the destination.
Basically, you should use float instead of int when dealing with positions in cocos2d. And there are couple functions can do the work nicely.
You may change your code to this:
[ship stopAllActions]; // Nullifies previous actions
float distance = ccpDistance(tap, ship.position);
[ship runAction: [CCMoveTo actionWithDuration:distance/100.0f position:tap]];
First of All you can use the ccpDistance function of cocos2d to calculate distance between two point.
And it jumps because if u tap too near the distance is too small suppose 2 or 10 ... the you are dividing it on 100
e.g 5/100 = 0.05 which is too low that's why it jumps.
You need to handle it manually i think some thing like if(speed<1) speed++; adding 1 to speed. shortcut solution :)

Animating rotation changes of UIImageView

I'm making an app that (among other things) displays a simplified compass image that rotates according to the device's rotation. The problem is that simply doing this:
float heading = -1.0f * M_PI * trueHeading / 180.0f; //trueHeading is always between 0 and 359, never 360
self.compassNeedle.transform = CGAffineTransformMakeRotation(heading);
inside CLLocationManager's didUpdateHeading method makes the animation ugly and choppy.
I have already used Instruments to find out whether its simply my app not being able to render at more than 30-48 fps, but that's not the case.
How can I smooth out the image view's rotation so that it's more like Apple's own Compass app?
Instead of using the current instant value, try using the average of the last N values for the true heading. The value may be jumping around a lot in a single instant but settle down "in the average".
Assuming you have a member variable storedReadings which is an NSMutableArray:
-(void)addReading(float):newReading
{
[storedReadings addObject:[NSNumber numberWithFloat:newReading]];
while([storedReadings count] > MAX_READINGS)
{
[storedReadings removeObjectAtIndex:0];
}
}
then when you need the average value (timer update?)
-(float)calcReading
{
float result = 0.0f;
if([storedReadings count] > 0)
{
foreach(NSNumber* reading in storedReadings)
{
result += [reading floatValue];
}
result /= [storedReadings count];
}
return result;
}
You get to pick MAX_READINGS a priori.
NEXT LEVEL(S) UP
If the readings are not jumping around so much but the animation is still choppy, you probably need to do something like a "smooth" rotation. At any given time, you have the current angle you are displaying, theta (store this in your class, start it out at 0). You also have your target angle, call it target. This is the value you get from the smoothed calcReading function. The error is defined as the difference between the two:
error = target-theta;
Set up a timer callback with a period of something like 0.05 seconds (20x per second). What you want to do is adjust theta so that the error is driven towards 0. You can do this in a couple of ways:
thetaNext += kProp * (target - theta); //This is proportional feedback.
thetaNext += kStep * sign(target-theta); // This moves theta a fixed amount each update. sign(x) = +1 if x >= 0 and -1 if x < 0.
The first solution will cause the rotation to change sharply the further it is from the target. It will also probably oscillate a little bit as it swings past the "zero" point. Bigger values of kProp will yield faster response but also more oscillation. Some tuning will be required.
The second solution will be much easier to control...it just "ticks" the compass needle around each time. You can set kStep to something like 1/4 degree, which gives you a "speed" of rotation of about (1/4 deg/update) * (20 updates/seconds) = 5 degrees per second. This is a bit slow, but you can see the math and change kStep to suit your needs. Note that you may to "band" the "error" value so that no action is taken if the error < kStep (or something like that). This prevents your compass from shifting when the angle is really close to the target. You can change kStep when the error is small so that it "slides" into the ending position (i.e. kStep is smaller when the error is small).
For dealing with Angle Issues (wrap around), I "normalize" the angle so it is always within -Pi/Pi. I don't guarantee this is the perfect way to do it, but it seems to get the job done:
// Takes an angle greater than +/- M_PI and converts it back
// to +/- M_PI. Useful in Box2D where angles continuously
// increase/decrease.
static inline float32 AdjustAngle(float32 angleRads)
{
if(angleRads > M_PI)
{
while(angleRads > M_PI)
{
angleRads -= 2*M_PI;
}
}
else if(angleRads < -M_PI)
{
while(angleRads < -M_PI)
{
angleRads += 2*M_PI;
}
}
return angleRads;
}
By doing it this way, -pi is the angle you reach from going in either direction as you continue to rotate left/right. That is to say, there is not a discontinuity in the number going from say 0 to 359 degrees.
SO PUTTING THIS ALL TOGETHER
static inline float Sign(float value)
{
if(value >= 0)
return 1.0f;
return -1.0f;
}
//#define ROTATION_OPTION_1
//#define ROTATION_OPTION_2
#define ROTATION_OPTION_3
-(void)updateArrow
{
// Calculate the angle to the player
CGPoint toPlayer = ccpSub(self.player.position,self.arrow.position);
// Calculate the angle of this...Note there are some inversions
// and the actual image is rotated 90 degrees so I had to offset it
// a bit.
float angleToPlayerRads = -atan2f(toPlayer.y, toPlayer.x);
angleToPlayerRads = AdjustAngle(angleToPlayerRads);
// This is the angle we "wish" the arrow would be pointing.
float targetAngle = CC_RADIANS_TO_DEGREES(angleToPlayerRads)+90;
float errorAngle = targetAngle-self.arrow.rotation;
CCLOG(#"Error Angle = %f",errorAngle);
#ifdef ROTATION_OPTION_1
// In this option, we just set the angle of the rotated sprite directly.
self.arrow.rotation = CC_RADIANS_TO_DEGREES(angleToPlayerRads)+90;
#endif
#ifdef ROTATION_OPTION_2
// In this option, we apply proportional feedback to the angle
// difference.
const float kProp = 0.05f;
self.arrow.rotation += kProp * (errorAngle);
#endif
#ifdef ROTATION_OPTION_3
// The step to take each update in degrees.
const float kStep = 4.0f;
// NOTE: Without the "if(fabs(...)) check, the angle
// can "dither" around the zero point when it is very close.
if(fabs(errorAngle) > kStep)
{
self.arrow.rotation += Sign(errorAngle)*kStep;
}
#endif
}
I put this code into a demo program I had written for Cocos2d. It shows a character (big box) being chased by some monsters (smaller boxes) and has an arrow in the center that always points towards the character. The updateArrow call is made on a timer tick (the update(dt) function) regularly. The player's position on the screen is set by the user tapping on the screen and the angle is based on the vector from the arrow to the player. In the function, I show all three options for setting the angle of the arrow:
Option 1
Just set it based on where the player is (i.e. just set it).
Option 2
Use proportional feedback to adjust the arrow's angle each time step.
Option 3
Step the angle of the arrow each timestep a little bit if the error angle is more than the step size.
Here is a picture showing roughly what it looks like:
And, all the code is available here on github. Just look in the HelloWorldLayer.m file.
Was this helpful?

Resources