0

I'm developing a simple game which uses normal android views, not openGL or other apis, simply uses views and moves them on the scren. I have a game loop which calls to AsteroidManager.updateAsteroidsPositions() which iterates in all the screen asteroids calculating it's possitions.

After that, the thread, calls to a AsteroidManager.invalidateAsteroids() method using runOnUiThread() method, because in Android you need to manipulate views on the main thread. AsteroidManager.invalidateAsteroids() method simply iterates all the asteroids and set's x,y positions to the view and calls invalidate().

The problem is that I disscovered that it gives a much more smooth and faster behaviour if you put the logic of calculatePositions inside the onDraw method of the view. Doing that, the logic of calculating possitions is not being done in the game loop thread... its being done in the main UI thread!!

How is that possible? It is breaking all the game development logic... about doing the position calculations on Game Loop thread instead of other places like main thread or onDraws...

This the slower original code:

AsteroidManager class:

public void updateAsteroidsPositions(){
    for (int i = 0; i<onScreenAsteroids.size(); i++){
        onScreenAsteroids.get(i).updatePosition();
    }
}

public void invalidateAsteroids() {
    for (int i = 0; i<onScreenAsteroids.size(); i++){
        onScreenAsteroids.get(i).invalidate();
    }
}

Asteroid Class:

public void updatePosition(){
    currentScale = (Float) scaleX.getAnimatedValue();
    factor = currentScale/MAX_SCALE;

    //adding a minimum of factor, because with too low factor the movement is not realistic
    if (factor < 0.250f)
        factor = 0.250f;

    x = x-((float)GameState.getInstance().getJoyX()*factor);
    y = y-((float)GameState.getInstance().getJoyY()*factor);
}

public void invalidate(){
    view.setX(x);
    view.setY(y);
    view.invalidate();
}

this is the trick done in Asteroid class which does the behaviour of the game smooth and faster:

Asteroid Class:

public Asteroid(Bitmap bitmap, Context context) {
    view = new ImageView(context){
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            currentScale = (Float) scaleX.getAnimatedValue();
            factor = currentScale/MAX_SCALE;

            //adding a minimum of factor, because with too low factor the movement is not realistic
            if (factor < 0.250f)
                factor = 0.250f;

            x = x-((float)GameState.getInstance().getJoyX()*factor);
            y = y-((float)GameState.getInstance().getJoyY()*factor);

            view.setX(x);
            view.setY(y);
        }
    };
    view.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
    view.setImageBitmap(bitmap);
}

public void updatePosition(){
}

public void invalidate(){
    view.invalidate();
}
NullPointerException
  • 36,107
  • 79
  • 222
  • 382

2 Answers2

1

If you have too many items in onScreenAsteroids list it takes some time to execute updatePosition() for each of them. Try to use single method for them:

public void updateAndInvalidateAsteroidsPositions(){
    for (int i = 0; i<onScreenAsteroids.size(); i++){
        onScreenAsteroids.get(i).updatePosition();
        onScreenAsteroids.get(i).invalidate();
    }
}
Dmytro Batyuk
  • 957
  • 8
  • 15
  • but update and invalidate supposedly must be sepparated, because you may need to skip frame drawings – NullPointerException Sep 04 '19 at 21:26
  • @NullPointerException In case positions are changed you need to request view to be invalidated, don't you? – Dmytro Batyuk Sep 04 '19 at 21:33
  • yes, but also, the slow and laggy behaviour occurs with only 8-10 asteroids on screen – NullPointerException Sep 05 '19 at 06:20
  • you could add debug prints and see how much time takes only updatePosition() method. if it takes a lot you might speedup updatePosition method to calculate new coordinates. For example: calculation takes 3 seconds -> you'll see updates ~ every 3 seconds. – Dmytro Batyuk Sep 05 '19 at 21:02
0

Not all games need game loop. Thread switching has its own cost.

Game Loop separates game state from rendering. Ideally the game loop has the responsibility to processes all the onscreen objects in the game and objects have the responsibility to draw itself in its place. This way we have central place to react to events(mouse click, user touch etc) and update view positions and views have the responsibility to draw themselves at updated position.

For eg consider that we have 10 moving asteroids on screen and we are updating them in onDraw(), now two of them collide, but asteroid1 does not know position of asteroid2, so how will they detect collision? By game logic the game loop knows position of all 10 asteroids, it can detect collision. If don't care about messy code, then collision can be detected in onDraw also. But consider following...

If two are colliding , then we need to check if some other asteroid is near by collision region, if so then how much impact? Mess increases linearly...

After collision we decide to show collision graphic effects. Mess increases exponentially....

Asteroids collided, game state = 'EXPLOSIONS IN THE SKY', user gets a call, game goes to background, game state is to be saved, but our asteroids are master of their own destiny, now we need to provide every asteroid's state to our activity and save it in onPause(). Its all mess now...

User returns after few hours, we can't welcome them directly with 'EXPLOSIONS IN THE SKY', need to rewind to the state where asteroids are about to collide and then show BANG-BANG... Mess goes ALL HELL BREAK LOOSE.....

Views are slaves and they should not be empowered.

Where to display view, its dimens? -> comes from outside.

What to draw in view? -> comes from outside/ can have little say here.

How to animate view? -> comes from outside.

Coming to your particular case, you are using both versions differently, in onDraw() case you are directly invalidating asteroid's (first one is drawn instantly) whereas in game loop case you are first computing all asteroid's position and then invalidating, I don't know how many asteroids you have but if they are significant number, then this coupled with thread switching costs, may trick you to believe onDraw() is faster.

Rishabh Dhawan
  • 519
  • 1
  • 3
  • 11