http://badlogicgames.com/forum/viewtopic.php?f=11&t=2447
http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/OrthographicCamera.html
There's hundreds of other links I could show you that I've looked at, but it's just not worth it because they all say the same thing.
public class InGame implements Screen {
SpriteBatch batch;
GameWorld world;
OrthographicCamera camera;
#Override
public void show() {
batch = new SpriteBatch();
world = new GameWorld();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.setToOrtho(true, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
world.render(batch, delta);
batch.end();
}
}
What am I doing wrong? WHY is my world still being rendered with the 0,0 being at the bottom right. The math behind this while trying to work on my Tile-System is driving me absolutely insane.
World->Render
public void render(SpriteBatch batch, float delta) {
for(int xAxis = 0; xAxis < worldColumns; xAxis++) {
for(int yAxis = 0; yAxis < worldRows; yAxis++) {
tiles[xAxis][yAxis].render(batch, delta);
}
}
}
WorldTile->Render
public void render(SpriteBatch batch, float delta) {
myShape.begin(ShapeType.Filled);
myShape.setColor(tileColor);
myShape.rect(pos.getX(), pos.getY(), TILE_WIDTH, TILE_HEIGHT);
myShape.end();
}
The "pos" is the Position(x, y) that was passed in the World class.
If you are drawing a Sprite or TextureRegion using your SpriteBatch the code should work fine. However, you pass your SpriteBatch 'batch' all the way down to WorldTile.render and never use it?! Instead you use myShape which I assume is a ShapeRenderer. You need to set the projection matrix for the ShapeRenderer as well otherwise it will draw 'upside-down'.
Try calling myShape.setProjectionMatrix(camera.combined); before you use your myShape.
Its probably best to declare and initialise myShape in your InGame class, usemyShape.setProjectionMatrix(camera.combined);, and then pass myShape down to tiles.render() as you did with your SpriteBatch.
Hope this helps.
Related
I'm making a prototype project in LibGDX and I'm using Box2D for the physics.
I've created a map and added some collision to him, i added entities that has circle shapes too.
These circles are correctly positioned in the World of Box2D, but the textures that i want to get fixed to them are getting based on other coordinates, from the user camera I think.
This is the result:
My render method in the base "Entity" class
public void update() {
handleInput();
}
public void render(SpriteBatch batch) {
update();
batch.begin();
batch.draw(texture, body.getPosition().x, body.getPosition().y);
batch.end();
}
My render method in the BaseMap class
private void update(OrthographicCamera camera) {
camera.position.x = player.getPosition().x;
camera.position.y = player.getPosition().y;
world.step(1/60f, 6, 2);
}
public void render(OrthographicCamera camera, SpriteBatch batch) {
update(camera);
renderer.setView(camera);
batch.begin();
renderer.render();
batch.end();
for(Entity entity : entities) {
entity.render(batch);
}
}
The renderer here is a OrthogonalTiledMapRenderer
Make sure you're setting the matrix for the batch as well:
batch.setProjectionMatrix(camera.combined);
Likewise for the Box2DDebugRenderer:
renderer.render(world, camera.combined);
This is assuming you aren't using a scaled box2d world.
In the picture below you can see that I have drawn a black circle into the screen and in the code I have tried to get the black circle to match the coordinates of the clear circle which is my physics body which is completely working.
My problem is when I move the clear circle around my map the black circle stays there in the corner. The black circle moves a little about a few pixels so it seems its mmoves around a bit but very scaled down and I don't know why. Thanks.
public void update(float dt){
handleInput(dt);
world.step(1 / 60f, 6, 2);
player.update(dt);
camX();
gameCam.update();
renderer.setView(gameCam);
}
#Override
public void render(float delta) {
update(delta);
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.batch.setProjectionMatrix(hud.stage.getCamera().combined);
hud.stage.draw();
renderer.render();
b2dr.render(world, gameCam.combined);
game.batch.begin();
game.batch.draw(ball,player.getX() , player.getY(), 70, 70);
game.batch.end();
}
public void camX(){
gameCam.position.x = gamePort.getWorldWidth() / 2;
if(player.b2Body.getPosition().x >= gamePort.getWorldWidth() / 2){
gameCam.position.x = player.b2Body.getPosition().x;
}
}
public class Character extends Sprite {
public World world;
public Body b2Body;
public float x;
public float y;
public Character(World world){
this.world = world;
defineMario();
}
public void update(float dt) {
setPosition(b2Body.getPosition().x,b2Body.getPosition().y );
}
You set the Batch's projection matrix to the UI's camera for UI drawing, but you never change it to the gameCamera's projection before drawing your game elements. Before the game.batch.begin(); line you need to add game.batch.setProjectionMatrix(gameCamera.combined);.
By the way, it doesn't make sense for Character to extend Sprite, since you don't use Character for drawing anything, only for tracking a position.
Please try this :
public class Character extends Sprite {
public World world;
public Body b2Body;
public float x;
public float y;
public Character(World world){
this.world = world;
defineMario();
}
public void update(float dt) {
//so the sprite and body will have the same center
setPosition(b2Body.getPosition().x-getWidth()/2, b2Body.getPosition().y- getHeight()/2 );
setCenterOrigin(); // to handle when body does a rotation
}
please, tell me if it works or not in comment
Good luck
The problem
I cannot seem to be able to get Tiled maps to render properly. I am using LibGDX as a library for loading the map (Release 1.6.0).
Video demonstration
I have created a video to show you the actual problem and make things easier by skipping the whole process of explaining it. Here is a link to it.
The code I've used
protected Level level;
protected OrthogonalTiledMapRenderer mapRenderer;
protected OrthographicCamera camera;
protected TiledMap map;
protected MainGameLoop game;
protected SpriteBatch batch;
private BitmapFont font;
private int w, h;
public Level1(MainGameLoop game) {
this.game = game;
}
#Override
public void show() {
w = Gdx.graphics.getWidth();
h = Gdx.graphics.getHeight();
int CAMERA_WIDTH = 800;
int CAMERA_HEIGHT = 450 * (w / h);
camera = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
camera.setToOrtho(false);
camera.update();
map = new TmxMapLoader().load("maps/map1.tmx");
mapRenderer = new OrthogonalTiledMapRenderer(map);
Gdx.input.setInputProcessor(this);
font = new BitmapFont();
font.setColor(Color.BLUE);
batch = new SpriteBatch();
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
mapRenderer.setView(camera);
mapRenderer.render();
batch.begin();
font.draw(batch, "Camera zoom: " + camera.zoom, 40, 40);
batch.end();
}
#Override
public void resize(int width, int height) {
camera.viewportWidth = width;
camera.viewportHeight = height;
camera.update();
}
#Override
public void dispose() {
mapRenderer.dispose();
map.dispose();
background.dispose();
Gdx.input.setInputProcessor(null);
}
#Override
public boolean scrolled(int amount) {
camera.zoom += amount;
camera.update();
return true;
}
// Here go the rest of the methods, such as pause, resume, hide, keyDown, keyUp, keyTyped, touchDown, touchUp, touchDragged & mouseMoved.
Solutions I've tried
I have tried using different numbers for the camera's x and y with no luck. I have also tried tranlating the camera to the proper position (hardcoded it), as well as using another map (different tilemap and dimensions) but that did not work either.
Conclusion
I can't seem to find a way to fix this problem. Any help is much appreciated. Thank you very much.
Quick introduction
Ok after a good while, I managed to solve this matter by hardcoding some stuff. But it works properly, so I am happy with it.
What I did to solve the problem
First of all, I found out the exact number I had to use to scale up my Tiled map, which is this number: 3.125f .
Then, instead of using pixels for the camera, I used my own units (something I should have done from the the first moment).
After doing those two things, I noticed that the map was zoomed in a lot. So, by using the scrolled method from the InputProcessor, I managed to find the exact number the map had to be "unzoomed".
I also found out that if I call the setToOrtho(false) method from the OrthographicCamera object, it zooms the map in 19 times for some weird reason. If that method does not get called, the map is zoomed in only 1 time
The code I am currently using
TiledMap tiledMap;
OrthographicCamera camera;
TiledMapRenderer tiledMapRenderer;
final float WIDTH = 8000;
final float HEIGHT = 4500;
final float num = 3.125f;
#Override
public void show() {
tiledMap = MapLoader.realm1_level1;
tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap, num);
camera = new OrthographicCamera(WIDTH, HEIGHT);
Gdx.input.setInputProcessor(this);
camera.zoom += 1f;
camera.update();
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
tiledMapRenderer.setView(camera);
tiledMapRenderer.render();
}
// This method was just used for testing to see where
// the map was or should have been placed.
#Override
public boolean keyDown(int keycode) {
if (keycode == Input.Keys.LEFT)
camera.translate(-32, 0);
if (keycode == Input.Keys.RIGHT)
camera.translate(32, 0);
if (keycode == Input.Keys.UP)
camera.translate(0, 32);
if (keycode == Input.Keys.DOWN)
camera.translate(0, -32);
if (keycode == Input.Keys.NUM_1)
tiledMap.getLayers().get(0).setVisible(!tiledMap.getLayers().get(0).isVisible());
return true;
}
#Override
public void resize(int width, int height) {
camera.position.set(WIDTH, HEIGHT, 0);
camera.update();
}
#Override
public boolean scrolled(int amount) {
camera.zoom += amount;
camera.update();
return true;
}
#Override
public void dispose() {
tiledMap.dispose();
}
// And here go the rest of the methods that come from the
//Screen and the InputProcessor interfaces.
Notes
The numbers I used for the WIDTH and the HEIGHT variables work properly, strictly with tiled maps that their width is 80 tiles, and their height is 45 tiles.
If you are facing the same problem, you'll have to find the appropriate number to scale your map up/down, depending on the unit numbers you're using. Trial and error is your only guide. If we ignore StackOverflow of course.
Basically, I want my coordinate system to have (0,0) as the top left corner of the screen(used to this in Swing and Android Canvas drawing).
I saw a great thread for doing so Changing the Coordinate System in LibGDX (Java). I used the code that the creator provided but the rectangle I am drawing is still at the bottom of the screen.(screenshot)
Here is my code for drawing the rectangle(I use one class, GameWorld, to manage all the "game objects" and another class, GameRenderer to render all the game objects) Both classes have a method that will be called from the GameScreen's render method.
Relevant code in GameScreen.java
public class GameScreen implements Screen {
//manage game objects
private GameWorld world;
//render game objects
private GameRenderer renderer;
public GameScreen() {
world = new GameWorld();
renderer = new GameRenderer(world);
}
public void render(float delta) {
world.update(delta);
renderer.render();
}
.....
}
And in GameWorld.java
public class GameWorld {
private Rectangle rectangle;
public GameWorld() {
rectangle = new Rectangle(0, 0, 17, 12);
}
public void update(float delta) {
rectangle.setX(rectangle.getX() + 1);
if(rectangle.getX() > Gdx.graphics.getWidth()) {
rectangle.setX(0);
}
}
public Rectangle getRect() {
return rectangle;
}
}
And in GameRenderer.java
public class GameRenderer {
private ShapeRenderer shapeRenderer;
private OrthographicCamera cam;
private GameWorld myWorld;
public GameRenderer(GameWorld theWorld) {
myWorld = theWorld;
cam = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.setToOrtho(true, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
public void render() {
shapeRenderer = new ShapeRenderer();
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(87/255.0f, 109/255.0f, 120/255.0f, 1);
Rectangle toDraw = myWorld.getRect();
shapeRenderer.rect(toDraw.getX(), toDraw.getY(),
toDraw.getWidth(), toDraw.getHeight());
shapeRenderer.end();
}
}
And finally in Desktop.java where I am testing the game,
public class DesktopLauncher {
public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title = "My test";
config.width = 272;
config.height = 408;
new LwjglApplication(new MyGdxGame(), config);
}
}
Does anyone see where i am doing wrong? Why is the rectangle still being drawn at the bottom of the screen. I used the provided code from the creator and specified the rectangle's top y coordinate as zero.
You didn't apply your camera's projection to the ShapeRenderer.
Add this line right before shapeRenderer.begin():
shapeRenderer.setProjectionMatrix(cam.combined);
Also, you should move ShapeRenderer's instantiation from render() to the class constructor so you aren't creating a new one on every frame.
I'm using libgdx and I have a tiled map which i want to draw the sprite onto. However the sprite is drawn onto the actual window so when I move the camera, the sprite stays in the same place. ?I want to the sprite to move on the map.
This is how i currently render my objects
#Override
public void render(float delta) {
translateCamera();
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
renderer.setView(camera);
renderer.render(bgLayers);
batch.begin();
batch.draw(splayerSprite, Gdx.graphics.getWidth() / 2,
Gdx.graphics.getHeight() / 2);
batch.end();
renderer.render(fgLayers);
}
It always end up being in the middle of the screen, however I want to be able to move them seperatly like for example the camera with (W,A,S,D) and move my player with the direction keys. Then if I want the camera locks onto the player but other wise its free.
I'm new to libgdx so please bear with me, Thanks
The problem is the SpriteBatch projection matrix isn't being set to the Camera projection matrix. This means the Sprite is not being rendered relative to the Camera. This is why the camera is moving, but the sprite is not; the correct matrix is not being used.
Also the sprite is being rendered always at half the screen's width, and half the screen's height. To fix this Call sprite.draw. This will use the Sprite's internal position.
Set the SpriteBatch projection matrix via batch.setProjectionMatrix(camera.combined). This will cause the sprite to be rendered relative to the camera.
#Override
public void render(float delta) {
translateCamera();
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
renderer.setView(camera);
renderer.render(bgLayers);
//here's the line that was missing.
batch.setProjectionMatrix(camera.combined);
batch.begin();
//be sure to call this instead of specifying position yourself!
splayerSprite.draw(batch);
batch.end();
renderer.render(fgLayers);
}
You'll still need to handle snapping the camera's position to the sprite's position whenever WASD is pressed, but that's trivial.
//snap the camera to the sprite's center.
if(wasd_isDown){
float centerX = sprite.getX()+sprite.getWidth()/2;
float centerY = sprite.getY()+sprite.getHeight()/2;
camera.position.set(x,y, 0);
}
If direction keys are pressed, just translate the camera's position vector via Vector3.add like so:
if(!wasd_isDown){
float deltaX = 0;
float deltaY = 0;
float MOVE_DIST = 10;//or whatever you need.
if(leftPressed) deltaX = -MOVE_DIST;
else if(rightPressed) deltaX = MOVE_DIST;
if(upPressed)deltaY = MOVE_DIST;
else if(downPressed)deltaY = -MOVE_DIST;
camera.position.add(deltaX, deltaY, 0);
}
This will allow the camera to move independently only when the player uses directional keys, and will allow the sprite be be rendered in relation to the camera's orientation. It will also snap the camera immediately back to the sprite when WASD is pressed.
batch.draw(splayerSprite, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
You are telling the code to draw it to the center of your screen every time. You need to change Gdx.graphics.getWidth() / 2 and Gdx.graphics.getHeight() / 2 to actual values that change based on your input.
Edit #2: The line batch.setProjectionmatrix(camera.combined); is needed in addition to everything I have mentioned, I both did not notice that specific line was already in my code (it is included in the default libGDX project), and did not try running my demo with that line removed. I hope that clears up any confusion I may have caused.
Edit: Since apparently nobody really liked my answer, I went and wrote a demo using the controls specified in a clean libGDX game. Regardless of where the camera is aimed at (since it is being translated), the sprite was always being rendered in the center of the global screen. It is very much necessary to use the sprite's position in the batch.draw() instead of a static position, otherwise it will not move.
package com.me.mygdxgame;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
public class MyGdxGame implements ApplicationListener {
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
private Sprite sprite;
private Sprite background;
private boolean lockToSprite;
private Vector2 vecCamera;
private Vector2 vecSprite;
#Override
public void create() {
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
camera = new OrthographicCamera(w, h);
batch = new SpriteBatch();
lockToSprite = true;
vecCamera = new Vector2();
vecSprite = new Vector2();
texture = new Texture(Gdx.files.internal("data/libgdx.png"));
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
TextureRegion region = new TextureRegion(texture, 0, 0, 512, 275);
sprite = new Sprite(region);
sprite.setSize(0.1f * sprite.getWidth(), 0.1f * sprite.getHeight());
sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
background = new Sprite(region);
background.setOrigin(background.getWidth() / 2, background.getHeight() / 2);
System.out.println(background.getOriginX());
background.setPosition(-background.getWidth() / 2, -background.getHeight() / 2);
}
#Override
public void dispose() {
batch.dispose();
texture.dispose();
}
#Override
public void render() {
camera.translate(vecCamera);
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
camera.translate(vecCamera.cpy().mul(-1));
float moveSensitivity = 0.9f;
Vector2 vecInputSprite = new Vector2();
if (Gdx.input.isKeyPressed(Keys.UP))
vecInputSprite.y += moveSensitivity;
if (Gdx.input.isKeyPressed(Keys.DOWN))
vecInputSprite.y -= moveSensitivity;
if (Gdx.input.isKeyPressed(Keys.LEFT))
vecInputSprite.x -= moveSensitivity;
if (Gdx.input.isKeyPressed(Keys.RIGHT))
vecInputSprite.x += moveSensitivity;
if (Gdx.input.isKeyPressed(Keys.N))
vecSprite.set(new Vector2());
Vector2 vecInputCamera = new Vector2();
if (Gdx.input.isKeyPressed(Keys.W))
vecInputCamera.y += moveSensitivity;
if (Gdx.input.isKeyPressed(Keys.S))
vecInputCamera.y -= moveSensitivity;
if (Gdx.input.isKeyPressed(Keys.A))
vecInputCamera.x -= moveSensitivity;
if (Gdx.input.isKeyPressed(Keys.D))
vecInputCamera.x += moveSensitivity;
if (Gdx.input.isKeyPressed(Keys.R)) {
vecCamera.set(new Vector2());
lockToSprite = false;
}
if (vecInputCamera.len2() != 0)
lockToSprite = false;
else if (Gdx.input.isKeyPressed(Keys.L))
lockToSprite = true;
if (lockToSprite) {
vecCamera.set(vecSprite);
} else {
vecCamera.add(vecInputCamera);
}
vecSprite.add(vecInputSprite);
batch.setProjectionMatrix(camera.combined);
batch.begin();
background.draw(batch);
sprite.setPosition(vecSprite.x, vecSprite.y);
sprite.draw(batch);
//batch.draw(sprite, vecSprite.x, vecSprite.y);
batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}