官术网_书友最值得收藏!

Good practices for game developers

In general, you should avoid premature optimization. This means, do not optimize your code unless you have a performance problem.

Nevertheless, in games, we have two methods (onUpdate and onDraw) for which the execution time is critical. So, we will be providing a few tips that should be enough to get performance under a reasonable threshold.

For the rest of the cases, your code will be probably good. If you find a performance problem, you should measure it carefully to find where the bottleneck is and only then optimize it. Most of the time, the problem is not where we think it is. Premature optimization can lead to a less readable code without significant improvement.

Object pools

The creation and destruction of objects is an expensive operation that should be limited. This is one area where a real-time game is a lot more sensitive than an app.

Every time you create an object, the garbage collector has a chance to be run. In the old versions of Android, it meant that everything stopped for 200ms. While it is no longer this bad, it may still be noticeable.

Note

We should avoid object creation as much as we can.

We want to avoid any expensive operation to be performed inside the onUpdate method—which must run as fast as it can—so we are going to take the creation and destruction of objects out of it.

The solution for this is a well-known software pattern called object pool.

Before we start the game, we will precreate the objects we are going to need and put them in a pool. The pool can be something as simple as a stack or list.

Instead of creating an object, we will pick one from the pool and initialize it. If the pool is empty, it means that we underestimated the number of objects. So as a lesser evil, a new instance of the object must be created.

Instead of destroying an object, we will put it back into the pool.

The fact that we have to return objects to the pool forces us to figure out when an object is no longer needed instead of just relying on the garbage collector to do that for us. While it requires a bit of effort, this mental exercise will improve the game performance and structure. If you have ever worked with C++, this should be easy-peasy for you.

We will use object pools for all the game objects in the code; this means enemies and bullets basically.

Avoiding enhanced loop syntax in lists

Related to the object creation, we should avoid the use of an enhanced loop syntax in the lists. While the for-each syntax is easier to read, it creates an iterator on-the-fly, which makes the execution slower and gives the garbage collector a chance to be run.

In the case of the onUpdate method of GameEngine, we could have written it using the for-each syntax like this:

public void onUpdate(long elapsedMillis) {    
  for (GameObject gameObject : mGameObjects) {
    gameObject.onUpdate(elapsedMillis, this);
  }
}

But this is significantly slower than using the standard for loop syntax. This is why it looks like this instead:

public void onUpdate(long elapsedMillis) {
  int numGameObjects = mGameObjects.size();
  for (int i=0; i<numGameObjects; i++) {
    mGameObjects.get(i).onUpdate(elapsedMillis, this);
  }
}

In the particular case of arrays, the enhanced syntax is as fast as the traditional one on the devices with the JIT (just-in-time) compiler—which should be the case for all devices nowadays—so there is no drawback in always using the default loop syntax instead of the enhanced one.

It is also important to use a variable for the size instead of requesting it for every iteration, which leads us to the next tip.

Precreating objects

Related to the inefficiency of creating objects inside the onUpdate loop, we should always precreate the objects we are going to use.

A good example of this practice is the Runnable objects that are created inside the GameObject to run onRemovedFromGameUiThread and onAddedToGameUiThread.

We could create them on-demand inside the game engine as a part of addGameObject and removeGameObject, but it will be much less efficient.

Accessing variables directly

As often as we can, we will use a direct variable access instead of using getters and setters. This is a good practice in general, since accessors are expensive and the compiler does not inline them.

In the case of games, it makes sense to extend this practice to variables of other classes. As we mentioned several times before, the execution time of onUpdate and onDraw is critical; a difference of just milliseconds counts. This is why, when variables from the game objects are accessed by other game objects, we make them public and work with them directly.

This is a bit counter-intuitive for Java developers, since we are used to encapsulating everything through getters and setters. In this case, efficiency is more important than encapsulation.

Being careful with floating points

In the case of doing calculations, integer operations are about twice as fast as float operations.

When integers are not enough, there is no real difference in speed between float and double. The only difference is in space, where doubles are twice as large.

Also, even for integers, some processors have hardware multiply, but lack hardware pide. In such cases, integer pision and modulus operations are performed in the software. All in all, this is a case where premature optimization can harm you.

Performance myths – avoid interfaces

On the older versions of Android, before the JIT compiler was introduced, accessing methods via an interface instead of the exact type was slightly more efficient. In these versions, it made sense to declare a variable of ArrayList instead of the generic List interface to access the class directly.

In the modern versions of Android, however, there is no difference between accessing a variable via an interface and doing it directly. So, for the sake of generality, we will be using the generic interface instead of the class, as seen inside the GameEngine:

private List<GameObject> mGameObjects = new ArrayList<GameObject>();
主站蜘蛛池模板: 渝北区| 江安县| 南岸区| 读书| 黎川县| 靖西县| 治多县| 孝义市| 柘荣县| 长沙市| 珠海市| 久治县| 武邑县| 周口市| 夏邑县| 桐乡市| 梅河口市| 岳普湖县| 盐池县| 高陵县| 县级市| 车险| 互助| 和林格尔县| 巴林左旗| 体育| 新乡市| 博客| 佛冈县| 昌图县| 衡阳县| 鄂托克前旗| 全州县| 承德县| 陆川县| 班戈县| 石首市| 旺苍县| 清河县| 大丰市| 浠水县|