libGDX Draw viewport only partly while cutting off the rest - java

This problem seemed very obvious for me to solve, but whatever I try, it doesn't work. What I'm trying to do is to incorporate a mini-version of my PlayScreen in a ScrollPane as a tutorial where you can read text and try it out immediately.
Because I didn't find any better solution to add this to the Table inside the ScrollPane, I edited the draw() method of the PlayScreen to take the ScrollPane.getScrollPercentY() and offset the camera of the PlayScreen accordingly.
What I want to do now is to only render only part of the viewport that would be normally visible in the real game. Subsequently, I want to be able to control the size and position of this "window".
I also want to be able to resize and move the content, while cutting off the edges that are not visible to the camera. This is what I tried inside the PlayScreenDraw:
public void draw(final float yOffset,
final int xTiles,
final int yTiles) {
view.getCamera().position.y = yTiles / 2f - yOffset * yTiles / HEIGHT; // HEIGHT = 800
view.getCamera().position.x = xTiles / 2f;
view.setWorldSize(xTiles, yTiles); //Do i even need to change the world size?
b.setProjectionMatrix(view.getCamera().combined);
b.begin();
...
b.end();
view.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
What this gives me, in terms of the picture above, is this
How do I need to change the viewport and/or the camera? Btw., this is how i set the two up:
cam = new OrthographicCamera();
cam.setToOrtho(false, WIDTH, HEIGHT); // WIDTH = 8, HEIGHT = 16
batch.setProjectionMatrix(cam.combined);
view = new FitViewport(WIDTH, HEIGHT, cam);

The Pixmap class can help you achieve what you want since you stated that you wanted to "cut off" the parts outside of the green selection box.
You need to render what the camera sees to an FBO and then get the pixmap from the FBO itself.
Framebuffer Objects are OpenGL Objects, which allow for the creation of user-defined Framebuffers. With them, one can render to non-Default Framebuffer locations, and thus render without disturbing the main screen.
-- OpenGL wiki
// Construct an FBO and keep a reference to it. Remember to dispose of it.
FrameBuffer fbo = new FrameBuffer(Format.RGBA8888, width, height, false);
public void render() {
//Start rendering to the fbo.
fbo.begin();
//From the camera's perspective.
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Draw whatever you want to draw with the camera.
batch.end();
// Finished drawing, get pixmap.
Pixmap pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, width, height);
//Stop drawing to your fbo.
fbo.end();
}
After getting the pixmap you can iterate through the pixels and set the alpha of the pixels outside your green selection window to 0 making them invisible or "cutting them off"

Related

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
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)
}

Scaling to sceen using libgdx on Android

I'm creating a simple game for Android using libgdx. I've come to the issue of having different screen sizes for different devices yet haven't found any concreted documentation on how to deal with this problem.
I think I'm supposed to use an OrthographicCamera? An example of code I have so far is:
private OrthographicCamera camera;
public void create() {
batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("data/cube.png"));
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
camera = new OrthographicCamera(1280, 720);
sprite = new Sprite(texture);
sprite.setOrigin(0, 0);
sprite.setPosition(1280/2, 600);
}
public void render() {
Gdx.gl.glClearColor(0.204f, 0.255f, 0.255f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
sprite.draw(batch);
batch.end();
}
Am I going along the right lines? I don't have any other devices to test on and my emulators are causing me issues.
In case you didn't already do so, you should upgrade your LibGDX version to the latest release which is 1.0.0. In this version the socalled Viewport has been introduced.
You can find some screenshots and code snippets and everything you need to know here.
Basically you will have to decide for a certain strategy (your question sounds like you are interesting in either ScreenViewport or StretchViewport) and then let that manage your camera.
What I use in my libGDX projects is to override the resize method and set the OrthographicCamera to the size of the screen as follows, using the built in method called setToOrtho(boolean yDown) which sets camera centered on the current size which a parameter if you want the y-Axis pointing down or not:
#Override
public void resize(int width, int height){
camera.setToOrtho(false);
}
This, however will not change the size of your textures if you want to rescale your textures as well then I would recommend rather than setting them to an absolute size e.g 15 pixels try setting them to a percentage of the screen size.
Or, another, sometimes more effective method is to work out the correct sizes at a certain size e.g 800 x 480 then work out the percentage increase in width and apply that to your sprites E.g:
#Override
public void resize(int width, int height){
super.resize(width, height);
camera.setToOrtho(false);
//The following is if the sprites are normally correct scaled
//for a screen size of 800 x 480, change to whatever you need
//They should both be floats and class variables
widthChange = width / 800;
heightChange = height / 480;
}
public void render() {
Gdx.gl.glClearColor(0.204f, 0.255f, 0.255f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Rather than using sprite.draw(batch), use:
batch.draw(sprite, sprite.x * widthChange, sprite.y * heightChange, sprite.width * widthChange, sprite.height * heightChange);
batch.end();
}

LibGDX resolution independence

I've searched all around google and this website for infos about this problem, but cannot solve it..
I'm a newbie in game development and LibGDX, and cannot find a solution well explained on how to port my game to all the various screen sizes..
Would you kindly help me?
Thanx
When using the newest libgdx version, you will find the Viewport class...
The viewport describes the transformation of the coordinate system of the screen (being the pixels from 0,0 in the lower left corner to e.g. 1280,768 in the upper right corner (depending on the device)) to the coordinate system of your game and scene.
The Viewport class has different possibilities on how to do that transformation. It can either stretch your scene coordinate system to exactly fit the screen coordinate system, which might change the aspect ratio and for example "stretch" your images or buttons.
It's also possible to fit the scene viewport with its aspect ratio into the viewport, which might produce a black border. E.g. when you have developed the game for 4:3 screens and now embed it into 16:10 displays.
The (in my opinion) best option is through fitting the scene viewport into the screen by matching either the longest or shortest edge.
This way, you can have a screen/window coordinate system from (0,0) to (1280,768) and create your game coordinate system maybe from (0,0) to (16,10) in landscape mode. When matching the longest edge, this means that the lower left corner of the screen will be (0,0), the lower right will be (16,0)... On devices that don't have the same aspect ratio, the y-values on the upper corners might differ a bit.
Or when matching the shortest edge, this means your scene coordinates will always be shown from (x,0) to (x,10) ... But the right edge might not exactly have and x value of 16, since device resolutions differ...
When using that method, you might have to reposition some buttons or UI elements, when they are supposed to be rendered on the top or the right edges...
Hope it helps...
Once me too suffered from this problem but at end i got the working solution, for drawing anything using SpriteBatch or Stage in libgdx. Using OrthographicCamera we can do this.
first choose one constant resolution which is best for game. Here i have taken 1280*720 (landscape).
class ScreenTest implements Screen {
final float appWidth = 1280, screenWidth = Gdx.graphics.getWidth();
final float appHeight = 720, screenHeight = Gdx.graphics.getHeight();
OrthographicCamera camera;
SpriteBatch batch;
Stage stage;
Texture img1;
Image img2;
public ScreenTest() {
camera = new OrthographicCamera();
camera.setToOrtho(false, appWidth, appHeight);
batch = new SpriteBatch();
batch.setProjectionMatrix(camera.combined);
img1 = new Texture("your_image1.png");
img2 = new Image(new Texture("your_image2.png"));
img2.setPosition(0, 0); // drawing from (0,0)
stage = new Stage(new StretchViewport(appWidth, appHeight, camera));
stage.addActor(img2);
}
#Override
public void render(float delta) {
batch.begin();
batch.draw(img, 0, 0);
batch.end();
stage.act();
stage.act(delta);
stage.draw();
// Also You can get touch input according to your Screen.
if (Gdx.input.isTouched()) {
System.out.println(" X " + Gdx.input.getX() * (appWidth / screenWidth));
System.out.println(" Y " + Gdx.input.getY() * (appHeight / screenHeight));
}
}
// ...
}
run this code in any type of resolution it will going to adjust in that resolution without any disturbance.

Drawing a gradient in Libgdx

Ok I have this code
#Override
public void render() {
// do not update game world when paused
if (!paused) {
// Update game world by the time that has passed
// since last render frame
worldController.update(Gdx.graphics.getDeltaTime());
}
// Sets the clear screen color to: Cornflower Blue
Gdx.gl.glClearColor(0x64/255.0f, 0x95/255.0f, 0xed/255.0f,
0xff/255.0f);
// Clears the screen
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// Render game world to screen
worldRenderer.render();
}
And it draws a light blue background onto the screen. I am attempting to create a gradient that goes from a dark blue at the top, to a light blue towards the bottom. Is there a simple way to do this? I'm new to Libgdx, and OpenGL so i'm trying to learn from a book but I can't seem to find the answer to this one. I've heard of drawing a big square and having the vertices different colors, but I'm unsure of how to do this.
In libGDX, the ShapeRenderer object contains a drawRect() method that takes arguments for its position and size as well as four colors. Those colors are converted to a 4-corners gradient. If you want a vertical gradient, just make the top corners one color and the bottom corners another color. Something like this:
shapeRenderer.filledRect(x, y, width, height, lightBlue, lightBlue, darkBlue, darkBlue);
From the API for ShapeRenderer:
The 4 color parameters specify the color for the bottom left, bottom right, top right and top left corner of the rectangle, allowing you to create gradients.
It seems ShapeRenderer.filledRect method has been removed in late libGDX versions. Now the way to do this is as follows:
shapeRenderer.set(ShapeRenderer.ShapeType.Filled);
shapeRenderer.rect(
x,
y,
width,
height,
Color.DARK_GRAY,
Color.DARK_GRAY,
Color.LIGHT_GRAY,
Color.LIGHT_GRAY
);
The parameters for rect method work in the same way as those in filledRect used to do, like in Kevin Workman answer.
There are some further details worth bearing in mind before comitting to ShapeRenderer. I for one will be sticking with stretching and tinting Texture.
private Color topCol = new Color(0xd0000000);
private Color btmCol = new Color(0xd0000000);
#Override
public void render(float delta) {
...
batch.end(); //Must end your "regular" batch first.
myRect.setColor(Color.YELLOW); // Must be called, I don't see yellow, but nice to know.
myRect.begin(ShapeRenderer.ShapeType.Filled); //Everyone else was saying call `set`.
//Exception informed me I needed `begin`. Adding `set` after was a NOP.
myRect.rect(
10, 400,
//WORLD_H - 300, // WORLD_H assumed 1920. But ShapeRenderer uses actual pixels.
420,
300,
btmCol, btmCol, topCol, topCol
);
myRect.end();
I was hoping to change transparency dynamically as player health declines. The btmCol and topCol had no effect on transparency, hence I'll stick to Textures. Translating pixel space is no biggie, but this is much more than the proferred single or double line above.

LibGdx How to repeat background?

Few days ago I figured out how to do some scrolling in LibGdx. Now I'm triying to do something related. I want to repeat the background. My scrolling follows a ship (Is an s[ace ship game). In the background there is a space photo loaded as a Texture. When the ship reach the end of the backgorund, It keeps going and there's no background anymore. I have read about wrap but I don't really understand How It works. I did that:
px=new Pixmap(Gdx.files.internal("fondo.jpg"));
background=new Texture(px);
background.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
And then, in my render method
spriteBatch.begin();
spriteBatch.draw(background,0,0,500,50);
drawShip();
spriteBatch.end();
Of course It doesn't work, It only draws the background once. I don't know how make this wrap method work. Any help?
SOLUTION
I figured It out. It's not a nice code but It works.
First I declare two Textures with the same image
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=new Texture(Gdx.files.internal("fondo.jpg"));
Also I declare two variables like this to specify the X value of the position of each bck
int posXBck1=0,posXBck2=0;
Then I use that in Render()
public void calculoPosicionFondos(){
posXBck2=posXBck1+ANCHODEFONDO;
if(cam.position.x>=posXBck2+cam.viewportWidth/2){
posXBck1=posXBck2;
}
}
Where:
ANCHODEFONDO is the width of my background
Cam is an OtrhoCam.
So I said that if the cam is in bck2 (wich means that you can't see bck1 anymore) It change positions, giving bck1 de position of bck2 and, in the next render loop, recalculating bck2
Then just paint both bck in your render mode.
Like Teitus said, do not load your texture multiple times, ever! Anyway, you where on the right track with the wrapper:
texture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
Now you can just use the draw method with the source location. The source location is the area you choose to draw on the texture.
batch.draw(texture, x, y, srcX, srcY, srcWidth, srcHeight)
To scroll your texture from right to left all you have to do is increase srcX incrementally. So create a int that increments in the update/render method.
int sourceX = 0;
//render() method
//Increment the variable where to draw from on the image.
sourceX += 10;
//Simply draw it using that variable in the srcX.
batch.draw(YourTexture, 0, 0, sourceX, 0, screenWidth, screenHeight);
Because you are wrapping the texture it will wrap/loop and scroll indefinitely. There might be a issue with the sourceX int if the game runs for a very long time because a int can only hold 2147483647. It takes a while but you can fix it by subtracting the image width each time the number goes over the total image width.
Don't to this, please:
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=new Texture(Gdx.files.internal("fondo.jpg"));
That will load your big background texture twice. That's a complete waste. If you want to keep your solution at least do:
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=bkg1;
Regarding the texture Wrapping. If your texture is 500px wide, and you draw a 500px sprite, you won't see any repetition. If you want it repeated 2 times, draw it 1000px wide with 0-2 texture coordinates.
I'm not sure how spriteBatch handles the call you posted, you could try that one, or may be use the overload that uses a texture region and set your region manually.
I see this is a pretty old question, but I think there is an easier way to accomplish background scrolling. Just use the Sprite class. Here is a snippet I use for layered background images that scroll from right to left.
public class LevelLayer
{
public float speedScalar = 1;
private List<Sprite> backgroundSprites = new ArrayList<Sprite>();
public LevelLayer()
{
}
public void addSpriteLayer(Texture texture, float startingPointX, float y, int repeats)
{
for (int k = 0; k < repeats; k++)
{
Sprite s = new Sprite(texture);
s.setX(startingPointX + (k*texture.getWidth()));
s.setY(y);
backgroundSprites.add(s);
}
}
public void render(SpriteBatch spriteBatch, float speed)
{
for (Sprite s : backgroundSprites)
{
float delta = s.getX() - (speed * speedScalar);
s.setX(delta);
s.draw(spriteBatch);
}
}
}
Then you can use the same texture or series of textures like so:
someLayer.addSpriteLayer(sideWalkTexture1, 0, 0, 15);
someLayer.addSpriteLayer(sideWalkTexture2, 15 * sideWalkTexture1.getWidth(), 0, 7);
I change background repeating sections randomly in code and make new ones or reset existing sets when they go off screen. All the layers go to a pool and get pulled randomly when a new one is needed.
SOLUTION
I figured It out. It's not a nice code but It works.
First I declare two Textures with the same image
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=new Texture(Gdx.files.internal("fondo.jpg"));
Also I declare two variables like this to specify the X value of the position of each bck
int posXBck1=0,posXBck2=0;
Then I use that in Render()
public void calculoPosicionFondos(){
posXBck2=posXBck1+ANCHODEFONDO;
if(cam.position.x>=posXBck2+cam.viewportWidth/2){
posXBck1=posXBck2;
}
}
Where:
ANCHODEFONDO is the width of my background
Cam is an OtrhoCam.
So I said that if the cam is in bck2 (wich means that you can't see bck1 anymore) It change positions, giving bck1 de position of bck2 and, in the next render loop, recalculating bck2
Then just draw both bck in your render()

Categories

Resources