After the previous two sections of analysis, the game's resource loading and initialization have been completed, and in addition to the main game interface, other interfaces have been drawn. Next we will implement the main game interface. First, let's talk about how the author defines objects in the game.
First, this is a 2D game. That is to say, all game objects are drawn in a plane (or within the screen size ), all objects should contain a position and a bounds. Therefore, the author defines a parent class named gameobject for all objects. The Code is as follows:
public class GameObject { public final Vector2 position; public final Rectangle bounds; public GameObject(float x, float y, float width, float height) { this.position = new Vector2(x,y); this.bounds = new Rectangle(x-width/2, y-height/2, width, height); }}
Position is vector2, which contains the X and Y coordinates and represents the position of an object. The bounds type is rectangle, which contains the X, Y, width, and height information, representing the range of the game object in the screen. Postion refers to the center point of the game object, and the X and Y coordinates in bounds refer to the starting point of the game element in the screen coordinate system, that is, the vertex in the lower left corner of the rectangle. The two are different. In addition to the position and bounds attributes, you also need to add the moving attributes for moving objects in the game. Therefore, the author defines a class inherited from the gameobject name dynamicgameobject. The Code is as follows:
public class DynamicGameObject extends GameObject { public final Vector2 velocity; public final Vector2 accel; public DynamicGameObject(float x, float y, float width, float height) { super(x, y, width, height); velocity = new Vector2(); accel = new Vector2(); }}
Among them, velocity represents speed, while accel represents acceleration, both of which are vector2 types, that is, motion information related to the X and Y axes. All objects in the game are inherited from two categories, and the features of different game objects are expanded. They are:
Bob, protagonist Castle, Castle coin, gold coin platform, platform spring, spring squirrel, air flying squirrel
The following describes the main game interface. When we click the play option on the menu interface, the game is officially started, and the gamescreen class is the implementation class of the main game interface. In this class, the state machine mode is used to implement the logic of the game. The state machine mode divides the game into specific States, and different States implement different logics and draw pictures. Simply put, some static variables are used to define the game status, and then different methods are executed by judging the current status in the method of drawing updates. For example, the following States are defined in the gamescreen class:
static final int GAME_READY = 0; static final int GAME_RUNNING = 1; static final int GAME_PAUSED = 2; static final int GAME_LEVEL_END = 3; static final int GAME_OVER = 4; int state;
We can see that these States are actually some static member variables of the int type, and their names actually define different states. The Int type state variable is also provided here, which indicates the current state. Like other screens, gamescreen first executes the present () method to draw the game interface, and then listens for touch screen events in the update () method. The difference is that different States must be processed in these two methods. Let's take a look at the present () method. The Code is as follows:
@Override public void present (float deltaTime) { GLCommon gl = Gdx.gl; gl.glClear(GL10.GL_COLOR_BUFFER_BIT); gl.glEnable(GL10.GL_TEXTURE_2D); renderer.render(); guiCam.update(); batcher.setProjectionMatrix(guiCam.combined); batcher.enableBlending(); batcher.begin(); switch (state) { case GAME_READY: presentReady(); break; case GAME_RUNNING: presentRunning(); break; case GAME_PAUSED: presentPaused(); break; case GAME_LEVEL_END: presentLevelEnd(); break; case GAME_OVER: presentGameOver(); break; } batcher.end();}
Here we can see how the state machine mode is used. It is very simple. swich and case are used to determine the current state and then different draw methods are executed. The Update () method is similar. The Code is as follows:
@Override public void update (float deltaTime) { if (deltaTime > 0.1f) deltaTime = 0.1f; switch (state) { case GAME_READY: updateReady(); break; case GAME_RUNNING: updateRunning(deltaTime); break; case GAME_PAUSED: updatePaused(); break; case GAME_LEVEL_END: updateLevelEnd(); break; case GAME_OVER: updateGameOver(); break; }}
In the update () method, you need to listen to touch screen events at different locations in different States, and then switch the status of different games by changing the state value. For example, in the game_ready state (that is, the initial state of the game), The updateready () method is executed. The code for this method is as follows:
private void updateReady () { if (Gdx.input.justTouched()) { state = GAME_RUNNING; }}
This method indicates that if a touch screen event occurs, the state is switched to the game_running state, and the game is officially started. Then the presentrunning () method corresponding to the game_running state will be executed in the same present () method. This is the application of the state machine mode, which will be widely used later. Next, let's take a look at other gamescreen member variables and constructor methods. The Code is as follows:
OrthographicCamera guiCam; Vector3 touchPoint; SpriteBatch batcher; World world; WorldListener worldListener; WorldRenderer renderer; Rectangle pauseBounds; Rectangle resumeBounds; Rectangle quitBounds; int lastScore; String scoreString; public GameScreen (Game game) { super(game); state = GAME_READY; guiCam = new OrthographicCamera(320, 480); guiCam.position.set(320 / 2, 480 / 2, 0); touchPoint = new Vector3(); batcher = new SpriteBatch(); worldListener = new WorldListener() { @Override public void jump () { Assets.playSound(Assets.jumpSound); } @Override public void highJump () { Assets.playSound(Assets.highJumpSound); } @Override public void hit () { Assets.playSound(Assets.hitSound); } @Override public void coin () { Assets.playSound(Assets.coinSound); } }; world = new World(worldListener); renderer = new WorldRenderer(batcher, world); pauseBounds = new Rectangle(320 - 64, 480 - 64, 64, 64); resumeBounds = new Rectangle(160 - 96, 240, 192, 36); quitBounds = new Rectangle(160 - 96, 240 - 36, 192, 36); lastScore = 0; scoreString = "SCORE: 0"; }
In addition to the conventional guict, touchpoint, batcher, and some bounds used to detect the touch screen area, there are three other types not available for the screen: World, worldrenderer, and worldlistener. The idea here is as follows: As gamescreen involves many objects and operations are complex, the author regards gamescreen as a GUI, only monitors game status and handles touch-screen events. All objects in the real game are placed in the world class, and the rendering of objects in the game is handled by the worldrender class. In addition, worldlistener acts as an interface for playing sound effects. That is to say, gamescreen is regarded as a logic framework, allowing the built-in world and worldrednder to handle the game implementation. Before further analyzing the world and worldrednder classes, we need to explain what is the difference between the worldrednder class and the gamescreen class using orthographiccamera.
For gamescreen, the code for defining orthographiccamera is as follows:
guiCam = new OrthographicCamera(320, 480); guiCam.position.set(320 / 2, 480 / 2, 0);
Apparently it specifies orthographiccamera of the same size as the screen and points the camera at the center of the screen. The code for defining orthographiccamera in the worldrender class is as follows:
static final float FRUSTUM_WIDTH = 10; static final float FRUSTUM_HEIGHT = 15; OrthographicCamera cam; public WorldRenderer (SpriteBatch batch, World world) { this.world = world; this.cam = new OrthographicCamera(FRUSTUM_WIDTH, FRUSTUM_HEIGHT); this.cam.position.set(FRUSTUM_WIDTH / 2, FRUSTUM_HEIGHT / 2, 0); this.batch = batch; }
Orthographiccamera is defined as 10 in width and 15 in height. The camera is also aligned with the center point. Two different orthographiccamera are defined here because, no matter which orthographiccamera is used, it binds the ing matrix to spritbatch and tells spritbatch how to draw a graph. The following code:
guiCam.update(); batcher.setProjectionMatrix(guiCam.combined); batcher.begin(); batcher.draw(...) batcher.end();
In gamescreen, you only need to draw a screen in a plane of the screen size. In this example, it is also a plane of 320*480. For example, when the game starts, it will enter the game_ready State. At this time, the ready English letter will be drawn in the middle of the screen, while the game continues, the score will be drawn above the screen. As shown in the previous analysis, different statuses will be displayed on the screen, as shown below:
These characters are drawn by spritebatch, which has been bound to the guict ing matrix in the present () method of gamescreen. The key code is as follows:
@ Override public void present (float deltatime) {glcommon GL = GDX. GL; GL. glclear (gl10.gl _ color_buffer_bit); GL. glable (gl10.gl _ texture_2d); Renderer. render (); guict. update (); batcher. setprojectionmatrix (guict. combined); batcher. enableblending (); batcher. begin (); Switch (state) {Case game_ready: presentready (); break; Case game_running: presentrunning (); break; Case game_paused: presentpaused (); break; case when: presentlevelend (); break; Case game_over: presentgameover (); break;} batcher. end ();} private void presentready () {batcher. draw (assets. ready, 160-192/2, 240-32/2, 192, 32); // draw ready characters in the middle of the screen}
The definition of orthographiccamera in worldrenderer must first talk about how the game is played. In the game, our protagonist Bob keeps jumping, but his highest point never exceeded the center of the screen, when he stays at the highest point, he will move all the objects to make him seem to jump up. In fact, he stays at the center of the screen. At the same time, we need to define a length for each level, that is, how high it will take to jump to the castle for smooth customs clearance. In addition, it is necessary to prepare what should occur during the entire close process, and then switch these objects continuously based on the height Bob reaches. Imagine that there is a vertical film, which is a level, that is, a world. It has prepared the length of a level and set all objects. While orthographiccamera in Bob and worldrender was first placed at the bottom of the film, Bob began to jump constantly. When the height of the screen point exceeded, orthographiccamera would be moved up, all objects that enter orthographiccamera are drawn. Until it reaches the highest point, or Bob dies. Therefore, orthographiccamera in worldrender is set to 10*15. In addition, the world class defines two variables:
Public static final float world_width = 10; public static final float world_height = 15*20;
The bandwidth of the same level is also defined as 10 units, consistent with that in worldrender (because we do not need to move in the X direction ); the height is defined as 15*20, which is the length of the mark, that is, the height of Bob to jump.
Note: here we need to define orthographiccamera in worldrender as 10*15. In fact, we need to map the screen 320*480 to every 32 pixels. This is because the materials in the game are basically constructed in 32 pixels, and the screen resolution can also be divided into 32 pixels.
From the above analysis, we can see that setting two different orthographiccamera is based on the needs of different scenarios. In addition, orthographiccamera in worldrender can also be set to 320*480. It is only for simplicity that the unit is set to 32 pixels to 10*15. So no matter which orthographiccamera it provides only a ing information, and the real user of this ing information is spritebatch, it determines the location where the image is to be drawn based on the ing information, and whether or not to draw these elements.
For example, if an image is drawn at (10, 20) in worldrender, It is not displayed because the unit is 32 pixels, apparently out of the screen range (unless orthographiccamera moves up ); but if you do this in gamescreen, it will be precisely drawn at (10, 20. The use of camera is like this. In addition, it is worth mentioning that even if gamescreen and worldrender use the same spritebatch, they do not affect each other as long as the corresponding projection matrix is bound at the right time. Transferred from:
Http://tonmly.blog.163.com/blog/static/174712856201163091130535/