Whenever I try to set my viewport to a Stage, the actors, that I puting into the Scene are blured and very large. I want to set my Game world size as a rectangle of 50x100 units and then scale everything (Sprites, actors, labels, fonts) according that units. Every state inharitate form a class State which provides render function etc.
public abstract class State {
public static final float GAME_SIZE_WIDTH = 50 ;
public static final float GAME_SIZE_HEIGHT = 100 ;
protected OrthographicCamera camera;
protected GameStateManager gsm;
protected Viewport viewport;
public State(GameStateManager gsm) {
this.gsm = gsm;
camera = new OrthographicCamera();
viewport = new ExtendViewport(GAME_SIZE_WIDTH, GAME_SIZE_HEIGHT, camera);
viewport.apply();
camera.position.set(GAME_SIZE_WIDTH / 2, GAME_SIZE_HEIGHT / 2, 0);
}
public abstract void handleInput();
public abstract void update(float dt);
public abstract void render (SpriteBatch sb);
public abstract void dispose();
public abstract void resize(int width, int height) ;
}
In every State I want to add the Stage and pass the viewPort for my State class according to keep the GAME_SIZE_WIDTH /GAME_SIZE_HEIGHT dimension, but Ive got unadjusted backgrounds (even if its full HD picture the white border on the left) and the Buttons are blured with unadjusted text on it.
public class MenuState extends State implements InputProcessor {
private Skin skin;
private Stage stage;
private Sprite background;
private Table table;
private TextButton startButton,quitButton;
private Sprite flappyButton;
private Sprite chodzenieButton;
private Sprite memoryButton;
private Sprite rememberSquare;
private static final String TAG = "kamil";
private Vector3 touchPoint;
public MenuState(GameStateManager gsm) {
super(gsm);
skin = new Skin(Gdx.files.internal("button/uiskin.json"));
stage = new Stage(viewport);
table = new Table();
table.setWidth(stage.getWidth());
table.align(Align.center | Align.top);
table.setPosition(0,GAME_SIZE_HEIGHT);
startButton = new TextButton("New Game",skin);
quitButton = new TextButton("Quit Game",skin);
startButton.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
Gdx.app.log("Clicked button","Yep, you did");
event.stop();
}
});
table.padTop(5);
table.add(startButton).padBottom(5).size(20,10);
table.row();
table.add(quitButton).size(20,10);
stage.addActor(table);
background =new Sprite(new Texture(Gdx.files.internal("backgrounds/bg.jpg")));
background.setSize(GAME_SIZE_WIDTH,GAME_SIZE_HEIGHT);
background.setPosition(0,0);
touchPoint= new Vector3();
InputMultiplexer im = new InputMultiplexer(stage,this);
Gdx.input.setInputProcessor(im);
flappyButton=new Sprite(new Texture("menuButton/floopyBirdButtonTexture.png"));
chodzenieButton =new Sprite(new Texture("menuButton/swipeMovementsButtonTexture.png"));
memoryButton =new Sprite(new Texture("menuButton/memory.png"));
rememberSquare = new Sprite(new Texture("menuButton/rememberSquare.png"));
rememberSquare.setSize(20,10);
flappyButton.setSize(20,10);
chodzenieButton.setSize(20,10);
memoryButton.setSize(20,10);
flappyButton.setPosition(GAME_SIZE_WIDTH/2-flappyButton.getWidth()/2,GAME_SIZE_HEIGHT/2-flappyButton.getHeight()/2);
chodzenieButton.setPosition(GAME_SIZE_WIDTH/2-flappyButton.getWidth()/2,flappyButton.getY()- chodzenieButton.getHeight() -2);
memoryButton.setPosition(chodzenieButton.getX(),chodzenieButton.getY()- flappyButton.getHeight() -2);
rememberSquare.setPosition(flappyButton.getX(),flappyButton.getY()+flappyButton.getHeight()+2);
}
#Override
public void render(SpriteBatch sb) {
sb.setProjectionMatrix(camera.combined);
sb.begin();
background.draw(sb);
flappyButton.draw(sb);
chodzenieButton.draw(sb);
memoryButton.draw(sb);
rememberSquare.draw(sb);
sb.end();
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
#Override
public void dispose() {
flappyButton.getTexture().dispose();
chodzenieButton.getTexture().dispose();
background.getTexture().dispose();
}
#Override
public void resize(int width, int height) {
Gdx.app.log("kamil","Resize");
viewport.update(width,height);
}
#Override
public void handleInput() {
if(Gdx.input.justTouched())
{
//
viewport.unproject(touchPoint.set(Gdx.input.getX(),Gdx.input.getY(),0));
if(flappyButton.getBoundingRectangle().contains(touchPoint.x,touchPoint.y)) {
gsm.set(new FlopyBirdState(gsm));
}
else if (chodzenieButton.getBoundingRectangle().contains(touchPoint.x,touchPoint.y)){
gsm.set(new ChodzenieState(gsm));
}
else if (memoryButton.getBoundingRectangle().contains(touchPoint.x,touchPoint.y)){
gsm.set(new MemoryState(gsm));
}
else if (rememberSquare.getBoundingRectangle().contains(touchPoint.x,touchPoint.y)){
gsm.set(new FoodEaterState(gsm));
}}
}
blured and very large : This is due to font size you're using.
Blured when some font scaled in appropriate manner, doesn't matter it is by you or by viewport.
You're using 50 and 100 as world width and height of viewport, and you're using BitmapFont from skin that having greater size as compare to 50 and 100. so when you update viewport by screenwidth and height then BitmapFont also scale up so look very large and blurry too.
Solution:
Use some greater world width and height of viewport by comparing your BitmapFont that you're using.
Use small size of your BitmapFont or you can scale down so that not scaled too much and look ugly.
skin.get("font-name",BitmapFont.class).getData().setScale(.25f);
you're using Table so you may need to check where your Actor is in cell/Table. You can enable debugging by this flag stage.setDebugAll(true);
EDIT
You're using ExtendViewport, from wiki
The ExtendViewport keeps the world aspect ratio without black bars by extending the world in one direction. The world is first scaled to fit within the viewport, then the shorter dimension is lengthened to fill the viewport.
Let's suppose you're device width and height is 400 and 640, then what happen when you update viewport by 400 and 640. First your background Sprite scaled in height and set his height to 640(because viewport worldheight is equal to background height) done, now it's time for width of background Sprite because you set width is half of height so scaled and set size 640/2 = 320. done
Your problem arrive, my device width is 400 but my background sprite size is only 320 rest (40*2 unit is white from both side).
Use 48*80(most of devices is in this ratio) instead of 50*100
Related
What I want to do is have a virtual size for my world and scale that world on the screen as much as possible without changing the aspect ratio, the FitViewport seemed like the best candidate. Thats how I intialised my viewport on the stage.
public class PlayStage extends Stage{
public static final int WIDTH = 480;
public static final int HEIGHT = 800;
private final Vector2 gravity = new Vector2(0.f, -9.8f);
private World physWorld;
public PlayStage(){
super();
OrthographicCamera camera = new OrthographicCamera();
camera.setToOrtho(false, WIDTH, HEIGHT);
setViewport(new FitViewport(WIDTH, HEIGHT, camera));
physWorld = new World(gravity, false);
Gdx.input.setInputProcessor(this);
Ball ball = new Ball(physWorld);
setKeyboardFocus(ball);
addActor(ball);
}
#Override
public void draw() {
super.draw();
}
#Override
public void act(float delta) {
super.act(delta);
physWorld.step(delta, 10, 5);
}
#Override
public void dispose() {
super.dispose();
physWorld.dispose();
}}
This is how the sprite looks when rendered (scaled too much on the x coordinate). Also I get no touch down events for my actors.
You need to update stage viewport. It's better to resize your viewport from resize method.
#Override
public void resize(int width, int height) {
stage.getViewport().update(width,height);
//stage.getViewport().update(width,height,true); // -> If you want camera to be at centre.
}
I solved it on my own, all I had to do was to update the viewport after creation like this getViewport().update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
I'm trying to use a TiledMap in a test game but I'm having issues with the size. I'm using an ExtendViewport with width 160 and height 90. I guess the problem is that the tiled map is drawing using the screen size, because it's zoomed in. Do I need 2 seperate cameras for the tiled map and the rest of the game (players, enemies, ...)?
This is all of my code:
public class Main extends Game {
private OrthographicCamera camera;
private ExtendViewport viewport;
private TiledMap tiledMap;
TiledMapRenderer tiledMapRenderer;
#Override
public void create () {
camera = new OrthographicCamera(160, 90);
camera.setToOrtho(false, 160, 90);
camera.update();
viewport = new ExtendViewport(160, 90, camera);
viewport.apply();
tiledMap = new TmxMapLoader().load("map1.tmx");
tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
tiledMapRenderer.setView(camera);
tiledMapRenderer.render();
}
#Override
public void resize(int width, int height) {
viewport.update(width, height, false);
}
#Override
public void dispose () {
}
}
Thanks
OrthogonalTiledMapRenderer's second parameter is unitScale. The unit scale tells the renderer how many pixels map to a single world unit. And it defaults to 1.
So in your case one pixel is equals to one unit size in tiled map. Try changing the unitScale parameter.
https://github.com/libgdx/libgdx/wiki/Tile-maps#rendering-tiled-maps
so I am doing what is suppose to be a simple game but I think I might be complicating things. I have a camera for the GameScreen and a viewPort which follow the position of the player and when it reaches to points on the sides, the camera stops following the player and stays in one point.
This by itself works fine, but then I wanted to add the pause menu and some other features in the game, creating a hud class with its own camera and viewport as well as a Stage and a shapeRenderer.
The problem comes when I create the instance of this hud inside my gameScreen, the camera that I am looking while I am playing looks like is the hudCam, which does not follow the player and basically does not let me see the player when it reaches the edges of the screen.
This is my GameScreen Class:
public class GameScreen implements Screen {
WowInvasion game;
ScrollingBackground background;
private OrthographicCamera gameCam;
private Viewport gameViewPort;
/*
Basically I wanna keep the same sprites running while in the menu, playing and till dead
therefore, I'll have a switch statement with cases on where the games at, inside the functions needed. That way I'll keep
the game has a background for the menu and there's no need for running a second screen.
*/
public static final int MAIN_MENU = 0;
public static final int GAME = 1;
private static int state = 1; //current state. starts with MAIN_MENU //DEBUGGING GAME SCREEN
//STAGES
private GameStage gameStage; //game ui
private menuStage mainMenu; //Main menu of the game
private Hud hud;
//Resources
private TextureAtlas atlas; //for the textures most
private Skin skin; //for the styles and fonts
//Sprites
private Player player;
//Shapes
private float progressPower; //for the power to build up
private final float POWER_CHARGED = 1000; //limit to get power
private final float DECREASING_POWER = 20; //limit to get power
public GameScreen(WowInvasion game){
this.game = game;
gameCam = new OrthographicCamera();
gameCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
gameViewPort = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, gameCam);
progressPower = 0f;
game.wowAssetManager.loadTexturesGameScreen(); // tells our asset manger that we want to load the images set in loadImages method
game.wowAssetManager.loadSkins(); //load the needed skins
game.wowAssetManager.manager.finishLoading(); // tells the asset manager to load the images and wait until finsihed loading.
skin = game.wowAssetManager.manager.get("ui/menuSkin.json");
}
#Override
public void show() {
game.batch.setProjectionMatrix(gameCam.combined);
background = new ScrollingBackground();
atlas = game.wowAssetManager.manager.get(WowAssetManager.GAME_ATLAS); //declaring atlas
mainMenu = new menuStage(gameViewPort, game.batch, skin, game); //pass it so that we only use one batch and one same viewport
gameStage = new GameStage(gameViewPort, game.batch, skin, game);
hud = new Hud(game.batch, skin);
player = new Player(atlas.findRegion("player"), (int) gameCam.viewportWidth / 2, (int) gameCam.viewportHeight / 2);
switch(state){
case MAIN_MENU:
Gdx.input.setInputProcessor(mainMenu);
break;
case GAME:
background.setFixedSpeed(false); //does not work in here
}
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(state == GAME) {
background.setFixedSpeed(false);
player.update(delta, gameCam.viewportWidth, gameCam.viewportHeight); //updating player for movement
//really cheap way to charge power with velocity
if(progressPower != POWER_CHARGED) {
progressPower += Math.abs(player.getVelocity().x) + Math.abs(player.getVelocity().y);
progressPower -= DECREASING_POWER;
}
else
progressPower = POWER_CHARGED / 4;
}
mainMenu.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f)); //updating while making sure delta won't be more than 1/30f.
gameStage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
game.batch.begin();
background.updateAndRender(delta, game.batch); //updating scrolling background
player.draw(game.batch);
game.batch.end();
mainMenu.draw(); //draw the menu stage
gameStage.draw(); //draw the ui stage for the game
hud.getStage().draw();
hud.renderRotateMeter();
updateCamera(0, WowInvasion.WIDTH);
System.out.println(player.getPosition().x);
}
public void updateCamera(float startX, float endX){
Vector3 position = gameCam.position;
//linear interpolation : a + (b - a) * lerp
//b = player position
//a = current camera position
//lerp = interpolation factor
position.x = gameCam.position.x + (player.getPosition().x - gameCam.position.x) * .1f;
//making the camera stay when the player gets to close to the sides
if(position.x < startX) {
position.x = startX;
}
if(position.x > endX){
position.x = endX;
}
gameCam.position.set(position);
gameCam.update();
}
#Override
public void resize(int width, int height) {
gameViewPort.update(width, height);
//hud.getViewport().update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
dispose();
}
#Override
public void dispose() {
mainMenu.dispose();
gameStage.dispose();
game.dispose();
hud.dispose();
}
public static void setState(int state) {
GameScreen.state = state;
}
}
And this is my HUD:
public class Hud implements Disposable{
private Stage stage;
private Viewport viewport;
Button buttonPause, buttonResume;
private OrthographicCamera hudCam;
private ShapeRenderer sp; //like a batch for shapes
public Hud(SpriteBatch sb, Skin skin){
hudCam = new OrthographicCamera();
hudCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
viewport = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, hudCam);
stage = new Stage(viewport, sb);
sp = new ShapeRenderer();
Table table = new Table();
table.top();
//this makes the table the size of the stage
table.setFillParent(true);
buttonPause = new Button(skin, "pause");
buttonPause.setTransform(true);
buttonPause.addListener(new ClickListener(){ //listener to handle event
#Override
public void clicked(InputEvent event, float x, float y) {
}
});
buttonResume = new Button(skin, "resume");
buttonResume.setTransform(true);
buttonResume.setScale(0.5f);
buttonResume.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
buttonResume.setVisible(false);
}
});
table.add(buttonPause);
table.row();
table.add(buttonResume);
stage.addActor(table);
}
public void renderRotateMeter(){
sp.setProjectionMatrix(hudCam.combined);
sp.begin(ShapeRenderer.ShapeType.Filled);
sp.setColor(Color.YELLOW);
sp.rect(hudCam.position.x,hudCam.position.y, WowInvasion.WIDTH / 2, 20);
sp.end();
}
public Viewport getViewport() {
return viewport;
}
public Stage getStage() {
return stage;
}
#Override
public void dispose() {
stage.dispose();
sp.dispose();
}
}
thanks in advance!
EDIT
so I tried passing the gameCam has a parameter to the hud and instead of making a new OrthographicCamera I used that one has the hudCamara as well and well, the movement with the player is perfect except now the thins from the Hud do not move at all..
It looks like you only set projectionMatrix to only HUD camera as seen in
sp.setProjectionMatrix(hudCam.combined);
Try to set it the same to other stuff outside of the HUD class prior to draw call too.
Another thing to keep in mind is that, when you involve using multiple Viewport and Camera in the game as most of the time it will be 1 Viewport matching with 1 Camera and work with another set as in your case. In draw call, you need to call apply() or apply(true) of Viewport class too to tell the system that you will draw based on which viewport thus in turn it will adhere to screen coordinate that was set up by viewport's attaching camera.
So assume you have 2 objects that needed to be called in different viewport consecutively, do it like the following code. The methods call is correct according to libgdx API but variable names are fictional.
// draw objA adhering to viewportA (thus cameraA) <-- assume it's player cam
sb.setProjectionMatrix(cameraA.combined);
viewportA.apply();
objA.draw();
// draw objB adhering to viewportB (thus cameraB) <-- assume it's HUD cam
sb.setProjectionMatrix(cameraB.combined);
viewportB.apply(true); // send in true as for HUD, we always want to center the screen
objB.draw();
In summary, 2 things to keep in mind when drawing objects that use multiple of camera and viewport in consecutive draw call.
Set projection matrix to either SpriteBatch or ShapeRenderer.
Call apply() or apply(true) of Viewport class to let it know you work with this viewport.
I got image of super mario level1 (1563x224):
But when you play the game you are not seeing all the level.
These are my values:
// level1.png image size: 1563x224
// viewPort size: 300x224
public static final float LEVEL1_WIDTH = 1563f;
public static final float LEVEL1_HEIGHT = 224f;
public static final float VIEWPORT_WIDTH = 300f;
public static final float VIEWPORT_HEIGHT = 224f;
This is part of my game constructor:
_gamecam = new OrthographicCamera(ZombieGame.LEVEL1_WIDTH, ZombieGame.LEVEL1_HEIGHT); // set the world size
_viewport = new FitViewport(ZombieGame.VIEWPORT_WIDTH, ZombieGame.VIEWPORT_HEIGHT, _gamecam); // set the viewport size
_gamecam.position.set(ZombieGame.VIEWPORT_WIDTH / 2f, ZombieGame.VIEWPORT_HEIGHT / 2f, 0); // set the camera to be in the middle of the viewport
_background = new Texture("level1.png"); // load level1 (background) image
This is my render method:
#Override
public void render(float delta) {
_gamecam.update();
Gdx.gl.glClearColor(0,1,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
_game.batch.setProjectionMatrix(_gamecam.combined);
_game.batch.begin();
_game.batch.draw(_background, 0, 0, ZombieGame.LEVEL1_WIDTH, ZombieGame.LEVEL1_HEIGHT);
_game.batch.end();
}
For some reason it seemed it's only working in these rations, when I try to change my VIEWPORT_HEIGHT or VIEWPORT_WIDTH it's shows out of my image:
view port size: 300x224 (working):
view port size: 300x100 (not working):
What am I doing wrong?
FitViewport always maintain the aspect ratio of the virtual screen size (virtual viewport), while scaling it as much as possible to fit the screen.
Pass viewportWidth and viewportHeight in constructor of OrthographicCamera not your .tmx total dimension.
_gamecam = new OrthographicCamera();
_gamecam.setToOrtho(false,ZombieGame.VIEWPORT_WIDTH,ZombieGame.VIEWPORT_HEIGHT);
_viewport = new FitViewport(ZombieGame.VIEWPORT_WIDTH, ZombieGame.VIEWPORT_HEIGHT, _gamecam);
We use viewport because it manages a Camera's viewportWidth and viewportHeight according to our ViewPort choice.
Calculate the viewport parameters and update the camera whenever resize event occurs.
#Override
public void resize(int width, int height) {
_viewport.update(width,height,true);
}
My goal is to create a game that is always displayed with an aspect ratio of 9:16 (basically 16:9, but upright) using FitViewport; it should be independet of a target device's resolution. In order to test this setup, I created the following minimal working example. A small green square indicates the origin of the coordinate system:
MyGame.java
public class MyGame extends ApplicationAdapter {
final int WORLD_WIDTH = 900;
final int WORLD_HEIGHT = 1600;
Stage stage;
Viewport vp;
public void create() {
stage = new Stage();
vp = new FitViewport(WORLD_WIDTH, WORLD_HEIGHT, stage.getCamera());
stage.setViewport(vp);
stage.addActor(new MySquare());
}
public void render() {
stage.act();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.draw();
}
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
// dispose...
}
MySquare.java
public class MySquare extends Actor {
ShapeRenderer renderer = new ShapeRenderer();
#Override
public void draw(Batch batch, float alpha){
batch.end();
renderer.begin(ShapeRenderer.ShapeType.Filled);
renderer.setColor(Color.GREEN);
renderer.rect(0, 0, 50, 50);
renderer.end();
batch.begin();
}
}
Unfortunately, the result is not as expected: As you can see, the green square is actually not a square. This behavior is the same for both Windows and Android (in landscape mode):
However, when setting the size of the window programmatically and explicitly via LwjglApplicationConfiguration in DesktopLauncher.java to a valid 9:16 resolution, the green square is displayed correctly. Why is that and how can I avoid this workaround (which does not work for Android anyway)?
Your problem is that your shape renderer is ignoring the camera. Update it like this:
public void draw(Batch batch, float alpha){
batch.end();
renderer.setProjectionMatrix(batch.getProjectionMatrix()); // <<<<< Add this
renderer.begin(ShapeRenderer.ShapeType.Filled);
renderer.setColor(Color.GREEN);
renderer.rect(0, 0, 50, 50);
renderer.end();
batch.begin();
}
If you are planning to eventually use sprites, and you're simply wanting rectangle placeholders for your actors, you don't need a custom actor for this. You can use a generic Actor and call setDebug(true) on it, and Stage will automatically draw its outline using an internal ShapeRenderer. Of course, you must first set a size and position on the Actor.