FitViewport doesnt scale properly Libgdx - java

My problem is that my FitViewport where i keep my game world, scales in a way that in the X axis i can see black bars, but the Y axis seems to not mind the Gdx.graphics limits and it goes out of the screen.
What seems to be happening is that even though i pass it a World width and world height, it fits itself in a wrong way, not minding the Y axis at all.
PlayScreen constructor:
gameCamera = new OrthographicCamera();
gameCamera.setToOrtho(false);
gameViewport = new FitViewport(MyGame.WORLD_WIDTH,MyGame.WORLD_HEIGHT,gameCamera);
gameViewport.setScreenBounds(0,0,MyGame.WORLD_WIDTH,MyGame.WORLD_HEIGHT);
MyGame.java:
public static final int WORLD_WIDTH = 600;
public static final int WORLD_HEIGHT = 300;
render():
MyGame.batch.setProjectionMatrix(gameCamera.combined);
gameViewport.apply();
MyGame.batch.begin();
resize(int width, int height):
gameViewport.update(width,height);
gameCamera.position.set(player.position.x + 200,player.position.y, 0);

The solution to this wasnt related to what i thought the problem was.
The problem was that i was using wrong coordinate system and references to spawn enemies and bind the players movement. Now im using the gameViewport.getWorldHeight , and this solved my problem.

Related

[Java][Libgdx] Isometric Tilemap Scaling and Coordinate Conversion

I have a 15x15 Isometric Tilemap consisting of 128 x 64 pixel tiles. Using an ExtendViewport and OrthogonalCamera I am able to render the map. However, it does not scale properly when resizing the window as the aspect ratio of the tiles becomes distorted and occasionally the map will move over to the right side of the window. The map does look fine if I force fullscreen resolution, but not if it's manually scaled or in windowed mode. When I say that the aspect ratio becomes distorted I mean that the tiles will appear stretched or jagged resulting in poor aesthetics.
Is there a simple way to render a perfectly squared (e.g. 10x10, 15x15) sized Isometric Tilemap, display it in the centre of the screen and have it scale properly without distorting the aspect-ratio as the screen size increases?
Then there is the issue of conversion between cartesian and isometric coordinates.
http://clintbellanger.net/articles/isometric_math/ The following post explains how to convert between cartesian and isometric coordinates, but I am not able to make it work. The code below is the closest thing I've come to a working solution so far, but it becomes increasingly more off as you move towards the right of the screen, not by terribly much, but it's definitely noticeable.
public static Vector2 cartesianToIsometric(Camera camera, GameMap map, Vector3 point) {
camera.unproject(point);
float tileWidth = (float)map.getMapWidth() * unitScale(map);
float tileHeight = (float)map.getMapHeight() * unitScale(map);
point.x /= tileWidth;
point.y = (point.y - tileHeight / 2) / tileHeight + point.x;
point.x -= point.y - point.x;
return new Vector2((int)point.x, (int)point.y);
}
Here is a snippet of the relevant code from my GameRenderer Class.
public GameRenderer(GameState world) {
this.map = new Map01();
this.world = world;
this.camera = new OrthographicCamera();
this.viewport = new ExtendViewport(0, 3072, camera);
centerCamera(map);
batchRenderer = new SpriteBatch();
}
public void render() {
Gdx.gl.glClearColor(0.11f, 0.6f, 0.89f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT | (Gdx.graphics.getBufferFormat().coverageSampling?GL20.GL_COVERAGE_BUFFER_BIT_NV:0));
renderer.setView(camera);
renderer.render();
map.render();
batchRenderer.begin();
AssetLoader.font.getData().setScale(8);
AssetLoader.font.draw(batchRenderer, map.getSelectedTile().getName(), -400, 1500);
batchRenderer.end();
}
public void resize(int width, int height){
viewport.update(width, height);
renderer = new IsometricTiledMapRenderer(map.getTiledMap(), MapUtils.unitScale(map));
batchRenderer.setProjectionMatrix(camera.combined);
centerCamera(map);
}
private void centerCamera(GameMap map){
camera.position.set(MapUtils.getMapCenter(map));
}
I could almost certainly make it work with what I have, but it seems like I'm missing something painfully simple. It's probably a good idea to have a solid foundation and functional coordinate system, before I start implementing the actual gameplay.
Here is a picture of the map in question if that is of any help. Thanks in advance for any advice.

How to draw a sprite within the FitViewport view. libgdx

The basic question here is: how to ALWAYS keep your sprites within the Fitviewport? How to keep a reference to the view in order to have the proper coordinates as to where to draw?
I'm trying to spawn enemies into the gameplay screen. But this is handled by a FitViewport, and enemies and even the player can move outside the FitViewport on certain screen resolutions. So far the problem seems to be in the Y axis.
The FitViewport is made like this:
gameCamera = new OrthographicCamera();
gameCamera.setToOrtho(false);
gameViewport = new FitViewport(MyGame.WORLD_WIDTH,MyGame.WORLD_HEIGHT,gameCamera);
gameViewport.setScreenBounds(0,0,MyGame.WORLD_WIDTH,MyGame.WORLD_HEIGHT);
Then the camera position gets updated like this at the resize() method:
gameViewport.update(width,height); //not used when using the virtual viewport in the render method.
gameCamera.position.set(player.position.x + 200,player.position.y, 0);
Then the update() method calls the Player's own update() method which includes these lines:
//POSITION UPDATE
if (this.position.x<0) this.position.x=0;
if (this.position.x>Gdx.graphics.getWidth() - width) this.position.x= Gdx.graphics.getWidth() - width;
if (this.position.y<0) this.position.y = 0;
if (this.position.y>PlayScreen.gameViewport.getScreenHeight() - height) this.position.y = PlayScreen.gameViewport.getScreenHeight()- height;
Notice for the X axis I'm still using Gdx.graphics dimensions because I'm yet to make it work with PlayScreen.gameViewport.getScreenHeight() (gameViewport has been set to static for this purpose).
Also on enemy spawn (the problem related here is that they spawn outside of the screen Y in terms of what I see) I have this code inside the update() method of the Screen implementing all these viewports:
//Alien Spawn
if (System.currentTimeMillis() - lastZSpawn >= SpawnTimer){
count++;
lastZSpawn= System.currentTimeMillis();
for (int i=0;i<count;i++){
int x = Gdx.graphics.getWidth();
int y = random.nextInt((int)gameViewport.getScreenHeight() - Alien.height);
if (entities.size()<6){
entities.add(new Alien(new Vector2(x,y),1, alienImages,(float)((0))));
}
}
}
Also using gameViewport.getScreenHeight() here cause Gdx.graphics wasnt giving the correct result (it gave me the same issue really).
The render() method is correctly implemented in terms of the batch and applying the viewport:
MyGame.batch.setProjectionMatrix(gameCamera.combined);
gameViewport.apply();
MyGame.batch.begin();
for (int i = entities.size()-1; i>=0;i--){
entities.get(i).render();
}
You should never change the position of your player or enemies when resizing, that's why a viewport is for, remove all the code that do that first, to make your viewport work as you expected you need to create a new instance of camera passing the new viewport width and height when you resize, i prefer to make my camera static so i can acess its atribbutes from everywhere i want, you should do something like this:
public static OrthographicCamera update(int width,int height){
instance = new OrthographicCamera(width, height);
instance.setToOrtho(false);
return instance;
}
The answer to my problem is posted by myself in another question of mine which was also driven by a confusion in the implementation of FitViewports and using WorldWidth and WorldHeight properties as coordinates of reference when drawing objects into the game, and also correctly setting the camera position taking these values into consideration aswell.
The answer is here even though its text and not code and its mainly what i already wrote in this very post. FitViewport doesnt scale properly Libgdx

Libgdx Box2D pixel to meter conversion?

When trying to program a game using Box2D, I ran into a problem with Box2D. I filled in pixel numbers for the lengths of the the textures and sprites to create a box around it. Everything was at the right place, but for some reason everything went very slowly. By looking on the internet I found out that if you didn't convert pixels to meters box2d might handle shapes as very large objects. this seemed to be a logical cause of everything moving slowly.
I found similar questions on this site, but the answers didn't really seem to help out. in most of the cases the solution was to make methods to convert the pixel numbers to meters using a scaling factor. I tried this out, but everything got misplaced and had wrong sizes. this seemed logical to me since the numbers where changed but had the same meaning.
I was wondering if there is a way to make the pixels mean less meters, so everything whould be at the same place with the same (pixel) size, but mean less meters.
If you have a different way which you think might help, I whould also like to hear it..
Here is the code i use to create the camera
width = Gdx.graphics.getWidth() / 5;
height = Gdx.graphics.getHeight() / 5;
camera = new OrthographicCamera(width, height);
camera.setToOrtho(false, 1628, 440);
camera.update();
This is the method I use to create an object:
public void Create(float X, float Y, float Width, float Height, float density, float friction, float restitution, World world){
//Method to create an item
width = Width;
height = Height;
polygonDef = new BodyDef();
polygonDef.type = BodyType.DynamicBody;
polygonDef.position.set(X + (Width / 2f), Y + (Height / 2f));
polygonBody = world.createBody(polygonDef);
polygonShape = new PolygonShape();
polygonShape.setAsBox(Width / 2f, Height / 2f);
polygonFixture = new FixtureDef();
polygonFixture.shape = polygonShape;
polygonFixture.density = density;
polygonFixture.friction = friction;
polygonFixture.restitution = restitution;
polygonBody.createFixture(polygonFixture);
}
To create an item, in this case a table, I use the following:
Table = new Item();
Table.Create(372f, 60f, 152f, 96f, 1.0f, 0.2f, 0.2f, world);
The Sprites are drawn on the item by using the following method:
public void drawSprite(Sprite sprite){
polygonBody.setUserData(sprite);
Utils.batch.begin();
if(polygonBody.getUserData() instanceof Sprite){
Sprite Sprite = (Sprite) polygonBody.getUserData();
Sprite.setPosition(polygonBody.getPosition().x - Sprite.getWidth() / 2, polygonBody.getPosition().y - Sprite.getHeight() / 2);
Sprite.setRotation(polygonBody.getAngle() * MathUtils.radiansToDegrees);
Sprite.draw(Utils.batch);
}
Utils.batch.end();
}
The sprites also have pixel sizes.
Using this methods it displays the images at the right places, but everything moves slowly.
I was wondering how or if I whould have to change this to make the objects move correctly, and / or mean less. Thanks in advance.
Box2D is an entirely independent of the graphics library that you use. It doesn't have any notion of sprites and textures. What you read online is correct, you'll have to convert pixels to metres, as Box2D works with metres(the standard unit for distance).
For example, if you drew a sprite of size 100x100 pixels, that's the size of the sprite that you want the user to see on the screen. In real world the size of the object should be in metres and not in pixels - so if you say 1px = 1m, then that'll map the sprite to a gigantic 100x100 meter object. In Box2D, large world objects will slow down calculations. So what you need to do is map the 100 pixels to a smaller number of meters, say, 1 meter - thus 100x100px sprite will be represented in Box2D world by a 1x1 meter object.
Box2D doesn't work well with very small numbers and very large numbers. So keep it in between, say between 0.5 and 100, to have good performance.
EDIT:
Ok. Now I get your question.
Don't code to pixels. Its as simple as that. I know it'll take some time to understand this(it took for me). But once you get the hang of it, its straight forward.
Instead of pixels, use a unit, say, you call it meter.
So we decide our viewport should be say 6mx5m.
So initialization is
Constants.VIEWPORT_WIDTH = 6;
Constants.VIEWPORT_HEIGHT = 5;
...
void init() {
camera = new OrthographicCamera(Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);
camera.position.set(Constants.VIEWPORT_WIDTH/2, Constants.VIEWPORT_HEIGHT/2, 0);
camera.update();
}
Once you know the actual width and height, you call the following function in order to maintain aspect ratio:
public void resize(int width, int height) {
camera.viewportHeight = (Constants.VIEWPORT_WIDTH / width) * height;
camera.update();
}
resize() can be called anytime you change your screen size(eg: when you screen orientation changes). resize() takes the actual width and height (320x480 etc), which is the pixel value.
Now you specify you sprite sizes, their positions etc. in this new world of size 6x5. You can forget pixels. The minimum size of the sprite that'll fill the screen will be 6x5.
You can now use the same unit with Box2D. Since the new dimensions will be smaller, it won't be a problem for Box2D. If I remember correctly Box2D doesn't have any unit. We just call it meter for convenience sake.
Now you might ask where you specify the dimensions of the window. It depends on the platform. Following code shows a 320x480 windowed desktop game:
public class Main {
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "my-game";
cfg.useGL20 = false;
cfg.width = 480;
cfg.height = 320;
new LwjglApplication(new MyGame(), cfg);
}
}
Our camera will intelligently map the 6x5 viewport to 480x320.

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

Touchevents using libgdx and scene2d

i am currently going through some tutorials on android development and libgdx/scene2d atm but got stuck on the touchevents:
For starters i am implementing a board game. By now i managed to draw the board and some meeples (player tokens) as well as adjust their position to the correct coordinates. When i started using libgdx i also successfully implemented a first test to see if the touchinput is working. The test looked something like this:
if(Gdx.input.isTouched()){
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
stone.x=touchPos.x - stone.width/2;
stone.y=touchPos.y - stone.height/2;
By now i rearranged my code to use scene2d because of some very convenient features. However it seems like i can't manage to get touchevents to work with scene2d despite i followed some rather simple tutorials. Since some of them reference some recent major changes to scene2d, i wondered if those tutorials might be outdated and hope you guys can help me fix my problem :) I will try and only paste the relevant parts of my code in here to present a minimal example of my non-working code.
Of course i am also glad about any hints on how to "improve" my code in general since i am still learning and probably break some conventions here ^^
Lets start with my actor-class:
//deleted: imports
public class Player extends Actor{
private int xval; //x Position of the Player on the board
private int yval; //y Position of the Player on the board
private Color color;
private TextureRegion playerTexture;
//deleted: some variables that are unimportant for touchevents
public Player(int x, int y, TextureRegion texture){
//initializing some Variables with default values
this.xval = x;
this.yval = y;
color = new Color(1,1,1,1);
playerTexture = texture;
this.setTouchable(Touchable.enabled); //this should be set by default, but i added it just to be sure.
//setBounds seems to be necessary in addition to the "touchable" flag to be able to "grab" or "touch" Actors.
//getX and getY are methods of the actor class which i do not overwrite. I use distinct getters and setters to modify the Variables xval and yval.
setBounds(getX(), getY(), playerTexture.getRegionWidth(), playerTexture.getRegionHeight());
//now i implement the InputListener. This doesnt seem to get called, since the "touch started" message doesn't appear in LogCat.
//In Theory the testevent should move the meeple one space to the right.
//To do that i call event.getTarget() which returns the actor of which the TouchDown is called. I then cast it to my Actor class (Player) and set xval to xval+1.
this.addListener(new InputListener(){
public boolean touchDown(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("Example", "touch started at (" + x + ", " + y + ")");
((Player)event.getTarget()).setXf(((Player)event.getTarget()).getXf()+1);
return true;
}
});
}
//This is my draw method, which just sets the color of my meeple and then draws it to its current position.
public void draw(Batch batch, float alpha){
batch.setColor(color);
batch.draw(playerTexture, getX(), getY());
}
//deleted: several getters and setters
}
So if i got it right, the gdx inputProcessor is managing all inputs. If i set the Gdx InputProcessor to the stage which contains the actors, it will in case of an event (for example a touchDown) call the inputProcessors of all actors on the stage. Thus since i just added one to my class which includes a touchDown, this should handle the touchDown event in the case that the actor is actually touched. The hitbox to verify that is set by the statement "setBounds" in my Player class.
I implemented this in my ApplicationListener class:
//deleted: imports
public class QuoridorApplicationListener implements ApplicationListener{
Stage stage;
OrthographicCamera camera;
//deleted: several variable declarations.
//deleted: constructor - this only fills some of my variables with values depending on game settings.
#Override
public void create() {
//set stage - since "stage = new Stage(800,400,false)" doesn't seem to work anymore, i did set up a viewport manually. If you got suggestions how to this straightforward, please leave a note :)
Gdx.app.log("Tag", "game started");
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
Viewport viewport = new Viewport() {};
viewport.setCamera(camera);
stage = new Stage(viewport);
Gdx.input.setInputProcessor(stage); //like i described above, this should forward incoming touchevents to the actors on the stage.
//deleted: setting textures, music, setting up the board (the boardtiles are actors which are added to the stage)
//deleted: setting TextureRegion, player colors, player positions and player attributes.
for (int i = 0; i < playercount; i++){
stage.addActor(players[i]);
}
}
//deleted: dispose(), pause(), resume(), resize() methods.
#Override
public void render() {
camera.update();
for (int i=0; i< playercount; i++){
players[i].setX(/*Formula to determine x-coordinates from Player.xval*/);
players[i].setY(/*Formula to determine x-coordinates from Player.yval*/);
}
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
}
I can't figure out what i am missing to get the touchevents working. So i hope you can help me :) Thanks a lot in advance!
I think your problem originates from your manual creation of a Viewport object, which handles the unprojecting internally.
Instead of
stage = new Stage(800,400,false);
you should use
stage = new Stage();
To resize the the viewport to the correct size once a resize event occurs you need to call
stage.getViewport().update( newWidth, newHeight )
in your ApplicationListener's resize method.

Categories

Resources