I've got a test texture in a new game that I'm creating, and for some reason, when I implement it into the game, the texture is flipped horizontally, instead of vertically. Why does this happen?
Here's a photo of the app.
As you can see, the little guy is flipped horizontally, almost as if he is sleeping. He should be facing vertically. Why is this happening, and how do I prevent it? I'll post both classes involved.
Player class:
import java.io.IOException;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
public class Player {
public Texture playerTexture;
// Positions & speed
public float xPos = 20.0f; // This is initial
public float yPos = 450.0f; // Same as above.
private float velocity = 20;
public float gravityForce = 6;
public float jumpVelocity = 25;
private static int moveSpeed = 30 / 2;
public boolean isSupported = false; // Once again, initial value.
boolean canJump = true;
// movement methods
public Texture grabTexture() {
try {
playerTexture = TextureLoader.
getTexture("PNG", ResourceLoader
.getResourceAsStream
("resources/test_char.png"));
} catch (IOException e) {
e.printStackTrace();
}
return playerTexture;
}
public void applyGravity() {
}
private void printPos(String moveMethod) {
System.out.println(moveMethod + " X: "
+ xPos + " Y: " + yPos);
}
public void moveRight() {
xPos += moveSpeed;
printPos("Moving right!");
}
public void moveLeft() {
xPos -= moveSpeed;
printPos("Moving left!");
}
public void jump() {
if (!isSupported) {
}
}
public void shoot() {
// do shooty stuff here
}
}
Main (Launcher) class:
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
public class Main {
private void display() {
try {
Display.setDisplayMode(new DisplayMode(1000, 550));
Display.setTitle("Unnamed Platformer Game");
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
// OpenGL
while (!Display.isCloseRequested()) {
initGL();
player.applyGravity();
Display.update();
Display.sync(60); // sync to 60 fps
if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
player.moveRight();
} else if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
player.moveLeft();
} else if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
player.jump();
}
}
Display.destroy();
}
private void initGL() {
// initial OpenGL items for 2D rendering
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 1000, 550, 0, 1, -1);
// start rendering player image
player.grabTexture().bind();
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex2f(player.xPos, player.yPos);
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(player.xPos + 100, player.yPos);
GL11.glTexCoord2f(1, 0);
GL11.glVertex2f(player.xPos + 100, player.yPos + 100);
GL11.glTexCoord2f(1, 1);
GL11.glVertex2f(player.xPos, player.yPos + 100);
GL11.glTexCoord2f(0, 1);
GL11.glEnd(); // stop rendering this image
}
Player player = new Player();
public static void main(String[] args) {
Main main = new Main();
main.display();
}
}
GL11.glVertex2f(player.xPos, player.yPos);
GL11.glTexCoord2f(0, 0);
The issue is you're calling these functions out of order. Whenever you call glVertex2f, OpenGL will grab whatever attributes are currently set and use that for the vertex. In this case, you're setting up the texture coordinate after setting the vertex, so the coords are "delayed" by one vertex. If you swap every pair of these calls, it should work.
Also, your call to glOrtho has the top and bottom parameters swapped. The bottom value is what the y value is supposed to be at the bottom of the screen (in this case, 0). Likewise for the top value.
Related
I am trying to draw a quad on click. Everytime I click on the screen, the quad flashes on the screen then disappears. If I keep clicking, it keeps flashing. Is it because of the order I am starting my methods? Thanks!
Edited with update code... I get the square to appear on click, but it will not stay. It follows the mouse across the screen
package main;
import static org.lwjgl.opengl.GL11.*;
import java.awt.event.MouseEvent;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.openal.*;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.tiled.TiledMap;
import static helper.Artist.*;
import helper.Artist.*;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.*;
public class GameWindow {
public TiledMap map;
public TiledMap menu;
private int sx,sy;
public int x,y;
public static int fx;
public static int fy;
public int fxy;
public boolean place = true;
public static enum states{
MAIN, GAME; }
public states state = states.MAIN;
public void render(){
switch(state){
case MAIN:
try {
init();
update();
menu.render(0, 0);
} catch (SlickException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
break;
case GAME:
try {
map = new TiledMap("res/map.tmx");
map.render(0,0);
Draw();
if(place == false){
Quad();
}
update();
//Draw();
System.out.println(place);
glBegin(GL_QUADS);
glVertex2i(x*64,y*64);
glVertex2i(x * 64 + 64, y*64);
glVertex2i(x*64 + 64,y*64 + 64);
glVertex2i(x*64,y*64 + 64);
glEnd();
} catch (SlickException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void Draw(){
fx = this.fx;
fy = this.fy;
while(Mouse.next()){
if(Mouse.getEventButtonState()){
if(Mouse.getEventButton() == 0){
System.out.println("Click");
int iy = Mouse.getY();
fy = (iy+0x20)>>0x06;
int ix = Mouse.getX();
fx = (ix+0x20)>>0x06;
place = false;
}else{
place = true;
}
}}
}
public void Quad(){
glBegin(GL_QUADS);
glVertex2i(fx*64,fy*64);
glVertex2i(fx * 64 + 64, fy*64);
glVertex2i(fx*64 + 64,fy*64 + 64);
glVertex2i(fx*64,fy*64 + 64);
glEnd();
}
public void update(){
int object = map.getLayerIndex("Tile Layer 2");
//System.out.println(x);
map.getTileId(0, 0, object);
while(Keyboard.next()){
if(Keyboard.getEventKeyState()){
if(Keyboard.getEventKey() == Keyboard.KEY_DOWN){
//System.out.println(y);
if(map.getTileId(x , y+1, object) ==0){
y++;
//System.out.println(y);
}
}}
if(Keyboard.isKeyDown(Keyboard.KEY_UP)){
System.out.println(y);
if(y>=1){
if(map.getTileId(x, y-1, object) ==0){
y--;
System.out.println(y);
}
}
}
if(Keyboard.isKeyDown(Keyboard.KEY_LEFT)){
System.out.println(x);
if(map.getTileId(x -1, y, object) ==0){
x--;
System.out.println(x);
}
}
if(Keyboard.isKeyDown(Keyboard.KEY_RIGHT)){
System.out.println(x);
if(map.getTileId(x + 1,y, object) ==0){
x++;
System.out.println(x);
}
}}
}
public void init() throws SlickException{
menu = new TiledMap("res/menu.tmx");
map = new TiledMap("res/map.tmx");
x = 1;
y = 0;
}
public void Mouse(){
int iy = Mouse.getY();
fy = (iy+0x20)>>0x06;
int ix = Mouse.getX();
fx = (ix+0x20)>>0x06;
//System.out.println(fy);
//System.out.println(fx);
}
public void checkInput(){
switch(state){
case MAIN:
if(Keyboard.isKeyDown(Keyboard.KEY_RETURN)){
state = states.GAME;
}
break;
case GAME:
if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)){
state = states.MAIN;
}
}
}
public GameWindow(){
try {
Display.setDisplayMode(new DisplayMode(1280,960));
Display.create();
} catch (LWJGLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Display.setTitle("Hello");
//initialization
grass = LoadTexture("dirt64");
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1280, 960, 0, 1, -1 );
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
while(!Display.isCloseRequested()){
//render code
checkInput();
Mouse();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
render();
Display.update();
Display.sync(60);
}
Display.destroy();
}
public static void main(String[] args){
new GameWindow();
}}
I use eclipse. so in my workspace, under my project, i create a new folder "res" with a subfolder "images" that have all my png's for use as textures. so here is the method im using for loading the textures:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Color;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
import org.lwjgl.input.Mouse;
import java.util.Random;
public class TextureDemo
{
public int count = 0;
private static Texture wood;
Random random = new Random();
public TextureDemo()
{
initGL(640, 480, "SLICK TEXTURES");
loadTexture("mozilla");
int x = 100, y = 100, count = 0, width = 0, height = 0, counter = 10;
while(true)
{
count++;
if(count == counter)
{
x--; y--; width++; height++; counter += random.nextInt(50) + 1;
}
render(x, y, width, height);
Display.update();
Display.sync(60);
if(Display.isCloseRequested())
{
Display.destroy();
System.exit(0);
}
}
}
private void initGL(int width, int height, String title)
{
try
{
Display.setDisplayMode(new DisplayMode(width, height));
Display.setTitle(title);
Display.create();
}
catch(LWJGLException e)
{
e.printStackTrace();
Display.destroy();
System.exit(1);
}
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
GL11.glDisable(GL11.GL_COLOR_MATERIAL);
GL11.glLoadIdentity();
GL11.glOrtho(0, width, height, 0, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
}
public void loadTexture(String key)
{
try
{
wood = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("./res/images/"+key+".png"));
System.out.println("working." + wood.getTextureID());
}
catch(FileNotFoundException e)
{
e.printStackTrace();
Display.destroy();
System.exit(1);
}
catch(IOException e)
{
e.printStackTrace();
}
}
public void render(int x, int y, int width, int height)
{
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
System.out.println("working." + wood.getTextureID());
GL11.glBindTexture(GL11.GL_TEXTURE_2D, wood.getTextureID());
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(x, y);
GL11.glTexCoord2f(1, 0);
GL11.glVertex2f(x + wood.getImageWidth(), y);
GL11.glTexCoord2f(1, 1);
GL11.glVertex2f(x + wood.getImageWidth(), y + wood.getImageHeight());
GL11.glTexCoord2f(0, 1);
GL11.glVertex2f(x, y + wood.getImageHeight());
GL11.glEnd();
GL11.glDisable(GL11.GL_BLEND);
System.out.println(wood.getHeight()+ " " +wood.getWidth());
}
public static void main (String[] args)
{
new TextureDemo();
}
}
WHY WHY WHY are my textures black lol? I really don't understand how my code did this. Is it possible that my computer could have problems that are causing that?
Asking the same question twice won't help. However, you should make sure you first call glBindTexture, before you start glBegin.
I'm sorry, however there are many problems with your code. I would recommend taking a look at some opengl tutorials. The arcsynthesis one is very in-depth.
Your problem is that the texture "wood" is never bound to opengl.
In order to bind your texture to the opengl context you must call
glBindTexture(GL_TEXTURE_2D, id);
before glbegin()
The id is the texture id generated by glGentextures or in your case the slick util method.
Also what is wood? In the first code block it is the texture id but in the second block you are calling 'getHeght()' on type wood. I am confused as to what 'wood' is.
Either way, you need to bind the texture for opengl to use it and I again recommending looking at a few tutorials on the opengl basics.
You haven't enabled textures!
In your initGL method call GL11.glEnable(GL11.GL_TEXTURE_2D).
I have a Pong game that I'm working on. Right now, I'm working on getting the player paddle working. Here's what happens right now. The game starts, and the paddle is situated on the screen. Pressing up on the paddle extends the square upwards. Pressing down does the opposite. What I need is for the square to actually move as an individual object and delete itself each time it moves so that it stays a square the entire time. Instead of the paddle getting bigger, I need it to stay the same shape, but just move. How would I go about doing this in openGL? Here are my two classes.
Startup (main class):
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import com.evanklein.pong.entitity.Player;
public class Startup {
// set up display
public void start() {
try {
Display.setDisplayMode(new DisplayMode(600, 400)); // these numbers
// pending
Display.setTitle("Evan's Pong!");
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 600, 400, 0, 1, -1);
while (!Display.isCloseRequested()) {
// render OpenGL here
GL11.glBegin(GL11.GL_QUADS);
GL11.glColor3f(3.0f, 7.2f, 6.7f);
GL11.glVertex2d(player.width, player.length);
GL11.glVertex2d(player.width + 100, player.length);
GL11.glVertex2d(player.width + 100, player.length + 100);
GL11.glVertex2d(player.width, player.length + 100);
GL11.glEnd();
Display.update();
Display.sync(60);
if (Keyboard.isKeyDown(Keyboard.KEY_UP)) {
player.moveUp();
}
if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
player.moveDown();
}
}
Display.destroy();
}
// Let's start this beyotch up!
Player player = new Player();
public static void main(String[] args) {
new Startup().start();
}
}
Player class:
public class Player {
// size variables
public int width = 50;
public int length = 120;
private int moveSpeed = 10; // mph
public Player() {
}
public void moveUp() {
length -= moveSpeed;
}
public void moveDown() {
length += moveSpeed;
}
}
Let me know if you have any other questions or need any other remaining details.
glClear() the framebuffer at the beginning of your isCloseRequested() loop.
I have not run the code but it should get you going
In your main class:
...
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 600, 400, 0, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW); // make active the ModelView matrix
GL11.glClearColor(0, 0, 0, 1); // setup black as the clear color
while (!Display.isCloseRequested()) {
GL11.glClear(GL.GL_COLOR_BUFFER_BIT); // clear the screen using the color above
GL11.glLoadIdentity(); // reset any transformations in the ModelView matrix
// move the player square to its current position
GL11.glTranslatef(player.x, player.y, 0);
// render OpenGL here
....
In your player class:
...
// add new fields for player's position
// change those values according to the desired initial position
public int x = 100;
public int y = 100;
...
// change the position of the square, not its dimensions
public void moveUp() {
y -= moveSpeed;
}
public void moveDown() {
y += moveSpeed;
}
Coming up with a title for this question was really really difficult. So I'm making a game, and I have a method that is used for handling movement called handleVelocity(). The way it works, is that in my game, it updates every tick, looking for character movement. If character movement is below 1 or -1, then it stops the character from moving at all. So I would set the character's movement to 0. But then, the character can no longer move, and I don't know how to get him to continue moving. How do I handle this velocity so that it can continue to move, even after it has previously stopped. I'll post both classes below.
Player class:
import java.io.IOException;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
public class Player {
public Texture playerTexture;
// Positions & speed
public float xPos = 20.0f; // This is initial
public float yPos = 0.0f; // Same as above.
public float acceleration = 15;
public static int gravityForce = 6;
public static int jumpVelocity = 100;
private float moveSpeed = 4f;
private static int MAX_MOVE_SPEED = 9;
public boolean isSupported = true; // Once again, initial value.
public boolean goingRight, goingLeft, canJump;
// movement methods & constants
public void update() {
handleVelocity();
applyGravity();
checkForSupport();
}
public void handleVelocity() {
float minMoveSpeed = 1;
if (this.moveSpeed < minMoveSpeed && this.moveSpeed > -minMoveSpeed) {
this.moveSpeed = 0;
} else {
float dampening = 0.00002f;
double sign = -(int) Math.signum(moveSpeed);
this.moveSpeed += (float) (dampening * sign);
}
xPos += this.moveSpeed;
}
public Texture grabTexture() {
try {
playerTexture = TextureLoader.getTexture("PNG",
ResourceLoader.getResourceAsStream("res/test_char.png"));
} catch (IOException e) {
e.printStackTrace();
}
return playerTexture;
}
private void checkForSupport() {
if (yPos == 0) {
isSupported = true;
} else if (yPos > 0 /* and is not on a platform */) {
isSupported = false;
}
}
private void applyGravity() {
if (!isSupported) {
yPos -= gravityForce;
}
}
public void printPos(String moveMethod) {
System.out.println(moveMethod + " X: " + xPos + " Y: " + yPos
+ " Going Right: " + goingRight + " Going Left: " + goingLeft
+ " Speed: " + moveSpeed);
}
// movement methods
private void accelerateX(float speed) {
moveSpeed += (float) (speed * 0.0096);
if (moveSpeed >= MAX_MOVE_SPEED) {
moveSpeed = MAX_MOVE_SPEED;
} else if (moveSpeed <= -MAX_MOVE_SPEED) {
moveSpeed = -MAX_MOVE_SPEED;
}
}
#SuppressWarnings("unused")
private void accelerateY() {
}
public void moveRight() {
printPos("Moving Right!");
accelerateX(acceleration);
}
public void moveLeft() {
printPos("Moving Left!");
accelerateX(-acceleration);
}
public void jump() {
printPos("Jumping!");
}
}
Main class:
import com.hasherr.platformer.entity.Player;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
public class Main {
private void display() {
try {
Display.setDisplayMode(new DisplayMode(1000, 550));
Display.setTitle("Unnamed Platformer Game");
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
// OpenGL
while (!Display.isCloseRequested()) {
Display.update();
Display.sync(60); // sync to 60 fps
initGL();
player.update();
handleKeyboardInput();
}
Display.destroy();
}
private boolean keyboardInUse() {
boolean keyboardInUse;
if (!(Keyboard.isKeyDown(Keyboard.KEY_A)
|| Keyboard.isKeyDown(Keyboard.KEY_D) || Keyboard
.isKeyDown(Keyboard.KEY_SPACE))) {
keyboardInUse = false;
} else {
keyboardInUse = true;
}
return keyboardInUse;
}
private void handleKeyboardInput() {
if (!keyboardInUse()) {
player.goingLeft = false;
player.goingRight = false;
}
if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
player.goingLeft = false;
player.goingRight = true;
player.moveRight();
} else if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
player.goingLeft = true;
player.goingRight = false;
player.moveLeft();
} else if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
player.jump();
}
}
private void initGL() {
// initial OpenGL items for 2D rendering
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glOrtho(0, 1000, 0, 550, 1, -1);
// start rendering player image
player.grabTexture().bind();
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(player.xPos, player.yPos);
glTexCoord2f(1, 0);
glVertex2f(player.xPos + 150, player.yPos);
glTexCoord2f(1, 1);
glVertex2f(player.xPos + 150, player.yPos + 150);
glTexCoord2f(0, 1);
glVertex2f(player.xPos, player.yPos + 150);
glEnd(); // stop rendering this image
}
Player player = new Player();
public static void main(String[] args) {
Main main = new Main();
main.display();
}
}
What triggers the movement? Say player should move when right arrow key is pressed and currently the player is on standing state, in that case set the moveSpeed back to default value 4f.
I know you've already accepted an answer, and the answer provided does do the job, however I feel this needs a better solution and more full explanation.
The code doesn't work as is because in your moveLeft and moveRight methods you are calling accelerateX(acceleration). acceleration value is 15. Lets look at accelerateX method.
private void accelerateX(float speed) {
moveSpeed += (float) (speed * 0.0096);
if (moveSpeed >= MAX_MOVE_SPEED) {
moveSpeed = MAX_MOVE_SPEED;
} else if (moveSpeed <= -MAX_MOVE_SPEED) {
moveSpeed = -MAX_MOVE_SPEED;
}
}
Its multiplying the supplied speed by .0096. 15*.0096 = .144. So here we're adding .144 to moveSpeed. Now let's go to handleVelocity
public void handleVelocity() {
float minMoveSpeed = 1;
if (this.moveSpeed < minMoveSpeed && this.moveSpeed > -minMoveSpeed) {
this.moveSpeed = 0;
} else {
float dampening = 0.00002f;
double sign = -(int) Math.signum(moveSpeed);
this.moveSpeed += (float) (dampening * sign);
}
xPos += this.moveSpeed;
}
This code sets moveSpeed to zero if its less than 1, and greater than -1 to force the player to stop moving.
This can be fixed a number of ways and maintain our nice pseudo-physics. The answer you've accepted breaks the physics system by forcing the movement speed to some value. Maybe this is what you want in a platformer, maybe not. Your choice.
I suggest you decrease minMoveSpeed to something smaller, like .01.
I'm currently trying to make a breakout clone using java and libgdx. I'm currently experiencing trouble getting the ball to bounce off of the blocks at the appropriate angle. In short the problem I'm having is that the ball moves 12 pixels every frame and doesn't always line up with the edge of a brick. If anyone has any suggestions on a better way to move the ball or a different way to check collision it would be much appreciated!
Main game class
package com.kyleparker.breakout;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL10;
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.utils.Array;
public class BreakoutGameScreen implements ApplicationListener {
Texture dropImage;
Sound dropSound;
Music rainMusic;
SpriteBatch batch;
OrthographicCamera camera;
Rectangle bucket;
Paddle paddle;
//Brick bricks[];
Array<Brick> bricks;
Ball ball;
#Override
public void create() {
// load the images for the droplet, 64x64 pixels
dropImage = new Texture(Gdx.files.internal("droplet.png"));
// load the drop sound effect and the rain background "music"
dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
// start the playback of the background music immediately
rainMusic.setLooping(true);
rainMusic.play();
// create the camera and the SpriteBatch
camera = new OrthographicCamera();
camera.setToOrtho(false, 1280, 720);
batch = new SpriteBatch();
paddle = new Paddle(new Texture(Gdx.files.internal("bucket.png")));
bricks = new Array<Brick>();
populateBricks();
ball = new Ball(new Texture(Gdx.files.internal("bucket.png")), paddle, bricks);
}
private void populateBricks() {
bricks.add(new Brick(200,100));
for (int i = 0; i < 5; i++) {
for (int j = 0; j <= 7; j++) {
bricks.add(new Brick (j * 144 + 76, i * 80 + 300)); //Offsets each new brick
}
}
}
#Override
public void render() {
// clear the screen with a dark blue color. The
// arguments to glClearColor are the red, green
// blue and alpha component in the range [0,1]
// of the color to be used to clear the screen.
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// tell the camera to update its matrices.
camera.update();
// tell the SpriteBatch to render in the
// coordinate system specified by the camera.
batch.setProjectionMatrix(camera.combined);
// begin a new batch and draw the bucket and
// all drops
batch.begin();
paddle.render(batch, camera);
ball.move();
ball.render(batch, camera);
for (int x = bricks.size - 1; x > 0; x--) {
bricks.get(x).render(batch,camera);
}
batch.end();
}
#Override
public void dispose() {
// dispose of all the native resources
dropImage.dispose();
dropSound.dispose();
rainMusic.dispose();
batch.dispose();
paddle.dispose();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
Ball class
package com.kyleparker.breakout;
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.utils.Array;
public class Ball{
Texture ballImage;
Rectangle ball;
private int xdir;
private int ydir;
Paddle paddle;
Array<Brick> bricks;
final int BALL_SPEED = 12;
public Ball(Texture ballImage, Paddle paddle, Array<Brick> bricks) {
// load the ball image
this.ballImage = ballImage;
xdir = 1;
ydir = -1;
// create a Rectangle for the balls collision
ball = new Rectangle();
ball.x = 1280 / 2 - 64 / 2; // center the ball
ball.y = 100; // put the ball 200px away from the bottom of the screen
ball.width = 64;
ball.height = 64;
this.paddle = paddle;
this.bricks = bricks;
}
public void render(SpriteBatch batch, OrthographicCamera camera) {
// draw the paddle onto the batch of the level
batch.draw(ballImage, ball.x, ball.y);
}
public void move() {
ball.x += xdir * BALL_SPEED;
ball.y += ydir * BALL_SPEED;
if (ball.x <= 0) {
setXDir(1);
}
if (ball.x >= 1280 - 64) {
setXDir(-1);
}
if (ball.y <= 0) {
setYDir(1);
}
if (ball.y >= 720 - 64) {
setYDir(-1);
}
if (ball.overlaps(paddle.getRect())) {
setYDir(1);
}
for (int i = 0; i < bricks.size; i++) {
if (ball.overlaps(bricks.get(i).getRect())) {
if ((ball.x == (bricks.get(i).getRect().x + 128)))
{
setXDir(1);
bricks.get(i).setDestroyed(true);
System.out.println("Collision RIGHT");
}
if (((ball.x + 64) == bricks.get(i).getRect().x))
{
setXDir(-1);
bricks.get(i).setDestroyed(true);
System.out.println("Collision LEFT");
}
if ((ball.y == (bricks.get(i).getRect().y + 64)))
{
setYDir(1);
bricks.get(i).setDestroyed(true);
System.out.println("Collision TOP");
}
if (((ball.y + 64) == bricks.get(i).getRect().y))
{
setYDir(-1);
bricks.get(i).setDestroyed(true);
System.out.println("Collision BOTTOM");
}
}
}// end of for
}
public void setXDir(int x) {
xdir = x;
}
public void setYDir(int y) {
ydir = y;
}
public int getYDir() {
return ydir;
}
public int getXDir() {
return xdir;
}
public Rectangle getRect() {
// return the collision rectangle for checking overlaps
return ball;
}
public void dispose() {
// dispose of all the native resources
ballImage.dispose();
}
}// end of class
Brick code just in case
package com.kyleparker.breakout;
import com.badlogic.gdx.Gdx;
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;
public class Brick{
Texture brickImage;
Rectangle brick;
boolean destroyed;
public Brick(int x, int y) {
brickImage = new Texture(Gdx.files.internal("brick.png"));
// create a Rectangle for the bricks collision
brick = new Rectangle();
brick.x = x;
brick.y = y;
brick.width = 128;
brick.height = 64;
destroyed = false;
}
public void render(SpriteBatch batch, OrthographicCamera camera) {
// draw the brick onto the batch of the level
batch.draw(brickImage, brick.x, brick.y);
}
public boolean isDestroyed() {
// return the collision rectangle for checking overlaps
return destroyed;
}
public void setDestroyed(boolean destroyed)
{
this.destroyed = destroyed;
if (this.destroyed == true) {
dispose();
brick.x = -1000;
brick.y = -1000;
}
}
public Rectangle getRect() {
return brick;
}
public void dispose() {
// dispose of all the native resources
brickImage.dispose();
}
}
Don't worry about the fact that the ball doesn't always line up with an object for which the collision needs handled -- that's not actually relevant. You can (and should) handle your collisions less 'precisely.' That is, the ball's path is fixed, so you can calculate its position at any future point. Check its position, calculate its position in the next frame (which you have to do to draw it anyway), and add some code to handle the collision that is going to happen, rather than trying to detect and handle the collision which has happened. You can slow down the ball if you really want a clean reflection, or you can speed up your framerate, or you can let the ball be partially 'absorbed' by the object before it reflects:
public class Ball {
. . .
public void move() {
. . .
if (collisionObject.overlaps(new Rectangle(ball.x + xdir, ball.y + ydir, ball.width, ball.height))) {
//a collision will have occurred in the next frame
//handle the collision however you please
}
}
}
I also note that your BALL_SPEED field is inaccurately named. As presently coded, the ball always moves at a 45° angle, with a speed of about 17 pixels per frame (in that direction). You've coded its x- and y-offset as 12 pixels, but if (when?) you change the ball's direction, you'll find that the speed fluctuates wildly depending on what values are placed in for the xdir and ydir fields. For example, if you were to (somewhat) randomize these, but keep the rest of your code as-is, you might find that xdir = 2 and ydir = 4 on one instance, and xdir = 6 and ydir = 12 on another. Note these describe the same direction, but the second version will move three times as fast.
To properly handle the ball's direction and speed, assign an angle, and calculate the xdir and ydir values through the appropriate trigonometric functions (xdir = BALL_SPEED * Math.cos(ballAngle) and ydir = BALL_SPEED * Math.sin(ballAngle)).
I would use box2d for the whole thing. That you haven't used box2d probably means you have no experience in it, so that would be a little hurdle, but I'm sure you'll be able to wrap your head around it quickly. Here's a link for that: http://code.google.com/p/libgdx/wiki/PhysicsBox2D