I recently got into LibGDX using the book "LibGDX Game Development By Example" (Pretty good one btw) and have been playing around with the tutorial projects for the last month.
One of these games is a FlappyBird-clone (of course it is) and I decided to add features, change sprites etc.
Now the problem is that the normal obstacle graphics (flowers) don't fit the new theme and need to be exchanged.
Doing so results in jiggering graphics for the new sprites.
I should point out that the code used to visualize these obstacles has not changed at all, simply exchanging the sprites causes this problem.
I tried a lot of different sprites and all that are not identical to the flowers seem to have this problem.
So whatever is the cause, the old flower sprites are unaffected, every other sprite is.
On to the code (Removed some Getters/Setters and other unrelated methods)
The Flower/Obstacle Class:
public class Flower
{
private static final float COLLISION_RECTANGLE_WIDTH = 13f;
private static final float COLLISION_RECTANGLE_HEIGHT = 447f;
private static final float COLLISION_CIRCLE_RADIUS = 33f;
private float x = 0;
private float y = 0;
private static final float MAX_SPEED_PER_SECOND = 100f;
public static final float WIDTH = COLLISION_CIRCLE_RADIUS * 2;
private static final float HEIGHT_OFFSET = -400.0f;
private static final float DISTANCE_BETWEEN_FLOOR_AND_CEILING = 225.0f;
private final Circle floorCollisionCircle;
private final Rectangle floorCollisionRectangle;
private final Circle ceilingCollisionCircle;
private final Rectangle ceilingCollisionRectangle;
private boolean pointClaimed = false;
private final TextureRegion floorTexture;
private final TextureRegion ceilingTexture;
float textureX,textureY;
public Flower(TextureRegion floorTexture, TextureRegion ceilingTexture)
{
this.floorTexture = floorTexture;
this.ceilingTexture = ceilingTexture;
this.y = MathUtils.random(HEIGHT_OFFSET);
this.floorCollisionRectangle = new Rectangle(x,y,COLLISION_RECTANGLE_WIDTH,COLLISION_RECTANGLE_HEIGHT);
this.floorCollisionCircle = new Circle(x + floorCollisionRectangle.width / 2, y + floorCollisionRectangle.height, COLLISION_CIRCLE_RADIUS);
this.ceilingCollisionRectangle = new Rectangle(x,floorCollisionCircle.y + DISTANCE_BETWEEN_FLOOR_AND_CEILING,COLLISION_RECTANGLE_WIDTH,
COLLISION_RECTANGLE_HEIGHT);
this.ceilingCollisionCircle = new Circle(x + ceilingCollisionRectangle.width / 2, ceilingCollisionRectangle.y, COLLISION_CIRCLE_RADIUS);
}
public void update(float delta)
{
setPosition(x - (MAX_SPEED_PER_SECOND * delta));
}
public void setPosition(float x)
{
this.x = x;
updateCollisionCircle();
updateCollisionRectangle();
}
private void updateCollisionCircle()
{
floorCollisionCircle.setX(x + floorCollisionRectangle.width / 2);
ceilingCollisionCircle.setX(x + ceilingCollisionRectangle.width / 2);
}
private void updateCollisionRectangle()
{
floorCollisionRectangle.setX(x);
ceilingCollisionRectangle.setX(x);
}
public void draw(SpriteBatch batch)
{
drawFloorFlower(batch);
drawCeilingFlower(batch);
}
private void drawFloorFlower(SpriteBatch batch)
{
textureX = floorCollisionCircle.x - floorTexture.getRegionWidth() / 2;
textureY = floorCollisionRectangle.getY() + COLLISION_CIRCLE_RADIUS;
batch.draw(floorTexture,textureX,textureY);
}
private void drawCeilingFlower(SpriteBatch batch)
{
textureX = ceilingCollisionCircle.x - ceilingTexture.getRegionWidth() / 2;
textureY = ceilingCollisionRectangle.getY() - COLLISION_CIRCLE_RADIUS;
batch.draw(ceilingTexture,textureX, textureY);
}
}
And the GameScreen/Main Class:
public class GameScreen extends ScreenAdapter
{
private static final float WORLD_WIDTH = 480;
private static final float WORLD_HEIGHT = 640;
private java.util.prefs.Preferences prefs;
private int highscore;
FlappeeBeeGame flappeeBeeGame;
private ShapeRenderer shapeRenderer;
private Viewport viewport;
private Camera camera;
private SpriteBatch batch;
private Flappee flappee;
private Flower flower;
private Array<Flower> flowers = new Array<Flower>();
private static final float GAP_BETWEEN_FLOWERS = 200.0f;
private boolean gameOver = false;
int score = 0;
BitmapFont bitmapFont;
GlyphLayout glyphLayout;
private TextureRegion background;
private TextureRegion flowerBottom;
private TextureRegion flowerTop;
private TextureRegion bee;
private TextureRegion smallCloud;
private TextureRegion lowCloud;
private Music music_background;
TextureAtlas textureAtlas;
List<Cloud> activeClouds = new ArrayList<Cloud>();
List<Cloud> cloudBarriers = new ArrayList<Cloud>();
private float cloud_minScale = 0.6f;
private float cloud_maxScale = 1.0f;
private float cloud_minY, cloud_maxY;
private float cloud_minDis, cloud_maxDis;
private float cloud_minSpeed = 17.0f;
private float cloud_maxSpeed = 27.0f;
private final float barrierCloud_speed = 150.0f;
private boolean inputBlocked = false;
private float blockTime = 0.5f;
private float remainingblockTime = blockTime;
public GameScreen(FlappeeBeeGame fpg)
{
flappeeBeeGame = fpg;
flappeeBeeGame.getAssetManager().load("assets/flappee_bee_assets.atlas",TextureAtlas.class);
flappeeBeeGame.getAssetManager().finishLoading();
textureAtlas = flappeeBeeGame.getAssetManager().get("assets/flappee_bee_assets.atlas");
prefs = java.util.prefs.Preferences.userRoot().node(this.getClass().getName());
highscore = prefs.getInt("highscore",0);
music_background = Gdx.audio.newMusic(Gdx.files.internal("assets/backgroundmusic.ogg"));
music_background.setLooping(true);
music_background.setVolume(0.5f);
music_background.play();
}
private void createNewFlower()
{
Flower newFlower = new Flower(flowerBottom,flowerTop);
newFlower.setPosition(WORLD_WIDTH + Flower.WIDTH);
flowers.add(newFlower);
}
private void checkIfNewFlowerIsNeeded()
{
if(flowers.size == 0)
{
createNewFlower();
}
else
{
Flower flower = flowers.peek();
if(flower.getX() < WORLD_WIDTH - GAP_BETWEEN_FLOWERS)
{
createNewFlower();
}
}
}
private void drawFlowers()
{
for(Flower flower : flowers)
{
flower.draw(batch);
}
}
private void removeFlowersIfPassed()
{
if(flowers.size > 0)
{
Flower firstFlower = flowers.first();
if(firstFlower.getX() < -Flower.WIDTH)
{
flowers.removeValue(firstFlower,true);
}
}
}
#Override
public void resize(int width, int height) {
super.resize(width, height);
viewport.update(width,height);
}
#Override
public void show() {
super.show();
camera = new OrthographicCamera();
camera.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2, 0);
camera.update();
viewport = new FitViewport(WORLD_WIDTH,WORLD_HEIGHT, camera);
shapeRenderer = new ShapeRenderer();
batch = new SpriteBatch();
bitmapFont = new BitmapFont(Gdx.files.internal("assets/score_new.fnt"));
glyphLayout = new GlyphLayout();
background = textureAtlas.findRegion("bg");
flowerBottom = textureAtlas.findRegion("pipeBottom");
flowerTop = textureAtlas.findRegion("flowerTop");
bee = textureAtlas.findRegion("bee");
smallCloud = textureAtlas.findRegion("smallCloud");
lowCloud = textureAtlas.findRegion("lowerCloud");
flower = new Flower(flowerBottom,flowerTop);
flappee = new Flappee(bee,textureAtlas);
flappee.setPosition(WORLD_WIDTH/4,WORLD_HEIGHT/2);
cloud_minDis = smallCloud.getRegionWidth() / 4;
cloud_maxDis = smallCloud.getRegionWidth();
cloud_maxY = viewport.getWorldHeight() - smallCloud.getRegionHeight()/2;
cloud_minY = viewport.getWorldHeight() - smallCloud.getRegionHeight() * 2;
Cloud a = generateCloud(null);
Cloud b = generateCloud(a);
Cloud c = generateCloud(b);
Cloud d = generateCloud(c);
Cloud e = generateCloud(d);
activeClouds.add(a);
activeClouds.add(b);
activeClouds.add(c);
activeClouds.add(d);
activeClouds.add(e);
a = new Cloud(lowCloud,batch,0,0 - lowCloud.getRegionHeight()/4,barrierCloud_speed,1.0f);
b = new Cloud(lowCloud,batch,lowCloud.getRegionWidth(),0 - lowCloud.getRegionHeight()/4,barrierCloud_speed,1.0f);
c = new Cloud(lowCloud,batch,lowCloud.getRegionWidth()*2,0 - lowCloud.getRegionHeight()/4,barrierCloud_speed,1.0f);
cloudBarriers.add(a);
cloudBarriers.add(b);
cloudBarriers.add(c);
}
public Cloud generateCloud(Cloud formerCloud)
{
Cloud d;
if(formerCloud == null)
{
float randomVal = (float)Math.random();
d = new Cloud(smallCloud,batch,viewport.getWorldWidth(),
(float)Math.random() * (cloud_maxY - cloud_minY) + cloud_minY,
randomVal * (cloud_maxSpeed-cloud_minSpeed) + cloud_minSpeed,
randomVal * (cloud_maxScale-cloud_minScale) + cloud_minScale);
return d;
}
float randomVal = (float)Math.random();
d = new Cloud(smallCloud,batch,formerCloud.getPosX() + ((float)
Math.random() * (cloud_maxDis - cloud_minDis) + cloud_minDis),(float)Math.random() * (cloud_maxY - cloud_minY) + cloud_minY,
randomVal * (cloud_maxSpeed-cloud_minSpeed) + cloud_minSpeed,
randomVal * (cloud_maxScale-cloud_minScale) + cloud_minScale);
return d;
}
#Override
public void render(float delta) {
super.render(delta);
clearScreen();
shapeRenderer.setProjectionMatrix(camera.projection);
shapeRenderer.setTransformMatrix(camera.view);
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
shapeRenderer.end();
draw(delta);
update(delta);
}
private void draw(float delta)
{
batch.setProjectionMatrix(camera.projection);
batch.setTransformMatrix(camera.view);
batch.begin();
batch.draw(background,0,0);
drawClouds(delta);
drawScore();
drawFlowers();
//drawDebug();
if(!gameOver)
{
flappee.draw(batch,delta);
}
drawBarrierClouds(delta);
batch.end();
}
private void updateClouds(float delta)
{
boolean move = false;
Cloud tmp = null;
for(Cloud c : cloudBarriers)
{
c.update(delta);
if(c.getPosX() <= -lowCloud.getRegionWidth())
{
tmp = c;
move = true;
}
}
if(move)
{
float positionX = cloudBarriers.get(cloudBarriers.size()-1).getPosX() + lowCloud.getRegionWidth();
if(positionX < viewport.getWorldWidth())
{
positionX = viewport.getWorldWidth();
}
tmp.setPos(positionX,0 - lowCloud.getRegionHeight()/4);
cloudBarriers.remove(tmp);
cloudBarriers.add(tmp);
tmp = null;
move = false;
}
for(Cloud c : activeClouds)
{
c.update(delta);
if(c.getPosX() <= -smallCloud.getRegionWidth())
{
tmp = c;
move = true;
}
}
if(move)
{
float randomVal = (float)Math.random();
float positionX = activeClouds.get(activeClouds.size()-1).getPosX() + ((float)
Math.random() * (cloud_maxDis - cloud_minDis) + cloud_minDis);
if(positionX < viewport.getWorldWidth())
{
positionX = viewport.getWorldWidth();
}
tmp.setPos(positionX,(float)Math.random() * (cloud_maxY - cloud_minY) + cloud_minY);
tmp.setSpeed(randomVal * (cloud_maxSpeed - cloud_minSpeed) + cloud_minSpeed);
tmp.setScale(randomVal * (cloud_maxScale - cloud_minScale) + cloud_minScale);
activeClouds.remove(tmp);
activeClouds.add(tmp);
move = false;
tmp = null;
}
}
private void drawBarrierClouds(float delta)
{
for(Cloud c : cloudBarriers)
{
c.render();
}
}
private void drawClouds(float delta)
{
for(Cloud c : activeClouds)
{
c.render();
}
}
private void clearScreen()
{
Gdx.gl.glClearColor(Color.BLACK.r,Color.BLACK.g,Color.BLACK.b, Color.BLACK.a);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
private void blockFlappeeLeavingTheWorld()
{
flappee.setPosition(flappee.getX(), MathUtils.clamp(flappee.getY(),0,WORLD_HEIGHT));
}
private void updateFlowers(float delta)
{
for(Flower flower : flowers)
{
flower.update(delta);
}
checkIfNewFlowerIsNeeded();
removeFlowersIfPassed();
}
private void update(float delta)
{
updateClouds(delta);
if(!gameOver) {
updateFlappee(delta);
updateFlowers(delta);
updateScore();
if (checkForCollision())
{
gameOver = true;
inputBlocked = true;
remainingblockTime = blockTime;
restart();
}
}
else
{
if((Gdx.input.isKeyJustPressed(Input.Keys.SPACE) || Gdx.input.isButtonPressed(Input.Buttons.LEFT)) && !inputBlocked)
{
gameOver = false;
score = 0;
}
if(inputBlocked)
{
if(remainingblockTime > 0)
{
remainingblockTime -= delta;
}
else
{
inputBlocked = false;
remainingblockTime = blockTime;
}
}
}
}
private void restart()
{
flappee.setPosition(WORLD_WIDTH / 4, WORLD_HEIGHT / 2);
flowers.clear();
}
#Override
public void dispose() {
super.dispose();
}
private boolean checkForCollision()
{
for(Flower flower : flowers)
{
if(flower.isFlappeeColliding(flappee))
{
if(score > highscore)
{
highscore = score;
prefs.putInt("highscore",highscore);
inputBlocked = true;
remainingblockTime = blockTime;
}
return true;
}
}
return false;
}
}
I'd love to give you a runnable jar but I've got some problems building a working version. Let's just say a jar is out of the question for now.
What I can give you are screenshots:
Jiggering Sprite View
Functional Flower Sprite View
The first image shows the problem: The new sprites (which are just debug) become edgy like the top of the sprite can't compete with the speed of its lower half.
The second image shows the old sprites for comparison: They don't show any of this behaviour, even if they are longer than the one on the screenshot.
So what do you people think?
What causes this behaviour and how should I fix it?
Thanks in advance for any help, I appreciate it :)
Greetz!
EDIT:
I kind of fixed it.
When switching to another computer and running the game the issue didn't come up anymore.
Specifically I went from Debian to Windows 10 and from NVIDIA Optimus to a standard desktop AMD-Card.
If you should encounter this problem try another PC with a different OS and/or GPU.
Sadly (if you were to google this question) I can't tell you how to solve it on the first machine or what exactly caused it, but at least it shouldn't come up on anyone else's computer when you send them your project.
Related
My app has 2 activities a main menu and a game play activity. you click play to start the game play and when you die you can click back to go to the main menu. For some reason when i click play for the third time (meaning ive died and went back to the main menu twice) the game crashes with this error.
FATAL EXCEPTION: main
Process: com.example.jordanschanzenbach.myapplication, PID: 1875
java.lang.OutOfMemoryError: Failed to allocate a 360012 byte allocation with 79976 free bytes and 78KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:700)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:535)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:558)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:588)
at com.example.jordanschanzenbach.myapplication.Obstacle.<init>(Obstacle.java:44)
at com.example.jordanschanzenbach.myapplication.ObstacleManager.populateObstacles(ObstacleManager.java:57)
at com.example.jordanschanzenbach.myapplication.ObstacleManager.<init>(ObstacleManager.java:38)
at com.example.jordanschanzenbach.myapplication.GamePlayScene.<init>(GamePlayScene.java:41)
at com.example.jordanschanzenbach.myapplication.GamePanel.<init>(GamePanel.java:29)
at com.example.jordanschanzenbach.myapplication.MainActivity.onCreate(MainActivity.java:27)
that's not the entire error message but i think i gave you what you needed. Now i tried to make an animation for the obstacles to be spikes instead of just black rectangles but for some reason the spikes will not display and i think it might correspond with the error i receive. But i don't understand why the spikes wont display as i used the same code for the animation for my player.
here is the code for my obstacle
public class Obstacle implements GameObject
{
private Rect rectangle;
private Rect rectangle2;
private int color;
private Animation falling;
private Animation fallingStill;
private Animation fallingAgain;
private AnimationManagerObstacle animationManagerObstacle;
public Rect getRectangle()
{
return rectangle;
}
public void IncrementY(float y )
{
rectangle.top += y;
rectangle.bottom += y;
rectangle2.top += y;
rectangle2.bottom += y;
}
public Obstacle(int rectHeight, int color, int startX, int startY, int playerGap)
{
this.color = color;
BitmapFactory bitfac = new BitmapFactory();
Bitmap fallings = bitfac.decodeResource(GlobalVariables.CURRENT_CONTEXT.getResources(), R.drawable.spikesupsidedown);
Bitmap fallingStills = bitfac.decodeResource(GlobalVariables.CURRENT_CONTEXT.getResources(), R.drawable.spikesupsidedown);
Bitmap fallingAgains = bitfac.decodeResource(GlobalVariables.CURRENT_CONTEXT.getResources(), R.drawable.spikesupsidedown);
falling = new Animation(new Bitmap[]{fallings}, 2);
fallingStill = new Animation(new Bitmap[]{fallings, fallingStills}, 0.5f);
fallingAgain = new Animation(new Bitmap[]{fallings, fallingAgains}, 0.5f);
animationManagerObstacle = new AnimationManagerObstacle(new Animation[]{falling, fallingStill, fallingAgain});
rectangle = new Rect(0, startY, startX, startY + rectHeight);
rectangle2 = new Rect(startX + playerGap, startY, GlobalVariables.SCREEN_WIDTH, startY + rectHeight);
}
public boolean playerCollide(RectPlayer player)
{
return Rect.intersects(rectangle, player.getRectangle()) || Rect.intersects(rectangle2, player.getRectangle());
}
#Override
public void draw(Canvas canvas)
{
Paint paint = new Paint();
paint.setColor(color);
animationManagerObstacle.draw(canvas, rectangle);
canvas.drawRect(rectangle, paint);
canvas.drawRect(rectangle2, paint);
}
#Override
public void update()
{
animationManagerObstacle.update();
}
public void update(Point point)
{
float oldTop = rectangle.top;
rectangle.set(point.x - rectangle.width() / 2,
point.y - rectangle.height() / 2,
point.x + rectangle.width() / 2,
point.y + rectangle.height() / 2);
int state = 0;
if (rectangle.left - oldTop > 1)
{
state = 1;
}
else if (rectangle.left - oldTop < 2)
{
state = 2;
}
animationManagerObstacle.playAnim(state);
animationManagerObstacle.update();
}
}
this is the line that the error message points to
Bitmap fallings = bitfac.decodeResource(GlobalVariables.CURRENT_CONTEXT.getResources(), R.drawable.spikesupsidedown);
here is my obstacle manager where i add and display the obstacles
public class ObstacleManager
{
private ArrayList<Obstacle> obstacles;
private int playerGap;
private int obstacleGap;
private int obstacleHeight;
private int color;
private long startTime;
private long initTime;
private int score = 0;
public ObstacleManager(int playerGap, int obstacleGap, int obstacleHeight, int color)
{
this.playerGap = playerGap;
this.obstacleGap = obstacleGap;
this.obstacleHeight = obstacleHeight;
this.color = color;
startTime = initTime = System.currentTimeMillis();
obstacles = new ArrayList<>();
populateObstacles();
}
public boolean playerCollide(RectPlayer player)
{
for(Obstacle ob : obstacles)
{
if(ob.playerCollide(player))
return true;
}
return false;
}
private void populateObstacles()
{
int currY = -5 * GlobalVariables.SCREEN_HEIGHT / 4;
while(currY < 0)
{
int xStart = (int)(Math.random()*(GlobalVariables.SCREEN_WIDTH - playerGap));
obstacles.add(new Obstacle(obstacleHeight, color, xStart, currY, playerGap));
currY += obstacleHeight + obstacleGap;
}
}
public void update()
{
if (GlobalVariables.GAMEOVER)
{
for (int i = 0; i < 3; i++)
{
obstacles.remove(obstacles.size() - 2);
}
}
int elapsedTime = (int)(System.currentTimeMillis() - startTime);
startTime = System.currentTimeMillis();
float speed = (float)(Math.sqrt((1 + startTime - initTime) / 1750.0)) * GlobalVariables.SCREEN_HEIGHT /17500.0f;
for(Obstacle ob : obstacles)
{
ob.IncrementY(speed * elapsedTime);
}
if(obstacles.get(obstacles.size() - 1).getRectangle().top >= GlobalVariables.SCREEN_HEIGHT * 3/4)
{
int xStart = (int)(Math.random()*(GlobalVariables.SCREEN_WIDTH - playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart,
obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size() - 1);
score++;
if (score > GlobalVariables.HIGHSCORE)
GlobalVariables.HIGHSCORE = score;
}
}
public void draw(Canvas canvas)
{
for(Obstacle ob : obstacles)
ob.draw(canvas);
Paint paint = new Paint();
paint.setTextSize(100);
paint.setColor(Color.RED);
canvas.drawText("" + score, 50, 50 + paint.descent() - paint.ascent(), paint);
canvas.drawText("HighScore: " + GlobalVariables.HIGHSCORE, GlobalVariables.SCREEN_WIDTH / 2 + 50, 50 + paint.descent() - paint.ascent(), paint);
}
}
here is my animation manager
public class AnimationManager
{
private Animation[] animations;
private int animationsIndex = 0;
public AnimationManager(Animation[] animations)
{
this.animations = animations;
}
public void playAnim(int index)
{
for (int i = 0; i < animations.length; i++)
{
if (i == index)
{
if (!animations[index].isPlaying())
{
animations[i].play();
}
}
else
{
animations[i].stop();
}
}
animationsIndex = index;
}
public void draw(Canvas canvas, Rect rect)
{
if (animations[animationsIndex].isPlaying())
{
animations[animationsIndex].draw(canvas, rect);
}
}
public void update()
{
if (animations[animationsIndex].isPlaying())
{
animations[animationsIndex].update();
}
}
}
and finally my animation class
public class Animation
{
private Bitmap[] frames;
private int frameIndex;
private boolean isPlaying = false;
public boolean isPlaying()
{
return isPlaying;
}
public void play()
{
isPlaying = true;
frameIndex = 0;
lastFrame = System.currentTimeMillis();
}
public void stop()
{
isPlaying = false;
}
private float frameTime;
private long lastFrame;
public Animation(Bitmap[] frames, float animTime)
{
this.frames = frames;
frameIndex = 0;
frameTime = animTime / frames.length;
lastFrame = System.currentTimeMillis();
}
public void draw(Canvas canvas, Rect destination)
{
if (!isPlaying)
return;
scaleRect(destination);
canvas.drawBitmap(frames[frameIndex], null, destination, new Paint());
}
private void scaleRect(Rect rect)
{
float whRatio = (float)(frames[frameIndex].getWidth() / frames[frameIndex].getHeight());
if (rect.width() > rect.height())
{
rect.left = rect.right - (int)(rect.height() * whRatio);
}
else
{
rect.top = rect.bottom - (int)(rect.width() * (1 / whRatio));
}
}
public void update()
{
if (!isPlaying)
return;
if (System.currentTimeMillis() - lastFrame > frameTime * 1000)
{
frameIndex++;
if (frameIndex >= frames.length)
frameIndex = 0;
lastFrame = System.currentTimeMillis();
}
}
}
i dont think you necessarily needed all that but just in case :). thanks for any comments to improve or help.
OutOfMemoryError basically means that you tried to use more memory than you can. In this specific example you (bitmap) tried to allocate 360KB though you could only allocate 78KB more. You might have a memory leak somewhere or your bitmap might be too big. I think your Bitmaps might be leaking memory. I'm not an Android expert though.
I'd recommend you to create a method in Obstacle like recycle or something along this way. Then every time you remove Obstacle from obstacles in ObstacleManager call that method. In that method you should aim to recycle all no longer used bitmaps. I'd do it by calling either Animation#recycle on every of your animations or calling AnimationManagerObstacle#recycle. (Animation#recycle would be meant to call recycle on every one of your frames.)
I try to do my own version of "fruit ninja" for training based on this version : https://github.com/emmaguy/FruitNinja
I have done some minor changes. What I want to do is to affect different scores to the object in the "enum" in fruittype.
So, I add this function (in the aim to retrieve the current random value):
public static int currentrandom() {
return random.nextInt(FruitType2.values().length );
}
and I add,
if (FruitType2.currentrandom()<=9) {
score++;
} else {
score=score-5;
}
at the end of FruitProjectileManager.
Complete code for FruitProjectileManager:
public class FruitProjectileManager02 implements ProjectileManager {
private final Random random2 = new Random();
private final List<Projectile> fruitProjectiles =
new ArrayList<Projectile>();
private final SparseArray<Bitmap> bitmapCache;
private Region clip;
private int maxWidth;
private int maxHeight;
private String FruitTypen = "FruitType2";
public FruitProjectileManager02(Resources r) {
bitmapCache = new SparseArray<Bitmap>(FruitType2.values().length);
for (FruitType2 t : FruitType2.values()) {
bitmapCache.put(t.getResourceId(), BitmapFactory.decodeResource(r, t.getResourceId(), new Options()));
}
}
public void draw(Canvas canvas) {
for (Projectile f : fruitProjectiles) {
f.draw(canvas);
}
}
public void update() {
if (maxWidth < 0 || maxHeight < 0) {
return;
}
if (random2.nextInt(1000) < 30) {
fruitProjectiles.add(createNewFruitProjectile());
}
for (Iterator<Projectile> iter = fruitProjectiles.iterator(); iter.hasNext(); ) {
Projectile f = iter.next();
f.move();
if (f.hasMovedOffScreen()) {
iter.remove();
}
}
}
private FruitProjectile02 createNewFruitProjectile() {
int angle = random2.nextInt(20) + 70;
int speed = random2.nextInt(30) + 120;
boolean rightToLeft = random2.nextBoolean();
float gravity = random2.nextInt(6) + 8.0f;
float rotationStartingAngle = random2.nextInt(360);
float rotationIncrement = random2.nextInt(100) / 3.0f;
if (random2.nextInt(1) % 2 == 0) {
rotationIncrement *= -1;
}
return new FruitProjectile02(bitmapCache.get(FruitType2.randomFruit().getResourceId()), maxWidth, maxHeight,
angle, speed, gravity, rightToLeft, rotationIncrement, rotationStartingAngle);
}
public void setWidthAndHeight(int width, int height) {
this.maxWidth = width;
this.maxHeight = height;
this.clip = new Region(0, 0, width, height);
}
#Override
public int testForCollisions(List<TimedPath> allPaths) {
int score = 0;
for (TimedPath p : allPaths) {
for (Projectile f : fruitProjectiles) {
if (!f.isAlive())
continue;
Region projectile = new Region(f.getLocation());
Region path = new Region();
path.setPath(p, clip);
if (!projectile.quickReject(path) && projectile.op(path, Region.Op.INTERSECT)) {
if (FruitType2.currentrandom() <= 9) {
score++;
} else {
score = score - 5;
}
f.kill();
}
}
}
return score;
}
}
Complete code for FruitType:
public enum FruitType2 {
T02(R.drawable.n002),
T04(R.drawable.n004),
T06(R.drawable.n006),
T08(R.drawable.n008),
T10(R.drawable.n010),
T12(R.drawable.n012),
T14(R.drawable.n014),
T16(R.drawable.n016),
T18(R.drawable.n018),
T20(R.drawable.n020),
OTHER1(R.drawable.n003),
OTHER2(R.drawable.n007),
OTHER3(R.drawable.n011);
private final int resourceId;
private FruitType2(int resourceId) {
this.resourceId = resourceId;
}
public int getResourceId() {
return resourceId;
}
private static final Random random = new Random();
public static int currentrandom() {
return random.nextInt(FruitType2.values().length);
}
public static FruitType2 randomFruit() {
return FruitType2.values()[random.nextInt(FruitType2.values().length)];
}
}
I understand the problem , the current random(when the fruit is generated) is not the same that the random when the fruit is sliced and my question is how to
solve this problem. I get no idea so if you have some clues, I am interested.
Thank you in advance.
Perhaps i don't understand the problem, but why don't you store the random number in a variable? Later you can take the random number out of the variable.
Im trying to use the technique shown here, but unfortunately I have one problem. Whenever the color fades into the next colour the screen flashes/blinks a white colour. The problem seems to be coming from this class:
public class ColorAnimationDrawable extends Drawable implements Animatable {
private static final long FRAME_DURATION = 1000 / 60;
private static final long ANIMATION_DURATION = 1500;
private static final int ACCCENT_COLOR = 0x33FFFFFF;
private static final int DIM_COLOR = 0x33000000;
private static final Random mRandom = new Random();
private final Paint mPaint = new Paint();
private boolean mIsRunning;
private int mStartColor;
private int mEndColor;
private int mCurrentColor;
private long mStartTime;
#Override
public void draw(Canvas canvas) {
final Rect bounds = getBounds();
mPaint.setColor(mCurrentColor);
canvas.drawRect(bounds, mPaint);
mPaint.setColor(ACCCENT_COLOR);
canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.top + 1, mPaint);
mPaint.setColor(DIM_COLOR);
canvas.drawRect(bounds.left, bounds.bottom - 2, bounds.right, bounds.bottom, mPaint);
}
#Override
public void setAlpha(int alpha) {
oops("setAlpha(int)");
}
#Override
public void setColorFilter(ColorFilter cf) {
oops("setColorFilter(ColorFilter)");
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
#Override
public void start() {
if (!isRunning()) {
mIsRunning = true;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartColor = randomColor();
mEndColor = randomColor();
scheduleSelf(mUpdater, SystemClock.uptimeMillis() + FRAME_DURATION);
invalidateSelf();
}
}
#Override
public void stop() {
if (isRunning()) {
unscheduleSelf(mUpdater);
mIsRunning = false;
}
}
#Override
public boolean isRunning() {
return mIsRunning;
}
private void oops(String message) {
throw new UnsupportedOperationException("ColorAnimationDrawable doesn't support " + message);
}
private static int randomColor() {
return mRandom.nextInt() & 0x00FFFFFF;
}
private static int evaluate(float fraction, int startValue, int endValue) {
return (int) (startValue + fraction * (endValue - startValue));
}
private final Runnable mUpdater = new Runnable() {
#Override
public void run() {
long now = AnimationUtils.currentAnimationTimeMillis();
long duration = now - mStartTime;
if (duration >= ANIMATION_DURATION) {
mStartColor = mEndColor;
mEndColor = randomColor();
mStartTime = now;
mCurrentColor = mStartColor;
} else {
float fraction = duration / (float) ANIMATION_DURATION;
//#formatter:off
mCurrentColor = Color.rgb(
evaluate(fraction, Color.red(mStartColor), Color.red(mEndColor)), // red
evaluate(fraction, Color.green(mStartColor), Color.green(mEndColor)), // green
evaluate(fraction, Color.blue(mStartColor), Color.blue(mEndColor))); // blue
//#formatter:on
}
scheduleSelf(mUpdater, SystemClock.uptimeMillis() + FRAME_DURATION);
invalidateSelf();
}
};
}
Ive noticed that if I change ANIMATION_DURATION to something longer the delay in between the flashing is also longer. Does anyone know what the issue is and how I can get rid of the blinking?
I am making my first game using LibGDX on android.
I have a background which is 288*511, Now in my game, I need to repeat that background and then translate over it. I have made that game for desktop using Slick2D, and had the same problem, just a bit lower than on my LG G2 phone (4.0.4), is there a way to fix this lag or I am doing something wrong?
The problem is that when it's translating, its moving fine and sometimes jumps 1-2 pixels forward or just stuck for 0.5 seconds or so.
This is my class:
public class Background {
private class RepeatedBackground implements GameObject {
private int x;
private int y = 0;
Sprite background;
public RepeatedBackground(int x, Sprite s) {
this.x = x;
this.background = s;
}
#Override
public void render() {
}
public float getPreferedWidth() {
int w = Gdx.graphics.getWidth();
return (float) (w / 1.15);
}
public float getPreferedHeight() {
int h = Gdx.graphics.getHeight();
return (float) (h / 1.15);
}
#Override
public int getX() {
return this.x;
}
#Override
public int getY() {
return this.y;
}
public Sprite getSprite() {
return this.background;
}
}
private int tra = 0;
private long traTime = 0;
private List<RepeatedBackground> backgrounds = new ArrayList<RepeatedBackground>();
private Level level;
private SpriteBatch backgroundRenderer;
public Background(Level level) {
this.level = level;
this.backgroundRenderer = new SpriteBatch();
}
public void generateBackgrounds() {
int x = 0;
Sprite background = this.level.getFactory().createBackgroundSprite();
for (int i = 0; i < LevelConfig.MAX_BACKGROUND_REPEATS; i++) {
this.backgrounds.add(new RepeatedBackground(x, background));
x += background.getWidth();
}
}
public void render() {
this.backgroundRenderer.getTransformMatrix().translate(-6, 0, 0);
this.backgroundRenderer.begin();
this.backgroundRenderer.setProjectionMatrix(this.level.getInstance().getCamera().combined);
Iterator<RepeatedBackground> itr = this.backgrounds.iterator();
while (itr.hasNext()) {
RepeatedBackground b = itr.next();
Sprite s = b.getSprite();
if (b.getX() - b.getPreferedHeight() < this.level.getInstance().getCamera().viewportWidth) { // this doesn't work properly, but it doesn't load all backgorunds at once but still lags..
this.backgroundRenderer.draw(s, b.getX(), b.getY(), b.getPreferedWidth(), b.getPreferedHeight());
}
}
this.backgroundRenderer.end();
}
}
My create values for camera:
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
camera = new OrthographicCamera(w,h);
camera.setToOrtho(false);
batch = new SpriteBatch();
Texture.setEnforcePotImages(false);
You can use ParallaxBackground class from here
http://www.badlogicgames.com/forum/viewtopic.php?f=17&t=1795
It doesn't depends on the size of the image or camera. It just translates your background continuously
source: http://code.google.com/p/libgdx-users/wiki/ParallaxBackgound
I am working on a game in LibGDX and both my players direction and walking animation won't change. He can walk up, down, left, and right, but only faces left no matter what direction he's walking.
public class WorldController {
enum KeyBinds {
LEFT, RIGHT, UP, DOWN, JUMP, FIRE
}
private World world;
private Player p;
static Map<KeyBinds, Boolean> keys = new HashMap<WorldController.KeyBinds, Boolean>();
static {
keys.put(KeyBinds.LEFT, false);
keys.put(KeyBinds.RIGHT, false);
keys.put(KeyBinds.UP, false);
keys.put(KeyBinds.DOWN, false);
keys.put(KeyBinds.JUMP, false);
keys.put(KeyBinds.FIRE, false);
};
public WorldController(World world) {
this.world = world;
this.p = world.getPlayer();
}
// ** Key presses and touches **************** //
public void leftPressed() {
keys.get(keys.put(KeyBinds.LEFT, true));
}
public void rightPressed() {
keys.get(keys.put(KeyBinds.RIGHT, true));
}
public void upPressed() {
keys.get(keys.put(KeyBinds.UP, true));
}
public void downPressed() {
keys.get(keys.put(KeyBinds.DOWN, true));
}
public void jumpPressed() {
keys.get(keys.put(KeyBinds.JUMP, true));
}
public void firePressed() {
keys.get(keys.put(KeyBinds.FIRE, false));
}
public void leftReleased() {
keys.get(keys.put(KeyBinds.LEFT, false));
}
public void rightReleased() {
keys.get(keys.put(KeyBinds.RIGHT, false));
}
public void upReleased() {
keys.get(keys.put(KeyBinds.UP, false));
}
public void downReleased() {
keys.get(keys.put(KeyBinds.DOWN, false));
}
public void jumpReleased() {
keys.get(keys.put(KeyBinds.JUMP, false));
}
public void fireReleased() {
keys.get(keys.put(KeyBinds.FIRE, false));
}
/** The main update method **/
public void update(float delta) {
processInput();
p.update(delta);
}
/** Change player's state and parameters based on input controls **/
private void processInput() {
if(Gdx.input.isKeyPressed(Keys.LEFT)) {
// left is pressed
//p.setFacingLeft(true);
p.setState(State.WALKING);
p.getVelocity().x = -Player.SPEED;
p.getVelocity().y = 0;
}
if(Gdx.input.isKeyPressed(Keys.RIGHT)){
// right is pressed
//p.setFacingRight(true);
p.setState(State.WALKING);
p.getVelocity().x = Player.SPEED;
p.getVelocity().y = 0;
}
if(Gdx.input.isKeyPressed(Keys.UP)) {
// up is pressed
//p.setFacingUp(true);
p.setState(State.WALKING);
p.getVelocity().y = Player.SPEED;
p.getVelocity().x = 0;
}
if(Gdx.input.isKeyPressed(Keys.DOWN)) {
// down is pressed
//p.setFacingDown(true);
p.setState(State.WALKING);
p.getVelocity().y = -Player.SPEED;
p.getVelocity().x = 0;
}
// need to check if both or none direction are pressed, then player is idle
// if ((keys.get(Keys.LEFT) && keys.get(Keys.RIGHT)) ||
// (!keys.get(Keys.LEFT) && !(keys.get(Keys.RIGHT)))) {
// p.setState(State.IDLE);
// // acceleration is 0 on the x
// p.getAcceleration().x = 0;
// // horizontal speed is 0
// p.getVelocity().x = 0;
// }
}
}
public class WorldRenderer {
private static final float CAMERA_WIDTH = 10f;
private static final float CAMERA_HEIGHT = 7f;
private World world;
private OrthographicCamera cam;
/** for debug rendering **/
ShapeRenderer debugRenderer = new ShapeRenderer();
private SpriteBatch spriteBatch;
private boolean debug = false;
private int width;
private int height;
private float ppuX;
private float ppuY;
private static final float RUNNING_FRAME_DURATION = 0.06f;
/** Textures **/
private TextureRegion pIdleLeft;
private TextureRegion pIdleRight;
private TextureRegion pIdleUp;
private TextureRegion pIdleDown;
private TextureRegion blockTexture;
private TextureRegion pFrame;
/** Animations **/
private Animation walkLeftAnimation;
private Animation walkRightAnimation;
private Animation walkUpAnimation;
private Animation walkDownAnimation;
private void loadTextures() {
TextureAtlas atlas = new TextureAtlas(Gdx.files.internal("images/textures/textures.pack"));
pIdleLeft = atlas.findRegion("Left1");
pIdleRight = new TextureRegion(pIdleLeft);
pIdleRight.flip(true, false);
pIdleUp = atlas.findRegion("Back1");
pIdleDown = atlas.findRegion("Front1");
blockTexture = atlas.findRegion("stone");
//Walking Left Animation
TextureRegion[] walkLeftFrames = new TextureRegion[2];
walkLeftFrames[0] = atlas.findRegion("Left1");
walkLeftFrames[1] = atlas.findRegion("Left2");
walkLeftAnimation = new Animation(RUNNING_FRAME_DURATION, walkLeftFrames);
//Walking Right Animation
TextureRegion[] walkRightFrames = new TextureRegion[2];
walkRightFrames[0] = new TextureRegion(walkLeftFrames[0]);
walkRightFrames[0].flip(true, false);
walkRightFrames[1] = new TextureRegion(walkLeftFrames[1]);
walkRightFrames[1].flip(true, false);
walkRightAnimation = new Animation(RUNNING_FRAME_DURATION, walkRightFrames);
//Walking Up Animation
TextureRegion[] walkUpFrames = new TextureRegion[2];
walkUpFrames[0] = atlas.findRegion("Back1");
walkUpFrames[1] = atlas.findRegion("Back2");
walkUpAnimation = new Animation(RUNNING_FRAME_DURATION, walkUpFrames);
//Walking Down Animation
TextureRegion[] walkDownFrames = new TextureRegion[2];
walkLeftFrames[0] = atlas.findRegion("Front1");
walkLeftFrames[1] = atlas.findRegion("Front2");
walkDownAnimation = new Animation(RUNNING_FRAME_DURATION, walkDownFrames);
}
public void drawPlayer() {
Player p = world.getPlayer();
if(p.getState().equals(State.IDLE)) {
if(Gdx.input.isKeyPressed(Keys.LEFT)) pFrame = pIdleLeft;
else if(Gdx.input.isKeyPressed(Keys.RIGHT)) pFrame = pIdleRight;
else if(Gdx.input.isKeyPressed(Keys.UP)) pFrame = pIdleUp;
else if(Gdx.input.isKeyPressed(Keys.DOWN)) pFrame = pIdleDown;
}
if(p.getState().equals(State.WALKING)) {
if(Gdx.input.isKeyPressed(Keys.LEFT)) walkLeftAnimation.getKeyFrame(p.getStateTime(), true);
else if(Gdx.input.isKeyPressed(Keys.RIGHT)) walkRightAnimation.getKeyFrame(p.getStateTime(), true);
else if(Gdx.input.isKeyPressed(Keys.UP)) walkUpAnimation.getKeyFrame(p.getStateTime(), true);
else if(Gdx.input.isKeyPressed(Keys.DOWN)) walkDownAnimation.getKeyFrame(p.getStateTime(), true);
}
spriteBatch.draw(pFrame, p.getXPosition() * ppuX, p.getYPosition() * ppuY, Player.SIZE * ppuX, Player.SIZE * ppuY);
}
public void setSize(int w, int h){
this.width = w;
this.height = h;
ppuX = (float)width / CAMERA_WIDTH;
ppuY = (float)height / CAMERA_HEIGHT;
}
public WorldRenderer(World world, boolean debug) {
this.world = world;
this.cam = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
this.cam.position.set(CAMERA_WIDTH / 2f, CAMERA_HEIGHT / 2f, 0);
this.cam.update();
this.debug = debug;
spriteBatch = new SpriteBatch();
loadTextures();
}
public void render(){
spriteBatch.begin();
drawBlocks();
drawPlayer();
spriteBatch.end();
if(debug){
drawDebug();
}
}
public void drawBlocks(){
for(Block block : world.getBlocks()){
spriteBatch.draw(blockTexture, block.getXPosition() * ppuX, block.getYPosition() * ppuY, Block.getSize() * ppuX, Block.getSize() * ppuY );
}
}
public void drawDebug() {
// render blocks
debugRenderer.setProjectionMatrix(cam.combined);
debugRenderer.begin(ShapeType.Line);
for (Block block : world.getBlocks()) {
Rectangle rect = block.getBounds();
float x1 = block.getXPosition() + rect.x;
float y1 = block.getYPosition() + rect.y;
debugRenderer.setColor(new Color(1, 0, 0, 1));
debugRenderer.rect(x1, y1, rect.width, rect.height);
}
// render Player
Player p = world.getPlayer();
Rectangle rect = p.getBounds();
float x1 = p.getXPosition() + rect.x;
float y1 = p.getYPosition() + rect.y;
debugRenderer.setColor(new Color(0, 1, 0, 1));
debugRenderer.rect(x1, y1, rect.width, rect.height);
debugRenderer.end();
}
}
public class Player {
public enum State{
IDLE, WALKING, JUMPING, DYING
}
public static final float SPEED = 4f; //units per second
public static final float SIZE = 0.5f; //half a unit
float stateTime = 0;
Vector2 position = new Vector2();
Vector2 acceleration = new Vector2();
Vector2 velocity = new Vector2();
Rectangle bounds = new Rectangle();
State state = State.IDLE;
boolean facingLeft;
boolean facingRight;
boolean facingUp;
boolean facingDown;
public Player(Vector2 position){
this.position = position;
this.bounds.height = SIZE;
this.bounds.width = SIZE;
}
public void setFacingLeft(boolean facingLeft) {
this.facingLeft = facingLeft;
}
public void setFacingRight(boolean facingRight) {
this.facingRight = facingRight;
}
public void setFacingUp(boolean facingUp) {
this.facingUp = facingUp;
}
public void setFacingDown(boolean facingDown) {
this.facingDown = facingDown;
}
public Vector2 getAcceleration() {
return acceleration;
}
public Vector2 getVelocity() {
return velocity;
}
public void setState(State newState){
this.state = newState;
}
public void update(float delta){
stateTime += delta;
position.add(velocity.cpy().scl(delta));
}
public Rectangle getBounds() {
return bounds;
}
public Object getPosition() {
return position;
}
public float getXPosition(){
return this.position.x;
}
public float getYPosition(){
return this.position.y;
}
public Object getState() {
return state;
}
public float getStateTime() {
return stateTime;
}
}
Sorry for all the code but I don't know where the problem is and didn't want to leave out anything relevant. I'm sure it's a silly mistake but I've been starring at it for a long time.
Also, I made an effort to put each class in it's own code block but it wasn't working out, I'm new to stackoverflow. Thanks to anyone who tries to help.