How do I reflect an angle of a Point2D object - java

I'm trying to make a pong game using JavaFX and I've decided to use Point2D for the paddle and ball positions.
I created this method to check for wall collisions
public void checkWallCollision(){
boolean ballHitBottom = posBall.getY() > 500;
boolean ballHitTop = posBall.getY() < 50;
boolean ballHitLeft = posBall.getX() < 0;
boolean ballHitRight = posBall.getX() > 725;
if (ballHitTop || ballHitBottom){
ballDirVector = ballDirVector.multiply(-1);
}
if(ballHitLeft || ballHitRight){
ballDirVector = ballDirVector.multiply(-1);
}
}
But with a Point2D object like that, I can't just multiply it like that. I need the angle to be reflected. I guess another thing I could show to help is how I launch the ball. In this method I do have the angle, but I'm not sure what way I should go about accessing it.
public void launchBall(){
// Launch the ball
boolean ballDirection = random.nextBoolean();
// bound to an acute angle on start
double ballAngle = Constants._PADDLE_ANGLES[random.nextInt(5) + 1];
if (ballDirection){
ballAngle *= -1;
}
ballSpeed = Constants._BallStartSpeed;
ballDirVector = new Point2D(Math.cos(ballAngle), Math.sin(ballAngle));
}

Related

How to make the bouncing ball collide with the array of rectangles on Processing?

im trying to make the bouncing ball bounce on the arrays of rectangles. I've looked at various other codes but cant seem to find a solution. Would appreciate any help!!!
Basically, i want the bouncing ball to recognise that theres the rectangles there and for it to be able to jump onto the rectangles.
PVector location; // Location of shape
PVector velocity; // Velocity of shape
PVector gravity; // Gravity acts at the shape's acceleration
PVector upwardForce;
PImage bg;
int radius = 10, directionX = 1, directionY = 0;
float x=20, y=20, speed=0.5;
int xarray[] = new int[20];
int yarray[] = new int[20];
// =========================================================
void setup() {
size(380,750);
location = new PVector(100,50);
velocity = new PVector(0.0,2.1);
upwardForce = new PVector(0.0,-10.0);
gravity = new PVector(0,0.4);
bg = loadImage("bg.png");
bg.resize(1600,1600);
background(0);
for(int i =0; i< 20;i++){
xarray[i]= i*100;
yarray[i] = 750-int(random(10))*50;
}
}
int xd =0, yd=0;
void draw() {
background(0);
noStroke();
xd--;
yd++;
// display image twice:
image(bg, y, 0);
image(bg, y+bg.height, 0);
// pos
y--;
if (y<-bg.height)
y=0;
for (int i = 0;i< 20;i++){
if (xarray[i] <100 && xarray[i]+100 >100){
fill(255,0,0);
}
else {
fill(255);
}
rect(xarray[i],yarray[i],100,1200);
fill(255);
xarray[i]=xarray[i]-4;
//yarray[i]=yarray[i]+1;
if (xarray[i] + 100 < 0){
xarray[i]+=2000;
// yarray[i]-=850;
}
}
// changing Position
x=x+speed*directionX;
y=y+speed*directionY;
// check boundaries
if ((x>width-radius) || (x<radius))
{
directionX=-directionX;
}
if ((y>height-radius) || (y<radius))
{
directionY=-directionY;
}
// draw
// if(direction==1)
// Add velocity to the location.
location.add(velocity);
// Add gravity to velocity
velocity.add(gravity);
// Bounce off edges
if ((location.x > width) || (location.x < 0)) {
velocity.x = velocity.x * -1;
}
if ((location.y > height) || (location.y < 0)){
// We're reducing velocity ever so slightly
// when it hits the bottom of the window
velocity.y = velocity.y * -0.95;
location.y = height;
}
// Display circle at location vector
stroke(255);
strokeWeight(0);
fill(255);
ellipse(location.x,location.y,30,30);
}
void keyPressed()
{
velocity.add(upwardForce);
}
The best advice we can give you is to break your problem down into smaller steps and to take those steps on one at a time.
For example, can you create a simple sketch that just shows a single hard-coded circle and a single hard-coded rectangle? Now add some code that prints a message to the console if they're colliding. You're going to have to do some research into collision detection, but here's a hint: a common technique is to treat the ball as a rectangle, so you can do rectangle-rectangle collision detection.
Get that working perfectly by itself, and then work your way forward in small steps. Can you add a second rectangle to your sketch? How about a third?
Then if you get stuck, you can post a MCVE (not your whole project, just a small example) along with a more specific question. Good luck.
Here's a few suggestions:
You're best off using a Rectangle class. That way, you don't have to store the locations in an array, and the collide function can be a method of the class. It's easier to just call the positions of the rectangles "x" and "y", but this would obviously conflict with the x and y global variables which you declared at the top of the code. Assuming that you would want to make the ball bounce if it collided, you would need to have a "ballLastx" and a "ballLasty" in order to keep track of which direction the ball came from. You would also need to store the Rectangles in an array or arrayList. It would be something like this:
PVector lastLocation;
Rectangle[] rects;
As for the rectangle class, here's how it would probably look like this:
class Rectangle {
float x, y;
Rectangle(float x_, float y_) {
x = x_;
y = y_;
}
void show() {
//Displays rectangle
if (x < 100 && x+100 > 100) fill(255,0,0);
else fill(255);
rect(x,y,100,1200);
fill(255);
x=x-4;
if (x + 100 < 0) x+=2000;
}
private boolean insideX(PVector pos) {
return (pos.x + 15 >= x && pos.x - 15 <= x+100);
}
private boolean insideY(PVector pos) {
return (pos.y + 15 >= y && pos.y - 15 <= x + 1200);
}
boolean collidedX() {
//Detects if the ball has collided along the x-axis
return ((insideX(location) && !insideX(lastLocation)) && insideY(location))
}
boolean collidedY() {
//Detects if the ball has collided along the y-axis
return ((insideY(location) && !insideY(lastLocation)) && insideX(location))
}
}
And then, in your setup function, you could declare the Rectangle classes in a for-loop:
//declare the rects array
rects = new Rectangle[20];
//declare each item of the rects array to be a Rectangle
for(int i = 0; i < rects.length; i++) {
rects[i] = new Rectangle(i*100, 750-int(random(0,10))*50;
}
In order to detect the collision and to bounce the ball, you would need to loop through all of the Rectangles and see if the ball should bounce off any of them:
boolean bouncex = false;
boolean bouncey = false;
//see if any of the rects are colliding with the ball
for(Rectangle r : rects) {
if(r.collidedX()) bouncex = true;
if(r.collidedY()) bouncey = true;
}
//if any are colliding, bounce the ball
if(bouncex) velocity.x = -velocity.x;
if(bouncey) velocity.y = -velocity.y;
Finally, don't forget to set the lastLocation PVector to the current location, just before moving the current location:
lastLocation = location.copy();
//move the ball...
Hope this was helpful!

How do I add some multiplayer code (for java) that does simple things like updates clients' positions

How do I add some code into my multiplayer game that I am creating using ThinMatrix's tutorials on YouTube? I have been following his tutorials when I decided to go... well... on a tangent and start adding my own things to my game. Could someone help me with some code that updates the position of player entities in the game? I already know how to do (basic) networking.
Thank you!
Edit: Code added from comments
Camera Class:
package entities;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.util.vector.Vector3f;
public class Camera {
private float distanceFromPlayer = 35;
private float angleAroundPlayer = 0;
private Vector3f position = new Vector3f(0, 0, 0);
private float pitch = 20;
private float yaw = 0;
private float roll;
private Player player;
public Camera(Player player){
this.player = player;
}
public void move(){
calculateZoom();
calculatePitch();
calculateAngleAroundPlayer();
float horizontalDistance = calculateHorizontalDistance();
float verticalDistance = calculateVerticalDistance();
calculateCameraPosition(horizontalDistance, verticalDistance);
this.yaw = 180 - (player.getRotY() + angleAroundPlayer);
}
public Vector3f getPosition() {
return position;
}
public float getPitch() {
return pitch;
}
public float getYaw() {
return yaw;
}
public float getRoll() {
return roll;
}
private void calculateCameraPosition(float horizDistance, float verticDistance){
float theta = player.getRotY() + angleAroundPlayer;
float offsetX = (float) (horizDistance * Math.sin(Math.toRadians(theta)));
float offsetZ = (float) (horizDistance * Math.cos(Math.toRadians(theta)));
position.x = player.getPosition().x - offsetX;
position.z = player.getPosition().z - offsetZ;
position.y = player.getPosition().y + verticDistance + 4;
}
private float calculateHorizontalDistance(){
return (float) (distanceFromPlayer * Math.cos(Math.toRadians(pitch+4)));
}
private float calculateVerticalDistance(){
return (float) (distanceFromPlayer * Math.sin(Math.toRadians(pitch+4)));
}
private void calculateZoom(){
float zoomLevel = Mouse.getDWheel() * 0.03f;
distanceFromPlayer -= zoomLevel;
if(distanceFromPlayer<5){
distanceFromPlayer = 5;
}
}
private void calculatePitch(){
if(Mouse.isButtonDown(1)){
float pitchChange = Mouse.getDY() * 0.2f;
pitch -= pitchChange;
if(pitch < 0){
pitch = 0;
}else if(pitch > 90){
pitch = 90;
}
}
}
private void calculateAngleAroundPlayer(){
if(Mouse.isButtonDown(0)){
float angleChange = Mouse.getDX() * 0.3f;
angleAroundPlayer -= angleChange;
}
}
}
Player Class:
package entities;
import models.TexturedModel;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.vector.Vector3f;
import renderEngine.DisplayManager;
import terrains.Terrain;
public class Player extends Entity {
private static final float RUN_SPEED = 40;
private static final float TURN_SPEED = 160;
private static final float GRAVITY = -50;
private static final float JUMP_POWER = 18;
private float currentSpeed = 0;
private float currentTurnSpeed = 0;
private float upwardsSpeed = 0;
private boolean isInAir = false;
public Player(TexturedModel model, Vector3f position, float rotX, float rotY, float rotZ,
float scale) {
super(model, position, rotX, rotY, rotZ, scale);
}
public void move(Terrain terrain) {
checkInputs();
super.increaseRotation(0, currentTurnSpeed * DisplayManager.getFrameTimeSeconds(), 0);
float distance = currentSpeed * DisplayManager.getFrameTimeSeconds();
float dx = (float) (distance * Math.sin(Math.toRadians(super.getRotY())));
float dz = (float) (distance * Math.cos(Math.toRadians(super.getRotY())));
super.increasePosition(dx, 0, dz);
upwardsSpeed += GRAVITY * DisplayManager.getFrameTimeSeconds();
super.increasePosition(0, upwardsSpeed * DisplayManager.getFrameTimeSeconds(), 0);
float terrainHeight = terrain.getHeightOfTerrain(getPosition().x, getPosition().z);
if (super.getPosition().y < terrainHeight) {
upwardsSpeed = 0;
isInAir = false;
super.getPosition().y = terrainHeight;
}
}
private void jump() {
if (!isInAir) {
this.upwardsSpeed = JUMP_POWER;
isInAir = true;
}
}
private void checkInputs() {
if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
this.currentSpeed = RUN_SPEED;
} else if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
this.currentSpeed = -RUN_SPEED;
} else {
this.currentSpeed = 0;
}
if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
this.currentTurnSpeed = -TURN_SPEED;
} else if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
this.currentTurnSpeed = TURN_SPEED;
} else {
this.currentTurnSpeed = 0;
}
if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
jump();
}
}
}
You need to make it so your player class can return the current locations and doing so makes things easy as all you need to do is create another instance of the game with new packages and run it but on the server end you need to send the players current location and make this update consistently as this would use a coordinate system but this is very basic so you will already need an entity in the game such as a player entity in the server side of the game and doing so you need this entity to update its position so you could do something like player.getPosition() and then serverSideModel = player.getPosition() as it would set both models to the same position if that makes sence
The task you want to achieve is a simple one, but because of the way your code is written it has become an unnecessarily hard task to achieve. Fixing your code is not something that you are ready for without understanding more about Object Oriented code and efficient networking.
So at this point I will not attempt to fix your code but I will simply suggest a way to make it work somewhat, and be aware that you will have a lot of issues later if you continue this project.
First we need a simple way to deal with updating the position of a Player entity. We can do this by adding a method to your Player class a bit like so:
public void moveByFloatInput(fload newX, float newY, float newY) {
//Here you need to get the current terrain object for this player entity
Terrain currentTerrain = getTerrain();
//Now update the terrain object with your new x,y,z points (not sure what methods Terrain contains, you may need to make changes or expose more variables)
currentTerrain.setNewPosition(newX, newY, newZ);
//Now call the normal move method using the updated terrain with a new position
move(updatedTerrainObject);
}
Note: there may be a better way to do this, but I have no idea how the terrain class is constructed, and what methods can be called. Also note that you will want to pass the direction the player is looking, but to keep this example simple I have not included any of that.
Now when we receive the x y and z info from a client/server we can update a player entity, for example if you where using an InputStreamReader (not the best idea, but it will help you get started):
float x;
float y;
float z;
while ((message = myBufferedReaderInputFromSocket.readLine()) != null) {
if (message != null) {
//check if X
if (message.startsWith("moveX"))
//save new X so we can update a player entity
x = Float.parseFloat(message.substring(5));
//check if Y
if (message.startsWith("moveY"))
//save new Y so we can update a player entity
y = Float.parseFloat(message.substring(5));
//check if Z
if (message.startsWith("moveZ"))
//save new Z so we can update a player entity
z = Float.parseFloat(message.substring(5));
}
//when x, y and y all have a new position we can update a player entity
if(x != null && y != null && z != null)
{
//call our new player move method and change that player entities position
playerEntityForThisSocket.moveByFloatInput(x, y, z);
//reset x,y,z to null so that the server can receive the next movement
x = null;
y = null;
z = null;
}
}
Then for sending over a socket you can do it a bit like:
//you could create a Print writer for your socket:
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
//then simply send a players new position info
//inculde the "moveX" keyword before the Float x,y,z so that the server/client at the other end knows how to process the info:
out.println(moveX + newXposition);
out.println(moveY + newYposition);
out.println(moveZ + newZposition);
If this does not make sense then I suggest that you complete the basic Java tutorials before you continue coding, because the tutorials contain the essential building blocks of coding with Java, and with that knowledge that you can build on to make a great project, but without that knowledge you are doomed to have never ending issues:
http://docs.oracle.com/javase/tutorial/index.html

LibGDX - Map Boundaries

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.

Method not recognizing the call this in JavaFX application

I'm working on a program that displays circles colliding with the wall and with themselves.
I'm having trouble with the method that will compensate for collisions.
public class bouncyFX extends Application {
public ArrayList<Ball> arr = new ArrayList<Ball>();
public static void main(String[] args) {
launch(args);
}
static Pane pane;
#Override
public void start(final Stage primaryStage) {
pane = new Pane();
final Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
pane.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(final MouseEvent event) {
final Ball ball = new Ball(event.getX(), event.getY(), 40, Color.AQUA);
ball.circle.relocate(event.getX(), event.getY());
pane.getChildren().addAll(ball.circle);
arr.add(ball);
final Bounds bounds = pane.getBoundsInLocal();
final Timeline loop = new Timeline(new KeyFrame(Duration.millis(10), new EventHandler<ActionEvent>() {
double deltaX = ball.ballDeltaX;
double deltaY = ball.ballDeltaY;
public void handle(final ActionEvent event) {
ball.circle.setLayoutX(ball.circle.getLayoutX() + deltaX);
ball.circle.setLayoutY(ball.circle.getLayoutY() + deltaY);
final boolean atRightBorder = ball.circle.getLayoutX() >= (bounds.getMaxX()-ball.circle.getRadius());
final boolean atLeftBorder = ball.circle.getLayoutX() <= (bounds.getMinX()+ball.circle.getRadius());
final boolean atBottomBorder = ball.circle.getLayoutY() >= (bounds.getMaxY()-ball.circle.getRadius());
final boolean atTopBorder = ball.circle.getLayoutY() <= (bounds.getMinY()+ball.circle.getRadius());
if(atRightBorder || atLeftBorder)
deltaX *= -1;
if(atBottomBorder || atTopBorder)
deltaY *= -1;
for(int i = 0; i<arr.size(); i++){
for(int j = i+1; j<arr.size()-1; j++){
arr.get(i).collisionMagnitued(arr.get(j));
}
}
}
}));
loop.setCycleCount(Timeline.INDEFINITE);
loop.play();
}
});
}
class Ball{
public Circle circle;
public double ballDeltaX = 3;
public double ballDeltaY = 3;
public void AddBall(Ball b){
arr.add(b);
}
public Ball(double X, double Y, double Rad, Color color) {
circle = new Circle(X, Y, Rad);
circle.setFill(color);
}
private boolean defineCollision(Ball b){
double xd = this.circle.getLayoutX() - b.circle.getLayoutX();
double yd = this.circle.getLayoutY() - b.circle.getLayoutY();
double sumRad = this.circle.getRadius() + b.circle.getRadius();
double squareRad = Math.pow(sumRad, 2);
double distSquare = Math.pow(xd, 2) + Math.pow(yd, 2);
if(distSquare <= squareRad){
return true;
}return false;
}
public void collisionMagnitued(Ball b){
if(this.defineCollision(b)){
double tempDeltaX = ballDeltaX;
double tempDeltaY = ballDeltaY;
if((this.ballDeltaX < 0 && b.ballDeltaX > 0) || (this.ballDeltaX >0 && b.ballDeltaX <0)){
this.ballDeltaX *= -this.ballDeltaX;
b.ballDeltaX *= -b.ballDeltaX;
System.out.println("tredje");
}
if((this.ballDeltaY < 0 && b.ballDeltaY > 0) || (this.ballDeltaY > 0 && b.ballDeltaY < 0)){
this.ballDeltaY *= -this.ballDeltaY;
b.ballDeltaY *= -b.ballDeltaY;
System.out.println("fjärde");
}
else{
System.out.println("Knull");
this.ballDeltaX *= -1;
b.ballDeltaX *= -1;
}
}
}
}
}
The Balls (or circles) are created and are bouncing against the Bounds as expected.
The Collision detection method works as I'm getting print statements inside the last method. However, it seems that there's something wrong with either my ArrayList not being filled with objects or the method trying to compare the parameter Ball and the Ball that calls the method.
Am I way off? Not sure how I'm suppossed to go forth from here.
I see a few issues with your logic:
The first problem is that when the balls "bounce" off the boundaries of the pane, you don't change their ballDeltaX or ballDeltaY values (you just change a local value in the animation loop and use the local value to update the position). So the first time two balls collide, both of their ballDeltaX and ballDeltaY values are equal to +3 (the initial value), which may not represent the actual direction the animation loop is moving them in. In fact, you never actually use any updated values of ballDeltaX or ballDeltaY to compute the new positions; you get the initial values of those variables, copy them into deltaX and deltaY, and then just compute the new positions using deltaX and deltaY. So if you change ballDeltaX and ballDeltaY, the animation loop never sees the change.
The for loops look wrong to me; I don't think they compare the last two elements of the list. (When i = arr.size()-2 in the penultimate iteration of the outer loop, your inner loop is for (int j = arr.size() - 1; j < arr.size() -1; j++) {...} which of course never iterates.) I think you want the bounding conditions to be i < arr.size() - 1 and j < arr.size(), i.e. the other way around.
And then your if/else structure in collisionMagnitued(...) is probably not exactly what you want. I'm not sure what you're trying to implement there, but the else clause only kicks in if the second if is false, and no matter what happens in the first if.
Finally, you are starting a new animation on each mouse click. So, for example, if you have three balls bouncing around, you have three animation loops running, each of which is updating values when the balls collide. You need to start just one loop; it shouldn't do any harm if it refers to an empty list.

How to do collision detection with many walls (maze)?

In my game, the player navigates a maze. I can't figure out how to do proper collision detection with the walls. It is easy to do collision detection for staying in a certain area:
if (x > rightWallX - playerWidth) x = rightWallX - playerWidth;
if (x < leftWallX) x = leftWallX;
//...
But how would I do collision detection for many walls?
I can do plain collision detection without correction (like if (intersecting) return true;), but I can't correct this correctly. If I just store the old x and y and reset them, then
The object never actually touches the wall
If the object can go up but is blocked to the right, it won't go up, it will just not move.
How is collision detection in a maze done?
The easiest way, once you have solved collision detection, to fix the collision is to move the actor to the closest valid position to where the actor would be were it not for the object it collides with. This assumes no inertia, but it is sufficient for maze-like games or top-down map-crawling games.
If you want to simplify your calculations further, you can limit yourself to detecting if changing the actor's x or y coordinate would be better. If your actor has an axis-aligned rectangular hit-box and all obstacles are axis-aligned rectangular as well (the simplest case), this assumption is indeed correct. However, the results might not be satisfactory in some other cases (potential artifact: speed boost from gliding diagonal walls - not the case in most maze games).
Keep in mind multiple collisions could happen concurrently (pushing against two walls). If there are no sharp angles between two walls that an actor could both intersect (say, if all your obstacles are axis aligned and sufficiently spaced), fixing each collision in turn will suffice - just don't stop after the first collision.
You can use Rectangle.intersects() method:
public Rectangle Player(){
return new Rectangle(PlayerX,PlayerY,PlayerWidth,PlayerHeight);
//we do this for getting players x and y values every tick
}
if(Player().intersects(new Rectangle(0,0,100,50)))//if(player touching wall)
new Rectangle(0,0,100,50) is just an example you can change it.
Ok so i'm currently making a 2D top down view game and I'm not sure how you created your maze. However, in my game my Level is created from a Tile[][] tiles = new Tile[levelWidth][levelHeight]; array. The way i handled collision detection was by checking the surrounding tiles to see if they were solid.
This is my getTile method.
public Tile[][] getTile(int x, int y) {
if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) {
return new VoidTile();
} else {
return tiles[x][y];
}
}
In my Tile.java class i have a isSolid() method which returns whether the tile is solid or not. All of my tiles extend my Tile.java so they inherit this method and I override it in their constructor. As i said previously, I am not sure whether or not you use the same style of level implementation as i do. However, It is good practice to do it this way :)
Personally, I am not a big fan of using the .intersects() and .contains() methods for Sprite collision detection. I mainly use them for buttons and alike.
Ok so,
In my player.java class i have a checkBlockedDirection(int x, int y) method and it looks like this.
public void checkBlockedDirection(int x, int y) {
boolean u = map.getTile(x, y - 1).isSolid();
boolean d = map.getTile(x, y + 1).isSolid();
boolean l = map.getTile(x - 1, y).isSolid();
boolean r = map.getTile(x + 1, y).isSolid();
if (u) {
uBlocked = true;
System.out.println("up tile blocked");
} else {
uBlocked = false;
}
if (d) {
dBlocked = true;
System.out.println("down tile blocked");
} else {
dBlocked = false;
}
if (l) {
lBlocked = true;
System.out.println("left tile blocked");
} else {
lBlocked = false;
}
if (r) {
rBlocked = true;
System.out.println("right tile blocked");
} else {
rBlocked = false;
}
}
Then in my player update method i have this
public void tick() {
float dx = 0;
float dy = 0;
if (input.up.isPressed()) {
direction = 0;
} else if (input.down.isPressed()) {
direction = 2;
} else if (input.left.isPressed()) {
direction = 3;
} else if (input.right.isPressed()) {
direction = 1;
} else {
direction = 4; // standing
}
checkBlockedDirection((int)x, (int)y);
if (input.up.isPressed() && y > 0 && !uBlocked) {
dy += -speed;
} else if (input.down.isPressed() && y < map.getHeight() - 1 && !dBlocked) {
dy += speed;
} else if (input.left.isPressed() && x > 0 && !lBlocked) {
dx += -speed;
} else if (input.right.isPressed() && x < map.getWidth() - 1 && !rBlocked) {
dx += speed;
}
x += dx;
y += dy;
}
Basically it just checks whether or not the blocks up, down, left, or right are solid. If they are solid then it wont move and if they arent solid then you can move in the desired direction.
Not sure if this helps or not but it's just my take on this kind of grid collision detection :)
Hope this helps :)
Enjoy

Categories

Resources