I am using the following class to render an atlas on screen:
public class AnimationDemo implements ApplicationListener {
private SpriteBatch batch;
private TextureAtlas textureAtlas;
private Animation animation;
private float elapsedTime = 0;
#Override
public void create() {
batch = new SpriteBatch();
textureAtlas = new TextureAtlas(Gdx.files.internal("data/packOne.atlas"));
animation = new Animation(1 / 1f, textureAtlas.getRegions());
}
#Override
public void dispose() {
batch.dispose();
textureAtlas.dispose();
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
//sprite.draw(batch);
elapsedTime += Gdx.graphics.getDeltaTime();
batch.draw(animation.getKeyFrame(elapsedTime, true), 0, 0);
batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
I am a beginner with libGDX, however with the above program my images are not rendered in order as random images appear. I was earlier using the following with the same . atlas file and it was working properly:
public class MyGdxGame implements ApplicationListener {
private SpriteBatch batch;
private TextureAtlas textureAtlas;
private Sprite sprite;
private int currentFrame = 1;
private String currentAtlasKey = new String("0001");
#Override
public void create() {
batch = new SpriteBatch();
textureAtlas = new TextureAtlas(Gdx.files.internal("data/packOne.atlas"));
TextureAtlas.AtlasRegion region = textureAtlas.findRegion("0001");
sprite = new Sprite(region);
sprite.setPosition(Gdx.graphics.getWidth() / 2 - sprite.getWidth() / 2, Gdx.graphics.getHeight() / 2 - sprite.getHeight() / 2);
sprite.scale(4.5f);
Timer.schedule(new Timer.Task() {
#Override
public void run() {
currentFrame++;
if (currentFrame > 393)
currentFrame = 1;
// ATTENTION! String.format() doesnt work under GWT for god knows why...
currentAtlasKey = String.format("%04d", currentFrame);
sprite.setRegion(textureAtlas.findRegion(currentAtlasKey));
}
}
, 0, 1 / 30.0f);
}
#Override
public void dispose() {
batch.dispose();
textureAtlas.dispose();
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
sprite.draw(batch);
batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
Any hints about what might be wrong here?
I am also trying to adapt my program with Screen Viewport any headings as in how to implement this would also be welcome.
Edit: The .atlas file is located here
Your atlas file isn't ordered. If you call the code below, it will be ordered.
regions.sort(new Comparator<AtlasRegion>() {
#Override
public int compare(AtlasRegion o1, AtlasRegion o2) {
return Integer.parseInt(o1.name) > Integer.parseInt(o2.name) ? 1 : -1;
}
});
But I'm still checking why your atlas regions isn't ordered.
you should create array with frames ordered alphabetically instead of using textureAtlas.getRegions() which just gives you an array without caring of order.
The example for atlas with regions named like: region1, region2 and so on would be:
AtlasRegion[] frames = new AtlasRegion[framesCount];
for(int i = 0; i < framesCount; i++)
{
frames[i] = atlas.findRegion("region" + i);
}
so you can adjust it to your regions names.
If you want to get all frames from textureAtlas you can also do it like this:
Array<String> names = new Array<String>();
for(AtlasRegion region : textureAtlas.getRegions())
{
names.add( region.name );
}
names.sort();
Array<AtlasRegion> frames = new Array<AtlasRegion>();
for(String s : names)
{
frames.add( textureAtlas.findRegion(s) );
}
and then after get frames array just create animation object:
animation = new Animation(1/1f, frames.items); //or just frames depending on which type frames is
TexturePacker will index the images for you as long as you follow the naming scheme set forth here https://github.com/libgdx/libgdx/wiki/Texture-packer#image-indexes.
so your frames would be named something like
anim1_001.png
anim1_002.png
...
anim1_100.png
and a separate animation would simply be
anim2_001.png
....
anim2_100.png
EDIT:
additionally you can get the regions only related to certain animations. So instead of
animation = new Animation(1 / 1f, textureAtlas.getRegions());
you could use (yes it's findRegions() not findRegion()):
animation1 = new Animation(1 / 1f, textureAtlas.findRegions("anim1"));
animation2 = new Animation(1 / 1f, textureAtlas.findRegions("anim2"));
EDIT2:
If you're are using a stage it is quite easy to implement a screen viewport. I do it like this, (stage is a field and this step is in the show/create method):
stage = new Stage(new ScreenViewport());
Then in the resize method:
stage.getViewport().update(width, height, true);
Without a stage it's only slightly more complex
camera = new WhateverCamera();
viewport = new ScreenViewport(camera);
Then in the resize method:
viewport.update(width, height, true);
Use whatever camera you want, WhateverCamera is a placeholder and can be OrthographicCamera or PerspectiveCamera.
The last argument true centers the camera, if you don't want to do this set it to false or leave it out, it assumes false.
Related
Currently I have 2 screens, a main menu and a battle screen. When i move directly from the main menu screen to the battle screen without resizing, everything works normally, but when i resize my main menu and then move to my battlescreen, the particles (and only the particles) get rendered incorrectly.
I have tried :
-updating the viewport in the show method of the battlescreen
-setting the projectionmatrix of the spritebatch to the camera's combined of either the mapcamera or the unitcamera.
-applying every viewport (there are 3) before rendering.
THE MAIN MENU SCEEN
https://snag.gy/cJCnRt.jpg
private Stage _stage;
public MainMenuScreen(Object... params){
_stage = new Stage();
backgroundbatch = new SpriteBatch();
}
#Override
public void render(float delta) {
backgroundbatch.begin();
backgroundbatch.draw(_currentFrame, 0, 0,_stage.getViewport().getWorldWidth(),_stage.getViewport().getWorldHeight()); // Draw current frame at (0, 0)
backgroundbatch.end();
_stage.act(delta);
_stage.draw();
}
#Override
public void resize(int width, int height) {
_stage.getViewport().setScreenSize(width, height);
//_stage.getViewport().update(width, height);
}
THE BATTLESCREEN CLASS
private void initializeHUD() {
_hudCamera = new OrthographicCamera();
_hudCamera.setToOrtho(false, VIEWPORT.physicalWidth, VIEWPORT.physicalHeight);
}
#Override
public void show() {
_camera = new OrthographicCamera();
_camera.setToOrtho(false, map.getMapWidth(), map.getMapHeight());
_mapRenderer = new OrthogonalTiledMapRenderer(_mapMgr.getCurrentTiledMap(), Map.UNIT_SCALE);
_mapRenderer.setView(_camera);
spritebatch = new SpriteBatch();
map.makeSpawnParticles();
}
#Override
public void render(float delta) {
_camera.update();
_hudCamera.update();
//draw particles
ParticleMaker.drawAllActiveParticles(spritebatch, delta);
}
#Override
public void resize(int width, int height) {
Player.getInstance().getEntityStage().getViewport().update(width, height, true);
_playerBattleHUD.resize(width, height);
map.getTiledMapStage().getViewport().update(width, height, true);
}
PARTICLE MANAGER CLASS
public static void drawAllActiveParticles(SpriteBatch spriteBatch, float delta) {
for (ArrayList<Particle> particleTypeList : allParticles.values()) {
spriteBatch.begin();
for(Particle particle : particleTypeList) {
if(particle.isActive()) {
particle.update(delta);
particle.draw(spriteBatch,delta);
}
if (particle.isComplete()) {
particle.delete();
//particles.remove(particle);
}
}
spriteBatch.end();
}
}
PARTICLE CLASS
public void draw(SpriteBatch spriteBatch, float delta) {
Gdx.app.debug("Particle : ", "spritebatch position = " + spriteBatch.getProjectionMatrix().getScaleX() + " , " + spriteBatch.getProjectionMatrix().getScaleY() + ")");
particleEffect.draw(spriteBatch, delta);
}
expected results :
https://snag.gy/q2txr5.jpg (particles are the yellow dots)
results (after resizing the main screen):
https://snag.gy/g81ZJ7.jpg
noteworthy : after resizing, the spritebatch projectionmatrix shows a different value in comparison to not having resized the main menu.
For anyone else with similar issues, I fixed my problem by using my tiledmaprenderer's batch to render my particles.
This is one of the oddest things that has ever occured in LibGDX for me. I have used these exact specifications for all my other States in my game, but under different names and they all work fine, except for my ShopState, which won't render ANYTHING at all! Here's my code for the class:
public class ShopState extends State{
private Texture bg;
private Sprite shopLayout;
private OrthographicCamera shopCam;
Viewport viewport;
public ShopState(GameStateManager gsm) {
super(gsm);
shopLayout = new Sprite(new Texture(Gdx.files.internal("shopLayout.png")));
bg = new Texture("bg2.png");
shopCam = new OrthographicCamera();
viewport = new StretchViewport(720, 1280, shopCam);
viewport.apply();
shopCam.position.set(shopCam.viewportWidth / 2, shopCam.viewportHeight / 2, 0);
shopLayout.setPosition(shopCam.viewportWidth / 2 - shopLayout.getWidth() / 2, shopCam.viewportHeight / 2 - shopLayout.getHeight() / 2);
shopLayout.setSize(650, 1100);
}
#Override
public void handleInput() {
}
#Override
public void update(float dt) {
handleInput();
}
#Override
public void resize(int width, int height){
viewport.update(width, height);
shopCam.position.set(shopCam.viewportWidth / 2, shopCam.viewportHeight / 2, 0);
}
#Override
public void render(SpriteBatch sb) {
shopCam.update();
sb.setProjectionMatrix(shopCam.combined);
sb.begin();
sb.draw(bg, 0 , 0, shopCam.viewportWidth, shopCam.viewportHeight);
shopLayout.draw(sb);
sb.end();
}
#Override
public void dispose() {
bg.dispose();
shopLayout.getTexture().dispose();
}
}
What am I doing wrong? Everything seems fine, but all I get when I click on the Shop button, it gives me a black screen!
Ok, found a solution, but I have no idea why this fixes it. All I had to do was add a stage that uses the current viewport.
stage = Stage(viewport);
And that was it! It is now working properly, for some reason...
On my 854 x 480 LG Leon it renders fine, but on a larger screen resolution (2560 x 1440) this happens: http://imgur.com/a/rcbJK.
The code for processing the text is: http://imgur.com/a/c316e.
I've tried expanding the bounds of the Label because I thought that it might be constricting the text, but it still won't display properly.
What could be causing this?
The different mobile phone has the different aspect ratio in their screen size . that is the reason it shows different different screen. to avoid this problem you must use the viewPort. So you will be able to handle the different screen size. for more details, you must read the libgdx documentation. here I'm posting a sample code which can help you to handle the different screen size. there are three kinds of viewports mainly using
1) fillviewport
2) fitviewPort
3)stretchviewport
here is the documentation in detail.
https://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/utils/viewport/FillViewport.html
public class myGame extends ApplicationAdapter {
public OrthographicCamera camera;
public Viewport viewPort;
private SpriteBatch batch;
public myGame() {
}
#Override
public void create() {
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
camera = new OrthographicCamera();
camera.position.set(0, 0, 0);
camera.update();
camera.setToOrtho(false, 1280, 800);
viewPort = new FillViewport(1280, 800, camera);
}
#Override
public void dispose() {
batch.dispose();
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
float deltaTime = Gdx.graphics.getDeltaTime();
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(sprie,0,0)
batch.end();
}
#Override
public void resize(int width, int height) {
viewPort.update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
// don't copy paste it . just try to understand and implement it.
You should use Scene2D and ViewPort.
Also , be careful to use coordinates (i.e : x: 64 , y:32 changes in every different screen size)
This is the common issue when your running your game in multiple devices. Because your game is running in different aspect ratios in different screens . it's generally called the multi screen issue. To solve this problem the libgdx is providing it's on class called the viewPort. Mainly you can see three viewpors.
1)Fill viewPort.
2)Fit viewPort.
3)stretch viewport.
For more details you can go through the libgdx documentation. Here im posting some example code which can solve your multi screen issues.
public class myGame extends ApplicationAdapter {
public OrthographicCamera camera;
public Viewport viewPort;
private SpriteBatch batch;
private BitmapFont myScoreFont;
//General screen resolution
public int APP_WIDTH=1280;
public int APP_HEIGHT=800;
public int fontPositionIn_X=600;
public int fontPositionIn_Y=400;
public myGame() {
}
#Override
public void create() {
myScoreFont = new BitmapFont(Gdx.files.internal(Constants.PATH_TO_MY_SCORE_FONT), true);
batch = new SpriteBatch();
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
camera = new OrthographicCamera();
camera.position.set(0, 0, 0);
camera.update();
camera.setToOrtho(false, APP_WIDTH, APP_HEIGHT);
viewPort = new fillViewPort(1280, 800, camera);
}
#Override
public void dispose() {
batch.dispose();
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
float deltaTime = Gdx.graphics.getDeltaTime();
batch.setProjectionMatrix(camera.combined);
batch.begin();
myScoreFont.draw(batch,"any texts", fontPositionIn_X, fontPositionIn_Y)
batch.end();
}
#Override
public void resize(int width, int height) {
viewPort.update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
so by using the viewPort you can be able to play your game in all the different screens.
The problem
I cannot seem to be able to get Tiled maps to render properly. I am using LibGDX as a library for loading the map (Release 1.6.0).
Video demonstration
I have created a video to show you the actual problem and make things easier by skipping the whole process of explaining it. Here is a link to it.
The code I've used
protected Level level;
protected OrthogonalTiledMapRenderer mapRenderer;
protected OrthographicCamera camera;
protected TiledMap map;
protected MainGameLoop game;
protected SpriteBatch batch;
private BitmapFont font;
private int w, h;
public Level1(MainGameLoop game) {
this.game = game;
}
#Override
public void show() {
w = Gdx.graphics.getWidth();
h = Gdx.graphics.getHeight();
int CAMERA_WIDTH = 800;
int CAMERA_HEIGHT = 450 * (w / h);
camera = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
camera.setToOrtho(false);
camera.update();
map = new TmxMapLoader().load("maps/map1.tmx");
mapRenderer = new OrthogonalTiledMapRenderer(map);
Gdx.input.setInputProcessor(this);
font = new BitmapFont();
font.setColor(Color.BLUE);
batch = new SpriteBatch();
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
mapRenderer.setView(camera);
mapRenderer.render();
batch.begin();
font.draw(batch, "Camera zoom: " + camera.zoom, 40, 40);
batch.end();
}
#Override
public void resize(int width, int height) {
camera.viewportWidth = width;
camera.viewportHeight = height;
camera.update();
}
#Override
public void dispose() {
mapRenderer.dispose();
map.dispose();
background.dispose();
Gdx.input.setInputProcessor(null);
}
#Override
public boolean scrolled(int amount) {
camera.zoom += amount;
camera.update();
return true;
}
// Here go the rest of the methods, such as pause, resume, hide, keyDown, keyUp, keyTyped, touchDown, touchUp, touchDragged & mouseMoved.
Solutions I've tried
I have tried using different numbers for the camera's x and y with no luck. I have also tried tranlating the camera to the proper position (hardcoded it), as well as using another map (different tilemap and dimensions) but that did not work either.
Conclusion
I can't seem to find a way to fix this problem. Any help is much appreciated. Thank you very much.
Quick introduction
Ok after a good while, I managed to solve this matter by hardcoding some stuff. But it works properly, so I am happy with it.
What I did to solve the problem
First of all, I found out the exact number I had to use to scale up my Tiled map, which is this number: 3.125f .
Then, instead of using pixels for the camera, I used my own units (something I should have done from the the first moment).
After doing those two things, I noticed that the map was zoomed in a lot. So, by using the scrolled method from the InputProcessor, I managed to find the exact number the map had to be "unzoomed".
I also found out that if I call the setToOrtho(false) method from the OrthographicCamera object, it zooms the map in 19 times for some weird reason. If that method does not get called, the map is zoomed in only 1 time
The code I am currently using
TiledMap tiledMap;
OrthographicCamera camera;
TiledMapRenderer tiledMapRenderer;
final float WIDTH = 8000;
final float HEIGHT = 4500;
final float num = 3.125f;
#Override
public void show() {
tiledMap = MapLoader.realm1_level1;
tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap, num);
camera = new OrthographicCamera(WIDTH, HEIGHT);
Gdx.input.setInputProcessor(this);
camera.zoom += 1f;
camera.update();
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
tiledMapRenderer.setView(camera);
tiledMapRenderer.render();
}
// This method was just used for testing to see where
// the map was or should have been placed.
#Override
public boolean keyDown(int keycode) {
if (keycode == Input.Keys.LEFT)
camera.translate(-32, 0);
if (keycode == Input.Keys.RIGHT)
camera.translate(32, 0);
if (keycode == Input.Keys.UP)
camera.translate(0, 32);
if (keycode == Input.Keys.DOWN)
camera.translate(0, -32);
if (keycode == Input.Keys.NUM_1)
tiledMap.getLayers().get(0).setVisible(!tiledMap.getLayers().get(0).isVisible());
return true;
}
#Override
public void resize(int width, int height) {
camera.position.set(WIDTH, HEIGHT, 0);
camera.update();
}
#Override
public boolean scrolled(int amount) {
camera.zoom += amount;
camera.update();
return true;
}
#Override
public void dispose() {
tiledMap.dispose();
}
// And here go the rest of the methods that come from the
//Screen and the InputProcessor interfaces.
Notes
The numbers I used for the WIDTH and the HEIGHT variables work properly, strictly with tiled maps that their width is 80 tiles, and their height is 45 tiles.
If you are facing the same problem, you'll have to find the appropriate number to scale your map up/down, depending on the unit numbers you're using. Trial and error is your only guide. If we ignore StackOverflow of course.
I need to know how i would go about setting up a stage and adding actors to it for my main menu.
Here is my code so far
public class MainMenu implements Screen {
CrazyZombies game;
Stage stage;
TextureAtlas atlas;
SpriteBatch batch;
Skin skin;
Button button;
TextureRegion firstLayer, secondLayer, thirdLayer, fourthLayer,
fifthLayer, sixthLayer, seventhLayer, eighthLayer, ninthLayer,
tenthLayer, eleventhLayer;
Sprite road, backTrees, sideTrees, bottemTrees, light, poles,
play, quit, store, custom, options;
public MainMenu(CrazyZombies game){
this.game = game;
}
#Override
public void render(float delta) {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
Gdx.gl.glClearColor(0.09f, 0.28f, 0.2f, 1);
batch.begin();
road.draw(batch);
backTrees.draw(batch);
sideTrees.draw(batch);
bottemTrees.draw(batch);
light.draw(batch);
poles.draw(batch);
play.draw(batch);
quit.draw(batch);
store.draw(batch);
custom.draw(batch);
options.draw(batch);
batch.end();
}
#Override
public void resize(int width, int height) {
Gdx.input.setInputProcessor(stage);
}
#Override
public void show() {
Audio.playMusic(true);
batch = new SpriteBatch();
atlas = new TextureAtlas("data/mainmenu/MainMenu.pack");
firstLayer = atlas.findRegion("1layer");
secondLayer = atlas.findRegion("2layer");
thirdLayer = atlas.findRegion("3layer");
fourthLayer = atlas.findRegion("4layer");
fifthLayer = atlas.findRegion("5layer");
sixthLayer = atlas.findRegion("6layer");
seventhLayer = atlas.findRegion("7layer");
eighthLayer = atlas.findRegion("8layer");
ninthLayer = atlas.findRegion("9layer");
tenthLayer = atlas.findRegion("10layer");
eleventhLayer = atlas.findRegion("11layer");
road = new Sprite(firstLayer);
backTrees = new Sprite(secondLayer);
sideTrees = new Sprite(thirdLayer);
bottemTrees = new Sprite(fourthLayer);
light = new Sprite(fifthLayer);
poles = new Sprite(sixthLayer);
play = new Sprite(seventhLayer);
quit = new Sprite(eighthLayer);
store = new Sprite(ninthLayer);
custom = new Sprite(tenthLayer);
options = new Sprite(eleventhLayer);
}
#Override
public void hide() {
dispose();
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
batch.dispose();
atlas.dispose();
Audio.dispose();
}
}
The bits that i need to become actors are:
- play
- quit
- store
- custom
- options
All my code does at the moment is just display my main menu i need to get the stage and actors setup in order to get the buttons working.
Take a look at TableLayout and also take a look at TextButton or maybe Button.
Here is a good tutorial. Work through it and you will understand how to work with the Screen2D and how to create a simple menu. -> Direkt link to Menucreation of the Blog