Thursday 4 April 2013

Basic Game tutorials: SpaceWar 3D


In the the the tutorials, I will show you how to make the 3D version of game spacewar. I presume you've done [ Introduce to game SpaceWar ]. In the 3D version of spacewar, we will replace the meteor (capital only move randomly) into the enemy that is the UFO aliens, They can move, change direction and firing toward the player. Most of the resources in the game we will by this time taken from the game Droid Invader 3D of libgdx, you see [here]. A spectacular explosion will take effect to describes the collisions of objects in the game.
Principles of Game scene, Collection item and object collection, asset and the Background in similar spacewar3D in the the The coal spacewar so we will not repeat more.

Game Optimization

With 3D Game, the optimization is extremely IMPORTANT because so much of the equipment, especially portable devices is limited the 3D graphics processing Capabilities. So if you want your game to be played on as many devices as possible, then you must optimize for the game.

If you use game engine to make the game, the positive is really you develop game very fast but together with that, if you have not used properly engine will result in a waste of memory and CPU load increases. Of course GDX Engine is no exception.
 Class DefaultObject3D of GDX Engine was built very Carefully. It allows you to render Model, support translate, rotate or scale the object in the three axis x, y, z and has full support for programmable shaders simulate light shining on the object. But Because it contains many features so i can not call this class is optimized for gaming. Therefore you should refrain from creating new objects from this class. Instead, with the large number of objects, such as bullets and enemies,

I recommend using CollectionItem andObjectCollection to optimize the game instead of just
using DefaultObject3D and add the object to the scene directly. , However, DefaultObject3D could be used easily to create the model of the player, because the player needs more features that really is supported in DefaultObject3D. On the other hand you usually only need a single player in the game. Thus wasting memory and CPU load is negligible.

Make Bullet and BulletCollection

purposes [ Game Optimization ], we're going to make bullet inherited from collection items and make BulletCollection inherit from the the object collection.
In Bullet, only Those attributes needed for each new separate bullet is declared. Resource for the bullet as Model or Shader'll Just be declared or called in BulletCollection to limit the waste of RAM.
Public class Bullet extends CollectionItem {

       public Sphere sphere;
       public final Vector3 position = new Vector3 ();

       public Bullet (Vector3 position, boolean isPlayerBullet) {
              this.position.set (position);
              this.isBulletOfPlayer = isPlayerBullet;
              sphere = new Sphere (position, 0.2f);
       }

sphere variable used to Simulate a sphere that cover globally the bullet in the 3D space. The sphere objects will help us check collisions in the 3D space by using the overlaps () method of the class. The technique here is the same as in the Rectangle class.
Next, declare class BulletCollection too simple:
public class BulletCollection extends Object3DCollection <Bullet> implements IService {
       public BulletCollection (IGame3DService services) {
       super.(services);

You see BulletCollection has overridden methods to render as renderGL1 () and renderGL2 (). In these methods, the entire bullets will be rendered on the screen:
@Override
       public void renderGL2(float gameTime)
       {
              //set color shader is active to render bullets
              shader.setActiveShader("color");
              //call begin() method here to reduce number of the method calling that lead to better performance
              shader.begin();
              //render all bullets if the bullet is not dead
              for(Bullet obj : getObjectCollection())
                     if(obj.isVisible())
                     {
                           if(obj.isBulletOfPlayer)
                                  shader.setUniformf("u_color", 1f, 1f, 0.5f, 1f);
                           else
                                  shader.setUniformf("u_color", 0.5f, 1f, 1f, 1f);
                           //set transformation matrix for shader
                           transform.set(camera.getCombined());
                           transform.translate(obj.position.x, obj.position.y, obj.position.z);
                           shader.setUniformMatrix("u_projView"transform);
                           Asset.bulletModel.render(shader.getShader());
                     }
              shader.end();
              //set textLight shader is active to render player and invaders later
              shader.setActiveShader("textLight");
       }
As you can see, to render all the bullets, you just call the begin () method of the shader only once. Limit number of call begin() method of shader will bring your game to a better performance.

Make the Enemy and EnemyCollection

to optimize game, the enemy can be built is similar bullet. But here i intend to show you how to use the DefaultObject3D class and DefaultObject3DCollection class, We will apply two class to build the Enemy and EnemyCollection class.
Example: public class Enemy extends DefaultObject3D {/ / ... }
Enemy, you can even completely customize the rendering stage model, simply you need to override method renderGL1 () for OpenGL ES 1.x or method renderGL2 () - OpenGL ES 2.0 for the class DefaultObject3D and get rid of unnecessary handling from parent class.
Example: Reduce render processing of Enemy overriden the process of rendering the DefaultObject3D.     

@Override
       public void renderGL2(float gameTime)
       {
              texture.bind();
              shader.begin();
              setLightParameters();
              normal.idt();
              normal.rotate(0, 1, 0, rotate.y);
              normal3.set(normal.toNormalMatrix());
              shader.setUniformMatrix("u_normal"normal3);
              transform.set(camera.getCombined());
              transform.translate(position.xposition.yposition.z);
              transform.rotate(0, 1, 0, rotate.y);
              shader.setUniformMatrix("u_projView"transform);
              model.render(shader.getShader());
              shader.end();
       }
objects DefaultObject3D also has its own collection class, that DefaultObject3DCollection. Simply after create new Default object 3D, you add the object to the instance of DefaultObject3DCollection instead add to scene directly. Of course then you still have to add the DefaultObject3DCollection to the game scene for make this collection can auto-update, auto-render ...
But in spacewar3D game, the Enemy will be added directly into Game scene, so you don’t see the class EnemyCollection.

Using Light Manager in GDX 3D game engine.

I can assure you that the light is always Indispensable when you develop 3D game. Because if there are no lights, then, do you distinguish a 3D space? You can not see the bright and dark regions on the object, the object is no drop shadow on the terrain or the object itself, the worse is the whole 3D space can only be a dark color. A 3D game no lights is not different from a 2D game.
When you use LightManager, you need ambientLight parameter, there is a small color value, is added to the final synthesis of color on each pixel in the Fragment Processor of the shader. If you have knowledge of [OpenGL ES shader language], you can see the file in light-tex-fs.glsl in the asset / data / shader folder ambientLight used to PREVENT your 3D Scene too dark Because somewhere on scene lights may not work.
Example: LightManager lightManager = new LightManager(new Vector3 (0.1f, 0.1f, 0.1f));
One problem is that how much light in a 3D space? There are a lot and how you can manage all of the lights, took out the light you need and remove the ones you do not need them? Meanwhile, the role of LightManager will be shown. You can create a new light, add light and LightManager and get light also from LightManager. LightManager is a 3D Services over it is always present in any 3D object. LightManager solve the problem of having a variety of types of lights in Scene. Light maybe Direction Light, PointLight, Spotlight ... but all lights always using the same parent class, BaseLight class, you can cast BaseLight to Special Light class you need with method get (key, Class) of the LightManager.
Example: cast a light to DirectionLight directlight = light.get("MainLight",DirectionLight.classes) ;// light is LightManager
Of course, To make get a light possible, you had to add the light before:
this.light.clear (); / / light is
   
when you get the light, you can use light to transmit values ​​for the shader ‘s light parameters by overriding method setLightParameters () or pass a default object ISetLight to the 3D object.
@Override
       public void setLightParameters() {
             
              shader.setUniformVector("u_light_ambient_color"light.ambientColor);
              shader.setUniformVector("u_light_color"directlight.getLightColor());
              shader.setUniformVector("u_light"directlight.getLightDirection());
       }
spacewar3D, both Player and Enemy needs to light, and set values ​​for lightParameters. So neat to program, we create class SpaceWarGameObject3D from the DefaultObject3D and override method setLightParameters () of DefaultObject3D in the class SpaceWarGameObject3D.
When Player spacewar3D the take damage, mainLight will turn red for 0.5 seconds. , This is very easily done by set the color in reference variable mainLight in the Player class. mainLight direct reference direction light in the game so it will influence the light that shine on the whole objects of the enemy and the player object.
However, handle light with shaders in game3D always is hard work for the GPU. Especially when the game has many different light sources. Thus let ’s reduce the amount of lights you must use at a minimum. Also you can throw away the light feature for the object is not so Important, for example the bullets are lighted unnecessary, you only need to render the model of the bullet up and Determine to give it a color. Shader is only supported in OpenGL ES 2.0, If the device does not support OpenGL version, to create light, you must use the method that OpenGL ES 1.x has built for you. You can not make the option by shader.

Use the ShaderManager Control different render process of objectes

Above the mentioned bullet I will render without lighting, mà Means the process of rendering of the bullet will be different from the Player or Enemy so bullets will use different shader to render. Sometime in the 3D games have usage of different shaders to render different object groups!
ShaderManager used to manage all the shader in your game. similar lightManager, you also add shader, get shader, set active a shader, set the value for shader parameters, call the method begin () of the shader ...
Example: Render Bullets in BulletCollection use a different shader rendering Player or Enemy
       @Override
       public void renderGL2(float gameTime)
       {
              //set color shader is active to render bullets
              //shader is instance of ShaderManager
              shader.setActiveShader("color");
              //call begin() method here to reduce number of the method calling that lead to better performance
              shader.begin();
              //render all bullets if the bullet is not dead
              for(Bullet obj : getObjectCollection())
                     if(obj.isVisible())
                     {
                           if(obj.isBulletOfPlayer)
                                  shader.setUniformf("u_color", 1f, 1f, 0.5f, 1f);
                           else
                                  shader.setUniformf("u_color", 0.5f, 1f, 1f, 1f);
                           //set transformation matrix for shader
                           transform.set(camera.getCombined());
                           transform.translate(obj.position.x, obj.position.y, obj.position.z);
                           shader.setUniformMatrix("u_projView"transform);
                           Asset.bulletModel.render(shader.getShader());
                     }
              shader.end();
              //set textLight shader is default active shader to render player and invaders later
              shader.setActiveShader("textLight");
       }

Here, to render all bullets, we have used shader "color", specified in method setActiveShader(). then you need to call begin () method of shader. You transmit values for shader parameters. then you render the model of bullet by method getShader () that will retrieve the current active shader. The last, you call method end() of the shader and set active shaders is "textLight”- It is default shader used when you render the player or enemy in the game scene.

Using CameraManager control many Cameras in the game

Like light, I can assure you that the camera is always Indispensable when you develop 3D game. Cameras like the "eyes" of the players, observed every object in your 3D space. Of course a 3D game will have at least one or more cameras. example of a role-playing game, you can have ThirdPersonShooter (TPS) Camera that follows Player, or FixedCamera to the observer a fixed point in the game ... Camera Manager will manage all in-game cameras. Camera is very necessary because there is a critical parameter when you render the 3D object, which is combined Matrix. This Matrix is combination of the Matrix View and Projection Matrix of a camera3D. Combined Matrix used as the genesis of the 3D transformation matrix of the object, and the shader using the transformation matrix to render objects:
For example:           //set combined matrix for the transformation matrix from CameraManager
                     transform.set(camera.getCombined ());
/ / Do more stuffs for transformation matrix like rotate, scale, ...
/ / set the transformation matrix for shader
shader. setUniformMatrix("u_projView", transform);

You can add the camera, get the camera and set the active camera using setActive...() method like the shader manager and light manager. Method getCombined () Camera Manager is get the Combined matrix of the active camera. You could refer source code of the engine for more information.
cameras need to be updated constantly, often a camera will be using the update () method by default, but these special cases, you will have to write using the update () method. example in spacewar3D , we use TPS Camera that follow player object, so we will need to update the TPS camera in the Player class
Example: activeCamera.Update (rotate.y, up, position, gameTime); / / activeCamera is TPS Camera
In BaseGameScene3D class, method update () method of the camera is not called, so you need to call update () to the camera in the sub-class of BaseGameScene3D to update camera.

Render an animated 2D texture on 3D

When there is a collision between objects in spacewar3D , usually there will be an explosion to illustrate the collision. During game2D spacewar, we use the particle effect, but the current particle effect can only be used for 2D games. Besides particle effects are consuming so much resources and make lower performance. Consequently we can use animated texture to represent an explosion occurred, as shown below:
 If you already have the basic concepts of 3D graphics programming , you will easily understand rendering techniques. 2D image here above has been split out and draw a square up from the pairs of vertices, is what has created in variable Mesh of game asset. 4 vertices will pair up to render a square contained texture from file explode.png
example: square Render:
Asset.explosionMesh.render(shader.getShader (), GL10.GL_TRIANGLE_FAN,
               (int)(obj.aliveTime / Explosion.EXPLOSION_LIVE_TIME * 15) * 4, 4);

The code to render explosions located in the ExplosionCollection.java file
Example: Render all explosion
      Gdx.gl.glEnable(GL10.GL_BLEND); //Enable Alpha blend to support transparent
       Gdx.gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
              Asset.explosionTexture.bind();
              shader.setActiveShader("text");
              shader.begin();
              shader.setUniformi("u_diffuse", 0);
              transform .set(camera.getCombined());
              for(Explosion obj : objectCollection)
                     if(obj.isVisible())
                     {
                          
                      transform.translate(obj.position.x, obj.position.y, obj.position.z);
                      shader.setUniformMatrix("u_projView"transform);
                      Asset.explosionMesh.render(shader.getShader(), GL10.GL_TRIANGLE_FAN,(int)(obj.aliveTime / Explosion.EXPLOSION_LIVE_TIME * 15) * 4, 4);
                     }
             
              shader.end();
              Gdx.gl.glDisable(GL10.GL_BLEND);
              shader.setActiveShader("textLight");
As you can see in this code using the "text" shader to render using texture. Unlike the "color" shader which used to render bullet, the “text” shader supports texture-based render while “color” shader only supports color-based render. After the render is done, you set active shaders is "textLight” shader - It is default shader which used when you render the player or enemy in the game scene.

No comments:

Post a Comment