I am creating a game which involves shooting in the direction where user clicked.
So from point A(x,y) to point B(x1,y1) i want the bullet bitmap to animate and i have done some calculation/math and figured out some way to do it, but it's not that great-looking doesn't feel so natural.
My approach to doing this is calculate the difference between x and x1 and y and y1 and just scale it.
In example if the X difference between x and x1 is 100 and Y difference between y and y1 i calculate X/Y and get 2.0 which is equal to 2:1 so I know that I should move X two times faster than Y.
Here is my code, if anyone has any suggestions how to make it better, let me know.
float proportion;
float diffX = (x1 - x);
if(diffX == 0) diffX = 0.00001f;
float diffY = (y1 - y);
if(diffY == 0) diffY = 0.00001f;
if(Math.abs(diffX)>Math.abs(diffY)){
proportion = Math.abs(diffX)/Math.abs(diffY);
speedY = 2;
speedX = proportion * speedY;
}
else if(Math.abs(diffX)<Math.abs(diffY)){
proportion = Math.abs(diffY)/Math.abs(diffX);
speedX = 2;
speedY = proportion * speedX;
}
else{
speedX = speedY = 2;
}
if(diffY<0) speedY = -speedY;
if(diffX<0) speedX = -speedX;
if(speedX>=10) speedX = 9;
if(speedX<=-10) speedX = -9;
if(speedY>=10) speedY = 9;
if(speedY<=-10) speedY = -10;
The following implements LERP (linear interpolation) to move you along a straight line.
// move from (x1, y1) to (x2,y2) with speed "speed" (that must be positive)
final double deltay = y2-y1;
final double deltax = x2-x1;
double deltalen = sqrt(deltay*deltay + deltax*deltax);
if (deltalen <= speed)
display(x2, y2);
else {
double finalx = x1 + deltax * speed/deltalen; // surely deltalen > 0, since speed >=0
double finaly = y1 + deltay * speed/deltalen;
display(finalx, finaly);
}
Here is the code to elaborate on my comment:
float slope = (x2 -x1)/(y2 - y1);
float dx = 0.1f; // tweake to set bullet's speed
float x = x1;
while(x < x2)
{
float y = slope*(x - x1) + y1;
DisplayBullet(x, y);
x += dx;
}
// at this point x = x2 and, if everything went right, y = y2
Here I'm assuming that x1 < x2. You'll have to swap points when that's not the case.
I am trying to create a pad-like view in android. I got a circle that follows user's gestures and I am using distance to keep the circle of going outside the main circle of the pad control.
My problem is I want the circle to keep following the gesture, but to stay inside of the main circle. I am using the formula for finding a point using angle and radius, but it does some funky stuff.
I am translating the canvas, so that the center of the main circle is at 0, 0.
Here is the code:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(this.mainRadius, this.mainRadius);
canvas.drawCircle(0, 0, this.mainRadius, this.debugPaint);
canvas.drawCircle(this.handleX, this.handleY, this.handleRadius, this.handlePaint);
}
private void translateHandle(MotionEvent event) {
int x = (int) (event.getX() - this.mainRadius);
int y = (int) (event.getY() - this.mainRadius);
double distance = distanceFromCenter(x, y);
if (distance <= this.maxDistance) {
this.handleX = x;
this.handleY = y;
} else {
float angle = (float) Math.toDegrees(Math.atan2(y, x));
if (angle < 0)
angle += 360;
this.handleX = (int) ((this.mainRadius - this.handleRadius) * Math.cos(angle));
this.handleY = (int) ((this.mainRadius - this.handleRadius) * Math.sin(angle));
}
//onTranslateHandle(distance);
}
And here is the funky stuff in a gif image:
I cannot verify this change into your code snippet but do hope it gives some idea how to proceed further anyway;
private void translateHandle(MotionEvent event) {
float x = event.getX() - this.mainRadius;
float y = event.getY() - this.mainRadius;
double distance = distanceFromCenter(x, y);
if (distance > this.maxDistance) {
// If distance is i.e 2.0 and maxDistance is 1.0 ==> adjust is 0.5
// which repositions x and y making distance 1.0 maintaining direction
double adjust = this.maxDistance / distance;
x = (float)(x * adjust);
y = (float)(y * adjust);
}
this.handleX = (int)x;
this.handleY = (int)y;
}
I can update the answer where needed if this does not give any useful results.
Hello I searched in the forum,but coudn't find a helpful answer.
I'm making a game with AndEngine and I'm stuck for 3 days on shooting from rotating sprite.
That is my code and how I rotate the gun.I tried here to shoot a bullet ,but it shoots from a wrong starting point I would want to shoot a bullet from the end of the gun.
#Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
if(pSceneTouchEvent.isActionMove()){
final float dX = pSceneTouchEvent.getX() - machine.getX();
final float dY = pSceneTouchEvent.getY() - machine.getY();
float angle = (float) Math.atan2(dX,dY);
float rotation = MathUtils.radToDeg(angle) + 1;
machine.setRotation(rotation - 90);
Log.d("BUG",machine.getRotation() + "");
if(machine.getRotation() >= 84 ){
machine.setRotation(84);
}
if(machine.getRotation() <= -54 ){
machine.setRotation(-54);
}
final int incrementXValue = 15;
long sElapsed = System.currentTimeMillis() - lastFire;
if(bulletsAmout > 0 && sElapsed > cooldownBetweenShoot * cdModd){
e = new Entity(0,machine.getY());
e.setRotation(getRotation());
SceneManager.getInstance().getCurrentScene().attachChild(e);
float x2 = (float) (machine.getSceneCenterCoordinates()[0] + machine.getWidth() /2 * Math.cos(machine.getRotation()));
float y2 = (float) (machine.getSceneCenterCoordinates()[1] + machine.getWidth() /2 * Math.sin(machine.getRotation()));
float realX = (float) (Math.toRadians(x2) + machine.getWidth());
realY = (float) Math.toRadians(y2);
bullets = new Sprite(realX,realY, resourcesManager.bulletRegion.deepCopy(), vbom){
protected void onManagedUpdate(float pSecondsElapsed) {
float currentX = this.getX();
this.setX(currentX + incrementXValue);
super.onManagedUpdate(pSecondsElapsed);
}
};
bullets.setScale(0.06f);
e.attachChild(bullets);
projectilesToBeAdded.add(bullets);
bulletsAmout--;
lastFire = System.currentTimeMillis();
setBulletsText(bulletsAmout);
resourcesManager.pistolSound.play();
}
return true;
}
return false;
}
Assuming you are using GLES2-AnchorCenter:
You can position the bullet by setting it to the position of the end of the gun that you can get by calling gun.convertLocalToSceneCoordinates(gunMuzzleX, gunMuzzleY).
Then set the bullets rotation to the rotation of the gun.
apply velocity to the bullet. Calculate the speed-vector as follows FloatMath.sin(rotationOfBulletInRadians) * speed and FloatMath.cos(rotationOfBulletInRadians) * speed.
Be aware that you have to pass the rotation in radians to the sin and cos function NOT in degrees!
So I found how to fix that.
The problem is in this line of code :
e = new Entity(0,machine.getY());
Should be :
e = new Entity(machine.getX() - (machine.getHeight() / 2),machine.getY())
I'm implementing cubic bezier curve logic in my one of Android Application.
I've implemented cubic bezier curve code on canvas in onDraw of custom view.
// Path to draw cubic bezier curve
Path cubePath = new Path();
// Move to startPoint(200,200) (P0)
cubePath.moveTo(200,200);
// Cubic to with ControlPoint1(200,100) (C1), ControlPoint2(300,100) (C2) , EndPoint(300,200) (P1)
cubePath.cubicTo(200,100,300,100,300,200);
// Draw on Canvas
canvas.drawPath(cubePath, paint);
I visualize above code in following image.
[Updated]
Logic for selecting first control points, I've taken ,
baseX = 200 , baseY = 200 and curve_size = X of Endpoint - X of Start Point
Start Point : x = baseX and y = baseY
Control Point 1 : x = baseX and y = baseY - curve_size
Control Point 2 : x = baseX + curve_size and y = baseY - curve_size
End Point : x = baseX + curve_size and y = baseY
I want to allow user to change EndPoint of above curve, and based on the new End points, I invalidate the canvas.
But problem is that, Curve maintain by two control points, which needs to be recalculate upon the change in EndPoint.
Like, I just want to find new Control Points when EndPoint change from (300,200) to (250,250)
Like in following image :
Please help me to calculate two new Control Points based on new End Point that curve shape will maintain same as previous end point.
I refer following reference links during searching:
http://pomax.github.io/bezierinfo/
http://jsfiddle.net/hitesh24by365/jHbVE/3/
http://en.wikipedia.org/wiki/B%C3%A9zier_curve
http://cubic-bezier.com/
Any reference link also appreciated in answer of this question.
changing the endpoint means two things, a rotation along P1 and a scaling factor.
The scaling factor (lets call it s) is len(p1 - p0) / len(p2 - p0)
For the rotation factor (lets call it r) i defer you to Calculating the angle between three points in android , which also gives a platform specific implementation, but you can check correctness by scaling/rotationg p1 in relation to p0, and you should get p2 as a result.
next, apply scaling and rotation with respect to p0 to c1 and c2. for convenience i will call the new c1 'd1' and the new d2.
d1 = rot(c1 - p0, factor) * s + p0
d2 = rot(c2 - p0, factor) * s + p0
to define some pseudocode for rot() (rotation http://en.wikipedia.org/wiki/Rotation_%28mathematics%29)
rot(point p, double angle){
point q;
q.x = p.x * cos(angle) - p.y * sin(angle);
q.y = p.x * sin(angle) + p.y * cos(angle);
}
Your bezier curve is now scaled and rotated in relation to p0, with p1 changed to p2,
Firstly I would ask you to look into following articles :
Bezier Curves
Why B-Spline Curve
B-Spline Curve Summary
What you are trying to implement is a piecewise composite Bézier curve. From the Summary page for n control points (include start/end) you get (n - 1)/3 piecewise Bézier curves.
The control points shape the curve literally. If you don't give proper control points with new point, you will not be able to create smoothly connected bezier curve. Generating them will not work, as it is too complex and there is no universally accepted way.
If you don't have/want to give extra control points, you should use Catmull-Rom spline, which passes through all control points and will be C1 continous (derivative is continuous at any point on curve).
Links for Catmull Rom Spline in java/android :
http://hawkesy.blogspot.in/2010/05/catmull-rom-spline-curve-implementation.html
https://github.com/Dongseob-Park/catmull-rom-spline-curve-android
catmull-rom splines for Android (similar to your question)
Bottom line is if you don't have the control points don't use cubic bezier curve. Generating them is a problem not the solution.
It seems that you are here rotating and scaling a square where you know the bottom two points and need to calculate the other two. The two known points form two triangles with the other two, so we just need to find the third point in a triangle. Supose the end point is x1, y1:
PointF c1 = calculateTriangle(x0, y0, x1, y1, true); //find left third point
PointF c2 = calculateTriangle(x0, y0, x1, y1, false); //find right third point
cubePath.reset();
cubePath.moveTo(x0, y0);
cubePath.cubicTo(c1.x, c1.y, c2.x, c2.y, x1, y1);
private PointF calculateTriangle(float x1, float y1, float x2, float y2, boolean left) {
PointF result = new PointF(0,0);
float dy = y2 - y1;
float dx = x2 - x1;
float dangle = (float) (Math.atan2(dy, dx) - Math.PI /2f);
float sideDist = (float) Math.sqrt(dx * dx + dy * dy); //square
if (left){
result.x = (int) (Math.cos(dangle) * sideDist + x1);
result.y = (int) (Math.sin(dangle) * sideDist + y1);
}else{
result.x = (int) (Math.cos(dangle) * sideDist + x2);
result.y = (int) (Math.sin(dangle) * sideDist + y2);
}
return result;
}
...
There is other way to do this where it does not matter how many points you have in between the first and the last point in the path or event its shape.
//Find scale
Float oldDist = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
Float newDist = (float) Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0));
Float scale = newDist/oldDist;
//find angle
Float oldAngle = (float) (Math.atan2(y1 - y0, x1 - x0) - Math.PI /2f);
Float newAngle = (float) (Math.atan2(y2 - y0, x2 - x0) - Math.PI /2f);
Float angle = newAngle - oldAngle;
//set matrix
Matrix matrix = new Matrix();
matrix.postScale(scale, scale, x0, y0);
matrix.postRotate(angle, x0, y0);
//transform the path
cubePath.transform(matrix);
A small variant on the suggestion by Lumis
// Find scale
Float oldDist = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
Float newDist = (float) Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0));
Float scale = newDist/oldDist;
// Find angle
Float oldAngle = (float) (Math.atan2(y1 - y0, x1 - x0));
Float newAngle = (float) (Math.atan2(y2 - y0, x2 - x0));
Float angle = newAngle - oldAngle;
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
matrix.postRotate(angle);
float[] p = { c1.x, c1.y, c2.x, c2.y };
matrix.mapVectors(p);
PointF newC1 = new PointF(p[0], p[1]);
PointF newC2 = new PointF(p[2], p[3]);
I know location1.distanceTo(location2)
I need to calculate
distanceX
and
distanceY
from two locations. Is there a way to do it?
CORRECT ANSWER based on njzk2 answer
private Point getDistanceXY(Location location1, Location location2) {
int angle = (int) location1.bearingTo(location2);
Location interm = new Location(location1);
interm.setLongitude(location2.getLongitude());
int distanceX = (int) location1.distanceTo(interm);
int distanceY = (int) location2.distanceTo(interm);
if (angle<=-90) distanceX=-distanceX;
else if (angle>-90 && angle<0) {
distanceX=-distanceX;
distanceY=-distanceY;
}
Log.e("distXY "+name, distanceX+" "+distanceY+" "+angle);
return new Point(distanceX, distanceY);
}
What you could do is create a middle point :
Location interm = new Location(location1);
iterm.setLongitude(location2.getLongitude());
And then,
double distanceX = location1.distance(interm);
double distanceY = interm.distance(location2);
You can use bearingTo() in combination with distanceTo() and a little bit of trigonometry to calculate the X and Y components.
double distY = distance * sin(angle);
double distX = distance * cos(angle);