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);
}
}
}
Related
I am using libgdx to make a card game but I have hit a deadlock.
Touches or clicks on sprites are not being detected.
There is extra space that appears on my board which is not there in the
tiled map file.
I made a simple project to illustrate my problem.
A compilable project is here that shows the code in its enterity https://dl.dropboxusercontent.com/u/51343591/card.zip
public class Card extends ApplicationAdapter {
SpriteBatch batch;
TmxMapLoader tmxMapLoader;
TiledMap tiledMap;
OrthogonalTiledMapRenderer mapRenderer;
OrthographicCamera camera;
Viewport gamePort;
Sprite cardSprite;
Sprite dotSprite;
Vector3 screenPointTouched;
#Override
public void create () {
batch = new SpriteBatch();
tmxMapLoader=new TmxMapLoader();
tiledMap= tmxMapLoader.load("level1.tmx");
mapRenderer=new OrthogonalTiledMapRenderer(tiledMap);
float worldwidth=19*16;//19tiles*16pixelspertile. This is what its like on the level1.tmx file
float worldheight=12*16;//12tiles*16pixelspertile. This is what its like on the level1.tmx file
gamePort= new StretchViewport(worldwidth,worldheight,camera);//new StretchViewport(,
camera= new OrthographicCamera(10,10*(Gdx.graphics.getWidth()/Gdx.graphics.getHeight()));
camera.position.set(gamePort.getWorldWidth()/2,gamePort.getWorldHeight()/2,0);//center the camera
cardSprite=new Sprite(new Texture("badlogic.jpg"));
dotSprite=new Sprite(new Texture("dot.png"));
screenPointTouched=new Vector3();
positionCardSpriteFromFirstRectangle();
}
#Override
public void render () {
update();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
mapRenderer.render();
batch.begin();
cardSprite.draw(batch);
dotSprite.draw(batch);
batch.end();
}
private void positionCardSpriteFromFirstRectangle() {
MapObject mo=tiledMap.getLayers().get("cardlayer").getObjects().get(0);//get the firstone onlys
Rectangle rect=((RectangleMapObject)mo).getRectangle();
cardSprite.setBounds(rect.x,rect.y,rect.width,rect.height);
}
public void update() {
camera.update();
mapRenderer.setView(camera);
if(Gdx.input.isTouched()){//handle touch event
screenPointTouched.set(Gdx.input.getX(),Gdx.input.getY(),0);
screenPointTouched=camera.unproject(screenPointTouched);
if(cardSprite.getBoundingRectangle().contains(screenPointTouched.x,screenPointTouched.y)){
System.out.println("being touched");
dotSprite.setPosition(screenPointTouched.x,screenPointTouched.y);
}
}
}
}
Is it possible to set a minimum and maximum aspect ratio?
Lets say from 1:1 to 2.5:1. Or at least set min and max for width and height?
You can use a FitViewport to maintain a fixed width and height:
FitViewport viewport = new FitViewport(SCREEN_WIDTH, SCREEN_HEIGHT, camera);
If you are using a Stage to draw all your actors then all you need to do is set the viewport on the stage like this:
stage.setViewport(viewport);
However if you are not using a Stage then you need to do the following. If you are drawing directly with a SpriteBatch you will need to set up a Camera too:
SpriteBatch batch = new SpriteBatch();
OrthographicCamera camera = new OrthographicCamera(SCREEN_WIDTH, SCREEN_HEIGHT);
camera.setToOrtho(true);
//center the camera in the center of the screen
camera.position.set(SCREEN_WIDTH / 2f, SCREEN_HEIGHT / 2f, 0f);
//pass the camera to the third argument of the viewport
FitViewport viewport = new FitViewport(SCREEN_WIDTH, SCREEN_HEIGHT, camera);
Then you will need to set up the projection matrix on the Camera and SpriteBatch when rendering:
#Override
public void render(float deltaTime) {
camera.update();
batch.setProjectionMatrix(camera.combined);
// do all the rendering of textures and stuff
}
Also you need to update the viewport every time the screen is resized:
#Override
public void resize(int width, int height) {
viewport.update(width, height);
}
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.
Here is an example of what it looks like when I drag a picture with the mouse
http://i.stack.imgur.com/4RQDU.jpg
And here is the code for it.
When I load the project on the desktop application, the picture displays perfectly in the middle, but when I move it with the mouse it drags itself like in Windows xp.
public class SlingshotSteve implements ApplicationListener{
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
private Sprite sprite;
#Override
public void create() {
camera = new OrthographicCamera(1280, 720);
batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("image.png"));
sprite = new Sprite(texture);
sprite = new Sprite(texture);
sprite.setOrigin(0,0);
sprite.setPosition(-sprite.getWidth()/2,-sprite.getHeight()/2);
}
#Override
public void dispose() {
batch.dispose();
texture.dispose();
}
#Override
public void render() {
// Make the background color Black
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// Mouse imput
if(Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
sprite.setPosition(Gdx.input.getX() - sprite.getWidth()/2,
Gdx.graphics.getHeight() - Gdx.input.getY() - sprite.getHeight()/2);
}
if(Gdx.input.isButtonPressed(Input.Buttons.RIGHT)){
sprite.setPosition(Gdx.graphics.getWidth()/2 -sprite.getWidth()/2,
Gdx.graphics.getHeight()/2 - sprite.getHeight()/2);
}
batch.setProjectionMatrix(camera.combined);
batch.begin();
sprite.draw(batch);
batch.end();
}
I have no idea what I'm doing wrong
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() {
}
}