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() {
}
}
Related
The problem
When I move the Camera every texture that is getting rendered flickers about the screen on 2 different positions.
What I want
example: When I move the camera to the left, I want the all the textures to move 32 pixels to the right. camera moves 32 pixels per button press.
Current code
I added some extra explanation in comments.
MainProgramEntryPoint
/**
* DefaultCamera: Setting up OrthographicCamera
* CameraMovement: Using camera.translate on keypresses to move the screen.
* TestMoveable: Creating a texture for testing rendering.
*/
public class WorldSimGame extends ApplicationAdapter {
private DefaultCamera defaultCamera;
private CameraMovement cameraMovement;
private TestMoveable testMoveable;
// Setting up the camera and texture.
public void create () {
defaultCamera = new DefaultCamera();
cameraMovement = new CameraMovement(defaultCamera.getCamera());
testMoveable = new TestMoveable();
testMoveable.create(defaultCamera);
}
// The testMoveable.render(defaultCamera) should keep track of the testMoveable position
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
defaultCamera.render();
testMoveable.render(defaultCamera);
}
}
TestMoveable
public class TestMoveable {
private Texture tex;
private SpriteBatch batch;
private Vector3 position;
public void create(DefaultCamera defaultCamera) {
tex = new Texture(("wall.png"));
batch = new SpriteBatch();
position = new Vector3(100, 100, 0);
defaultCamera.getCamera().unproject(position);
}
I cant imagine setting the x and y coordinates on the world coordinates wouldn't work.
public void render(DefaultCamera defaultCamera) {
batch.begin();
batch.draw(tex, position.x, position.y);
batch.end();
}
}
What am I doing wrong here? And is there a better way to implement position checking for the renderers?
You don't need to check the position of the renderer. All what you must do is to set the size and position of your camera. Then with batch.setProjectionMatrix(camera.combined) you say the batch to draw what the camera sees.
So when you create a camera with size = 50x50 and position = 100,100
and you now create a Texture with size = 50x50 and position = 75,75
The Texture will perfectly fit the hole screen.
The position of camera is in the center. So the position of Texture is 75,75 and not 100,100
When you now will move your camera, you can use the method: translate() of your camera.
Call: camera.translate(25,0) to move your camera 25 units to the right and now you only see the half of your Texture on the left side of the screen.
This is an easy example of a Moveable camera. You can use the arrow keys to move around:
public class WorldSimGame extends ApplicationAdapter {
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
public WorldSimGame() { }
#Override
public void create(){
//Create texture, batch and camera
texture = new Texture(Gdx.files.internal("badlogic.jpg"));
batch = new SpriteBatch();
camera = new OrthographicCamera(60,60);
}
#Override
public void render(){
//clear screen
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
moveCamera();
//update camera that he recalculate his position and Matrix
camera.update();
batch.setProjectionMatrix(camera.combined); //set batch to draw what the camera sees
batch.begin();
batch.draw(texture,0,0); //draw texture
batch.end();
}
private void moveCamera(){
//move camera
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)){
camera.translate(4,0);
}
if(Gdx.input.isKeyPressed(Input.Keys.LEFT)){
camera.translate(-4,0);
}
if(Gdx.input.isKeyPressed(Input.Keys.UP)){
camera.translate(0,4);
}
if(Gdx.input.isKeyPressed(Input.Keys.DOWN)){
camera.translate(0,-4);
}
}
}
I'm new in creating game.
I have several boxes that move from top to bottom of screen and i need to make 1 unit distance between them.
Every thing works fine except the distance is not precise and i think the deltatime cause this problem
This is how i move the boxes :
objX -= deltatime * speed;
Update
time += deltatime :if(time >= 3.0f) spawn
Im using libgdx
You can schedule your task in this way :
Array<Sprite> sprites;
Texture texture;
SpriteBatch batch;
float speed=35;
#Override
public void create() {
sprites=new Array<Sprite>();
texture=new Texture("badlogic.jpg");
batch=new SpriteBatch();
Timer.instance().scheduleTask(new Timer.Task() {
#Override
public void run() {
spawn();
}
},1,2);
}
private void spawn(){
Sprite sprite=new Sprite(texture);
sprite.setSize(50,50);
sprite.setPosition(Gdx.graphics.getWidth()/2,0);
sprites.add(sprite);
}
#Override
public void render() {
Gdx.gl.glClearColor(1,1,1,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
float delta=Gdx.graphics.getDeltaTime();
batch.begin();
for (Sprite sprite:sprites){
sprite.setY(sprite.getY()+delta*speed);
sprite.draw(batch);
}
batch.end();
}
After a lot of struggling i came up with an idea which is
instead of moving boxes and spawning them by deltatime , make boxes static and move camera over them!
So i just move camera here
public void render(float delta) {
posCameraDesired.y += 2000.0f * Gdx.graphics.getDeltaTime();
guiCam.position.lerp(posCameraDesired, 0.1f);
SemiRender();
}
and i have static Y that i'll update after each spawn
lastY = 10 + lastY + ((width / 4) / 2) * 3;
and some other logic to recycle them when they passed the viewport.
and every one is happy!
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.
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.