Android游戏编程 - onDraw()口吃

时间:2016-06-24 14:52:00

标签: java android android-canvas surfaceview game-physics

我正在创建一个动画,基本上一个球在屏幕上移动。但是,我遇到了口吃,使游戏不那么可玩。请看一下我的代码,非常欢迎任何指向正确的方向!

GameActivity.java - >启动游戏循环,并处理更新。

Thread gameThread = new Thread(this);

public boolean running = true;

private final static int MAX_FPS = 60;
private final static int MAX_FRAME_SKIPS = 5;
private final static int FRAME_PERIOD = 1000000000/MAX_FPS;

private final static int FPS_COUNTER_REPORTTIME = 1000000000;
long lastFPSReport;
int UPS; //updates per second
public static int DPS; //draws per second

public SurfaceView gameView;

public void init() {
    WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();

    Point screenDim = new Point();
    display.getRealSize(screenDim);

    width = screenDim.x;
    height = screenDim.y;

    initGame();

    gameThread.start();
}

public void initGame() {
    fingerX = (width / 2);
    fingerY = height * 0.90f;

    GamePlatform platform = new GamePlatform(fingerX, fingerY, width * 0.3f, height * 0.025f, 0, 0);
    GameBall ball = new GameBall(fingerX - 50, height * 0.8f - 50, 100, 100, 20, 20);
}

@Override
public void run() {
    Canvas canvas = null;

    Log.d("testlog", "Starting game loop");

    lastFPSReport = System.nanoTime();

    while(running) {
        beginTime = System.nanoTime();
        framesSkipped = 0;

        try{
            canvas = gameView.getHolder().lockCanvas();

            update();

            synchronized (gameView.getHolder()) {
                gameView.postInvalidate();
            }

            timeDiff = System.nanoTime() - beginTime;
            sleeptime = (int)(FRAME_PERIOD - timeDiff);

            long sleeptimeMillis = (long) (Math.floor(sleeptime / 1000000));
            int sleeptimeNanos = (int)(sleeptime - (sleeptimeMillis * 1000000));

            if(sleeptime > 0) {
                try{
                    Thread.sleep(sleeptimeMillis, sleeptimeNanos);
                }catch(InterruptedException e) {}
            }

            while (sleeptime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
                //only update

                update();

                sleeptime += FRAME_PERIOD;
                framesSkipped++;
            }
        }finally {
            if(canvas != null) {
                gameView.getHolder().unlockCanvasAndPost(canvas);
            }
        }
    }
}

public void update() {
    UPS++;

    //FPS counter
    if(System.nanoTime() - lastFPSReport > FPS_COUNTER_REPORTTIME) {
        Log.d("testlog", "UPS: " + UPS + " | DPS: " + DPS);
        lastFPSReport = System.nanoTime();
        UPS = 0;
        DPS = 0;
    }

    //Entities
    GameEntity.updateAllEntities();
}

GameBall.java - &gt;处理更新和绘制球,扩展GameEntity.java(见下文)

public class GameBall extends GameEntity {

public GameBall(float x, float y, float width, float height, float dx, float dy) {
    super(x, y, width, height, dx, dy, "ballMain");
}

@Override
public void update() {
    ArrayList<GameEntity> entityList = getEntityList();

    //Collision

    for(int i = 0; i < entityList.size(); i++) {
        GameEntity entity = entityList.get(i);

        if(entity.getType() != "ballMain") {
            if(this.getX() < entity.getX() + entity.getWidth() && this.getX() + this.getWidth() > entity.getX() && this.getY() < entity.getY() + entity.getHeight() && this.getY() + this.getHeight() > entity.getY()) {
                if(Math.abs(this.getDx()) < Math.abs(entity.getDx())) this.setDx(entity.getDx());
                if(Math.abs(this.getDy()) < Math.abs(entity.getDy())) this.setDy(entity.getDy());

                if(this.getDx() > 20) entity.setDx(20);
                else if(this.getDx() < -20) entity.setDx(-20);
                if(this.getDy() > 20) entity.setDy(20);
                else if(this.getDy() < -20) entity.setDy(-20);
            }
        }
    }

    this.setX(this.getX() + this.getDx());
    this.setY(this.getY() + this.getDy());

    //walls

    if(this.getX() + this.getWidth() >= GameActivity.width || this.getX() <= 0) {
        this.setDx(-this.getDx());
    }

    if(this.getY() <= 0) {
        this.setDy(-this.getDy());
    }else if(this.getY() + this.getHeight() >= GameActivity.height) {
        Log.d("testlog", "game over");
        this.setDy(-this.getDy());
    }
}

@Override
public void draw(Canvas canvas) {
    Paint paint = new Paint();
    paint.setColor(Color.BLUE);

    canvas.drawOval(new RectF(this.getX(), this.getY(), this.getX() +      this.getWidth(), this.getY() + this.getHeight()), paint);
}
}

GameEntity.java:

public abstract class GameEntity {

float x = 0, y = 0, width = 0, height = 0;
float dx = 0, dy = 0;
String type;

static ArrayList<GameEntity> entityList = new ArrayList();

public GameEntity(float x, float y, float width, float height, float dx, float dy, String type) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.dx = dx;
    this.dy = dy;
    this.type = type;

    entityList.add(this);
}

public static void updateAllEntities() {
    for(int i = 0; i < entityList.size(); i++) {
        entityList.get(i).update();
    }
}

public void update() {
    this.x += this.dx;
    this.y += this.dy;
}

public static void drawAllEntities(Canvas canvas) {
    for(int i = 0; i < entityList.size(); i++) {
        entityList.get(i).draw(canvas);
    }
}

public abstract void draw(Canvas canvas);

//Getters and Setters

public ArrayList<GameEntity> getEntityList() {
    return entityList;
}

public float getX() {
    return this.x;
}

public void setX(float x) {
    this.x = x;
}

public float getY() {
    return this.y;
}

public void setY(float y) {
    this.y = y;
}

public float getWidth() {
    return this.width;
}

public void setWidth(float width) {
    this.width = width;
}

public float getHeight() {
    return this.height;
}

public void setHeight(float height) {
    this.height = height;
}

public Point getAbsCenter() {
    return new Point((int)(this.x + (this.width / 2)), (int)(this.y + (height / 2)));
}

public Point getSurfaceCenter() {
    return new Point((int)(this.x + (this.width / 2)), (int)(this.y));
}

public float getDx() {
    return this.dx;
}

public void setDx(float speed) {
    this.dx = speed;
}

public float getDy() {
    return this.dy;
}

public void setDy(float speed) {
    this.dy = speed;
}

public String getType() {
    return this.type;
}

public void setType(String type) {
    this.type = type;
}
}

FPS计数器日志示例:

06-24 16:40:43.304 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 51 | DPS: 46
06-24 16:40:44.308 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 55 | DPS: 55
06-24 16:40:44.786 30689-30793/com.zenodhaene.keepitup D/testlog: game over
06-24 16:40:45.324 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 49 | DPS: 49
06-24 16:40:46.330 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 53 | DPS: 53
06-24 16:40:46.566 30689-30793/com.zenodhaene.keepitup D/testlog: game over
06-24 16:40:47.345 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 54 | DPS: 54
06-24 16:40:48.344 30689-30793/com.zenodhaene.keepitup D/testlog: game over
06-24 16:40:48.363 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 56 | DPS: 56
06-24 16:40:49.490 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 50 | DPS:  19
06-24 16:40:50.213 30689-30793/com.zenodhaene.keepitup D/testlog: game over
06-24 16:40:50.514 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 60 | DPS: 0
06-24 16:40:51.516 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 63 | DPS: 1
06-24 16:40:51.855 30689-30793/com.zenodhaene.keepitup D/testlog: game over
06-24 16:40:52.522 30689-30793/com.zenodhaene.keepitup D/testlog: UPS: 62 | DPS: 44

2 个答案:

答案 0 :(得分:1)

在视频游戏中使用的解决方案是将每个移动因子乘以最后一帧(也称为增量时间)之间的时间,您应该尝试去看看这个

答案 1 :(得分:1)

根据经验,您绝对应该避免在onDraw()中分配/取消分配内存。

您正在使用它来创建Paint对象。由于onDraw()每秒最多被调用60次,因此创建方法的性能受到巨大影响。只需将Paint对象移动到类的字段并初始化一次即可。这将为初学者提供一个很好的保存。

Yan绝对是关于更新的。使用某种插值将为您提供更流畅的动画。有一个很棒的article你可以看看优化你的游戏线程。