Bitmap Bounding RectF Collision - android

I have been trying to create a simple breakout type game. I have a ball and a bat and want to invert the ball's direction when it collides with the bat. The bat is controlled by the user's touch and can freely move around the screen.
#Override
public boolean onTouch(View v, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
return true;
}
In my game loop:
int cx = -1;
int cy = -1;
private int xVelocity = -11;
private int yVelocity = -14;
Bitmap player, ball;
RectF boundsPlayer, boundsBall;
float boundCX, boundCY;
boundCX = (float) cx;
boundCY = (float) cy;
player = BitmapFactory.decodeResource(getResources(), R.drawable.nightplayer);
canvas.drawBitmap(player, x - (player.getWidth() / 2), y - (player.getHeight() + 100), null);
ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
boundsPlayer = new RectF((x - (player.getWidth()/2)), (y - (player.getHeight() + 150)), ((x - (player.getWidth()/2)) + player.getWidth()), ((y - (player.getHeight() + 150)) + player.getHeight()));
boundsBall = new RectF(boundCX, boundCY, boundCX + ball.getWidth(), boundCY + ball.getHeight());
if (RectF.intersects(boundsBall, boundsPlayer)) {
collision();
}
private void collision() {
yVelocity = yVelocity * -1;
score = score + 1;
//Log.d(TAG, String.valueOf(score));
yVelocity = yVelocity - 2;
if(xVelocity < 0) {
xVelocity = xVelocity - 2;
} else if (xVelocity > 0) {
xVelocity = xVelocity + 2;
}
collision = false;
}
The issue I am having is that when the ball collides with the bat it seems to collide multiple times and repeatedly inverts the direction instead of "bouncing off" of the bat.

Related

How to give interactions to a bar in a bar graph i.e. increase and decrease of height of each bar in android without using any library

I am able to plot bar graph using canvas and drawing rectangle in the view. But the onTouch interaction works for the whole view and hence I am not able to interact with each bar separately.I am not looking for using any library for plotting graphs. Any suggestions would be helpful. Thanks!
protected void onDraw(Canvas canvas) {
float border = 20;
float horstart = border * 2;
float height = getHeight();
float width = getWidth() - 1;
float max = getMax();
float min = getMin();
float diff = max - min;
float graphheight = height - (2 * border);
float graphwidth = width - (2 * border);
paint.setTextAlign(Align.LEFT);
int vers = verlabels.length - 1;
for (int i = 0; i < verlabels.length; i++) {
paint.setColor(Color.BLUE);
float y = ((graphheight / vers) * i) + border;
//canvas.drawLine(horstart, y, width, y, paint);
paint.setColor(Color.BLUE);
canvas.drawText(verlabels[i], 0, y, paint);
}
int hors = horlabels.length - 1;
for (int i = 0; i < horlabels.length; i++) {
paint.setColor(Color.BLUE);
float x = ((graphwidth / hors) * i) + horstart;
//canvas.drawLine(x, height - border, x, border, paint);
paint.setTextAlign(Align.CENTER);
if (i == horlabels.length - 1)
paint.setTextAlign(Align.RIGHT);
if (i == 0)
paint.setTextAlign(Align.LEFT);
paint.setColor(Color.BLUE);
canvas.drawText(horlabels[i], x, height - 4, paint);
}
paint.setTextAlign(Align.CENTER);
canvas.drawText(title, (graphwidth / 2) + horstart, border - 4, paint);
if (max != min) {
paint.setColor(Color.BLUE);
if (type == BAR) {
float datalength = values.length;
float colwidth = (graphwidth / hors);
for (int i = 0; i < values.length; i++) {
float val = values[i] - min;
float rat = val / diff;
float h = graphheight * rat;
canvas.drawRect((i * colwidth) + horstart, (border - h)
+ graphheight+curY, ((i * colwidth) + horstart)
+ (colwidth - 1), height - (border - 1), paint);
}
} else {
float datalength = values.length;
float colwidth = (width - (2 * border)) / datalength;
float halfcol = colwidth / 2;
float lasth = 0;
float h = 0;
for (int i = 0;i<values.length;i++)
canvas.drawLine(((i - 1) * colwidth) + (horstart + 1)
+ halfcol, (border - lasth) + graphheight+curY,
(i * colwidth) + (horstart + 1) + halfcol,
(border - h) + graphheight, paint);
lasth = h;
}
}
}
onTouch method for the view :
public boolean onTouch(View v, MotionEvent event)
{
boolean result=false;
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
curX= (int)event.getX();
curY= (int)event.getY();
result=true;
break;
case MotionEvent.ACTION_MOVE:
curX= (int)event.getX();
curY= (int)event.getY();
result=true;
break;
case MotionEvent.ACTION_UP:
curX= (int)event.getX();
curY= (int)event.getY();
result=true;
break;
}
if (result) invalidate();
return result;
}
Whenever an user touches that view, it causes the onTouch method gets called. Once it's getting called, you're given two float numbers: x and y indicating where user's finger touches your view relative to the view coordination system.
As you might grasped the idea, you should get those numbers and internally in your custom view (i.e. bar chart) calculate which bar is affected. Then, you can for example apply a hover effect or do something else.
Note: For updating appearance of your view, you should store changes in data models of your chart view and then issue invalidate(). Subsequently, as a result, your onDraw is invoked and in which you can re-draw your chart. (i.e. You should each time, re-draw your whole chart again)

Place a Canvas Circle within the nearest Canvas Circle Point

I am currently developing a tree shaped structure using Canvas Circles and I have almost brought about the layouts using Canvas but facing problem while implementing Drag and Drop in Canvas. The code is as follows for better understanding :
private HashSet<CircleArea> mCircles = new HashSet<CircleArea>(CIRCLES_LIMIT);
private SparseArray<CircleArea> mCirclePointer = new SparseArray<CircleArea>(CIRCLES_LIMIT);
private static class CircleArea {
int radius;
int centerX;
int centerY;
CircleArea(int centerX, int centerY, int radius) {
this.radius = radius;
this.centerX = centerX;
this.centerY = centerY;
}
public int getCenterX()
{
return centerX;
}
public int getCenterY()
{
return centerY;
}
public int getRadius()
{
return radius;
}
#Override
public String toString() {
return "Circle[" + centerX + ", " + centerY + ", " + radius + "]";
}
}
private void init(final Context ct) {
Display display = ((Activity) getContext()).getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
System.out.println("Width is " + width + "Height is " + height);
scrWidth = width;
scrHeight = height;
}
#Override
public void onDraw(final Canvas canv) {
// setWillNotDraw(false);
for (int i = 0; i < name.length; i++) {
if (i == 0) {
for (int j = 0; j < latitude_0.length; j++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_0[j]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_0[j]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
} else if (i == 1) {
for (int k = 0; k < latitude_1.length; k++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_1[k]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_1[k]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
} else if (i == 2) {
for (int l = 0; l < latitude_2.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_2[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_2[l]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
} else if (i == 3) {
for (int l = 0; l < latitude_3.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_3[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_3[l]));
mCircles.add(new CircleArea(x, y, RADIUS_LIMIT));
}
} else if (i == 4) {
for (int l = 0; l < latitude_4.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_4[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_4[l]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
}else if (i == 5) {
for (int l = 0; l < latitude_5.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_5[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_5[l]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
}else if (i == 6) {
for (int l = 0; l < latitude_6.length; l++) {
x = (int) ((scrWidth / 360.0) * (90 + longitude_6[l]));
y = (int) ((scrHeight / 180.0) * (90 - latitude_6[l]));
mCircles.add(new CircleArea(x,y,RADIUS_LIMIT));
}
}
}
for (CircleArea circle : mCircles) {
canv.drawCircle(circle.getCenterX(),circle.getCenterY(),RADIUS_LIMIT, mCirclePaint);
}
// invalidate();;
}
#Override
public boolean onTouchEvent(final MotionEvent event) {
boolean handled = false;
CircleArea touchedCircle;
int xTouch;
int yTouch;
int pointerId;
int actionIndex = event.getActionIndex();
// get touch event coordinates and make transparent circle from it
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
xTouch = (int) event.getX(0);
yTouch = (int) event.getY(0);
touchedCircle = obtainTouchedCircle(xTouch, yTouch);
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
mCirclePointer.put(event.getPointerId(0), touchedCircle);
invalidate();
handled = true;
break;
case MotionEvent.ACTION_POINTER_DOWN:
Log.w(TAG, "Pointer down");
// It secondary pointers, so obtain their ids and check circles
pointerId = event.getPointerId(actionIndex);
xTouch = (int) event.getX(actionIndex);
yTouch = (int) event.getY(actionIndex);
// check if we've touched inside some circle
for(CircleArea circle: mCircles)
{
touchedCircle = obtainTouchedCircle(circle.getCenterX(), circle.getCenterY());
mCirclePointer.put(pointerId, touchedCircle);
touchedCircle.centerX = circle.getCenterX();
touchedCircle.centerY = circle.getCenterY();
}
invalidate();
handled = true;
break;
case MotionEvent.ACTION_MOVE:
final int pointerCount = event.getPointerCount();
Log.w(TAG, "Move");
for (actionIndex = 0; actionIndex < pointerCount; actionIndex++) {
// Some pointer has moved, search it by pointer id
pointerId = event.getPointerId(actionIndex);
for(CircleArea circle: mCircles)
{
xTouch = (int) event.getX(actionIndex);
yTouch = (int) event.getY(actionIndex);
float dx = xTouch - circle.getCenterX();
float dy = yTouch - circle.getCenterY();
float r = FloatMath.sqrt((dx * dx) + (dy * dy));
touchedCircle = mCirclePointer.get(pointerId);
if (null != touchedCircle) {
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
}
}
}
invalidate();
handled = true;
break;
case MotionEvent.ACTION_UP:
//clearCirclePointer();
invalidate();
handled = true;
break;
case MotionEvent.ACTION_POINTER_UP:
// not general pointer was up
pointerId = event.getPointerId(actionIndex);
mCirclePointer.remove(pointerId);
// invalidate();
handled = true;
break;
case MotionEvent.ACTION_CANCEL:
handled = true;
break;
default:
// do nothing
break;
}
return super.onTouchEvent(event) || handled;
}
private CircleArea obtainTouchedCircle(final int xTouch, final int yTouch) {
CircleArea touchedCircle = getTouchedCircle(xTouch, yTouch);
if (null == touchedCircle) {
touchedCircle = new CircleArea(xTouch, yTouch, RADIUS_LIMIT);
}
return touchedCircle;
}
private CircleArea getTouchedCircle(final int xTouch, final int yTouch) {
CircleArea touched = null;
for (CircleArea circle : mCircles) {
if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + (circle.centerY - yTouch) * (circle.centerY - yTouch) <= circle.radius * circle.radius) {
touched = circle;
break;
}
}
return touched;
}
I am well aware of the shabby code that I've written but highly helpless to generate a mathematical calculation on how to place the dragged circle to the nearest available canvas circle. Below depicted is a screenshot which helps in more understanding.
Points to be noted :
The function obtainTouchedCircle and getTouchedCircle are vital.
The MotionEvent.ACTION_MOVE helps me in moving a circle but what it does is that it generates a new circle with the same dimensions every time which is totally fine but when a user drags and drops in somewhere, it should get placed in the nearest available circle.
Consider the below image where the yellow circles need to be moved around and placed in the blue circles according to the user's wish. That is my objective.
If someone can help me with the calculation on how I should figure out the nearest available circle while dragging would be of great help !!!! Thanks in advance.

How to determine the normal when ball hits rectangle?

I have this constants defined, but I'm not sure how to find the normal index.
public static final int LEFT = 0;
public static final int RIGHT = 1;
public static final int UP = 2;
public static final int DOWN = 3;
public float[] normals = {-1, 0, 1, 0, 0, 1, 0, -1};
public float4 getNormal(int index)
{
return new float4(normals[index * 2], normals[index * 2 + 1], 0);
}
public static int normal(float x, float y, RectF rect)
{
// need to find the normal index here
return normal_index;
}
I think this should work for inside and outside points (radius centers):
public static int normal_index(float x, float y, RectF rect)
{
float cx = rect.left + rect.width() * 0.5f;
float cy = rect.top + rect.height() * 0.5f;
boolean a = x - rect.left < rect.bottom - y;
boolean b = x - rect.left < y - rect.top;
boolean c = x - rect.right < y - rect.bottom;
boolean d = x - rect.right < rect.top - y;
if (a && b && x < cx ) return LEFT;
else if (!c && !d && x >= cx ) return RIGHT;
else if (!a && c && y >= cy ) return DOWN;
else if (!b && d && y < cy ) return UP;
return -1;
}
The question seems related to this question Given a point inside a rectangle, determine the side that's closest to the point
For the lines a, b, c, d, I've got similar picture:

Touch events while the screen is being touched in Android

I am trying to implement a basic virtual joystick in an Android custom View, using the following code.
this.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent e) {
switch(e.getAction()){
case(MotionEvent.ACTION_DOWN):
moving = true;
joystickVisibility = true;
joystickCoordinates[0] = (int) e.getX() - imageJoystick.getWidth() / 2;
joystickCoordinates[1] = (int) e.getY() - imageJoystick.getHeight() / 2;
joystickBaseCoordinates[0] = (int) e.getX() - imageJoystickBase.getWidth() / 2;
joystickBaseCoordinates[1] = (int) e.getY() - imageJoystickBase.getHeight() / 2;
break;
case(MotionEvent.ACTION_MOVE):
joystickCoordinates[0] = (int) e.getX() - imageJoystick.getWidth() / 2;
joystickCoordinates[1] = (int) e.getY() - imageJoystick.getHeight() / 2;
if (joystickCoordinates[0] > joystickBaseCoordinates[0] + 50)
joystickCoordinates[0] = joystickBaseCoordinates[0] + 50;
if (joystickCoordinates[0] < joystickBaseCoordinates[0] - 50)
joystickCoordinates[0] = joystickBaseCoordinates[0] - 50;
if (joystickCoordinates[1] > joystickBaseCoordinates[1] + 50)
joystickCoordinates[1] = joystickBaseCoordinates[1] + 50;
if (joystickCoordinates[1] < joystickBaseCoordinates[1] - 50)
joystickCoordinates[1] = joystickBaseCoordinates[1] - 50;
break;
case(MotionEvent.ACTION_UP):
moving = false;
joystickVisibility = false;
break;
}
move();
postInvalidate();
return true;
}
});
It is working except when the joystick is being held in one direction but not being moved.
I can't find any event that works while the screen is being touched.

Sprite movement based on rotation

I have a sprite in Android OpenGL. This sprite (a small beetlebug) is always moving in a forward direction and I use:
sprite.setPosition(posX, posY);
Now I have a rotation method, when the user gestures left or right the bug rotates:
private void applyRotation() {
for(int i=0;i<beetleBug.size;i++) {
Sprite s = beetleBug.get(i);
s.setOrigin(s.getWidth() / 2, s.getHeight() / 2);
s.setRotation(angle);
}
}
Now when the bug is moving forward which he always does the new x and y coordinates have to be calculated which depend on the rotation-angle, so that the bug is always moving forward. Does anybody have an algorithm to calculate the direction by the rotation-angle?
Here is the whole Bug-class:
public class Bug {
private SpriteBatch spriteBatch = null;
private TextureAtlas spriteSheet;
private Array<Sprite> beetleBug;
private int currentFrame = 0;
private final float frameLength = 0.10f; //in seconds, how long a frame last
private float animationElapsed = 0.0f;
private float angle = 0.0f;
private float posX = 0.0f;
private float posY = 0.0f;
private float sizeX = 100.0f;
private float sizeY = 100.0f;
private float offSet = 50.0f;
public Bug() {
spriteBatch = new SpriteBatch();
spriteSheet = new TextureAtlas("assets/data/bug.txt");
beetleBug = spriteSheet.createSprites("bug");
// dont forget to set the size of your sprites!
for(int i=0; i<beetleBug.size; i++){
beetleBug.get(i).setSize(sizeX, sizeY);
}
applyPosition();
}
public void handleInput() {
boolean leftKey = Gdx.input.isKeyPressed(Input.Keys.LEFT);
boolean rightKey = Gdx.input.isKeyPressed(Input.Keys.RIGHT);
if(rightKey) {
if(angle <= 0) {
angle = 360;
}
angle -= 2f;
applyRotation();
}
if(leftKey) {
if(angle >= 360) {
angle = 0;
}
angle += 2f;
applyRotation();
}
applyPosition();
}
private void applyPosition() {
float x = (float) Math.cos(angle);
float y = (float) Math.sin(angle);
posX = posX + x;
posY = posY + y;
for(int i=0; i<beetleBug.size; i++){
beetleBug.get(i).setPosition(posX - offSet, posY -offSet); // optional: center the sprite to screen
}
}
private void applyRotation() {
for(int i=0;i<beetleBug.size;i++) {
Sprite s = beetleBug.get(i);
s.setOrigin(s.getWidth() / 2, s.getHeight() / 2);
s.setRotation(angle);
}
}
public void render(OrthographicCamera cam) {
float dt = Gdx.graphics.getDeltaTime();
animationElapsed += dt;
while(animationElapsed > frameLength){
animationElapsed -= frameLength;
currentFrame = (currentFrame == beetleBug.size - 1) ? 0 : ++currentFrame;
}
spriteBatch.setProjectionMatrix(cam.combined);
spriteBatch.begin();
beetleBug.get(currentFrame).draw(spriteBatch);
spriteBatch.end();
}
}
Works perfectly now:
Converted degrees to radians
Set x-coordintae to -
private void applyPosition() {
float radians = (float) Math.toRadians(angle);
float x = -(float) Math.sin(radians);
float y = (float) Math.cos(radians);
posX = posX + x;
posY = posY + y;
for(int i=0; i<beetleBug.size; i++){
beetleBug.get(i).setPosition(posX - offSet, posY -offSet);
}
}
Create a normalized vector to represent the beetle's direction, then multiply by the speed. Add that vector to the beetle's current position and you've got his new position.
Create the normalized vector (i.e. has a length of 1) using your angle. vx = cos(angle), vy = sin(angle)
Multiply by your beetle's speed. vx = vx*speed, vy = vy*speed
Add it to the current position. x = x + vx, y = y + vy
Repeat
Some gotchas: Watch out that your sprite's graphical rotation and your own internal representation of rotation go the same way. Some frameworks flip which way they rotate graphics. The above [cos(angle), sin(angle)] is for an angle of zero pointing towards the positive x axis. Many implementations of cos/sin/tan use radians instead of degrees for their calculations, so convert as appropriate.
[cos angle, sin angle]is for zero to the right (positive x), counterclockwise. [-sin angle, cos angle]is for zero pointing up (positive y), counterclockwise.
This might work:
int currentX = 100; //beetleCurrentX
int currentY = 100; //beetleCurrentY
int angle = 200; //beetleAngle
int len = 2; //Step that the beetle makes (jumps 2 in this case)
int x2Pos = sin(angle)*len + currentX;
int y2Pos = cos(angle)*len + currentY;
sprite.setPosition(x2Pos,y2Pos);
If you execute this each frame you will have your beetle moving in the angles direction.

Categories

Resources