Jeremiah Crowell
Jeremiah Crowell

Reputation: 177

libgdx drawing an image at touch location

I'm just trying to get libgdx to create a picture wherever I touch the screen.

here's what i have that isn't doing anything

SpriteBatch batch;
Texture img;

@Override
public void create () {
    batch = new SpriteBatch();
    img = new Texture("badlogic.jpg");
}

@Override
public void render () {
    Gdx.gl.glClearColor(1, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}

public class MyInputProcessor implements InputProcessor {
    public boolean touchDown (int x, int y, int pointer, int button) {
        batch.begin();
        batch.draw(img,Gdx.input.getX(),Gdx.input.getY());
        batch.end();
        return true;
    }
    ... (the rest of the input methods)

if you can't tell, I don't really know what I'm doing yet, I think it has to do with the batch.draw() being in the touchDown method instead of the render area but I can't figure out from research how to do it a different way either

or maybe this is all wrong too, point is I'm doing this to learn so hopefully the correct answer will help me understand some important things about java in general

Upvotes: 2

Views: 1325

Answers (1)

Tenfour04
Tenfour04

Reputation: 93699

LibGDX, like basically all game engines, re-renders the entire scene every time render() is called. render() is called repeatedly at the frame rate of the game (typically 60fps if you don't have a complex and unoptimized game). The first drawing-related thing you usually do in the render() method is to clear the screen, which you have already done with Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);. Then you re-draw the whole scene with whatever changes there might be since the last frame.

You are trying to draw something with the batch outside of the render method. In this case, you are doing it when there is a touch down. But since you are doing this only when there is a touch down, the object will appear and disappear on the next call to render(), so it will only be on screen for 1/60th of a second. If you want to do this with an input processor, you need to set some variable to true to indicate the render method should draw it, and other variables to indicate where to draw it. Then in the render() method, you draw the stuff if the variable is true.

Secondly, the x and y that an input processor gets do not necessarily (and usually don't) correspond with the x and y in OpenGL. This is because OpenGL has it's own coordinate system that is not necessarily sized exactly the same as the screen's coordinate system. The screen has (0,0) in the top left with the Y axis going down, and the width and height of the screen matching the number of actual pixels on the screen. OpenGL has (0,0) in the center of the screen with the Y axis going up, and the width and height of the screen being 2 regardless of the actual screen pixels.

But the OpenGL coordinate system is modified with projection matrices. The LibGDX camera classes make this simpler. For 2D drawing, you need an OrthographicCamera. You set the width and size of the OpenGL world using the camera, and can also position the camera. Then you pass the camera's calculated matrices to the SpriteBatch for it to position the scene in OpenGL space.

So to get an input coordinate into your scene's coordinates, you need to use that camera to convert the coordinates.

Finally, LibGDX cannot magically know that it should pass input commands to any old input processor you have created. You have to tell it which InputProcessor it should use with a call to Gdx.input.setInputProcessor().

So to fix up your class:

SpriteBatch batch;
Texture img;
boolean isTouchDown;
final Vector3 touchPosition = new Vector3();
OrthographicCamera camera;

@Override
public void create () {
    batch = new SpriteBatch();
    img = new Texture("badlogic.jpg");
    camera = new OrthographicCamera();
    Gdx.input.setInputProcessor(new MyInputProcessor()); // Tell LibGDX what to pass input to
}

@Override 
void resize (int width, int height) {
    // Set the camera's size in relation to screen or window size
    // In a real game you would do something more sophisticated or 
    // use a Viewport class to manage the camera's size to make your 
    // game resolution-independent.
    camera.viewportWidth = width;
    camera.viewportHeight = height;

    camera.update(); // re-calculate the camera's matrices
}

@Override
public void render () {
    Gdx.gl.glClearColor(1, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    batch.setProjectionMatrix(camera.combined); // pass camera's matrices to batch
    batch.begin();

    if (isTouchDown) { // Only draw this while the screen is touched.
        batch.draw(img, touchPosition.x, touchPosition.y);
    }

    batch.end();
}

public class MyInputProcessor implements InputProcessor {
    public boolean touchDown (int x, int y, int pointer, int button) {
        isTouchDown = true;

        touchPosition.set(x, y, 0); // Put screen touch coordinates into vector
        camera.unproject(touchPosition); // Convert the screen coordinates to world coordinates
        return true;
    }

    public boolean touchUp (int screenX, int screenY, int pointer, int button){
        isTouchDown = false;
        return true;
    }

    //... (the rest of the input methods)
}

Upvotes: 2

Related Questions