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
Related
I currently have the class for my main character "Student", it has no behaviors except the left and right movements. I managed to make it so all the frames of my spritesheet will render, so I need help in drawing the first 3 frames (which are the walk cycle) when I press LEFT / RIGHT keys.
Here's the spritesheet: http://imgur.com/a/HHdm9
Edit: AND 2nd and 3rd rows when pressing UP and Down Keys.
Student Class
public class Student {
private Texture player;
private Animation<TextureRegion> playerAnimation;
private int pX;
private int pY;
private SpriteBatch batch;
private int HP;
public Student(float speed, int x, int y){
player = new Texture("student.png");
TextureRegion[][] tmp= TextureRegion.split(player,450/3,450/3);
TextureRegion[] character = new TextureRegion[9];
int index = 0;
for(int i=0; i <3; i++){
for(int j=0; j < 3; j++){
character[index++] = tmp[i][j];}
}
playerAnimation = new Animation<TextureRegion>(speed,character);
pX=x;
pY=y;
}
public SpriteBatch getBatch() {
return batch;
}
public void setBatch(SpriteBatch batch) {
this.batch = batch;
}
public void goLeft(){
pX-=5;
if(pX < -10){
pX = -10;
}
}
public void goRight(){
pX+=5;
}
public void renderStudent(float stateTime){
TextureRegion currentFrame = playerAnimation.getKeyFrame(stateTime,true);
batch.draw(currentFrame, (float) pX, (float) pY, 0, 0, currentFrame.getRegionWidth(),
currentFrame.getRegionHeight(),1.5f,1.5f,0);
}
public void dispose(){
player.dispose();
}
}
Main Class
public class HaxMain extends ApplicationAdapter {
SpriteBatch batch;
Texture bg;
float stateTime;
private Student student1;
private Guard mguard, fguard;
private Admin madmin, fadmin;
#Override
public void create () {
batch = new SpriteBatch();
bg = new Texture("Level1.png");
student1 = new Student(.5f, 100, 0);
student1.setBatch(batch);
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stateTime += Gdx.graphics.getDeltaTime();
if(Gdx.input.isKeyPressed(Input.Keys.LEFT)){
student1.goLeft();
//Code to render image to the left
}else if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)){
//Code to render image to the right
student1.goRight();
}
batch.begin(); /*default code*/
batch.draw(bg, 0, 0);
student1.renderStudent(stateTime);
batch.end();
}
#Override
public void dispose () {
batch.dispose();
bg.dispose();
student1.dispose();
}
}
You need to create Animation only for Running and use that animation to show your player is running left or right. You can flip TextureRegion according to direction of your player like this :
TextureRegion[] character;
boolean isRight;
public Student(float speed, int x, int y){
player = new Texture("student.png");
TextureRegion[][] tmp= TextureRegion.split(player,450/3,450/3);
character = new TextureRegion[9];
int index = 0;
for(int i=0; i <3; i++){
for(int j=0; j < 3; j++){
character[index++] = tmp[i][j];}
}
TextureRegion runningFrames[]=new TextureRegion[3];
for (int i=0;i<runningFrames.length;i++)
runningFrames[i]= character[i];
playerAnimation = new Animation<TextureRegion>(speed, runningFrames);
playerAnimation.setPlayMode(Animation.PlayMode.LOOP);
pX=x;
pY=y;
}
public void goLeft(){
pX-=5;
if(pX < -10){
pX = -10;
}
if(isRight){
isRight=false;
for (TextureRegion textureRegion:playerAnimation.getKeyFrames())
if(textureRegion.isFlipX()) textureRegion.flip(true,false);
}
}
public void goRight(){
pX+=5;
if(!isRight){
isRight=true;
for (TextureRegion textureRegion: playerAnimation.getKeyFrames())
if(!textureRegion.isFlipX()) textureRegion.flip(true,false);
}
}
Create another animation of other action and draw frame of animation using flag.
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 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.
I was trying to animate a sprite for an android app and i've encounter a small problem. I'm drawing on a surfaceView that i add on top of already existing layouts. On this surfaceView, i wanted to animate few sprites so they would walk along a path.
So this is the result i'm facing right now :
The walking sprite is leaving a trail. So i decided to google this problem and apparently i had to clear the canvas first before drawing on it.
This was my onDraw method before :
public void onDraw(Canvas canvas) {
update();
int srcX = currentFrame * width;
int srcY = directionX * height;
Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
Rect dst = new Rect(x, y, x + width, y + height);
canvas.drawBitmap(bmp, src, dst, null);
}
And this was my onDraw method after
public void onDraw(Canvas canvas) {
canvas.drawRGB(0, 0, 0);
update();
int srcX = currentFrame * width;
int srcY = directionX * height;
Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
Rect dst = new Rect(x, y, x + width, y + height);
canvas.drawBitmap(bmp, src, dst, null);
}
So i now have this as a result
which is great because it leaves no more trail BUT i cant see the layouts underneath. Is there anyway i can get the best of both world ? I thought clearing the canvas would work but it obviously doesnt not work as expected.
I'll post my code below
SurfaceView :
public class MonsterView extends SurfaceView {
private Bitmap monsterImg;
private SurfaceHolder holder;
private MonsterThread mainThread;
private Sprite monsterSprite;
private int x;
private int xSpeed = 1;
public MonsterView(Context context) {
super(context);
this.mainThread = new MonsterThread(this);
this.x = 0;
holder = getHolder();
holder.setFormat(PixelFormat.TRANSPARENT);
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
mainThread.setRunning(true);
mainThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
mainThread.setRunning(false);
while (retry) {
try {
mainThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
});
setZOrderOnTop(true);
monsterImg = BitmapFactory.decodeResource(getResources(), R.drawable.fire);
monsterSprite = new Sprite(this,monsterImg);
}
#Override
public void onDraw(Canvas canvas) {
if (x == getWidth() - monsterImg.getWidth()) {
xSpeed = -1;
}
if (x == 0) {
xSpeed = 1;
}
x = x + xSpeed;
monsterSprite.onDraw(canvas);
}
Thread :
public class MonsterThread extends Thread {
private MonsterView monsterSurface;
private boolean running;
static final long FPS = 35;
public MonsterThread(MonsterView monsterSurface){
this.monsterSurface = monsterSurface;
this.running = false;
}
#Override
public void run() {
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while(running){
Canvas c = null;
startTime = System.currentTimeMillis();
try{
c = monsterSurface.getHolder().lockCanvas();
synchronized (monsterSurface.getHolder()){
monsterSurface.onDraw(c);
}
} finally {
if( c!= null){
monsterSurface.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if(sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e){}
}
}
public MonsterView getMonsterSurface() {
return monsterSurface;
}
public void setMonsterSurface(MonsterView monsterSurface) {
this.monsterSurface = monsterSurface;
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean running) {
this.running = running;
}
Sprite :
public class Sprite {
private static final int BMP_ROWS = 4;
private static final int BMP_COLUMNS = 3;
private int x = 0;
private int y = 0;
private int xSpeed = 5;
private MonsterView monsterView;
private Bitmap bmp;
private int currentFrame = 0;
private int width;
private int height;
private int directionX;
public Sprite(MonsterView monsterView, Bitmap bmp) {
this.monsterView = monsterView;
this.bmp=bmp;
this.width = bmp.getWidth() / BMP_COLUMNS;
this.height = bmp.getHeight() / BMP_ROWS;
this.directionX = 2;
}
private void update() {
if (x > monsterView.getWidth() - width - xSpeed) {
xSpeed = -5;
directionX = 1;
}
if (x + xSpeed < 0) {
xSpeed = 5;
directionX = 2;
}
x = x + xSpeed;
currentFrame = ++currentFrame % BMP_COLUMNS;
Log.d("test", ""+currentFrame);
}
public void onDraw(Canvas canvas) {
update();
int srcX = currentFrame * width;
int srcY = directionX * height;
Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
Rect dst = new Rect(x, y, x + width, y + height);
canvas.drawBitmap(bmp, src, dst, null);
}
You're using setZOrderOnTop(), which is putting the Surface part of the SurfaceView on a layer above everything else. (By default, it's a separate layer below everything else.) When you clear it with canvas.drawRGB(0, 0, 0) you're setting the entire layer to opaque black, which is going to obscure the View layer beneath it.
If instead you clear it to transparent black, with canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR), you should get the results you want.
FWIW, you shouldn't override onDraw() if you're only drawing on the Surface. The onDraw() method is called by the View hierarchy to draw on the View part of the SurfaceView. If something manages to invalidate the SurfaceView's View, your onDraw() will be called, and you'll end up with a character sprite on the View layer. (Depending on your layout this may not be visible.) Just give the method a different name.
There are a number of ways to handle this, but generally, the way to do this is by maintaining a background layer (which is just another image), and sprite layers. Instead of clearing, at each frame you blit (copy) your background onto the surface (which erases everything), then you blit your sprites.
So you're going to have to draw the background bitmap onto the surface first before drawing your sprite. Right now you're drawing black which is covering your background layouts.
Alternatively you might be able to do canvas.drawRect with a Paint with paint.setColor to Color.Transparent.
I really could use some help in order to find a working solution for my game.
My game is almost done, but the walls in my game are still not working as they should.
I have tried to find a solution on the internet for this problem, but i still haven't found a simple way to stop a rectangle just before it will collide with a wall (another rectangle).
Right now i have implemented a collision detection between the player rectangle and the wall rectangle and then stopped it to move, but then it gets stuck inside a wall when it hits.
Want it to stop just before, so it still can move. The code i have done this with so far is here:
Pacman Class
public class Pacman {
private String pacmanup = "pacmanup.png";
private String pacmandown = "pacmandown.png";
private String pacmanleft = "pacmanleft.png";
private String pacmanright = "pacmanright.png";
private int dx;
private int dy;
private int x;
private int y;
private int width;
private int height;
private boolean visible;
private Image imageup;
private Image imagedown;
private Image imageleft;
private Image imageright;
public Pacman() {
ImageIcon i1 = new ImageIcon(this.getClass().getResource(pacmanup));
imageup = i1.getImage();
ImageIcon i2 = new ImageIcon(this.getClass().getResource(pacmandown));
imagedown = i2.getImage();
ImageIcon i3 = new ImageIcon(this.getClass().getResource(pacmanleft));
imageleft = i3.getImage();
ImageIcon i4 = new ImageIcon(this.getClass().getResource(pacmanright));
imageright = i4.getImage();
width = imageup.getWidth(null);
height = imageup.getHeight(null);
visible = true;
x = 270;
y = 189;
}
public int getDx() {
return dx;
}
public void setDx(int dx) {
this.dx = dx;
}
public int getDy() {
return dy;
}
public void setDy(int dy) {
this.dy = dy;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public Image getImageup() {
return imageup;
}
public Image getImagedown() {
return imagedown;
}
public Image getImageleft() {
return imageleft;
}
public Image getImageright() {
return imageright;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public boolean isVisible() {
return visible;
}
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
public void move() {
x += dx;
y += dy;
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
dx = -2;
dy = 0;
}
if (key == KeyEvent.VK_RIGHT) {
dx = 2;
dy = 0;
}
if (key == KeyEvent.VK_UP) {
dx = 0;
dy = -2;
}
if (key == KeyEvent.VK_DOWN) {
dx = 0;
dy = 2;
}
}
Here i have created a Rectangle getBounds method which i use to create an rectangle of the pacman and place an image over it.
Barrier class / Wall class
public class Barrier {
private String barrier = "barrier.png";
private int x;
private int y;
private int width;
private int height;
private boolean visible;
private Image image;
public Barrier(int x, int y) {
ImageIcon ii = new ImageIcon(this.getClass().getResource(barrier));
image = ii.getImage();
width = image.getWidth(null);
height = image.getHeight(null);
visible = true;
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public boolean isVisible() {
return visible;
}
public void setVisible(Boolean visible) {
this.visible = visible;
}
public Image getImage() {
return image;
}
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
This class also have the Rectangle getBounds class which i use to detect collision.
The last code i show is how i do the collision detection so far:
Code inside Board class
Rectangle r3 = pacman.getBounds();
for (int j = 0; j<barriers.size(); j++) {
Barrier b = (Barrier) barriers.get(j);
Rectangle r4 = b.getBounds();
if (r3.intersects(r4)) {
System.out.println("Wall hit");
pacman.setDx(0);
pacman.setDy(0);
}
}
Well, what i do, if there is a collision between r3 and r4, i gonna set Dx and Dy to 0.. what i want to find another solution so it detect for collision but i wont get stuck inside a wall, but i don't know how to :/
Hope someone will help.
There are two approaches you can follow. One is ugly but easier, the other one requires a deeper redesign of your classes.
1) Ugly/Simple Approach
In the ugly one, you keep moving your guy before doing the collision checks. Simply move your pacman back to the point where it was not stuck. You accomplish that by inverting the last directions used:
Code inside Board class
Change your reaction in case you find a collision: just walk the same distance, backwards.
if (r3.intersects(r4)) {
System.out.println("Wall hit, move back");
pacman.setDx(-pacman.getDx());
pacman.setDy(-pacman.getDy());
// Possibly need to call move() here again.
pacman.move();
break;
}
Ugly, but should work.
Not recommended to coding perfectionists with OCD and heart disease, though.
2) Redesign
In this approach, you test the position pacman will occupy before doing any actual moves. If that spot is not into any barrier, then perform the movement for real.
Code inside Pacman class
Add this method, so that you can check for collisions against the new bounds.
public Rectangle getOffsetBounds() {
return new Rectangle(x + dx, y + dy, width, height);
}
Code inside Board class
// Strip the call to pacman.move() prior to this point.
Rectangle r3 = pacman.getOffsetBounds(); // Check against the candidate position.
for (int j = 0; j<barriers.size(); j++) {
Barrier b = (Barrier) barriers.get(j);
Rectangle r4 = b.getBounds();
if (r3.intersects(r4)) {
System.out.println("Wall hit");
pacman.setDx(0);
pacman.setDy(0);
// Quit the loop. It's pointless to check other barriers once we hit one.
break;
}
}
// Now we're good to move only in case there's no barrier on our way.
pacman.move();
Particularly I prefer this approach, but it's up to you to pick the best one.