Helllo, im new to libgdx, i need some help, if my desktoplauncher resolution is 480x320, sprite takes 80% of the screen, but if 1280x720, sprite is small, i need to make it look the same at all resoltions so how do i do this? may be easy for you, but not for my, im using libgdx 1.3.1
this is my libgdx code:
SpriteBatch batch;
Texture img;
Sprite mysprite;
#Override
public void create ()
{
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
mysprite = new Sprite(img);
stage = new Stage(new StretchViewport(480, 320));
}
StretchViewport myviewport = new StretchViewport(480, 320);
public void resize(int width, int height)
{
// use true here to center the camera
// that's what you probably want in case of a UI
stage.setViewport(myviewport);
stage.getCamera().position.set(640/2, 480/2, 0);
}
private Stage stage;
#Override
public void render ()
{
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
mysprite.draw(batch);
batch.end();
}
You are allready using the right thing: Viewport.
There are different Viewport classes, some of them support working with a virtual screen size, which is what you are looking for.
What is a virtual screen size? Well it is the screen size your code is working with and which is then scaled up to match the real resolution.
Basicly you can work with your own units and they are then automatically scaled up to match pixels.
I guess in your case there are 2 possible Viewport-types:
- StretchViewport supports virtual screen sizes and scales it up to match the real screen size and the real aspect ratio. If the real aspect ration does not match the virtual one the Sprites will be stretched, which could look strange.
- FitViewport is the same as the StretchViewport, but it will keep the aspect ratio. If the real aspect ration does not match the virtual one, black borders will appear.
How to use it:
First you need to create it:
myViewport = new StretchViewport(VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
Then set the Stages Viewport:
stage = new Stage(myViewport);
In the resize method you need to update your Viewport:
myViewport.update(width, height);
Thats all.
The stage now uses the Viewport and its camera to render. You don't need to touch the camera, unless you need to move it arround.
So your errors are:
stage = new Stage(new StretchViewport(480, 320));
Which creates a new StretchViewport you don't store/use.
stage.setViewport(myviewport);
You only need to set it once, when you create the stage
You never call update(width, height) for the Viewport.
Related
In LibGdx when I use the below code to render a tiled map the tiledMapRenderer.render() call leaves a flickering image in the black area after just one call (I tested this by rendering in the create method only) and I haven't been able to remove it. This flickering image becomes a problem when the window size changes.
TiledMap tiledMap;
OrthogonalTiledMapRenderer tMR;
ExtendViewport viewPort;
#Override
public void create () {
OrthographicCamera camera = new OrthographicCamera();
camera.setToOrtho(false, 512, 512);
viewPort = new ExtendViewport(512, 512, 512, 512, camera );
viewPort.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
tiledMap = new TmxMapLoader().load("LibGdxTutorial.tmx");
tMR = new OrthogonalTiledMapRenderer(tiledMap);
}
#Override
public void render () {
viewPort.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
tMR.setView((OrthographicCamera) viewPort.getCamera());
tMR.render();
}
This is the default window you get after pressing run
This is the window after I dragged the right side out
The map image on the right of the second photo is the full and correct one. The map image on the left is the flickering after image (it appears full because it's one frame). Any help would be appreciated, thanks.
You need to clear the screen each frame. This happens in the render method and should be used before you start drawing.
Gdx.gl.glClearColor(0f,0f,0f,1); // set the clear color
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // clear the screen with the given color.
.. draw next frame ..
Is it possible to put viewport inside a custom actor, so that viewport width and height will be actor's width and height, and rendering will start from actor position, no from (0,0).
Stage is a 2D scene graph. It has a hierarchical structure, which means that Actors added to a Group should already be rendered only inside that given Group. Moving the Group will also move all children inside. Via clipBegin() and clipEnd you can also "cut off" everything that's not inside the actor, which is kind of what a Viewport does as well, when setting the glViewport.
So probably you won't need an extra Viewport for whatever you are trying to do. If you still think you need one, you can create an ActorViewport extends Viewport which gets an Actor field. You would have to override the apply(boolean) method and synchronize the worldWidth, worldHeight, screenX, screenY, screenWidth and screenHeight variables to match the Actor. Remember that you will have to update the viewport everytime the actor changes, which is in every frame in the worst case.
Thanks to noone solution was very simple:
public class ActorViewport extends Viewport
{
private Actor m_actor;
public ActorViewport(Actor actor, int worldWidth, int worldHeight, Camera camera)
{
m_actor = actor;
setWorldSize(worldWidth, worldHeight);
setCamera(camera);
}
#Override
public void update(int screenWidth, int screenHeight, boolean centerCamera)
{
setScreenPosition((int)m_actor.getX(), (int)m_actor.getY());
setScreenSize((int)m_actor.getWidth(), (int)m_actor.getHeight());
}
}
For 2 days I've been trying to fix bug with fickering and distored textures in my game. I was serching on the internet and i tried a few solutions like using scene2d, but it didn't work. What should i do ?
This screenshot shows the problem: as the character moves, one eye is sometimes bigger than the other:
edit:
I still got the problem widthdistored eye when i use sprite.setPosition((int) sprite.getX(), (int) sprite.getY()); every time before i render my character.
When i use custom viewport from the answer i see nothing on the game window what i do wrong?
package com.mygdx.redHoodie;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.viewport.StretchViewport;
public class GameScreen implements Screen {
public static final int GAME_WIDTH = 800;
public static final int GAME_HEIGHT= 480 ;
SpriteBatch batch;
Background background;
public Hoodie hoodie;
public PixelMultipleViewport viewport;
OrthographicCamera camera;
public int gameMode; // 0 normalna gra, 1 level up, 2 end game
public GameScreen(){
camera= new OrthographicCamera(GAME_WIDTH,GAME_HEIGHT);
viewport = new PixelMultipleViewport(GAME_WIDTH, GAME_HEIGHT, camera);
viewport.update();
camera.setToOrtho(false, GAME_WIDTH, GAME_HEIGHT);
batch = new SpriteBatch();
//klasy wyswietlane
background= new Background(this);
hoodie = new Hoodie(this);
startNewGame();
}
#Override
public void render(float delta) {
// TODO Auto-generated method stu
Gdx.gl.glClearColor(1, 1, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.projection);
batch.setTransformMatrix(camera.view);
camera.update();
//batch.setProjectionMatrix(camera.combined);
this.update(delta);
this.batch.begin();
toRender(delta);
this.batch.end();
}
public void update(float delta){
hoodie.update(delta);
}
public void toRender(float delta){
background.render();
hoodie.render();
}
public void startNewGame(){
}
public void startNevLevel(){
}
#Override
public void resize(int width, int height) {
// TODO Auto-generated method stub
viewport.update(width, height,false);
}
#Override
public void show() {
// TODO Auto-generated method stub
}
#Override
public void hide() {
// TODO Auto-generated method stub
}
#Override
public void pause() {
// TODO Auto-generated method stub
}
#Override
public void resume() {
// TODO Auto-generated method stub
}
#Override
public void dispose() {
// TODO Auto-generated method stub
}
}
When loading your texture, use linear filtering and mip-mapping. The default filter is Nearest/Nearest, which will cause the issue you're seeing.
Texture myTexture = new Texture("textureFilename", true); //must enable mip-mapping in constructor
myTexture.setFilter(TextureFilter.MipMapLinearNearest, TextureFilter.Linear);
EDIT:
I realize now, looking at your screenshot, that you are doing pixelated graphics in a larger window. In order to do this, yes you need to keep the Nearest/Nearest filtering, instead of what I suggested.
To avoid having the some of the pixels vary in size, you must round off character movement and camera movement to the nearest world unit. When your character is partway between pixels, the size of the sprite pixels varies because they don't line up with the screen pixels.
You have your world scaled so one unit equals one of your large pixels. So whenever you draw anything, you need to first round its position to the nearest integer in the x and the y, as well as the camera position. So after you move the camera or the sprites, you must do something like this:
sprite.position.set((int)sprite.position.x,(int)sprite.position.y,sprite.position.z);
As far as your Viewport goes, if you don't want any black bars, you will probably need a custom Viewport class that tries to match your desired resolution as closely as possible and then extends it outwards to avoid distortion. ExtendViewport does something similar, but the difference with pixellated graphics is that you need the world resolution to be an integer multiple of the screen's resolution so the edges of pixels look crisp rather than fuzzy.
I think this will do what you want. It takes your desired screen resolution and shrinks it to fit where the size of each of your pixels in screen pixels is an integer. Then it extends the view beyond your desired resolution to avoid distortion and black bars. This class makes the assumption that all screen dimensions are always a multiple of 4. I think that's true. If you want to get fancy, you could use OpenGL scissoring to round down the viewport size to the nearest multiples of 4, to be safe. At most you would be having 2 pixels of black bar, which I don't think would be noticeable.
public class PixelMultipleViewport extends Viewport {
private int minWorldWidth, minWorldHeight;
public PixelMultipleViewport (int minWorldWidth, int minWorldHeight, Camera camera) {
this.minWorldHeight = minWorldHeight;
this.minWorldWidth = minWorldWidth;
this.camera = camera;
}
#Override
public void update (int screenWidth, int screenHeight, boolean centerCamera) {
viewportWidth = screenWidth;
viewportHeight = screenHeight;
int maxHorizontalMultiple = screenWidth / minWorldWidth;
int maxVerticalMultiple = screenHeight / minWorldHeight;
int pixelSize = Math.min(maxHorizontalMultiple, maxVerticalMultiple);
worldWidth = (float)screenWidth/(float)pixelSize;
worldHeight = (float)screenHeight/(float)pixelSize;
super.update(screenWidth, screenHeight, centerCamera);
}
}
Here's a different option I just came across. This is a way to draw your scene at the scale you like without black bars, at any resolution.
The visual quality will be slightly worse than in my other answer (where you draw at an integer multiple of your desired scene scale), but significantly better than using straight nearest filtering like in your screenshot.
The basic idea is to draw everything to a small FrameBuffer at the scale you want, and then draw the FrameBuffer's color texture to the screen using an upscaling shader that (unlike linear filtering) interpolates pixel colors only along the edges of sprite pixels.
The explanation is here. I have not ported this to Libgdx or tested it. And I'm not sure how well this shader would run on mobile. It involves running four dependent texture look-ups per screen fragment.
I know this topic is old but as my search led me here, there may be more people following in the future. I was having the same pixel tearing issue, but only on my iOS 9 iPhone 4S. It was rendering fine on my Android 9 Pixel 2. Tried many things (especially rounding to full pixels) but even using an unzoomed fullscreen orthographic camera it suffered from the artefacts.
Forcing my texture to be POT (power of two) fixed the issue!
I am beginning to write a game in LibGDX, only just beginning. I have got a basic tile map loaded, a player sprite and can move the character around and the screen (camera) scrolls around - perfect.
I have two overlayed textures in the bottom right of the screen, a left and right arrow, which are used as the joypad to control the character. I position these in relation to the players.x position, which is always fixed to the centre of the screen. Cool so far .... As below:
/////////////////////////////////////////////////////////////////
private void renderJoypad(float deltaTime)
{
batch.draw(Assets.leftJoypad, blockman.position.x-14, 1, 3, 3);
batch.draw(Assets.rightJoypad, blockman.position.x-9, 1, 3, 3);
}
I am now trying to put the player's score in the top left of the screen. The score is made of a Bitmap font, as below.
font = new BitmapFont(Gdx.files.internal("fonts/minecrafter.fnt"),Gdx.files.internal("fonts/minecrafter.png"),false);
font.setScale(0.02f);
In my render() method I cam calling some other methods to update, like the positions of the
leftJoypad etc, as in renderJoypad(). I am also calling my draw font method to update the position of the score, however, when I scroll it is all jerky, and sometimes it shows less characters than there should be.
public void drawScore(float deltaTime)
{
font.draw(batch, "00000", blockman.position.x, 10);
}
I believe that I need to place the score (and any other on screen texts, HUD etc) into a stage, but I cannot understand how to get it working with my existing code.
My show method is as follows:
public void show()
{
//load assets class to get images etc
Assets Assets = new Assets();
//start logging Framerate
Gdx.app.log( GameScreen.LOG, "Creating game" );
fpsLogger = new FPSLogger();
//set the stage - bazinga
Gdx.input.setInputProcessor(stage);
//stage.setCamera(camera);
//stage.setViewport(480, 360, false);
batch = new SpriteBatch();
//load the Joypad buttons
loadJoypad();
//background image
loadBackground();
//sounds
jumpSound = Gdx.audio.newSound(Gdx.files.internal("sounds/slime_jump.mp3"));
//LOAD block man here
// load the map, set the unit scale to 1/16 (1 unit == 16 pixels)
loadMap();
//draw the score
font = new BitmapFont(Gdx.files.internal("fonts/minecrafter.fnt"),Gdx.files.internal("fonts/minecrafter.png"),false);
font.setScale(0.02f);
// create an orthographic camera, shows us 30x20 units of the world
camera = new OrthographicCamera();
camera.setToOrtho(false, 30, 20);
camera.update();
// create the Blockman we want to move around the world
blockman = new Blockman();
blockman.position.set(25, 8);
}
and my render() method is as follows:
public void render(float delta)
{
// get the delta time
float deltaTime = Gdx.graphics.getDeltaTime();
stage.act(deltaTime);
stage.draw();
// clear the screen
Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
renderBackground(deltaTime);
batch = renderer.getSpriteBatch();
//updateBlockman(deltaTime);
blockman.update(deltaTime);
// let the camera follow the blockMan, x-axis only
camera.position.x = blockman.position.x;
camera.update();
// set the tile map rendere view based on what the
// camera sees and render the map
renderer.setView(camera);
renderer.render();
//start the main sprite batch
batch.begin();
// render the blockMan
renderBlockman(deltaTime);
renderJoypad(deltaTime);
drawScore(deltaTime);
batch.end();
fpsLogger.log();
}
I have tried to change the way things work with relation to the Spritebatch etc and just cannot seem to get it working as I require.
Can anyone suggest how I may approach getting a stage and actors to work, or a second camera or something to help me achieve a fixed score display in the corner.
Do I need to use Scene 2D or something - aahhh! My head is exploding....
I look forward and thank you in advance.
Regards
James
I have a couple of suggestions:
Check to see if you have setUseIntegerPositions set to true
(the default) when you draw your font. If you do, and you're scaling
it, then it can cause some odd effects similar to those that you
describe. Set it to false and see if it fixes the problem.
Reset your spritebatch's matrices before drawing the text, that way you won't need to adjust it for the scrolling.
I'd even go as far as to recommend not scaling the font if you can help it, because fonts often look a little odd after being scaled.
How should I follow the directory structure and what to specify so that assetManger will use that folder for different resolution.
I have studied assetManager and ResolutionFileResolver but until now I couldn't exactly figured how to specify the folder to support different resolutions.
Update: Newer versions of Libgdx use a different strategy to resolve filenames (with directories, not files), so this answer does not apply to newer Libgdx versions after (0.9.2, I think? https://github.com/libgdx/libgdx/commit/3afcfe49c40196765033a90b4610159183dde981)
The built-in ResolutionFileResolver does not use a directory hierarchy. It uses file suffixes to match screen resolutions to assets.
Here's the code from the libGDX AssetManagerTest:
Resolution[] resolutions = { new Resolution(320, 480, ".320480"),
new Resolution(480, 800, ".480800"),
new Resolution(480, 856, ".480854") };
ResolutionFileResolver resolver = new ResolutionFileResolver(new InternalFileHandleResolver(), resolutions);
AssetManager manager = new AssetManager();
manager.setLoader(Texture.class, new TextureLoader(resolver));
...
The resolver appends one of those suffixes based on the current screen resolution. So, for example, if you look up "foo.jpg" on a 480x800 device, the file "foo.jpg.480800" will be opened.
If you want to resolve files based on directories (so "foo.jpg" would be resolved to "480x800/foo.jpg" or something like that), you can write a new resolver. Just implement FileHandleResolver. The ResolutionFileResolver.java source is probably a good place to start.
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 orthogrphic camera 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.
If you use an OrthographicCamera you won't need to deal with different image resolutions.
Say that you have a camera with the size 400x300. Then you draw a 400x300 large image at coordinate (0,0). Then, regardless of screen resolution, the image will be scaled to fill the screen. This is really neat on Android where the screen resolution can vary a lot between different devices, and doing it like this you won't have to think about it.