I'm working on a simple game and I have problem with setting the map size. I am using OrthographicCamera. I want the map to be visible. How should I set the proper size of viewport new OrthographicCamera(viewport_width, viewport_height)?
Currently I'm passing some value and when I'm drawing elements on the map I don't see them because they are out of bound, if I make the width and height enormous I can see them. I want to have the whole map visible and draw everything on the sight of the user. I'm not sure where I'm making a mistake
You map is in square size and most of devices are in rectangular size so you need to keep your map in center of screen, either you're using portrait or landscape mode.
public class TileTest extends ApplicationAdapter {
ExtendViewport extendViewport;
OrthogonalTiledMapRenderer mapRenderer;
OrthographicCamera camera;
float worldWidth,worldHeight;
#Override
public void create() {
float tileWidth=64,tileHeight=64;
float mapWidth=20,mapHeight=20;
worldWidth=tileWidth*mapWidth;
worldHeight=tileHeight*mapHeight;
camera=new OrthographicCamera();
extendViewport =new ExtendViewport(worldWidth,worldHeight,camera);
TmxMapLoader mapLoader=new TmxMapLoader();
TiledMap map=mapLoader.load("square.tmx");
mapRenderer=new OrthogonalTiledMapRenderer(map);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
mapRenderer.setView(camera);
mapRenderer.render();
}
#Override
public void dispose() {
mapRenderer.dispose();
}
#Override
public void resize(int width, int height) {
extendViewport.update(width,height,false);
extendViewport.getCamera().position.set(worldWidth/2,worldHeight/2,0);
extendViewport.getCamera().update();
}
}
Output is :
Related
I'm making a game for android using libgdx and I want the game look good (with the same proportion) on different screens of smartphones, but not achievement. In a device the picture looks normal and another is very small.
I used viewports and OrthographicCamera but I don't see good results. Maybe what I'm doing wrong.
Currently I have this code (excerpt):
public class PlayScreen extends BaseScreen {
private Stage stage;
private FaceActor faceActor64;
private Texture faceTexture64;
private int sw;
private int sh;
public PlayScreen(MainGame game) {
super(game);
faceTexture64 = new Texture("images/all64.png");
}
#Override
public void show() {
sw = Gdx.app.getGraphics().getWidth();
sh = Gdx.app.getGraphics().getHeight();
stage = new Stage(new FitViewport(sw, sh));
faceActor64 = new FaceActor(faceTexture64);
faceActor64.setBounds(150, 150, 64, 64);
stage.addActor(faceActor64);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
stage.draw();
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
#Override
public void hide() {
stage.dispose();
}
#Override
public void dispose() {
faceTexture64.dispose();
}
}
I'm using an image of 64px.
Result in a smartphone of 480x800.
Result in a smartphone of 1080x1920.
Any idea how to fix it?
What you are doing there is this:
For a 720x1280 device you are setting the view of your game world to 720x1280 and for a 1080x1920 device you are setting it to 1080x1920. You assets most likely do not change in size so the more pixels a device has the more it will show of your game world. I always tell people to forget about pixels, unless they want a pixel perfect game and that is what you are creating if you do not resize your assets, a pixel perfect game world.
If you think about it, your game does not even need to know about the device. It just needs to know how much of your game world to render in that FitViewport. So let's say I have a tile game and my tiles have a size of 1x1 units. If I would want to show 16 tiles vertically and 9 horizontally I would setup my FitViewport as new FitViewport(9, 16). This would fill up the screen on most devices since they often have a aspect ratio of 16:9.
The short explanation is, pass only constants into the Viewport constructor, not the actual screen dimensions. Pick dimensions that you want to work with, and the viewport will stretch them to fit the actual screen.
Also, if you don't want black bars ("letterboxing / pillarboxing"), use ExtendViewport instead of FitViewport.
Objective: to rotate an image in the center of the screen with movement equal to left or right touchDragged event.
Right now I have a basic Stage that is created and adds an actor (centerMass.png) to the stage. it is created and rendered like this:
public class Application extends ApplicationAdapter {
Stage stageGamePlay;
#Override
public void create () {
//setup game stage variables
stageGamePlay = new Stage(new ScreenViewport());
stageGamePlay.addActor(new CenterMass(new Texture(Gdx.files.internal("centerMass.png"))));
Gdx.input.setInputProcessor(stageGamePlay);
}
#Override
public void render () {
Gdx.gl.glClearColor(255f/255, 249f/255, 236f/255, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//before drawing, updating actions that have changed
stageGamePlay.act(Gdx.graphics.getDeltaTime());
stageGamePlay.draw();
}
}
I then have a separate class file that contains the CenterMass class, extending Image. I am familiar enough to know I could extend Actor, but I am not sure the benefit I would gain using Actor vs Image.
In the CenterMass class I create the texture, set bounds, set touchable and center it on the screen.
Inside CenterMass class I also have an InputListener listening for events. I have an override set for touchDragged where I am trying to get the X and Y of the drag, and use that to set the rotate actions accordingly. That class looks like this:
//extend Image vs Actor classes
public class CenterMass extends Image {
public CenterMass(Texture centerMassSprite) {
//let parent be aware
super(centerMassSprite);
setBounds(getX(), getY(), getWidth(), getHeight());
setTouchable(Touchable.enabled);
setPosition(Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2);
setRotation(90f);
addListener(new InputListener(){
private int dragX, dragY;
private float duration;
private float rotateBy = 30f;
#Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
//get
float dX = (float)(x-dragX)/(float)Gdx.graphics.getWidth();
float dY = (float)(dragY-y)/(float)Gdx.graphics.getHeight();
duration = 1.0f; // 1 second
Actions.sequence(
Actions.parallel(
Actions.rotateBy(rotateBy, duration),
Actions.moveBy( dX, dY, duration)
)
);
}
});
}
#Override
protected void positionChanged() {
//super.positionChanged();
}
#Override
public void draw(Batch batch, float parentAlpha) {
//draw needs to be available for changing color and rotation, I think
batch.setColor(this.getColor());
//cast back to texture because we use Image vs Actor and want to rotate and change color safely
((TextureRegionDrawable)getDrawable()).draw(batch, getX(), getY(),
getOriginX(), getOriginY(),
getWidth(), getHeight(),
getScaleX(), getScaleY(),
getRotation());
}
#Override
public void act(float delta) {
super.act(delta);
}
}
The Problem:
I have not been able to get it to rotate the way I would like. I have been able to get it to shift around in unpredictable ways. Any guidance would be much appreciated.
As from you code it seems everything is good. except you don't set any origin of the image. without setting the origin it is by default set to 0,0.(bottom left of your image)
So if yow want to rotate the image with origin to centre you have to set the origin to imageWidth/2. imageHeight/2.
setOrigin(imageWidth/2,imageHeight/2)// something like this
In my game I am drawing a scene2d Stage using a custom world coordinate system.
I then want to draw a debug UI with some text like FPS on top of that, but simply using screen coordinates, i.e. where the text is positioned in the upper right corner of the screen. My main render method looks something like this:
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
gameStage.act(delta);
gameStage.draw();
debugInfo.render(delta);
}
The Stage sets a custom viewport:
public GameStage(GameDimensions dimensions, OrthographicCamera camera) {
// mapWith will be smaller than screen width
super(new FitViewport(dimensions.mapWidth, dimensions.mapHeight, camera));
...
}
The DebugInfo render code looks like this:
public DebugInfo() {
Matrix4 mat = new Matrix4();
mat.setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.setProjectionMatrix(mat);
}
#Override
public void render(float delta) {
batch.begin();
font.setScale(2f);
drawText("FPS " + Gdx.graphics.getFramesPerSecond());
batch.end();
}
private void drawText(String text) {
final BitmapFont.TextBounds textBounds = font.getBounds(text);
textPos.x = Gdx.graphics.getWidth() - textBounds.width - MARGIN;
textPos.y = Gdx.graphics.getHeight() - MARGIN;
textPos.z = 0;
font.draw(batch, text, textPos.x, textPos.y);
}
The problem is that even though I make no reference whatsoever to the stage's world system or its camera, but strictly use pixel values and an according projection matrix, the text appears in the upper right corner of the stage's viewport, which is smaller than the screen. I want it to appear detached from the stage in the screen's corner instead.
I could pinpoint the problem down to the creation of the Stage instance itself; stopping to draw it is not enough. I actually have to remove the call to super(new FitViewport(...)) to prevent this from happening.
This leads me to believe that I need to somehow reset the viewport of the rendering pipeline before I render the UI overlay? How would I do that?
My first answer was wrong. Now, when I've looked into Viewport source code I can see that it uses OpenGL's glViewPort and indeed it affects all subsequent draw calls. So you have to call glViewport with appropriate dimensions before rendering your stuff:
#Override
public void render(float delta) {
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.begin();
font.setScale(2f);
drawText("FPS " + Gdx.graphics.getFramesPerSecond());
batch.end();
}
I have taken some code from a book on LibGDX, and adapted it for my own project, unfortunately, I have got a little stuck.
I have a class called MenuScreen (see below) which of course displays the Menu Screen. It is basically the same as the original code, just adapted for the Images I am using. In the project that was in the book, the Background image used for the Menu displayed correctly, filling an 800x480 Window on the Desktop, and filling the Screen of my Nexus 7.
With my project, the Image displays fine on the Desktop, but much smaller on the Nexus 7. I have a recent build for this project, and I understand that there has been some changes regarding the Viewport. I assume it is displaying now at actual size on the screen, but it is in fact now a lot smaller than is should be anyway... I have a 2012 N7, which has a resolution of 1280 x 800, but is displaying at about 550 x 300, even though the image itself is 800 x 480.
Could someone please point me in the right direction to get this image displaying correctly ?
public class MenuScreen extends AbstractGameScreen {
private static final String TAG = MenuScreen.class.getName();
private Stage stage;
private Skin demoSkin;
private Skin skinLibGdx;
private Viewport viewport;
private Image imgBackground;
// options
private Window winOptions;
private TextButton btnWinOptSave;
private TextButton btnWinOptCancel;
private TextButton optionsButton;
private TextButton startButton;
private Slider numBlocks;
private CheckBox chkShowFpsCounter;
public MenuScreen (Game game)
{
super(game);
}
#Override
public void render(float deltaTime) {
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(deltaTime);
stage.draw();
Table.drawDebug(stage);
}
#Override
public void resize(int width, int height) {
stage.getViewport().update((int)Constants.VIEWPORT_GUI_WIDTH, (int)Constants.VIEWPORT_GUI_HEIGHT, false);
}
#Override
public void show() {
stage = new Stage();
rebuildStage();
}
private void rebuildStage() {
demoSkin = new Skin(Gdx.files.internal(Constants.SKIN_PHYSICSDEMO_UI), new TextureAtlas(Constants.TEXTURE_ATLAS_UI));
skinLibGdx = new Skin(Gdx.files.internal(Constants.SKIN_LIBGDX_UI), new TextureAtlas(Constants.TEXTURE_ATLAS_LIBGDX_UI));
// build all layers
Table layerBackground = buildBackgroundLayer();
Table layerControls = buildControlsLayer();
Table layerOptionsWindow = buildOptionsWindowLayer();
// assemble stage for menu screen
stage.clear();
Stack stack = new Stack();
stage.addActor(stack);
stack.setSize(Constants.VIEWPORT_GUI_WIDTH, Constants.VIEWPORT_GUI_HEIGHT);
stack.add(layerBackground);
stack.add(layerControls);
//stage.addActor(layerOptionsWindow);
}
private Table buildOptionsWindowLayer() {
winOptions = new Window("Options", skinLibGdx);
// Make options window slightly transparent
winOptions.setColor(1, 1, 1, 0.8f);
// Hide options window by default
winOptions.setVisible(false);
//showOptionsWindow(false, false);
// Let TableLayout recalculate widget sizes and positions
winOptions.pack();
// Move options window to bottom right corner
winOptions.setPosition(Constants.VIEWPORT_GUI_WIDTH - winOptions.getWidth() - 50, 50);
return winOptions;
}
private void showOptionsWindow (boolean visible, boolean animated) {
float alphaTo = visible ? 0.8f : 0.0f;
float duration = animated ? 1.0f : 0.0f;
Touchable touchEnabled = visible ? Touchable.enabled : Touchable.disabled;
winOptions.addAction(sequence(touchable(touchEnabled), alpha(alphaTo, duration)));
}
private Table buildControlsLayer() {
Table layer = new Table();
startButton = new TextButton("Start Simulation", skinLibGdx);
optionsButton = new TextButton("Options", skinLibGdx);
layer.add(startButton);
layer.row();
layer.add(optionsButton);
return layer;
}
private Table buildBackgroundLayer() {
Table layer = new Table();
imgBackground = new Image(demoSkin, "background");
layer.add(imgBackground);
return layer;
}
#Override
public void hide() {
stage.dispose();
skinLibGdx.dispose();
demoSkin.dispose();
}
#Override
public void pause() {
// TODO Auto-generated method stub
}
#Override
public InputProcessor getInputProcessor() {
return stage;
}
}
You need to set size of the background image separately.
imgBackground.setSize(imageWidth, imageHeight);
Since you are changing Viewport size in resize method, for a screen with higher resolution, image will become even smaller. So image size must be set to be large enough.
You can calculate required image size by multiplying original image size by scaling factor (ratio of actual resolution and assumed resolution).
Good luck.
It is possible to render a view at a low resolution, and then scale it up to fit the actual size of your view using the setFixedSize() method of a SurfaceHolder. However, the scaling is done with some kind of interpolation, causing everything to blur.
Is there any method for changing the method of interpolation to nearest neighbour or just turning it off?
Here is an example of what I mean, Made with a 4x4 surface in a fullscreen-view:
Left image: This is how I want the result to look (here achieved by drawing a nonfiltered bitmap)
Right image: This is what happens when the 4x4 canvas is scaled to fullscreen.
Sample code for generating the right image if anyone's interested:
public class ScaleView extends SurfaceView implements SurfaceHolder.Callback {
private final static float[] points = {0,0, 2,0, 4,0, 1,1, 3,1, 0,2, 2,2, 4,2, 1,3, 3,3};
private Paint white;
public ScaleView(Context context) {
super(context);
white = new Paint();
white.setColor(0xFFFFFFFF);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){
Canvas c = holder.lockCanvas();
try{
c.drawPoints(points, white);
}finally{
holder.unlockCanvasAndPost(c);
}
}
#Override
public void surfaceCreated(SurfaceHolder holder){
holder.setFixedSize(4, 4);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder){}
}
Note: The square pattern above is just an example of the effect I want to avoid. I am looking for a general solution that can be applied to any view (with a surfaceholder) with any content.
Sorry, you can't control this. It is not defined what kind of scaling will be done on these surfaces -- it depends on the hardware and how it is configured. Also, this kind of extreme scaling really should be avoided, since in some cases hardware can't do it so you will end up in slower paths. (For example if surfaces are being put into overlays, many hardware overlay engines can't do that kind of extreme scaling.)