The bitmap A has a position which is its X/Y position is static, bitmap B is moving bitmap which moves from the bottom of the screen to where ever the user touches on screen. When the moving bitmap reaches the static bitmap however it is not recognized as a collision, the moving bitmap uses float which i round off and the static bitmap uses int. Below is a snippet of code, I want to figure out what is needed to be changed for my bitmaps crossing paths to be considered a collision
canvas.drawBitmap(rock2, 70, 32, null);
//this is where it checks to see if the moving bitmaps y coordinate is matched to the Y coordinate of the static bitmap
if(canvas.getHeight() - Math.round(animY*-1) == (canvas.getHeight() - (canvas.getHeight()-32))){
Log.d("ALERT", "COLLIDE");
}
//the Y value is *-1 because the moving bitmap is starting from the bottom of the screen so this converts the value to positive value
I wonder if my calculations are off, or I am going about this collision the wrong way. Below is my movement code snippet
//UP Y ANIMATION
if(animY*-1 < canvas.getHeight() && !bumpY){
animY += speedY;
//IF ROCK IS NOT AT EXTREME LEFT OR EXTREME RIGHT KEEP ON TRACK
if(animX < (canvas.getWidth()/2) || animX*-1 < (canvas.getWidth()/2) && !bumpX){
animX += speedX;
//IF ROCK HITS EDGE LEFT/RIGHT RETURN TO SENDER (INDICATE BUMP)
if(animX*-1 > (canvas.getWidth()/2) - rock.getWidth()/2 || animX > (canvas.getWidth()/2) - rock.getWidth()/2){
bumpX = true;
bumpY = true;
}
}
//IF Y HITS TOP OF SCREEN
if(animY*-1 > canvas.getHeight()){
bumpY = true;
}
}
//DOWN Y ANIMATION
if(animY < 0 && bumpY){
//REVERSE DIRECTION OF Y
animY -= speedY;
//IF ROCK HITS TOP OR SIDE REVERSE X DIRECTION
if(bumpX || bumpY)
animX -= speedX;
//IF AT STARTING POINT
if(animY > 0){
bumpY = false;
bumpX = false;
}
}
//in an ontouch method where the X and Y values are calculated
case MotionEvent.ACTION_UP:
finalX = event.getX() - (cross.getWidth() / 2);
finalY = event.getY() - (cross.getHeight() / 2);
moveToX = finalX - startX;
moveToY = finalY - startY;
speedX = moveToX / 50;
speedY = moveToY / 50;
break;
Any tips would be appreciated, thank you.
sorry if this is a dumb answer, but should your check be:
if(canvas.getHeight() - Math.round(animY*-1) >= (canvas.getHeight() - (canvas.getHeight()-32))){
instead? (change the "==" to ">=" ) otherwise you are only checking for when the rock lands EXACTLY on (canvas.getheight -32), rather than checking past it
Related
so recently, we've been assigned to code multiple circles that act like robots in a GUI interface. Basically, a robot simulator.
I've got the code to spawn in multiple circles that can act like robots.
This is my current code for detecting wall collision between the robot and the end of the square:
private void checkCollisions(double maxX, double maxY) {
for (ListIterator<Ball> slowIt = balls.listIterator(); slowIt.hasNext();) {
Ball b1 = slowIt.next();
// check wall collisions:
double xVel = b1.getXVelocity();
double yVel = b1.getYVelocity();
if ((b1.getCenterX() - b1.getRadius() <= 0 && xVel < 0)
|| (b1.getCenterX() + b1.getRadius() >= maxX && xVel > 0)) {
b1.setXVelocity(-xVel);
}
if ((b1.getCenterY() - b1.getRadius() <= 0 && yVel < 0)
|| (b1.getCenterY() + b1.getRadius() >= maxY && yVel > 0)) {
b1.setYVelocity(-yVel);
}
for (ListIterator<Ball> fastIt = balls.listIterator(slowIt.nextIndex()); fastIt.hasNext();) {
Ball b2 = fastIt.next();
final double deltaX = b2.getCenterX() - b1.getCenterX() ;
final double deltaY = b2.getCenterY() - b1.getCenterY() ;
if (colliding(b1, b2, deltaX, deltaY)) {
bounce(b1, b2, deltaX, deltaY);
}
}
}
}
The
b1.setXVelocity(-xVel);
b1.setYVelocity(-yVel);
are the main bits that make the circle bounce back from the wall. However, instead of this, I want the ball to detect the wall and rotate 90 degrees rather than bounce back form the wall like a bouncing ball.
Any help will be fully appreciated or a working piece of code that ca do this for me. I have an AraryList of all the balls called 'balls'.
If needed, I can give source code.
This is what I have so far. But I need each ball to have a sensor attached to them detecting if there a wall ahead.
https://i.stack.imgur.com/XsQvX.png
Assuming you have just square walls:
If the ball hits the right wall for example, you want to remove all x velocity, and then add either positive or negative velocity.
The issue with this is that the robot will end up just going around the outside edges of the map.
I have custom view control, which looks like this:
Inside my activity I want to be able to move this view around the screen by dragging it on green Arcs (left or right does not matter).
Also want to be able to detect if yellow arc at top is tapped, middle circle or bottom arc.
I'm having trouble to detect where the tap is in which area. This is the code that I'm using inside of my activity:
float dX, dY;
final MyCustomView myCustomView = (MyCustomView)findViewById(R.id.test);
final Boolean[] movable = {false};
myCustomView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
movable[0] = false;
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
int x = (int) event.getX();
int y = (int) event.getY();
if (myCustomView.leftArcRegion.contains(x,y) || myCustomView.rightArcRegion.contains(x,y)){
movable[0] = true;
} else if (myCustomView.topArcRegion.contains(x,y)){
//todo: do something if top arc area is selected
} else if (myCustomView.midRoundedBitmapRegion.contains(x,y)){
//todo: do something if mid bitmap area is selected
} else if (myCustomView.bottomArcRegion.contains(x,y)){
//todo: do something if bottom arc area is selected
}
break;
case MotionEvent.ACTION_MOVE:
if (movable[0]) {
view.animate()
.x(event.getRawX() + dX)
.y(event.getRawY() + dY)
.setDuration(0)
.start();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
default:
return false;
}
return true;
}
});
And these are public fields from my custom view control:
public Region topArcRegion;
private Path topArc;
//topArc is my top arc path
RectF rectFTop = new RectF();
topArc.computeBounds(rectFTop, true);
topArcRegion = new Region();
topArcRegion.setPath(topArc, new Region((int) rectFTop.left, (int) rectFTop.top,
(int) rectFTop.right, (int) rectFTop.bottom));
But it looks like it uses rectangular shapes for these regions, not arcs when checking with this "contains" method. And because of that I'm not getting expected results.
So, how can I detect where is initial tap (top arc, bottom arc, side arcs or middle bitmap) in order to apply my app logic?
Since you're only looking to detect touches inside an arc segment, it should't be too complicated.
Each of your arc segments is defined as the space between two concentric circles and between start and end angles. So all you really want to do is do a little trig to determine the distance from the center of the circles to your touch point and the angle from the center to your touch point.
float x = touchevent.getX();
float y = touchevent.getY();
// Transform relative to arc centers
x -= circle1.x;
y -= circle1.y;
double dist = Math.sqrt(x*x + y*y);
double angle = Math.atan2(y,x) * 180 / Math.PI;
// Given an arc segment defined by circle1, circle2, angle1, angle2:
boolean touch = dist > circle1.radius && dist < circle2.radius &&
angle > angle1 && angle < angle2;
You'll probably have to play around a bit depending on whether angle1 > angle2 or vice versa. If there's any chance that any of the angles cross the zero-degree angle, it gets a little trickier.
Meta: For clarity, I used sqrt() to compute distance, but you can optimize this code by skipping the sqrt() and compare distance² instead:
double dist2 = x*x + y*y;
if (dist2 > circle1.radius * circle1.radius &&
dist2 < circle2.radius * circle2.radius &&
...
One more edit: computing trig functions can be expensive; certainly a lot more expensive than computing distance².
In the interest of optimization, you should check the distance against the circle radii before bothering with the trig:
boolean touch = dist > circle1.radius && dist < circle2.radius;
if (touch) {
// This is only a *possible* touch, check the angles now
double angle = Math.atan2(y,x) * 180 / Math.PI;
touch = angle > angle1 && angle < angle2;
}
Synopsis
Well, I'm making a little top-down JRPG and today I was like 'Yeah, I'm gonna bust out this whole map collision thing!'. I failed.
Problem
So I went on the internet and looked up 'LibGDX Tiled Map Collision Detection' and found a really neat post about Map Objects so I added in a map object layer and did all that biz and came out with this little method to ensure the player can move freely around the map but at the same time can't exit it but each time I've tried it ends up with a horrible result such as the player moving off the screen. The latest error is that the player gets stuck doing a walk animation and can't move anywhere else!
Code
package com.darkbyte.games.tfa.game.entity.entities;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.maps.objects.RectangleMapObject;
import com.badlogic.gdx.math.Rectangle;
import com.darkbyte.games.tfa.game.entity.Entity;
import com.darkbyte.games.tfa.game.entity.SpriteSheet;
import com.darkbyte.games.tfa.game.world.map.MapManager;
import com.darkbyte.games.tfa.render.Batch;
import com.darkbyte.games.tfa.render.Camera;
public class Player extends Entity {
// The constructor for the player class
public Player(String name, SpriteSheet spriteSheet) {
super(name, spriteSheet);
direction = Direction.DOWN;
collisionBox = new Rectangle(x, y, 64, 64);
}
// A flag to see if the player is moving
private boolean isMoving;
// The variable that holds the state time
private float stateTime;
// The player's walking animations
private Animation[] walkAnimations = {
spriteSheet.getAnimation(8, 8, 1 / 16f),
spriteSheet.getAnimation(9, 8, 1 / 16f),
spriteSheet.getAnimation(10, 8, 1 / 16f),
spriteSheet.getAnimation(11, 8, 1 / 16f) };
// The player's static frames
private TextureRegion[] staticFrames = {
spriteSheet.getTexture(8, 0),
spriteSheet.getTexture(9, 0),
spriteSheet.getTexture(10, 0),
spriteSheet.getTexture(11, 0) };
// The render code for the player
#Override
public void render() {
// Makes the camera follow the player
Camera.setCameraPosition(x, y);
Batch.getGameBatch().setProjectionMatrix(Camera.getCamera().combined);
// Updates the state time
stateTime += Gdx.graphics.getDeltaTime();
// Gets the player's direction, if the player's moving, it sets the
// current frame to the frame that would be played at the current moment
// based on the state time
// If the player isn't moving, it sets the current frame to the static
// frame associated to the direction
switch (direction) {
case UP:
if(isMoving) {
currentFrame = walkAnimations[0].getKeyFrame(stateTime, true);
} else
currentFrame = staticFrames[0];
break;
case LEFT:
if(isMoving) {
currentFrame = walkAnimations[1].getKeyFrame(stateTime, true);
} else
currentFrame = staticFrames[1];
break;
case DOWN:
if(isMoving) {
currentFrame = walkAnimations[2].getKeyFrame(stateTime, true);
} else
currentFrame = staticFrames[2];
break;
case RIGHT:
if(isMoving) {
currentFrame = walkAnimations[3].getKeyFrame(stateTime, true);
} else
currentFrame = staticFrames[3];
break;
}
}
// The tick code for the player
#Override
public void tick() {
// The object to represent the bounds of the land on the map
RectangleMapObject land = (RectangleMapObject) MapManager.getCurrentMap().getMap().getLayers().get("collision").getObjects().get("land");
// Checks if the player is within the bounds of the map
if(land.getRectangle().contains(collisionBox)) {
// If the player is moving but the arrow keys aren't pressed, sets isMoving to false
isMoving = (isMoving && (Gdx.input.isKeyPressed(Keys.W) || Gdx.input.isKeyPressed(Keys.UP)
|| Gdx.input.isKeyPressed(Keys.A) || Gdx.input.isKeyPressed(Keys.LEFT)
|| Gdx.input.isKeyPressed(Keys.S) || Gdx.input.isKeyPressed(Keys.DOWN)
|| Gdx.input.isKeyPressed(Keys.D) || Gdx.input.isKeyPressed(Keys.RIGHT)));
// Checks to see if the arrow / WASD keys are pressed and moves the
// player in the correct direction at the speed of 1.5 pixels/tick
// (45/second)
// It also sets the players state to moving and corresponds it's
// direction to the key pressed
// Doesn't move if opposing keys are pressed
if(Gdx.input.isKeyPressed(Keys.W) || Gdx.input.isKeyPressed(Keys.UP)) {
if(!(Gdx.input.isKeyPressed(Keys.S) || Gdx.input.isKeyPressed(Keys.DOWN))) {
direction = Direction.UP;
y += 1.5f;
isMoving = true;
}
}
if(Gdx.input.isKeyPressed(Keys.A) || Gdx.input.isKeyPressed(Keys.LEFT)) {
if(!(Gdx.input.isKeyPressed(Keys.D) || Gdx.input.isKeyPressed(Keys.RIGHT))) {
direction = Direction.LEFT;
isMoving = true;
x -= 1.5f;
}
}
if(Gdx.input.isKeyPressed(Keys.S) || Gdx.input.isKeyPressed(Keys.DOWN)) {
if(!(Gdx.input.isKeyPressed(Keys.W) || Gdx.input.isKeyPressed(Keys.UP))) {
direction = Direction.DOWN;
y -= 1.5f;
isMoving = true;
}
}
if(Gdx.input.isKeyPressed(Keys.D) || Gdx.input.isKeyPressed(Keys.RIGHT)) {
if(!(Gdx.input.isKeyPressed(Keys.A) || Gdx.input.isKeyPressed(Keys.LEFT))) {
direction = Direction.RIGHT;
x += 1.5f;
isMoving = true;
}
}
} else {
if(!isMoving) {
// If the player's just spawned puts the player to the map's spawn point
x = MapManager.getCurrentMap().getPlayerSpawnX();
y = MapManager.getCurrentMap().getPlayerSpawnY();
} else { // If not, it just moves them back till they're no longer out of the map
if(x > (land.getRectangle().getX() + land.getRectangle().getWidth())) x -= 1.5;
if(y > (land.getRectangle().getY() + land.getRectangle().getHeight())) y -= 1.5;
}
}
// Synchronises the collision box with the player's x and y position
collisionBox.x = x;
collisionBox.y = y;
}
// Returns if the player is moving
public boolean isMoving() {
return isMoving;
}
}
Can you guys make it so that when he reaches the border that he stops but he can still keep moving in other directions instead of staying static!
Thanks for reading!
At the moment it sounds you just copy/pasted it and you need to familiarize yourself with it first. If you don't know what it does then you should learn or stop the project imho.
Anyway, from what I can tell it's just a player class that handles the animation frames based on which direction it is moving. Nothing to do with collision detection at all. It does update some kind of collisionBox but functionality for this is handled elsewhere, perhaps in the parent class Entity?
My guess is that this is a tile map and units are restricted to the grid. It's pretty easy to detect if A tile exists or not.
private boolean tileExists(int tileX, int tileY, tile[][] map)
{
return tileX >= 0 && tileY >= 0 &&
tileX < map.length && tileY < map[0].length;
}
Now whenever a entity requests a move you should check if the destination is within the map bounds.
private void moveRequest(int destinationX, int destinationY, Tile[][] map)
{
//Just return if the tile is outside of the map
if (!tileExists(destinationX, destinationY, map) return;
//Same goes for your other checks...
//Return if the tile is not walkable
if (!tileIsWalkable(destinationX, destinationY, map) return;
//Return if the tile is already occupied
if (tileIsOccupied(destinationX, destinationY, otherEntities) return;
//etc..
//Now the move is valid and you can set it's state to moving in that direction.
}
Tile maps are not very hard to understand. I will make an attempt to give you some better insight into tile maps. You have a 2D array where you store your tiles in. Tiles have a width and a height and from that you can make your own tile engine:
//Find out which tiles to draw based on the camera position and viewport size.
int startX = (int)(camera.position.x - camera.viewportWidth / 2) / tileWidth;
int startY = (int)(camera.position.y - camera.viewportHeight / 2) / tileHeight;
int endX = (int)(startX + camera.viewportWidth / tileWidth) + 1;
int endY = (int)(startY + camera.viewportHeight / tileHeight) + 1;
//Loop using this data as boundaries
for (int y = startY; y < endY; y++)
{
for (int x = startX; x < endX; x++)
{
//If out of bounds continue to next tile.
if (!tileExists(x, y, map) continue;
//Now all we need to draw the on screen tiles properly:
//x == tile position x in array
//y == tile position y in array
//World position of this tile:
//worldX = x * tileWidth;
//worldY = y * tileHeight;
//Let's draw:
batch.draw(map[x][y].getTexture, worldX, worldY,
tileWidth, tileHeight)
}
}
There really is no magic involved here at all. Drawing only what is on screen like in the above example is very important for larger maps. Other then that you should draw thing in the back first. You have several options to do this, the easiest but least versatile is just a separate the ground from the objects that can obscure things and draw this later.
Characters, creatures or other entities can just use a world position and be easily converted back to tile position.
tileX = worldX / tileWidth;
tileY = worldY / tileHeight;
So if you want to move something with the world position calculate it's tile position first using the aforementioned method. Then lookup if this tile is valid to move to. Then block that tile for other and move to it.
I have a rectangle which when I hold down the mouse button I want that rectangle to move to that point following a strait line 1 pixel at a time.
This is my code so far (I put comments in it so you can understand)
float distanceX = finalX - x; //the number of pixels needed to get to destination on the X axis
float distanceY = finalY - y; // same as above but Y axis
float moveX = distanceX > 0 ? 1 : -1; // I only want it to move 1 pixel per render
float moveY = distanceY > 0 ? 1 : -1; // same as above
Array<Stuff> collidedX = new Array<Stuff>(); //saves collisions seperately for x and y
Array<Stuff> collidedY = new Array<Stuff>(); //because I want the square to move where the mouse is pointing even if it means only aligning one axis
for (Stuff s : collidables) {
if (overlapsT(s, x + moveX, y)) {
collidedX.add(s);
}
}
if (collidedX.size < 1) {
if (distanceX != 0)
x += moveX;
}
for (Stuff s : collidables) {
if (overlapsT(s, x, y + moveY)) {
collidedY.add(s);
}
}
if (collidedY.size < 1) {
if (distanceY != 0)
y += moveY;
}
right now the problem is it goes perfectly diagonal until it lines up with one of the axis and then moves up down left or right to the destination.
I don't want to move fractions of pixels. The way my custom physics engine works is each pixel matters, fractional pixels are no good so I am trying to figure out how to smooth the path or rather how to decide when to add 1 to x and then y.
Currently I can't comment, so I have to answer. I think the Bresenham's line algorithm will help you out. It's for drawing rasterize lines.
Bresenham
I've written a program to bounce a ball around a screen. The program as written below does not work (the ball just moves off screen).
However if I declare the boolean variables atHorizontalEdge and atVerticalEdge inside the while loop, it seems to work. Why is that the case? As the booleans are defined for the entire run() method, shouldn't it be callable by the while loop even though its outside the while loop?
import acm.program.*;
import acm.graphics.*;
import java.awt.*;
public class BouncingBallv3 extends GraphicsProgram {
public void run() {
double x = (getWidth() - BALL_SIZE)/2 ; //sets the starting position of ball at center
double y = (getHeight() - BALL_SIZE)/2 ;
GOval ball = new GOval (x, y, BALL_SIZE, BALL_SIZE ); // creates a red ball at center of screen
ball.setFilled(true);
ball.setColor(Color.red);
add (ball);
double dx = 1; //increments by which the ball moves
double dy = 1;
//declares boolean variables to test if ball position is at an edge
boolean atHorizontalEdge = (ball.getX() == getWidth() - BALL_SIZE) || ball.getX() == 0 ;
boolean atVerticalEdge = (ball.getY() == getHeight() - BALL_SIZE) || ball.getY() == 0 ;
/* while loop keeps the ball moving in direction dx,dy
* if ball reaches a position at any edge, the direction dx or dy changes
*/
while (true) {
if (atHorizontalEdge) { //changes direction of ball if it hits a left/right wall
dx = -dx;
} else if (atVerticalEdge) { //changes direction of ball if it hits a top/bottom wall
dy = -dy;
}
ball.move(dx,dy);
pause (PAUSE_TIME);
}
}
private static final double BALL_SIZE = 50;
private static final int PAUSE_TIME = 5;
}
The issue is not that the declaration of the booleans is outside the while loop. It is that you are checking for your boundraies outside the while loop. Because of this, your condition is never updated and it only checks for the original state of the ball.
You should update the atHorizontalEdge and atVerticalEdge in the loop body after each iteration, I think.
UPDATE:
The while-loop body should be like this,
`
//declares boolean variables to test if ball position is at an edge
boolean atHorizontalEdge = false;
boolean atVerticalEdge = false;
/* while loop keeps the ball moving in direction dx,dy
* if ball reaches a position at any edge, the direction dx or dy changes
*/
while (true) {
atHorizontalEdge = (ball.getX() == getWidth() - BALL_SIZE) || ball.getX() == 0;
atVerticalEdge = (ball.getY() == getHeight() - BALL_SIZE) || ball.getY() == 0;
if (atHorizontalEdge) { //changes direction of ball if it hits a left/right wall
dx = -dx;
} else if (atVerticalEdge) { //changes direction of ball if it hits a top/bottom wall
dy = -dy;
}
ball.move(dx,dy);
pause (PAUSE_TIME);
}`
The reason why it works if you define atHorizontalEdge and atVerticalEdge inside loop is because each iteration these two variables are re-computed (i.e. updated).
atHorizontalEdge and atVerticalEdge can be declared inside or outside the while loop, that's not important.
The important thing is, the following is calculated only once, before the loop starts:
atHorizontalEdge = (ball.getX() == getWidth() - BALL_SIZE) || ball.getX() == 0 ;
atVerticalEdge = (ball.getY() == getHeight() - BALL_SIZE) || ball.getY() == 0 ;
Therefore atHorizontalEdge and atVerticalEdge will each have the same value from the start to the end of your run method (which is forever).
You obviously want the above two lines to be executed at every iteration in your loop, since they're not going to update by themselves...
while (true) {
atHorizontalEdge = (ball.getX() == getWidth() - BALL_SIZE) || ball.getX() == 0 ;
atVerticalEdge = (ball.getY() == getHeight() - BALL_SIZE) || ball.getY() == 0 ;
...
}
EDIT: Also, it'd be a better idea to check if the x and y were greater than or equal to the width/height, and less than or equal to 0 for 2 reasons:
If you decide to change the increment from 1 you could skip that exact value and cause a bug, but more importantly:
You're using a double and the floating-point representation of the number might not be exactly what you're comparing it to, so == can cause bugs, and the ball might go past the edge and keep going.
ie. ball.getX() >= getWidth() ... ball.getX() <= 0
What Every Computer Scientist Should Know About Floating-Point Arithmetic