Right I'm working with libGDX/Box2D at the moment and I am completely stumped by a bug I discovered while following this set of tutorials libGDX.
In my game I create an actor and add it to my game stage. The actor is placed on the screen at a given y coordinate. The game simply loops through an animation to simulate running.
The problem is, when I place the actor on the game stage it's y coordinate is somehow given the wrong y coordinate, retrieved using the following code snippet.
body.getPosition().y
The y coordinate of the actor changes from the correct position of 2.5 to an incorrect position of 3.6 on game startup. The actor then operates as normal from it's updated (incorrect) y position.
I've debugged the bejaysus of my code and I can't see anything that would be causing this. Has anyone come across this kind of behaviour before? Is this some kind of Box2D nuance I am not aware of?
Game Actor Class
public abstract class GameActor extends Actor {
protected Body body;
protected UserData userData;
protected Rectangle screenRectangle;
public GameActor() {
}
public GameActor(Body body) {
this.body = body;
this.userData = (UserData) body.getUserData();
screenRectangle = new Rectangle();
}
#Override
public void act(float delta) {
super.act(delta);
if (body.getUserData() != null) {
updateRectangle();
} else {
// This means the world destroyed the body (enemy or runner went out of bounds)
remove();
}
}
public abstract UserData getUserData();
private void updateRectangle() {
screenRectangle.x = transformToScreen(body.getPosition().x - userData.getWidth() / 2);
screenRectangle.y = transformToScreen(body.getPosition().y - userData.getHeight() / 2);
screenRectangle.width = transformToScreen(userData.getWidth());
screenRectangle.height = transformToScreen(userData.getHeight());
}
protected float transformToScreen(float n) {
return Constants.WORLD_TO_SCREEN * n;
}
}
Runner Class
public class Runner extends GameActor {
...
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
float x = screenRectangle.x - (screenRectangle.width * 0.1f);
float y = screenRectangle.y;
float width = screenRectangle.width * 1.5f;
stateTime += Gdx.graphics.getRawDeltaTime();
batch.draw(runningAnimation.getKeyFrame(stateTime, true), x, y - 40, width, screenRectangle.height);
}
}
}
Related
So I've been trying to figure out what's wrong with my code, but I've had basically no luck when it comes to finding what's wrong. I've seen plenty of folks who had similar issues but none of the fixes that were suggested helped with my problem.
I've made a video showcasing my problem, but I'll give an explanation here too: whenever I move on the screen, the tiles in my game engine don't sync up with each other in terms of positioning, and it seems to be only on the rendering end. When I do any sort of checks to see if the distance between two tiles changes, I don't get any sort of errors. I get these gaps between the tiles specifically when I'm moving up or left, and they instead merge slightly when I move down or right, as if some of the tiles are updating faster than the others.
I don't want to just start dumping my couple thousand lines of code here since that won't really help anyone, but I'm also not well versed in the game development world, so I'm not entirely sure what pieces of code are super relevant here. If there's anything that y'all would like to see, let me know and I'll happily provide it.
The main method and postInit looks like this:
public static void main (String[] args)
{
EventQueue.invokeLater(() ->{
ex = new ShootyGame();
ex.dispose();
ex.setLayout(null);
ex.setUndecorated(Settings.isFullscreen);
ex.setVisible(true);
ex.setCursor(ex.getToolkit().createCustomCursor(
new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB), new Point(), null)); //hides the system cursor
ex.createBufferStrategy(2);
postInit();
});
}
private static void postInit()
{
//add a window listener to the main JFrame
ex.addWindowListener(new WindowAdapter()
{
/**
* Autosaves when the window is closed, then kills the game.
*/
#Override
public void windowClosing(WindowEvent e)
{
//TODO autosaving
kill();
}
});
render.start(); //these are just the two threads being started
run.start();
}
My two threads, which basically just run the step and render methods in my game class:
private static class Running extends Thread
{
boolean stop = false;
int fps = TickManager.fromFPS(Settings.FPS);
public void run()
{
while (!stop)
{
try
{
Running.sleep(fps);
}
catch(InterruptedException e)
{
System.err.println("B");
}
game.step();
}
}
}
private static class Render extends Thread
{
boolean stop = false;
int fps = TickManager.fromFPS(Settings.FPS);
public void run()
{
while (!stop)
{
try
{
Running.sleep(fps);
}
catch(InterruptedException e)
{
System.err.println("A");
}
game.render();
}
}
}
The run method in my Game class just calls the current character's step method (it'll call more later), which is what's directly used to move the Camera class' x and y position. The character step looks like this:
public void step()
{
this.calculateMove(); //gets info on which keys are being pressed, adds or subtracts an integer from dx and dy (which are also ints) based on which are being pressed
Game.cam.update(-this.dx, -this.dy); //sends the opposite movement to the camera
//change the position of the player character by the appropriate x and y amounts
this.changeX(this.dx);
this.changeY(this.dy);
this.reset(); //just sets dx and dy back to 0 after moving
}
And now to the rendering stuff here's the entire camera class since it's probably the most relevant class:
public class Camera
{
private int x;
private int y;
public Camera(int x, int y)
{
this.x = x;
this.y = y;
}
public void update(int dx, int dy)
{
moveX(dx);
moveY(dy);
}
public void moveX(int dx)
{
this.x += dx;
}
public void moveY(int dy)
{
this.y += dy;
}
/**
* Gets the top left corner of the camera's x position.
* #return The x position of the top left corner of the camera
*/
public int getCamX()
{
return this.x;
}
/**
* Gets the top left corner of the camera's y position.
* #return The y position of the top left corner of the camera
*/
public int getCamY()
{
return this.y;
}
}
And finally, the relevant code for the actual painting of the tiles:
public void render() //this is the part that the render thread calls initially
{
Toolkit.getDefaultToolkit().sync();
repaint();
}
public void paint(Graphics g) //the paint method, mostly handled in doDrawing
{
super.paint(g);
doDrawing(g);
}
private void doDrawing(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
//stuff that happens if the game isn't paused
if(!GameStates.isPaused)
{
currentMap.drawMap(g2d, this);
currentChar.draw(g2d, this);
}
}
public void drawMap(Graphics2D g2d, ImageObserver observer)
{
//for each tile in the map, call its draw method
for(int i = 0; i < this.tileArray.length; i++)
{
for(int j = 0; j < this.tileArray[0].length; j++)
{
this.tileArray[i][j].draw(g2d, observer);
}
}
}
public void draw(Graphics2D g2d, ImageObserver observer, BufferedImage image)
{
g2d.drawImage(image, this.getX() + Game.cam.getCamX(), this.getY() + Game.cam.getCamY() observer);
}
Sorry for the long winded post, I've just tried everything I can think of (and that my Googling abilities can help me think of) and I'm throwing this out as a last-ditch effort before I just move to an actual game engine. I wanted to make my own engine from scratch for the sake of getting more experience in Java, but if I can't figure this out I'd rather just move to a proper engine and actually make a game. Any help is massively appreciated :)
EDIT: capitalization
So I've got a class which extends ApplicationAdapter implements InputProcessor and does the following when you drag around on the screen.
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
float x = Gdx.input.getDeltaX();
float y = Gdx.input.getDeltaY();
if (Store.isGameState) {
Store.Entity.player.setxMove(x);
Store.Entity.player.setyMove(-y);
}
return true;
}
In my player class I have a update method which does the following:
#Override
public void update() {
x += xMove;
y += yMove;
Store.Camera.Camera.position.set(Store.Entity.player.getXPos() + Store.Entity.player.getWidth() / 2, Store.Entity.player.getYPos() + Store.Entity.player.getHeight() / 2, 0);
Store.Camera.Camera.update();
}
and a render method which is:
public void render(SpriteBatch SB) {
SB.begin();
Store.Entity.sprite.setPosition(Store.Entity.player.getXPos(), Store.Entity.player.getYPos());
Store.Entity.sprite.draw(SB);
SB.end();
}
Which all-in-all works, the camera will move around as will my sprite. However my sprite does not move at the same speed as my camera and I can't for the life of me figure out why this is the case. The sprite moves roughly twice as fast as the camera does, and isn't centered on the player which I'd like ideally.
EDIT:
So in my GameState I have the following:
package com.imjoshscott.dataria.states;
import com.imjoshscott.dataria.Game;
import com.imjoshscott.dataria.Store;
public class GameState extends State {
public GameState(Game game) {
super(game);
Store.isGameState = true;
Store.Entity.getPlayer(game, Store.Entity.getSprite());
Store.Graphics.getSpriteBatch();
Store.Graphics.getTiledMap();
Store.Graphics.getTiledMapRenderer();
Store.Camera.getCamera();
Store.Camera.getHudCamera();
Store.Camera.Camera.setToOrtho(false, Game.GAME_WIDTH, Game.GAME_HEIGHT);
Store.Camera.HudCamera.setToOrtho(false, Game.GAME_WIDTH, Game.GAME_HEIGHT);
Store.Camera.Camera.viewportHeight = Game.GAME_HEIGHT / 2.5f;
Store.Camera.Camera.viewportWidth = Game.GAME_WIDTH / 2.5f;
}
#Override
public void update() {
Store.Graphics.tiledMapRenderer.setView(Store.Camera.Camera);
Store.Entity.player.update();
}
#Override
public void render() {
Store.Graphics.tiledMapRenderer.render();
Store.Entity.player.render(Store.Graphics.SB);
}
}
The Camera stuff in the Store class:
public static class Camera {
public static OrthographicCamera Camera;
public static OrthographicCamera HudCamera;
public static OrthographicCamera getCamera() {
if(Camera == null)
Camera = new OrthographicCamera();
return Camera;
}
public static OrthographicCamera getHudCamera() {
if(HudCamera == null)
HudCamera = new OrthographicCamera();
return HudCamera;
}
}
EDIT: Showing update & render methods
public void update() {
moveCreature();
Store.Entity.sprite.setPosition(Store.Entity.player.getXPos(), Store.Entity.player.getYPos());
Store.Camera.Camera.position.set(Store.Entity.player.getXPos(), Store.Entity.player.getYPos(), 0);
Store.Camera.Camera.update();
xMove = 0f;
yMove = 0f;
}
public void render(SpriteBatch SB) {
SB.begin();
Store.Entity.sprite.draw(SB);
SB.end();
}
Updated My previous answer is not correct, upon seeing and checking the project I saw this render() method for GameState class (after looked around for several places but no dice, if ever found this very weird problem again I would direct to this).
public void render() {
Store.Graphics.tiledMapRenderer.render();
Store.Entity.player.render(Store.Graphics.SB);
}
Just one thing we need to add to fix very weird problem is to add the following code
Store.Graphics.SB.setProjectionMatrix(Store.Camera.Camera.combined);
so we would have
public void render() {
Store.Graphics.SB.setProjectionMatrix(Store.Camera.Camera.combined);
Store.Graphics.tiledMapRenderer.render();
Store.Entity.player.render(Store.Graphics.SB);
}
That means we need to set projection matrix to current active SpriteBatch to properly render stuff. It's also safe to set it before even render the tile as it uses the same camera as of player.
So I'm trying to create a game and it's my first time. My game is a 2D side scroller and is about the player in space avoiding the incoming meteors. I have successfully managed to get the meteors spawning randomly on the x and y axis off screen and re position once it has gone pass the screen.
But the problem I face now is sometimes the spawn of the meteors will clump together which I don't want. How do I get the meteors to spawn at a certain distance from each other so they don't clump together. I couldn't find any good tutorials or if anyone can point me to the right direction. Below are my codes so far.
Meteor Class
public class Meteors {
private Texture bigMeteor;
private Vector2 posBigMeteor;
private Random yrand;
//Constructor
public Meteors(float x){
bigMeteor = new Texture("meteor.png");
yrand = new Random();
//Spawn location of meteor
posBigMeteor = new Vector2(x, yrand.nextInt(AstroDemo.HEIGHT/2 - bigMeteor.getHeight()));
}
public Texture getBigMeteor() {
return bigMeteor;
}
public Vector2 getPosBigMeteor() {
return posBigMeteor;
}
//Reposition the meteors
public void reposition(float x){
posBigMeteor.set(x, yrand.nextInt(AstroDemo.HEIGHT/2 - bigMeteor.getHeight()));
}
}
PlayState Class
public class PlayState extends State {
//Total meteor count on screen
private static final int METEOR_COUNT = 8;
private Naught naught;
private Texture bg;
private Random xrand;
private Array <Meteors> meteors;
public PlayState(GameStateManager gsm) {
super(gsm);
//Starting co-ordinates of main character (Naught)
naught = new Naught(50, 100);
//Setting viewport of the camera
cam.setToOrtho(false, AstroDemo.WIDTH/2, AstroDemo.HEIGHT/2);
bg = new Texture("bg.png");
xrand = new Random();
meteors = new Array <Meteors>();
//Spawn meteors randomly off screen
for (int i = 1; i <= METEOR_COUNT; i++){
meteors.add(new Meteors(AstroDemo.WIDTH/2 + (xrand.nextInt(300))));
}
}
#Override
protected void handleInput() {
//If screen/mouse is held
if(Gdx.input.isTouched()){
//Main Character jumps/flys
naught.jump();
}
}
#Override
public void update(float dt) {
handleInput();
naught.update(dt);
//If meteors are left side of the screen, re-position to the right side of the screen
for(Meteors meteor : meteors){
if (cam.position.x - (cam.viewportWidth/2) > meteor.getPosBigMeteor().x + meteor.getBigMeteor().getWidth()){
meteor.reposition(meteor.getPosBigMeteor().x + (AstroDemo.WIDTH/2 + 20 + (xrand.nextInt(300))));
}
}
cam.position.x = naught.getPosition().x + 80;
cam.update();
}
#Override
public void render(SpriteBatch sb) {
//Adjust the spritebatch for co-ordinate system in relation to camera
sb.setProjectionMatrix(cam.combined);
sb.begin();
//Draw background where the camera is
sb.draw(bg, cam.position.x - (cam.viewportWidth/2), 0);
sb.draw(naught.getTexture(), naught.getPosition().x, naught.getPosition().y);
for (Meteors meteor : meteors) {
sb.draw(meteor.getBigMeteor(), meteor.getPosBigMeteor().x, meteor.getPosBigMeteor().y);
}
sb.end();
}
#Override
public void dispose() {
}
}
create a array of predefined value for your y position and then get value randomly from that array.
or
Divide height into sub portion then get random value from that portion so that each random value not collide with other value.
I want to create an infinite bouncing ball. For now I'm just trying to make the bouncing on Y (up & down).
This is my GameWorld class, you can see there is a method collides to detect the collision but How to make that "circle" go up?
public class GameWorld {
private Circle rond;
private Rectangle rect;
public GameWorld(int midPointY) {
rond = new Circle(100, midPointY - 5, 5);
rect = new Rectangle(0, 200, Gdx.graphics.getWidth(), 5);
}
public void update(float delta) {
if(collides(rond)){
}else
rond.y++;
}
public boolean collides(Circle rond) {
if (rect.y < rond.y + (rond.radius)*2) {
return (Intersector.overlaps(rond, rect));
}
return false;
}
public Circle getRond() {
return rond;
}
public Rectangle getRect() {
return rect;
}
}
Of course, I have another class GameRenderer that rendere these two objects
Typically with any kind of physics you want to store the speed of an object as well as its position. That way, each time though your update loop you just have to change the y position by the y speed. When the collision occurs, all you need to do is calculate the new yspeed and update rond.
public void update(float delta) {
if(collides(rond)){
rond.yspeed = -rond.yspeed;
}else{
rond.y+=rond.yspeed*delta;
}
}
Please excuse my bad english. I am having a problem while implementing a 2d camera. I made the camera to follow the player until it reaches near the edge of the game-level, where only the player moves and the camera stops. I made this easily but my problem is that the camera is not proper. The camera continues to move 1 extra pixel every time you reach at the edge of the game-level limit in all four directions (The code I posted only shows horizontal movement. This I did for simplicity). This means if you move 40 times to and fro near the left edge of the game, camera will move 40 extra pixels right! I have no idea how to solve this.
I posted a very simplified version from my original code below and made it as small as i could to show how the program works. Following just moves the player and camera horizontally across the screen.
This is 'theGamePanel' class (it is the main class) :-
public class TheGamePanel extends JPanel implements Runnable, KeyListener
{
private boolean left, right;
private float cameraX, cameraY;
private World world = new World();
private Player player = new Player();
public TheGamePanel()
{
//setting the size of panel
}
public static void main(String[] args)
{
//setting the window
}
public void paint(Graphics g)
{
super.paint(g);
// drawing the game-level and player
g.translate((int)cameraX, (int)cameraY);
world.paint(g);
player.paint(g);
g.translate(-(int)cameraX, -(int)cameraY);
}
public void upd()
{
player.update(left, right, this);
}
#Override
public void run()
{
// game-loop
}
#Override
public void keyPressed(KeyEvent e)
{
int code = e.getKeyCode();
if(code == KeyEvent.VK_LEFT)
{
left = true;
}
if(code == KeyEvent.VK_RIGHT)
{
right = true;
}
}
#Override
public void keyReleased(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_LEFT)
{
left = false;
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT)
{
right = false;
}
}
#Override
public void keyTyped(KeyEvent e)
{
}
//to set camera position
public void setCameraX(float cameraDx)
{
cameraX += cameraDx;
}
}
This is the 'Player' class. This class is where the player and camera movement takes place. The camera's x and values are then returned to 'TheGamePanel' :-
public class Player
{
private float x, y;
private float dx = 1;
private int width = 32, height = 32;
private float leftLimit, rightLimit;
public Player()
{
//player's initial x and y coordinates
x = 320;
y = 240;
//camera's limit (where the camera needs to stop following player)
leftLimit = x;
rightLimit = 960;
}
public void paint(Graphics g)
{
//for painting the player
g.setColor(Color.GREEN);
g.fillRect((int)x, (int)y, width, height);
}
//to move player and camera
public void update(boolean left, boolean right, TheGamePanel panel)
{
if(left == true)
{
x -= dx;
if(x > leftLimit && x < rightLimit)
{
panel.setCameraX(dx);
}
}
if(right == true)
{
x += dx;
if(x > leftLimit && x < rightLimit)
{
panel.setCameraX(-dx);
}
}
}
}
And lastly, the 'World' class. This class is used to simply paint a big map(level) in the background :-
public class World
{
private BufferedImage map;
private int tileWd = 32, tileHi = 32;
public World()
{
try
{
map = ImageIO.read(new File("map1.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
public void paint(Graphics g)
{
//simply paints a game-level in the background
}
}
If anything not understandable please tell. I will add more details.
Consider the case where the player is standing near the right edge of the map. One quick tap on the "right" key moves him out of the camera-follow area so the camera doesn't move. One quick tap on the "left" key moves him back into the camera-follow area. The camera will be moved "dx" units to the left. This will have the effect of the player slowly creeping closer to the right edge of the playing field, if I'm correct.
(To find bugs like this, I generally litter the code with System.out.println messages. If there's a lot of messages, I write them to a file and do text searches on keywords)
you have a location for your camera (cameraX,cameraY) but nothing for the player?
just create a location for player (playerX,playerY)
change the paint like:
public void paint(Graphics g)
{
super.paint(g);
// drawing the game-level and player
g.translate((int)cameraX, (int)cameraY);
world.paint(g);
g.translate((int)playerX, (int)playerY);
player.paint(g);
g.translate(-(int)(playerX+cameraX), -(int)(playerY+cameraY));
}
and when player come near borders don't move camera and only player.
I believe the following change fixes the problem:
Player class:
//to move player and camera
public void update(boolean left, boolean right, TheGamePanel panel)
{
if(left == true)
{
x -= dx;
if(x > leftLimit && x < rightLimit)
{
panel.setCameraX(dx);
}
}
if(right == true)
{
if(x > leftLimit && x < rightLimit)
{
panel.setCameraX(-dx);
}
x += dx; // Only change: moved this line AFTER the if block
}
}
By testing x after changing it when moving left and before changing it when moving right you compensate for the error that was otherwise accumulating every time you reached one of the boundaries.
Anyway I suggest you to change your approach to the problem in order to make your code easier to maintain and more flexible. One way to do that is to calculate the camera position based on current player's position on every frame.
Note: The line g.translate(-(int)cameraX, -(int)cameraY); in the paint() method of TheGamePanel class is unnecessary once the method translate(int, int) is not incremental.
Regards