Ärgerlich laggt/ruckelt in einem android Spiel

Ich begann mit der Spieleentwicklung in android, und ich bin auf ein super einfaches Spiel.

Das Spiel ist im Prinzip wie flappy bird.

Ich es geschafft, alles funktioniert, aber ich bekomme eine Menge stottert und hinkt.

Dem Handy bin ich mit für das testen LG G2, so sollte es und laufen tut Spiele viel schwerer und komplexer als dieser.

Grundsätzlich gibt es 4 "Hindernisse", die eine volle Bildschirm-Breite voneinander entfernt.

Wenn das Spiel beginnt, die Hindernisse beginnen sich zu bewegen (in Richtung der Zeichen) mit einer Konstanten Geschwindigkeit. Der Spieler-Charakter ist der x-Wert setzt sich durch das ganze Spiel, während der y-Wert ändert.

Die Verzögerung tritt auf, vor allem, wenn der Charakter geht durch ein Hindernis (und manchmal ein wenig nach, das Hindernis zu). Was passiert, ist, dass es ungleiche Verzögerungen in jeder Zeichnung der Spiel-Status verursacht stottert in den Bewegungen.

  • GC läuft nicht laut log.
  • Der stottert NICHT, verursacht durch die Geschwindigkeit nicht zu hoch werden (ich weiß, dass da am Anfang des Spiels, wenn die Hindernisse sind aus der Sicht der Charakter bewegt sich reibungslos)
  • Ich glaube nicht, dass das problem ist FPS Verwandte zu haben, denn auch wenn die MAX_FPS Feld auf 100 eingestellt ist, gibt es immer noch stottert.

Mein Gedanke ist, dass es eine Zeile oder mehrere Zeilen code, die dazu führen, dass einige ein bisschen Verzögerung passieren (und somit frames übersprungen). Ich denke auch, dass diese Zeilen werden sollten, um die update() und draw() Methoden der PlayerCharacter, Obstacle, und MainGameBoard.

Das problem ist, ich bin noch neu in android Entwicklung und android Spiel Entwicklung speziell, so dass ich keine Ahnung habe, was könnte die Ursache dieser Verzögerung.

Ich habe versucht, online-Suche für Antworten... Leider alles, was ich fand, zeigte über die GC Verschulden. Jedoch, wie ich glaube nicht, dass es der Fall ist (korrigieren Sie mich wenn ich bin falsch) diejenigen, die Antworten gelten nicht für mich. Ich habe auch gelesen das android-developer ' s Performance Tips Seite, konnte aber nichts finden, das hat geholfen.

So, bitte helfen Sie mir, finden Sie die Antwort auf die Lösung dieser nervigen lags!

Code

MainThread.java:

public class MainThread extends Thread {

public static final String TAG = MainThread.class.getSimpleName();
private final static int    MAX_FPS = 60;   //desired fps
private final static int    MAX_FRAME_SKIPS = 5;    //maximum number of frames to be skipped
private final static int    FRAME_PERIOD = 1000 / MAX_FPS;  //the frame period

private boolean running;
public void setRunning(boolean running) {
    this.running = running;
}

private SurfaceHolder mSurfaceHolder;
private MainGameBoard mMainGameBoard;

public MainThread(SurfaceHolder surfaceHolder, MainGameBoard gameBoard) {
    super();
    mSurfaceHolder = surfaceHolder;
    mMainGameBoard = gameBoard;
}

@Override
public void run() {
    Canvas mCanvas;
    Log.d(TAG, "Starting game loop");

    long beginTime;     //the time when the cycle begun
    long timeDiff;      //the time it took for the cycle to execute
    int sleepTime;      //ms to sleep (<0 if we're behind)
    int framesSkipped;  //number of frames being skipped 

    sleepTime = 0;

    while(running) {
        mCanvas = null;
        try {
            mCanvas = this.mSurfaceHolder.lockCanvas();
            synchronized (mSurfaceHolder) {
                beginTime = System.currentTimeMillis();
                framesSkipped = 0;


                this.mMainGameBoard.update();

                this.mMainGameBoard.render(mCanvas);

                timeDiff = System.currentTimeMillis() - beginTime;

                sleepTime = (int) (FRAME_PERIOD - timeDiff);

                if(sleepTime > 0) {
                    try {
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {}
                }

                while(sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
                    //catch up - update w/o render
                    this.mMainGameBoard.update();
                    sleepTime += FRAME_PERIOD;
                    framesSkipped++;
                }
            }
        } finally {
            if(mCanvas != null)
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
        }
    }
}
}

MainGameBoard.java:

public class MainGameBoard extends SurfaceView implements
    SurfaceHolder.Callback {

private MainThread mThread;
private PlayerCharacter mPlayer;
private Obstacle[] mObstacleArray = new Obstacle[4];
public static final String TAG = MainGameBoard.class.getSimpleName();
private long width, height;
private boolean gameStartedFlag = false, gameOver = false, update = true;
private Paint textPaint = new Paint();
private int scoreCount = 0;
private Obstacle collidedObs;

public MainGameBoard(Context context) {
    super(context);
    getHolder().addCallback(this);

    DisplayMetrics displaymetrics = new DisplayMetrics();
    ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
    height = displaymetrics.heightPixels;
    width = displaymetrics.widthPixels;

    mPlayer = new PlayerCharacter(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher), width/2, height/2);

    for (int i = 1; i <= 4; i++) {
        mObstacleArray[i-1] = new Obstacle(width*(i+1) - 200, height, i);
    }

    mThread = new MainThread(getHolder(), this);

    setFocusable(true);
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    mThread.setRunning(true);
    mThread.start();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    Log.d(TAG, "Surface is being destroyed");
    //tell the thread to shut down and wait for it to finish
    //this is a clean shutdown
    boolean retry = true;
    while (retry) {
        try {
            mThread.join();
            retry = false;
        } catch (InterruptedException e) {
            //try again shutting down the thread
        }
    }
    Log.d(TAG, "Thread was shut down cleanly");
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    if(event.getAction() == MotionEvent.ACTION_DOWN) {
        if(update && !gameOver) {
            if(gameStartedFlag) {
                mPlayer.cancelJump();
                mPlayer.setJumping(true);
            }

            if(!gameStartedFlag)
                gameStartedFlag = true;
        }
    } 


    return true;
}

@SuppressLint("WrongCall")
public void render(Canvas canvas) {
    onDraw(canvas);
}

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawColor(Color.GRAY);
    mPlayer.draw(canvas);

    for (Obstacle obs : mObstacleArray) {
        obs.draw(canvas);
    }

    if(gameStartedFlag) {
        textPaint.reset();
        textPaint.setColor(Color.WHITE);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setTextSize(100);
        canvas.drawText(String.valueOf(scoreCount), width/2, 400, textPaint);
    }

    if(!gameStartedFlag && !gameOver) {
        textPaint.reset();
        textPaint.setColor(Color.WHITE);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setTextSize(72);
        canvas.drawText("Tap to start", width/2, 200, textPaint);
    }

    if(gameOver) {      
        textPaint.reset();
        textPaint.setColor(Color.WHITE);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setTextSize(86);

        canvas.drawText("GAME OVER", width/2, 200, textPaint);
    }

}

public void update() {
    if(gameStartedFlag && !gameOver) {  
        for (Obstacle obs : mObstacleArray) {
            if(update) {
                if(obs.isColidingWith(mPlayer)) {
                    collidedObs = obs;
                    update = false;
                    gameOver = true;
                    return;
                } else {
                    obs.update(width);
                    if(obs.isScore(mPlayer))
                        scoreCount++;
                }
            }
        }

        if(!mPlayer.update() || !update)
            gameOver = true;
    }
}

}

PlayerCharacter.java:

public void draw(Canvas canvas) {
    canvas.drawBitmap(mBitmap, (float) x - (mBitmap.getWidth() / 2), (float) y - (mBitmap.getHeight() / 2), null);
}

public boolean update() {
    if(jumping) {
        y -= jumpSpeed;
        jumpSpeed -= startJumpSpd/20f;

        jumpTick--;
    } else if(!jumping) {
        if(getBottomY() >= startY*2)
            return false;

        y += speed;
        speed += startSpd/25f;
    }

    if(jumpTick == 0) {
        jumping = false;
        cancelJump(); //rename
    }

    return true;
}

public void cancelJump() { //also called when the user touches the screen in order to stop a jump and start a new jump
    jumpTick = 20;

    speed = Math.abs(jumpSpeed);
    jumpSpeed = 20f;
}

Obstacle.java:

public void draw(Canvas canvas) {
    Paint pnt = new Paint();
    pnt.setColor(Color.CYAN);
    canvas.drawRect(x, 0, x+200, ySpaceStart, pnt);
    canvas.drawRect(x, ySpaceStart+500, x+200, y, pnt);
    pnt.setColor(Color.RED);
    canvas.drawCircle(x, y, 20f, pnt);
}

public void update(long width) {
    x -= speed;

    if(x+200 <= 0) {
        x = ((startX+200)/(index+1))*4 - 200;
        ySpaceStart = r.nextInt((int) (y-750-250+1)) + 250;
        scoreGiven = false;
    }
}

public boolean isColidingWith(PlayerCharacter mPlayer) {
    if(mPlayer.getRightX() >= x && mPlayer.getLeftX() <= x+20)
        if(mPlayer.getTopY() <= ySpaceStart || mPlayer.getBottomY() >= ySpaceStart+500)
            return true;

    return false;
}

public boolean isScore(PlayerCharacter mPlayer) {
    if(mPlayer.getRightX() >= x+100 && !scoreGiven) {
        scoreGiven = true;
        return true;
    }

    return false;
}
  • Profil, das beispielsweise mit DDMS-Methode-profiling: software.intel.com/en-us/articles/... oder anderen Techniken. Weder das Protokoll noch einige magic MAX FPS (und max bedeutet nicht, dass es immer so laufen, dass schnell, es ist nur, wo es caps) wird Ihnen sagen, ein richtigen Grund.
  • Fehlende Konstruktoren in Ihrem Obstacle.java und PlayerCharacter.java Klassen
  • Sicherlich sollten Sie nicht verwenden synchronize Führung und Aufbewahrung der Leinwand gesperrt, während der Haupt-thread schlafen. Diese verlangt für alle Arten von Schwierigkeiten. Die animation Schleife macht nicht viel Sinn.
InformationsquelleAutor Asaf | 2014-02-17
Schreibe einen Kommentar