I am working on android studio and I want to create the effect of throwing an object, in this case a circle drawn on the canvas. But I'm having problems. Can someone guide me?
I take the "X" and the "Y" value where the user touch but how can i make that the circle move in that direction?
Thanks
Game class:
public class Game extends SurfaceView implements View.OnTouchListener {
Paint paint;
int x, y, radius = 100, speedX = 5, speedY = 5, touchY, touchX;
boolean move = false;
boolean one_time = true;
public Game(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnTouchListener(this);
setFocusable(true);
paint = new Paint();
}
public void onDraw(Canvas canvas){
paint.setColor(Color.WHITE);
canvas.drawRect(0,0,canvas.getWidth(),canvas.getHeight(),paint);
if (one_time == true){
x = canvas.getWidth()/2;
y = canvas.getHeight()/2;
one_time = false;
}
paint.setColor(Color.BLACK);
canvas.drawCircle(x, y, radius, paint);
if (move == true){
if (x >= canvas.getWidth() - radius) {
speedX = -5;
}
if (x <= radius) {
speedX = 5;
}
if (y >= canvas.getHeight() - radius) {
speedY = -5;
}
if (y <= radius) {
speedY = 5;
}
x = x + speedX;
y = y + speedY;
}
invalidate();
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case (MotionEvent.ACTION_DOWN):
touchX = (int) motionEvent.getX();
touchY = (int) motionEvent.getY();
move = true;
return true;
default:
return super.onTouchEvent(motionEvent);
}
}
}
Gonna need to be an x+=speedX or y+=speedY in there somewhere so that the x or y value changes causing the ball to move. If your trying to implement some physics in there ur gonna need some maths.
You need a way to change the (x, y) values at a certain interval. You can do this with the Timer class, but then you get into some difficult multithreading code. I suggest looking at using OpenGL ES or a higher level library such as LibGDX. Both of these provide an event loop which allow you to update objects that will be drawn.
Basically you want to move the circle to the place the user touched the screen, using some type of constant speed?
If all you want is to move a circle the Timer can do it - or you could use something like: https://github.com/MasayukiSuda/FPSAnimator
Finally, are you always looking for a linear straight line constant speed movement? Then your math is fine. If your looking for something with gravity etc. then you can reference this: https://developer.android.com/guide/topics/graphics/physics-based-animation.html and consider this: https://github.com/google/liquidfun
This will move the circle exactly to the point the user touched:
theta = atan2(touchY - y,touchX - x)
speedX = cos(theta)
speedY = sin(theta)
x += speedX
y += speedY
What happens once the circle reaches the point depends on how the calculation is implemented. If you want the circle to continue on its path and travel the direction infinitely, you must only calculate the velocity of x and y once. That way the same x and y velocity will always be used to update the position of the circle.
If the velocities are recalculated every time the position of the circle is to be updated, the circle will continuously move to the point even once it has technically reached it and must manually be stopped.
The velocities can be increased by multiplying them by a value greater than 1. To maintain the correct direction, the value should be the same for both velocities.
Example:
theta = atan2(touchY - y,touchX - x)
speedX = maxSpeed*cos(theta)
speedY = maxSpeed*sin(theta)
x += speedX
y += speedY
Related
The animation I am working on achieving is shown below in Figure 1.
1)I have a list containing points on a 2D plane(left) that I am working on animating.
2)A touch is made at location x, and an invisible circle is drawn (middle). I want all of the points contained in that circle to move away from the center (x).
3) A final result example (right)
I am planning on applying this in a way that I can supply any list of points, touch location, numFrames, and force applied on each point per frame. An array of length numFrames is returned, each array item being an already animated list.
Since I plan on implementing this on screen touch, it is possible for another touch to happen while the first touch animation is already in progress. How can I handle these collisions?
Figure 1:
here's my 2 cents, you need a list of points, these points should have a position, a velocity and a method to update the position. something like this
public class Point {
public float x, y;
public boolean isMoving;
private float dx,dy;
public Point(float x, float y){
this.x = x;
this.y = y;
}
public boolean move(float maxWidth, float maxHeight){
if(!isMoving) return false;
// update the position
x += dx;
y += dy;
dx /= FRICTION;
dy /= FRICTION;
// bounce...?
if(x < 0 || x > maxWidth){
dx *= -1;
}
if(y < 0 || y > maxHeight){
dy *= -1;
}
if(Math.abs(dx) < MOVEMENT_THRESHOLD && Math.abs(dy) < MOVEMENT_THRESHOLD){
isMoving = false;
}
return isMoving;
}
}
on every touch event you apply a force to every point within the radius and set their velocity
for(Point point : mPoints){
float distance = point.distance(x,y);
if(distance > mTouchRange) continue;
float force = (float) Math.pow(1 - (distance / mTouchRange), 2) * mForceFactor;
float angle = point.angle(x,y);
point.dx -= Math.cos(angle) * force;
point.dy -= Math.sin(angle) * force;
point.isMoving = true;
}
then you need an animation that call move on every frame and eventully stops when there are no moving points
you can find the full example here
Is it Possible to move and rotate an Image along a Circular Path based on a touch event as follows:
I have looked at this question:
Moving an Image in circular motion based on touch events in android
But it only tells me how to move the image along a circle, not rotate it.
Update: Full example posted on GitHub at https://github.com/jselbie/xkcdclock
Every time you get a touch event, grab the touch point's x,y coordinates and compute the angle of the rotation relative to the center of bitmap. Use that value to determine how much to rotate the bitmap you want draw.
First, let's assume a logical coordinate system in which the center point of your element above is at (0,0) in x,y space.
Therefore, the angle (in degrees) between any touch point relative to the center can be computed as follows:
double ComputeAngle(float x, float y)
{
final double RADS_TO_DEGREES = 360 / (java.lang.Math.PI*2);
double result = java.lang.Math.atan2(y,x) * RADS_TO_DEGREES;
if (result < 0)
{
result = 360 + result;
}
return result;
}
Note - the normalization of negative angles to positive angles. So if the touch point is (20,20), this function above will return 45 degrees.
To make use of this method, your Activity will need the following member variables defined:
float _refX; // x coordinate of last touch event
float _refY; // y coordinate or last touch event
float _rotation; // what angle should the source image be rotated at
float _centerX; // the actual center coordinate of the canvas we are drawing on
float _centerY; // the actual center coordinate of the canvas we are drawing on
Now let's examine how to keep track of touch coordinates to we can always have an up to date "_rotation" variable.
So our "touch handler" for Android will look something like this:
boolean onTouch(View v, MotionEvent event)
{
int action = event.getAction();
int actionmasked = event.getActionMasked();
if (!_initialized)
{
// if we haven't computed _centerX and _centerY yet, just bail
return false;
}
if (actionmasked == MotionEvent.ACTION_DOWN)
{
_refX = event.getX();
_refY = event.getY();
return true;
}
else if (actionmasked == MotionEvent.ACTION_MOVE)
{
// normalize our touch event's X and Y coordinates to be relative to the center coordinate
float x = event.getX() - _centerX;
float y = _centerY - event.getY();
if ((x != 0) && (y != 0))
{
double angleB = ComputeAngle(x, y);
x = _refX - _centerX;
y = _centerY - _refY;
double angleA = ComputeAngle(x,y);
_rotation += (float)(angleA - angleB);
this.invalidate(); // tell the view to redraw itself
}
}
There's some fine details left out such as drawing the actual bitmap. You might also want to handle the ACTION_UP and ACTION_CANCEL events to normalize _rotation to always be between 0 and 360. But the main point is that the above code is a framework for computing the _rotation at which your Bitmap should be drawn on the View. Something like the following:
void DrawBitmapInCenter(Bitmap bmp, float scale, float rotation, Canvas canvas)
{
canvas.save();
canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
canvas.scale(scale, scale);
canvas.rotate(rotation);
canvas.translate(-bmp.getWidth()/2, -bmp.getHeight()/2);
canvas.drawBitmap(bmp, 0, 0, _paint);
canvas.restore();
}
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.
I have seen how to draw a shape in Android, but what I want to know is how to rescale the shape when the user touches over the shape.
Imagine a square into a screen corner, so when you touch it, it grows until fitting the whole screen. I'd like to make that with a transition, animated, not instant.
Any idea of how to do that, or any known resource?
Android has built-in support for Animations. You can find many examples by searching the Web. This one is a good start.
In order to make your shapes touchable, you can implement them by overriding the View class (a nice example can be found here). Then you can use View.OnTouchListener.
The built in Animations are nice in Android but they aren't the most efficient by any means. When performance is a must I would recommend creating your own method. What I would do is create a class that extends View and give it a bounding box (Rect/RectF) and a circle. Then you can use the bounding box to detect when the circle is touched.
public class Circle extends View {
public static final float SCALE_AMOUNT = 1.0f;
public RectF boundingBox;
private Paint paint;
private float circleCenterX, circleCenterY, circleRadius;
private float x, y;
public Circle(Context context) {
super(context);
// Create paint
paint = new Paint();
paint.setColor(Color.BLACK);
paint.setAntiAlias(true);
// Set circle start radius
circleRadius = 50.0f;
// Set start x and y (this is the upper left hand corner)
x = 100.0f;
y = 100.0f;
// Create boundingBox
boundingBox = new RectF();
boundingBox.left = x;
boundingBox.top = y;
boundingBox.right = x + (circleRadius*2);
boundingBox.bottom = y + (circleRadius*2);
// Set circleCenterX and circleCenterY (the center of the bounding box and circle)
circleCenterX = x + circleRadius;
circleCenterY = y + circleRadius;
}
public void scale(boolean scaleUp) {
float scaleBy = (scaleUp) ? SCALE_AMOUNT : -SCALE_AMOUNT;
// Update circleRadius
circleRadius += scaleBy;
// Update the bounding box
boundingBox.left = x;
boundingBox.top = y;
boundingBox.right = x + (circleRadius*2);
boundingBox.bottom = y + (circleRadius*2);
// Update the circle center positions
circleCenterX = x + circleRadius;
circleCenterY = y + circleRadius;
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawCircle(circleCenterX, circleCenterY, circleRadius, paint);
}
}
... Then in your Activity class override the onTouchEvent() method and check if your Circle is touched.
Circle circle = new Circle(this);
#Override
public void onDraw(Canvas canvas) {
circle.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
float x = event.getX();
float y = event.getY();
// Detect if pointer goes down on screen
if(action == MotionEvent.ACTION_DOWN) {
if(circle.boundingBox.contains(x, y) == true) {
// Circle was touched so scale it
circle.scale(true); // true is scale up, false is scale down
}
}
return true;
}
... This will scale your circle/rectangle every time you touch it. If you wanted to make it continually grow you could have a boolean variable that gets set to true when you touch the shape and grows until you pick your finger up. I haven't tried this code, just typed it up real quick so it may not compile but this is going to be you're best bet. It is really easy to add many shapes and detect touches on all of the shapes. Add different effects to each one... etc. I didn't want to do all of it for you but this should point you in the right direction.
Maybe this github project could help you: https://github.com/markushi/android-circlebutton
I need an advice how to achieve the following functionality under Android:
I need an image that represents something like a graph (from discrete math), with vertices and edges, where I can click every vertice or edge and fire a different action.
Please advise me how to achieve this (maybe with imagebuttons) or another approach to represent this functionality.
I was bored, so I coded up this crude example...
It assumes straight edges between points.
public class App extends Activity
{
PlotView plot;
#Override
public void onCreate(Bundle sis)
{
super.onCreate(sis);
plot = new PlotView(this);
setContentView(plot);
}
public class PlotView extends View
{
Paint paint1 = new Paint();
Paint paint2 = new Paint();
Point[] points = new Point[10];
public PlotView(Context context)
{
super(context);
paint1.setColor(Color.RED);
paint2.setColor(Color.BLUE);
for (int i = 0; i < points.length; i++)
{
points[i] = new Point();
points[i].x = (float) (Math.random() * 320);
points[i].y = (float) (Math.random() * 480);
}
Arrays.sort(points);
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(Color.WHITE);
for (int i = 0; i < points.length; i++)
{
if (i < points.length - 1)
{
canvas.drawLine(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y, paint2);
}
canvas.drawCircle(points[i].x, points[i].y, 5, paint1);
}
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
float x = event.getX();
float y = event.getY();
int hitPoint = -1;
int closestLeft = -1;
int closestRight = -1;
for (int i = 0; i < points.length; i++)
{
float dx = x - points[i].x;
float dy = y - points[i].y;
if(i < points.length - 1)
{
if(points[i].x < x && x < points[i + 1].x)
{
closestLeft = i;
closestRight = i + 1;
}
}
if (Math.abs(dx) <= 16.0f && Math.abs(dy) <= 16.0f)
{
hitPoint = i;
break;
}
}
if (hitPoint != -1)
{
Toast.makeText(getContext(), "Hit Point: " + hitPoint, Toast.LENGTH_SHORT).show();
}
else
if(closestLeft != -1 && closestRight != -1)
{
float dx = points[closestLeft].x - points[closestRight].x;
float dy = points[closestLeft].y - points[closestRight].y;
final float u = ((x - points[closestLeft].x) * dx + (y - points[closestLeft].y) * dy) / (dx * dx + dy * dy);
float px = points[closestLeft].x + u * dx;
float py = points[closestLeft].y + u * dy;
if (Math.abs(x - px) <= 16.0f && Math.abs(y - py) <= 16.0f)
{
Toast.makeText(getContext(), "Hit Line Between: " + closestLeft + " & " + closestRight, Toast.LENGTH_SHORT).show();
}
}
}
}
return super.onTouchEvent(event);
}
public class Point implements Comparable<Point>
{
float x;
float y;
#Override
public int compareTo(Point other)
{
if (x < other.x) return -1;
if (x > other.x) return 1;
return 0;
}
}
}
}
I can imagine how to do this with SurfaceView:
create a Vertex class, which among other things, has an x,y coordinate representing where to draw the vertex. If your vertex was a png image of a circle, then the top-left x,y coordinates of the image are stored in the Vertex class.
Have all your verticies in a List, and iterate through and draw each vertex.
the edges are more complicated since they might criss-cross or curve around.
assuming they are straight lines, then you can have a Edge class that contains the starting x,y and ending x,y coordinates.
you can iterate through a List of Edges and draw the lines accordingly
In order to detect when a user clicks on them, you should override the onTouch method and check the event.rawX() and event.rawY() values to see if they match up to a Vertex or Edge class.
for a Vertex class, you can check if x <= event.rawX <= x + image_width and y <= event.rawY <= y + image_height
for an Edge, you can check if the event.rawX, event.rawY coordinates are found in the line formed by the two sets of coordinates you stored in the Edge class.
I've used a similar method to draw a set of nodes in a game. I'm not so sure how to do the edges though - the method I outline would only work if they were straight and do not criss-cross.
I am sure there is a better way to do this using openGL, but I have not used openGL before.
Hopefully you can get some ideas out of this.
I think you might be best off with a SurfaceView:
http://developer.android.com/reference/android/view/SurfaceView.html
And handling the onTouchEvent() as a whole for the surface, and mapping that to underlying entities in the image. If you're calculating the drawing the graph as you go should be easy to also create a map of tapable areas and grabbing the X and Y of the touch event to figure out if it corresponds to an element in the image.
If you literally have an image, as an already processed PNG for example, you would need some way to also carry in the touch event areas. Depends where that image comes in from.
According to android help, "drawing to a View, is your best choice when you want to draw simple graphics that do not need to change dynamically and are not part of a performance-intensive game." This is the right way to go when making a snake or a chess game, for instance. So I don't see a point in suggesting using a SurfaceView for this, it will just overcomplicate things.
For clickable areas you override public boolean onTouchEvent(MotionEvent event) where you manage x and y coordinates of the click for identifying the clicked area.