Using touchdown in libGDX - java

I am trying to use touchdown() in my game. Motive is simple, I just want to move the player based on touch detection on screen, But it is giving the following error:
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.mygdx.game.sprites.Ron.touchDown(Ron.java:39)
at com.badlogic.gdx.backends.lwjgl.LwjglInput.processEvents(LwjglInput.java:329)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:215)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:124)
and the code is:
package com.mygdx.game.sprites;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.mygdx.game.Constants;
/**
* Created by Vamsi Rao on 11/9/2016.
*/
public class Ron extends InputAdapter {
private Vector2 position;
private Vector2 velocity;
Vector2 worldClick;
boolean right;
boolean left;
private ExtendViewport viewport;
private Texture ron;
public Ron(int x, int y, ExtendViewport viewport) {
super();
position = new Vector2(x, y);
velocity = new Vector2(Constants.VELOCITY_X, 0);
this.viewport = viewport;
ron = new Texture("ron.png");
}
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
worldClick = viewport.unproject(new Vector2(screenX, screenY));
if (worldClick.x >= viewport.getWorldWidth() / 4) {
right = true;
}
if (worldClick.x < viewport.getWorldWidth() / 4) {
left = true;
}
return true;
}
public Vector2 getPosition() {
return position;
}
public Texture getTexture() {
return ron;
}
public void update(float delta) {
left=false;
right=false;
if (right) {
position.x += delta * velocity.x;
}
if (left) {
position.x -= delta * velocity.x;
}
}
}
and I had set Input processor in another class which is using the object ron of Ron class(the class shown above) like this:
Gdx.input.setInputProcessor(ron);

39 line is
worldClick = viewport.unproject(new Vector2(screenX, screenY));
so the NullPointerException is rather about viewport variable - you are passing this to the constructor
public Ron(int x, int y, ExtendViewport viewport) {
...
this.viewport = viewport;
...
so probably the viewport is being provided with null value.
Take a look at What is a NullPointerException, and how do I fix it?
The second thing is that maybe in this case using flags is not the best idea - your code is definitely less readable, also you have a redundant unnecessary code.
//these lines are almost the same
position.x += delta * velocity.x;
position.x -= delta * velocity.x;
better would be to set the direction vector of velocity then just multiply it by delta in update like you do
//in your touchdown
if (worldClick.x >= viewport.getWorldWidth() / 4) {
velocity.x *= (velocity.x >= 0) ? 1 : -1;
}
//also add else here - there is always one or another here
else if (worldClick.x < viewport.getWorldWidth() / 4) {
velocity.x *= (velocity.x < 0) ? 1 : -1;
}
//in update
position.x += delta * velocity.x;
you can also easily implement change-velocity logic this way (like when character changes direction he slow down then starting to move backward... etc)

Related

How can i get my "Enemy" to chase the "goal" - Java

I have been trying to code a half built simple java game i was given to by a friend (I wanted to try learning coding so he gave me this old assignment from his 1st year of programming class).
He told me to try and build a game using the 2 classes he gave me, a main class and a sub class.
So far I have got everything to work as intended except for finding a way to get my enemy class to move towards the goal regardless of its position. I can get it to always move in a fixed direction towards the goal if I set it up perfectly but I want to be able to find the position of the goal regardless and make its way towards it.
The code need to be written in the peformAction method under Enemy.
If anyone would be kind enough to explain how I can go about this, it would be much appropriated, or even if you can just link me to another post that explains how to do what I'm trying to do.
Thank you in advance.
GameManager (main) Class:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
public class GameManager extends JFrame implements KeyListener {
private int canvasWidth;
private int canvasHeight;
private int borderLeft;
private int borderTop;
private BufferedImage canvas;
private Stage stage;
private Enemy[] enemies;
private Player player;
private Goal goal;
private Graphics gameGraphics;
private Graphics canvasGraphics;
private int numEnemies;
private boolean continueGame;
public static void main(String[] args) {
// During development, you can adjust the values provided in the brackets below
// as needed. However, your code must work with different/valid combinations
// of values.
GameManager managerObj = new GameManager(1920, 1280, 30);
}
public GameManager(int preferredWidth, int preferredHeight, int maxEnemies) {
this.borderLeft = getInsets().left;
this.borderTop = getInsets().top;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
if (screenSize.width < preferredWidth)
this.canvasWidth = screenSize.width - getInsets().left - getInsets().right;
else
this.canvasWidth = preferredWidth - getInsets().left - getInsets().right;
if (screenSize.height < preferredHeight)
this.canvasHeight = screenSize.height - getInsets().top - getInsets().bottom;
else
this.canvasHeight = preferredHeight - getInsets().top - getInsets().bottom;
setSize(this.canvasWidth, this.canvasHeight);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
addKeyListener(this);
Random rng = new Random(2);
this.canvas = new BufferedImage(this.canvasWidth, this.canvasHeight, BufferedImage.TYPE_INT_RGB);
// Create a Stage object to hold the background images
this.stage = new Stage();
// Create a Goal object with its initial x and y coordinates
this.goal = new Goal(this.canvasWidth / 2, Math.abs(rng.nextInt()) % this.canvasHeight);
// Create a Player object with its initial x and y coordinates
this.player = new Player(this.canvasWidth - (Math.abs(rng.nextInt()) % (this.canvasWidth / 2)),
(Math.abs(rng.nextInt()) % this.canvasHeight));
// Create the Enemy objects, each with a reference to this (GameManager) object
// and their initial x and y coordinates.
this.numEnemies = maxEnemies;
this.enemies = new Enemy[this.numEnemies];
for (int i = 0; i < this.numEnemies; i++) {
this.enemies[i] = new Enemy(this, Math.abs(rng.nextInt()) % (this.canvasWidth / 4),
Math.abs(rng.nextInt()) % this.canvasHeight);
}
this.gameGraphics = getGraphics();
this.canvasGraphics = this.canvas.getGraphics();
this.continueGame = true;
while (this.continueGame) {
updateCanvas();
}
this.stage.setGameOverBackground();
updateCanvas();
}
public void updateCanvas() {
long start = System.nanoTime();
// If the player is alive, this should move the player in the direction of the
// key that has been pressed
// Note: See keyPressed and keyReleased methods in the GameManager class.
this.player.performAction();
// If the enemy is alive, the enemy must move towards the goal. The goal object
// is obtained
// via the GameManager object that is given at the time of creating an Enemy
// object.
// Note: The amount that the enemy moves by must be much smaller than that of
// the player above
// or else the game becomes too hard to play.
for (int i = 0; i < this.numEnemies; i++) {
this.enemies[i].performAction();
}
if ((Math.abs(this.goal.getX() - this.player.getX()) < (this.goal.getCurrentImage().getWidth() / 2))
&& (Math.abs(this.goal.getY() - this.player.getY()) < (this.goal.getCurrentImage().getWidth() / 2))) {
for (int i = 0; i < this.numEnemies; i++) {
// Sets the image of the enemy to the "dead" image and sets its status to
// indicate dead
this.enemies[i].die();
}
// Sets the image of the enemy to the "dead" image and sets its status to
// indicate dead
this.goal.die();
// Sets the background of the stage to the finished game background.
this.stage.setGameOverBackground();
this.continueGame=false;
}
// If an enemy is close to the goal, the player and goal die
int j=0;
while(j<this.numEnemies) {
if ((Math.abs(this.goal.getX() - this.enemies[j].getX()) < (this.goal.getCurrentImage().getWidth() / 2))
&& (Math.abs(this.goal.getY() - this.enemies[j].getY()) < (this.goal.getCurrentImage().getWidth()/ 2))) {
this.player.die();
this.goal.die();
this.stage.setGameOverBackground();
j=this.numEnemies;
this.continueGame=false;
}
j++;
}
try {
// Draw stage
this.canvasGraphics.drawImage(stage.getCurrentImage(), 0, 0, null);
// Draw player
this.canvasGraphics.drawImage(player.getCurrentImage(),
this.player.getX() - (this.player.getCurrentImage().getWidth() / 2),
this.player.getY() - (this.player.getCurrentImage().getHeight() / 2), null);
// Draw enemies
for (int i = 0; i < this.numEnemies; i++) {
this.canvasGraphics.drawImage(this.enemies[i].getCurrentImage(),
this.enemies[i].getX() - (this.enemies[i].getCurrentImage().getWidth() / 2),
this.enemies[i].getY() - (this.enemies[i].getCurrentImage().getHeight() / 2), null);
}
// Draw goal
this.canvasGraphics.drawImage(this.goal.getCurrentImage(),
this.goal.getX() - (this.goal.getCurrentImage().getWidth() / 2),
this.goal.getY() - (this.goal.getCurrentImage().getHeight() / 2), null);
} catch (Exception e) {
System.err.println(e.getMessage());
}
// Draw everything.
this.gameGraphics.drawImage(this.canvas, this.borderLeft, this.borderTop, this);
long end = System.nanoTime();
this.gameGraphics.drawString("FPS: " + String.format("%2d", (int) (1000000000.0 / (end - start))),
this.borderLeft + 50, this.borderTop + 50);
}
public Goal getGoal() {
return this.goal;
}
public void keyPressed(KeyEvent ke) {
// Below, the setKey method is used to tell the Player object which key is
// currently pressed.
// The Player object must keep track of the pressed key and use it for
// determining the direction
// to move.
if (ke.getKeyCode() == KeyEvent.VK_LEFT)
this.player.setKey('L', true);
if (ke.getKeyCode() == KeyEvent.VK_RIGHT)
this.player.setKey('R', true);
if (ke.getKeyCode() == KeyEvent.VK_UP)
this.player.setKey('U', true);
if (ke.getKeyCode() == KeyEvent.VK_DOWN)
this.player.setKey('D', true);
if (ke.getKeyCode() == KeyEvent.VK_ESCAPE)
this.continueGame = false;
}
#Override
public void keyReleased(KeyEvent ke) {
// Below, the setKey method is used to tell the Player object which key is
// currently released.
// The Player object must keep track of the pressed key and use it for
// determining the direction
// to move.
if (ke.getKeyCode() == KeyEvent.VK_LEFT)
this.player.setKey('L', false);
if (ke.getKeyCode() == KeyEvent.VK_RIGHT)
this.player.setKey('R', false);
if (ke.getKeyCode() == KeyEvent.VK_UP)
this.player.setKey('U', false);
if (ke.getKeyCode() == KeyEvent.VK_DOWN)
this.player.setKey('D', false);
}
#Override
public void keyTyped(KeyEvent ke) {
}
}
My Enemy (sub) Class:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Enemy {
//variables
private BufferedImage imageRunning;
private BufferedImage imageOver;
private BufferedImage imageCurrent;
int valX = 200;
int valY = 200;
public Enemy (GameManager gameManager, int x, int y) {
try {
this.imageRunning = ImageIO.read(new File("C:/Users/HUS/Desktop/Assigment222/images/enemy-alive.png"));
this.imageOver = ImageIO.read(new File("C:/Users/HUS/Desktop/Assigment222/images/enemy-dead.png"));
} catch (IOException e) {
e.printStackTrace();
}
this.imageCurrent = this.imageRunning;
}
public BufferedImage getCurrentImage() {
return this.imageCurrent;
}
public void performAction() {
valX += 1;
valY += 1;
return;
}
public void die() {
// TODO Auto-generated method stub
this.imageCurrent = this.imageOver;
}
public int getX() {
// TODO Auto-generated method stub
return this.valX;
}
public int getY() {
// TODO Auto-generated method stub
return this.valY;
}
}
My Goal (sub) class:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Goal {
private BufferedImage imageRunning;
private BufferedImage imageOver;
private BufferedImage imageCurrent;
int posX = 500;
int poxY = 500;
public Goal(int x, int y) {
try {
this.imageRunning = ImageIO.read(new File("C:/Users/HUS/Desktop/Assigment222/images/goal-alive.png"));
this.imageOver = ImageIO.read(new File("C:/Users/HUS/Desktop/Assigment222/images/goal-dead.png"));
} catch (IOException e) {
e.printStackTrace();
}
this.imageCurrent = this.imageRunning;
}
public BufferedImage getCurrentImage() {
return this.imageCurrent;
}
public int getX() {
return this.posX;
}
public int getY() {
return this.poxY;
}
public void die() {
this.imageCurrent = this.imageOver;
}
}
It's impossible with the current code if you want to restrict the modifications only to the performAction method.
1) The Enemy has no way of accessing (seeing) the coordinates of the goal. This means the code can't figure out the way to the goal because it doesn't have the X and Y of where the destination is.
2) To fix that you would need Enemy to be able to see the X and Y of the Goal. The quickest way would be to use getters and setters in the GameManager class to return the stored Goal object. Then you can modify your Enemy constructor to store the reference to the GameManager that created it.
GameManager:
public Goal getGoal() {
return goal;
}
Enemy:
private GameManager gameManager;
public Enemy (GameManager gameManager, int x, int y) {
...
this.gameManager = gameManager;
}
The last step would be getting the Goal object from the stored gameManager, and using its X and Y to do some logic (this would be performed inside the performAction method):
int goalX = gameManager.getGoal().getX();
int goalY = gameManager.getGoal().getY();
...some logic to calculate the direction here...
I'm sure you'll know what to do with the numbers and figure out how to move towards the goal from there.
#Edit
If not - one way would be comparing Xs and Ys of both the Enemy and the Goal and then incrementing/decrementing valX and/or valY accordingly.
A very simple implementation:
public void performAction()
{
valX += Math.signum(gameManager.getGoal().getX() - valX);
valY += Math.signum(gameManager.getGoal().getX() - valX);
}
In your method performAction you are currently changing the x and y position respectively by incrementing them by 1 each time the method gets called.
This will move your enemy exactly 1 unit in direction x and 1 unit in direction y.
You instead want to move your character in the direction of the Goal. That's more of a math problem than a programming issue but let's give it a go:
You have 2 positions:
Your Enemy has a position of form and the same goes for your goal with position
You now want to move your Enemy in the direction of the goal. So you calculate a vector connecting these two points with
You then change the length of the vector to the speed your enemy can move with (maybe define this with a field?) by normalizing the vector and doing a scalar multiplication with your speed.
So with a given speed S this gives you the full formula:
Now let us transform this into code. In the constructor of your Enemy class save the GameManager as a field so you can call the getGoal method on it.
So we add a field private GameManager gameManager and in the Constructor we do this.gameManager = gameManager to store the reference.
Also we adda field private double speed = 2 for our calculation.
And then we change the performAction method to implement our formula. Something like this:
public void performAction()
{
// Calculate our P_g - P_e
double dX = gameManager.getGoal().getX() - valX;
double dY = gameManager.getGoal().getX() - valX;
// Calculate its length to normalize it
double divider = Math.sqrt(dX * dX + dY * dY);
// Normalize it
dX /= divider;
dY /= divider;
// Do a scalar multiplication with our speed
dX *= speed;
dY *= speed;
valX += dX;
valY += dY;
}
The only problem we are running into here is that we are adding double values to our integer values valX and valY so probably change those to double values generally or else you might run into problems where your enemy will never move at all.

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

Java LibGDX Moving to Touch Position on Android

I'm using this code but somethings aren't perfect. It's working but not smooth. First it goes touchX and then goes touchY. But I want this at the same time. I tried different codes but didn't work. How can I fix it? What's the problem?
package com.anil.game1;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
public class Game1 extends ApplicationAdapter {
private SpriteBatch batch;
private OrthographicCamera camera;
private Texture player1Texture;
private Rectangle player1Rectangle;
private Vector3 touchPos;
#Override
public void create () {
batch = new SpriteBatch();
camera = new OrthographicCamera(480, 800);
player1Texture = new Texture("Player1.png");
player1Rectangle = new Rectangle();
player1Rectangle.set(240, 0, 64, 64);
touchPos = new Vector3();
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
if (Gdx.input.isTouched()) {
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
//player1Rectangle.setPosition(new Vector2(touchPos.x, touchPos.y));
}
if (touchPos.x > player1Rectangle.x)
player1Rectangle.x += 5;
else
player1Rectangle.x -= 5;
if (touchPos.y > player1Rectangle.y)
player1Rectangle.y += 5;
else
player1Rectangle.y -= 5;
batch.draw(player1Texture, player1Rectangle.x - 32, player1Rectangle.y - 32);
batch.end();
}
#Override
public void dispose () {
batch.dispose();
player1Texture.dispose();
}
}
In this section:
if (touchPos.x > player1Rectangle.x)
player1Rectangle.x += 5;
else
player1Rectangle.x -= 5;
if (touchPos.y > player1Rectangle.y)
player1Rectangle.y += 5;
else
player1Rectangle.y -= 5;
You are forcing the rectangle to always move in the X and Y directions by a set amount. It's never allowed to just sit still or slow down. Also, you probably don't want to be handling X and Y separately, because then its speed will be slower in the cardinal directions and faster in the diagonal directions, which would look weird. And finally, you need to work with speed and time to keep a stable movement speed.
So first of all, define a speed for your object. Also, you're going to want a Vector2 handy for calculations.
private static final float SPEED = 300f; //world units per second
private final Vector2 tmp = new Vector2();
Now after you have calculated touch position, you want to figure out what direction to move in, and how far to move along that direction. So right after your isTouched block:
//how far the player can move this frame (distance = speed * time):
float maxDistance = SPEED * Gdx.graphics.getDeltaTime();
//a vector from the player to the touch point:
tmp.set(touchPos.x, touchPos.y).sub(player1Rectangle.x, player1Rectangle.y);
if (tmp.len() <= maxDistance) {// close enough to just set the player at the target
player1Rectangle.x = touchPos.x;
player1Rectangle.y = touchPos.y;
} else { // need to move along the vector toward the target
tmp.nor().scl(maxDistance); //reduce vector length to the distance traveled this frame
player1Rectangle.x += tmp.x; //move rectangle by the vector length
player1Rectangle.y += tmp.y;
}

Ball stops moving after a few bounces

Ok so I have been try to get this ball to bounce naturally for a few weeks now and can't seem to get it right. The program should allow the user to input a set amount of gravity and then have the ball bounce according to that amount. It works for the first few bounces but stopping the ball is the problem. I have to deliberately set its movement to 0 or else it will endlessly bounce in place but not move on the x-axis. This issue only happens when the gravity is set to 15, other amount and things really go bad. The ball will bounce naturally but then keep rolling on x-axis forever. I've never taken a physics class so the issue is probably in the physics code. Anyone know where the issue is?
Here is the code my code-
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.util.Random;
import javax.swing.JOptionPane;
//key stuff
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
public class StartingPoint extends Applet implements Runnable{
//key press
private static boolean wPressed = false;
public static boolean isWPressed() {
synchronized (StartingPoint.class) {
return wPressed;
}
}
//for position of circle
int x = 0;
int y= 0;
//for position change
double dx = 2;
double dy = 2;
//for circle size
int rad = 11;
//image for update()
private Image i;
private Graphics gTwo;
//Physics
double grav = 15;
double engloss= .65;
double tc = .2;
double friction = .9;
#Override
public void init() {
// sets window size
setSize(800,600);
}
#Override
public void start() {
//"this" refers to the implemented run method
Thread threadOne = new Thread(this);
//this goes to run()
threadOne.start();
}
#Override
public void run() {
String input = JOptionPane.showInputDialog( "How much gravity?" );
grav= Double.parseDouble(input);
// sets frame rate
while (true){
//makes sure it doesn't go off screen x wise
//right side
if (x + dx > this.getWidth() - rad -1){
x= this.getWidth() -rad -1; //blocks it from moving past boundary
dx = -dx; //Reveres it
}
//left side
else if (x + dx < 1 + rad){
x= 1+rad; //ball bounces from the center so it adjusts for this by adding one and rad to pad out the radius of the ball plus one pixel.
dx = -dx; //Inverters its movement so it will bounce
}
//makes the ball move
else{
x += dx; // if its not hitting anything it keeps adding dx to x so it will move.
}
//friction
if(y == this.getHeight()-rad -1){
dx *= friction; //every time the ball hits the bottom dx is decreased by 10% by multiplying by .9
//Keeps it from micro bouncing for ever
if (Math.abs(dy) < 4){ // if the speed of y (dy) is less than .4 it is set to 0
dy= 0;
}
/**if (Math.abs(dx) < .00000000000000000001){ // if the speed of x (dx) is less than .00000000000000000001 it is set to 0, this value doesn't seem to matter
dx = 0;
}**/
}
//makes sure it doesn't go off screen y wise
//down
if (y > this.getHeight() - rad -0){ // TODO Check how getHieght is measured.
y= this.getHeight() -rad -0;
//makes ball loose speed.
dy *= engloss;
dy = -dy;
}
else {
//velocity
// tc makes grav smaller. Total of which is added to dy. To increase velocity as the ball goes down.
dy += grav *tc;
//gravity
//new dy is decreased by tc + .5 multiplied by gravity and tc squared. This makes the ball bounce lower every time based on its speed
y += dy*tc + .5*grav*tc*tc;
}
//frame rate
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//end frame rate
}
#Override
public void stop() {
}
#Override
public void destroy() {
}
#Override
public void update(Graphics g) {
//keeps it from flickering... don't know how though
if(i == null){
i = createImage(this.getSize().width, this.getSize().height);
gTwo = i.getGraphics();
}
gTwo.setColor(getBackground());
gTwo.fillRect(0, 0, this.getSize().width, this.getSize().height);
gTwo.setColor(getForeground());
paint(gTwo);
g.drawImage(i, 0, 0, this);
//do some thing with setDoubleBuffered
}
#Override
public void paint(Graphics g) {
g.setColor(Color.BLUE);
g.fillOval(x-rad, y-rad, rad*2, rad*2);
}
}

Beginner Java Question - "Breakout" game. What's my next step?

This is my first post on SO!
I've on my own been working a few weeks through Stanford's "Programming Methodology" class which is an intro to programming with java. I've been doing all the programs so far with little difficulty, researching what I needed with minimal difficulty.
Right now, all I have are a set of bricks, and a ball that I was able to get to bounce off the walls. Currently the ball doesn't do anything but bounce around in the canvas and just passes through the bricks.
There are lots of steps involved, the other ones I am pretty sure I can take care of. The ones I am having a hard time with are...
1) Get the ball to bounce off the bricks.
2) Get the bricks to disappear when the ball bounces off them.
some resources I've been using -
Using the ACM Graphics Package
Stanford PDF with the assignment guidelines
I guess my question is. What do I need to understand in order to be able to solve the problems I listed above. In one of the lectures, the professor talks about using "getElementAt()" But I don't really understand how that method works or how I can use it to get my ball to bounce off the bricks and then after, make them disappear.
Code I wrote so far -
/*
* File: Breakout.java
* -------------------
* Name: Sandi
* Section Leader: I'm learning this online
*
* This file will eventually implement the game of Breakout.
*/
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Breakout extends GraphicsProgram {
/** Width and height of application window in pixels */
public static final int APPLICATION_WIDTH = 400;
public static final int APPLICATION_HEIGHT = 600;
/** Dimensions of game board (usually the same) */
private static final int WIDTH = APPLICATION_WIDTH;
private static final int HEIGHT = APPLICATION_HEIGHT;
/** Dimensions of the paddle */
private static final int PADDLE_WIDTH = 60;
private static final int PADDLE_HEIGHT = 10;
/** Offset of the paddle up from the bottom */
private static final int PADDLE_Y_OFFSET = 30;
/** Number of bricks per row */
private static final int NBRICKS_PER_ROW = 10;
/** Number of rows of bricks */
private static final int NBRICK_ROWS = 10;
/** Separation between bricks */
private static final int BRICK_SEP = 4;
/** Width of a brick */
private static final int BRICK_WIDTH = (WIDTH - (NBRICKS_PER_ROW - 1)
* BRICK_SEP)
/ NBRICKS_PER_ROW;
/** Height of a brick */
private static final int BRICK_HEIGHT = 8;
/** Radius of the ball in pixels */
private static final int BALL_RADIUS = 10;
/** Offset of the top brick row from the top */
private static final int BRICK_Y_OFFSET = 70;
/** Number of turns */
private static final int NTURNS = 3;
/* Method: run() */
/** Runs the Breakout program. */
public void run() {
addBricks();
addBall();
//velocity of the ball's x and y
ballVX = 1;
ballVY = 2;
while (true) {
moveBall();
pause(10);
}
}
private void addBricks() {
int startY = BRICK_Y_OFFSET;
GRect brick;
for (int i = 0; i < NBRICK_ROWS; i++) {
int startX = BRICK_SEP;
for (int j = 0; j < NBRICKS_PER_ROW; j++) {
brick = new GRect(startX, startY, BRICK_WIDTH, BRICK_HEIGHT);
add(brick);
colorBricks(brick, Color.RED, 0, 1);
colorBricks(brick, Color.ORANGE, 2, 3);
colorBricks(brick, Color.YELLOW, 4, 5);
colorBricks(brick, Color.GREEN, 6, 7);
colorBricks(brick, Color.CYAN, 8, 9);
startX += BRICK_WIDTH + BRICK_SEP;
}
startY += BRICK_HEIGHT + BRICK_SEP;
}
}
private void colorBricks(GRect brick, Color color, int fromRowNumber,
int toRowNumber) {
//this will make it so that if the bricks are between two y coordinates
//then the method will set it to a certain color and color it.
if (brick.getY() >= BRICK_Y_OFFSET + fromRowNumber
* (BRICK_HEIGHT + BRICK_SEP)
&& brick.getY() <= BRICK_Y_OFFSET + toRowNumber
* (BRICK_HEIGHT + BRICK_SEP)) {
brick.setColor(color);
brick.setFilled(true);
}
}
private void addBall() {
ball = new GOval(getWidth() / 2, getHeight() / 2, BALL_RADIUS,
BALL_RADIUS);
ball.setFilled(true);
add(ball);
}
private void moveBall() {
double borderX = ball.getX();
double borderY = ball.getY();
if (borderX == 0 || borderX == getWidth() - BALL_RADIUS) {
ballVX = -ballVX;
}
if (borderY == 0 || borderY == getHeight() - BALL_RADIUS) {
ballVY = -ballVY;
}
ball.move(ballVX, ballVY);
}
private GOval ball;
private double ballVX;
private double ballVY;
}
Thanks guys!
There are of course many things that could be done to enhance your program. But to solve your next problem I think you have to understand a little bit of the "magic" of the classes you are depend on.
I havn't studied your API in depth, but it looks like your call add(brick); adds your brick to an array/collection internally stored in GraphicsProgram (if you can get the source code of the classes you depend on it might be helpfull to read and understand the code). You can access the objects you have added later on with getElementAt. So if you add a brick at the location (10,20) and later call getElementAt(10,20) it should return this brick.
You should modify your moveBall method to check if the new position of the ball would contain a brick and, if so, act accordingly. Something like this:
private void moveBall() {
double borderX = ball.getX();
double borderY = ball.getY();
GRect brick = getElementAt(borderX, borderY);
if (brick != null) {
doSomethingWithBall();
} else {
if (borderX == 0 || borderX == getWidth() - BALL_RADIUS) {
ballVX = -ballVX;
}
if (borderY == 0 || borderY == getHeight() - BALL_RADIUS) {
ballVY = -ballVY;
}
}
ball.move(ballVX, ballVY);
}
You might have to fiddle a little bit with the values you pass into getElementAt and what exactly you do when you find a brick. You have to take into account that the bricks are not points but have a height and a width, the ball is moving and not a point, too ...
Edit: Your ball is added to the collection, too. So it might be possible that the ball is returned instead of a brick. You should not pass the actual position of your ball to the getElementAt method but an value where the ball is after it moves half the ball radius when there is no brick in the way.
private GObject getCollidingObject() {
double x = ball.getX(), y = ball.getY();
if (getElementAt(x, y) != null) { return getElementAt(x, y); }
else if (getElementAt(x, y + BALL_RADIUS * 2) != null) {
return getElementAt(x, y + BALL_RADIUS * 2); }
else if (getElementAt(x + BALL_RADIUS * 2, y + BALL_RADIUS * 2) != null) {
return getElementAt(x + BALL_RADIUS * 2, y + BALL_RADIUS * 2); }
else if (getElementAt(x + BALL_RADIUS * 2, y) != null) {
return getElementAt(x + BALL_RADIUS * 2, y); }
else { return null; }
}
... these two methods works perfectly together ...
private void elementsCollisions() {
GObject collider = getCollidingObject();
if (collider == paddle) {
vy = -vy;
}
else if (collider != null) {
remove(collider);
vy = -vy;
}
getElementAt(x, y) - returns object at the specified point. Decompose problem as much as possible if you can't solve fast.
Index the bricks in a way that you can easily check if a brick exists at a certain coordinate.
When you create a brick, you add it to the container, and then you lose it. You don't have a reference to it going forward that you can use to compare to the current ball location.
Based on your design, I believe you should do this through a 2-dimensional array. When you create the bricks, also put the GRect object in a 2-dimensional array. One dimension should be the row, and the other the column. Then you will be able to access these bricks by x and y coordinate using this array. (This is what you will need to do to detect a bounce.)
When the ball moves, the same way that you detect it bouncing off walls, check if the ball's position makes it border a spot where a brick could be. If it does, from any direction, then check if the brick is actually there by looking at your array. If there is a brick there, bounce the ball and remove the brick. Also be sure to set that location in the array to be null to indicate that the brick is gone.
Then you also have your victory condition... when all of the elements of the array have been set to null.

Categories

Resources