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.
Related
I made a game in which a turtle had to collect StarFish but when I added walking animation it doesn't work. What happens is it only shows up the first frame and the whole animation doesn't play and I get no errors.
And I'm using diffrent images to load animation
Here is my animation method-
// used to load animation from multiple files
public Animation loadAnimationFromFiles(String[] fileNames, float frameDuration, boolean loop)
{
int fileCount = fileNames.length;
Array<TextureRegion> textureArray = new Array<TextureRegion>();
for (int n = 0; n < fileCount; n++)
{
String fileName = fileNames[n];
Texture texture = new Texture( Gdx.files.internal(fileName) );
texture.setFilter( TextureFilter.Linear, TextureFilter.Linear );
textureArray.add( new TextureRegion( texture ) );
}
Animation anim = new Animation(frameDuration, textureArray);
if (loop) {
//setAnimation(anim);
System.out.println("animation run");
anim.setPlayMode(Animation.PlayMode.LOOP);
}
else {
anim.setPlayMode(Animation.PlayMode.NORMAL);
}
if (animation == null) {
System.out.println("animation null");
setAnimation(anim);
}
return anim;
}
And here is my turtle class where I used animation-
public class Turtle extends BaseActor
{
public Turtle(float x, float y, Stage s)
{
super(x, y, s);
String[] fileNames = {
"turtle2.png",
"turtle.png",
};
loadAnimationFromFiles(fileNames, 0.1f, true);
}
public void act(float dt)
{
super.act(dt);
if (Gdx.input.isKeyPressed(Input.Keys.LEFT))
this.moveBy(-5,0);
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT))
this.moveBy(5,0);
if (Gdx.input.isKeyPressed(Input.Keys.UP))
this.moveBy(0,5);
if (Gdx.input.isKeyPressed(Input.Keys.DOWN))
this.moveBy(0,-5);
}
}
You just created the animation, but never used it. To make use of the animation you need to get the textures from it, by measuring the delta time, that passed.
Your turtle class should look more like this:
public class Turtle extends BaseActor
{
private Animation<TextureRegion> animation;//store the animation in a field to use it
private float time;//use a timer for the animation to be played
public Turtle(float x, float y, Stage s)
{
super(x, y, s);
String[] fileNames = {
"turtle2.png",
"turtle.png",
};
//store the animation to the field instead of just creating it
animation = loadAnimationFromFiles(fileNames, 0.1f, true);
//set the animations timer
time = 0;
}
public void act(float dt)
{
//increase the time since the animation was started by the delta time
time += dt;
//tell the animation how much time passed, so it can give you the current animation texture
TextureRegion texture = animation.getKeyFrame(time);
//TODO draw texture
//I assume the super.act call also draws a texture; that should be deleted to not draw over the already created texture
super.act(dt);
if (Gdx.input.isKeyPressed(Input.Keys.LEFT))
this.moveBy(-5,0);
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT))
this.moveBy(5,0);
if (Gdx.input.isKeyPressed(Input.Keys.UP))
this.moveBy(0,5);
if (Gdx.input.isKeyPressed(Input.Keys.DOWN))
this.moveBy(0,-5);
}
}
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.
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.
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
stage.act(delta);
batch.setProjectionMatrix(camera.combined);
batch.begin();
if (lifeCount > 0) {
/*
When stage.draw is called here, it only displays the exit button.
The game still operates, but everything is invisible.
If I leave the game on, the game over screen is shown,
*/
stage.draw();
text.setColor(1.0f, 1.0f, 1.0f, 1.0f);
text.draw(batch, score + scoreCount, 25, 100);
text.draw(batch, lives + lifeCount, 25, 120);
text.draw(batch, speed + raindropSpeed, 25, 80);
batch.draw(bucketTexture, bucket.x, bucket.y);
for (Rectangle clearDrop : clearDrops) {
batch.draw(clearDropTexture, clearDrop.x, clearDrop.y);
}
for (Rectangle healthDrop : healthDrops) {
batch.draw(healthDropTexture, healthDrop.x, healthDrop.y);
/*
If I place stage.draw here, health drops are invisible.
This also happens if I place it in the raindrop for-loop and the
cleardrop for-loop
*/
}
for (Rectangle raindrop : raindrops) {
batch.draw(raindropTexture, raindrop.x, raindrop.y);
}
/*
If I place stage.draw here, the bucket, score, life, and speed
display correctly. The drops are still invisible.
*/
} else {
pause();
raindrops.clear();
game.setScreen(new GameOver(game));
}
batch.end();
What I have been trying to do is have an exit button in the top right corner of the GameScreen, although drawing the stage which the button resides in gives me difficulties (see comments in code).
Here is my code for the exit button and stage (resize()):
if (stage == null)
stage = new Stage(width, height, true);
stage.clear();
Gdx.input.setInputProcessor(stage);
TextButtonStyle styleQuit = new TextButtonStyle();
styleQuit.up = skin.getDrawable("buttonnormal");
styleQuit.down = skin.getDrawable("buttonpressed");
styleQuit.font = text;
quitButton = new TextButton(" ", styleQuit);
quitButton.setWidth(128);
quitButton.setHeight(128);
quitButton.setX(800 - 128);
quitButton.setY(480 - 100);
quitButton.addListener(new InputListener() {
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
return true;
}
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
Gdx.app.log(RainCatcher.LOG, "Quit Button Pressed");
game.setScreen(new MainMenu(game));
}
});
stage.addActor(quitButton);
And the rest (in show())
atlas = new TextureAtlas("gamebuttons.pack");
skin = new Skin();
skin.addRegions(atlas);
text = new BitmapFont();
Is there any special trick to allow a stage to be rendered alongside with the falling raindrops, bucket, and text? My friends and I have been stumped and couldn't find a solution anywhere.
Move stage.draw() after batch.end() or before batch.begin()
This is not the right approach you're taking in my opinion. If you're using a stage in libgdx, then you should benefit from other components of Scene2d. So, rather than drawing your raindrops and other game entities separately (and making your job complicated), you should make them actors and add them to stage wherever you need and then draw the stage in the render.
For example:
public class RaindDrop extends Actor {
TextureRegion region;
public RaindDrop () {
region = new TextureRegion(...);
}
public void draw (SpriteBatch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
batch.draw(...);
}
}
So on for other entities. Initialise and add them to your stage.
Here's the official wiki to read more:
https://code.google.com/p/libgdx/wiki/scene2d