Chapter 7

Adding Basic Enemy Artificial Intelligence

The artificial intelligence (AI) of the enemy will define how the enemy attacks the player and how easy or difficult the game is for the player to win. It would be easy to create an AI that anticipates every move of the player by intercepting the listener calls from the player to touchListener. However, that would not make a fun experience for the player, and your game would not be very fulfilling. The enemies that you created in the preceding chapter need some kind of plan of attack by which to engage the player and create a satisfying gaming experience.

In this chapter, you are going to add the three distinct AIs for the three different enemy types that were discussed in Chapter 2 and created in Chapter 6: the interceptors, scouts, and warships. On the surface this task may seem easy given what you learned in the last chapter, but the fact is that creating the enemies is more difficult than creating the playable character. Why? Playable characters do not have to think; that is what the player does. The enemies, on the other hand, need at least a basic AI to guide them through the game.

Getting the Enemies Ready for AI

You'll need to first initialize the enemies and their textures before you can deal with the AI. So, to begin, open and edit the game loop, SFGameRenderer(). You need to add an array that will hold all of the enemies in the game. To determine the number of enemies, add the values for TOTAL_INTECEPTORS, TOTAL_SCOUTS, and TOTAL_WARSHIPS (minus 1 to account for the zero-based array).

package com.proandroidgames;

import java.util.Random;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLSurfaceView.Renderer;

public class  SFGameRenderer implements Renderer{
        private SFBackground background = new SFBackground();
        private SFBackground background2 = new SFBackground();
        private SFGoodGuy player1 = new SFGoodGuy();

        private SFEnemy[] enemies = new SFEnemy[SFEngine.TOTAL_INTERCEPTORS +
SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS - 1];


        private int goodGuyBankFrames = 0;
        private long loopStart = 0;
        private long loopEnd = 0;
        private long loopRunTime = 0 ;

        private float bgScroll1;
        private float bgScroll2;



}

Next, create a new instantiation of the SFTextures class and a new int array to hold the common sprite sheets. For now, the spriteSheets[] array will contain one element. In the next chapter, you will change this array so it holds more.

TIP: You could go even further and modify the spriteSheets[] array and SFBackground() to hold the textures for the background as well as the sprite sheets. Doing so would be easy and will gain you more optimization.

public class  SFGameRenderer implements Renderer{



        private SFEnemy[] enemies = new SFEnemy[SFEngine.TOTAL_INTERCEPTORS +
SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS - 1];
        private SFTextures textureLoader;
        private int[] spriteSheets = new int[1];


        private int goodGuyBankFrames = 0;
        private long loopStart = 0;
        private long loopEnd = 0;
        private long loopRunTime = 0 ;

        private float bgScroll1;
        private float bgScroll2;



}

Now, you have an array to hold your enemies but no enemies to put in it. You need three private methods to fill your array: one each for interceptors, scouts, and warships.

;



public class  SFGameRenderer implements Renderer{



        private SFEnemy[] enemies = new SFEnemy[SFEngine.TOTAL_INTERCEPTORS +
SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS - 1];
        private SFTextures textureLoader;
        private int[] spriteSheets = new int[2];




        private void initializeInterceptors(){

        }

        private void initializeScouts(){

        }

        private void initializeWarships(){

        }




}

Creating Each Enemy's Logic

Use a simple for loop to instantiate a new enemy of the corresponding type, and add it to the array. For example, in the initializeInterceptors() method, create a for loop that counts up to the value of TOTAL_INTERCEPTORS. This loop will instantiate a new enemy of the type TYPE_INTERCEPTOR and add it to the array.

public class  SFGameRenderer implements Renderer{


        private SFEnemy[] enemies = new SFEnemy[SFEngine.TOTAL_INTERCEPTORS +
SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS - 1];
        private SFTextures textureLoader;
        private int[] spriteSheets = new int[2];


;



        private void initializeInterceptors(){

                for (int x = 0; x<SFEngine.TOTAL_INTERCEPTORS -1 ; x++){

                        SFEnemy interceptor = new SFEnemy(SFEngine.TYPE_INTERCEPTOR,
SFEngine.ATTACK_RANDOM);
                        enemies[x] = interceptor;
                }

        }

        private void initializeScouts(){

        }

        private void initializeWarships(){

        }




}

Use this same loop logic on the warships.

public class  SFGameRenderer implements Renderer{



        private SFEnemy[] enemies = new SFEnemy[SFEngine.TOTAL_INTERCEPTORS +
SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS - 1];
        private SFTextures textureLoader;
        private int[] spriteSheets = new int[2];





        private void initializeInterceptors(){

                for (int x = 0; x<SFEngine.TOTAL_INTERCEPTORS -1 ; x++){
                        SFEnemy interceptor = new SFEnemy(SFEngine.TYPE_INTERCEPTOR,
SFEngine.ATTACK_RANDOM);
                        enemies[x] = interceptor;
                }

        }

        private void initializeScouts(){

        }

        private void initializeWarships(){

                for (int x = SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS -1;
x<SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS -1;
x++){
                        SFEnemy interceptor = new SFEnemy(SFEngine.TYPE_WARSHIP,
SFEngine.ATTACK_RANDOM);
                        enemies[x] = interceptor;

                }

        }




}

The interceptors and the warships both attack from random directions. However, the scouts will attack from either the right or left. Therefore, in the loop that instantiates the scouts, split the load in half, and instantiate half from the right and half from the left.

package com.proandroidgames;



public class  SFGameRenderer implements Renderer{


        private SFEnemy[] enemies = new SFEnemy[SFEngine.TOTAL_INTERCEPTORS +
SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS - 1];
        private SFTextures textureLoader;
        private int[] spriteSheets = new int[2];





        private void initializeInterceptors(){

                for (int x = 0; x<SFEngine.TOTAL_INTERCEPTORS -1 ; x++){
                        SFEnemy interceptor = new SFEnemy(SFEngine.TYPE_INTERCEPTOR,
SFEngine.ATTACK_RANDOM);
                        enemies[x] = interceptor;
                }

        }

        private void initializeScouts(){

                for (int x = SFEngine.TOTAL_INTERCEPTORS -1;
x<SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS -1; x++){
                        SFEnemy interceptor;
                        if (x>=(SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS) / 2
){
                                interceptor = new SFEnemy(SFEngine.TYPE_SCOUT,
SFEngine.ATTACK_RIGHT);
                        }else{
                                interceptor = new SFEnemy(SFEngine.TYPE_SCOUT,
SFEngine.ATTACK_LEFT);
                        }
                        enemies[x] = interceptor;
                }

        }

        private void initializeWarships(){

                for (int x = SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS -1;
x<SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS -1;
x++){
                        SFEnemy interceptor = new SFEnemy(SFEngine.TYPE_WARSHIP,
SFEngine.ATTACK_RANDOM);
                        enemies[x] = interceptor;
                }

        }




}

Initializing the Enemies

You have your methods to initialize your enemies. All of your other game loop initialization has taken place in the onSurfaceCreated() method of SFGameRenderer. Therefore, it stands to reason that the new initialization methods you just created will also be called from here.

public class  SFGameRenderer implements Renderer{



        private SFEnemy[] enemies = new SFEnemy[SFEngine.TOTAL_INTERCEPTORS +
SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS - 1];
        private SFTextures textureLoader;
        private int[] spriteSheets = new int[2];





        private void initializeInterceptors(){

                for (int x = 0; x<SFEngine.TOTAL_INTERCEPTORS -1 ; x++){
                        SFEnemy interceptor = new SFEnemy(SFEngine.TYPE_INTERCEPTOR,
SFEngine.ATTACK_RANDOM);
                        enemies[x] = interceptor;
                }

        }

        private void initializeScouts(){

                for (int x = SFEngine.TOTAL_INTERCEPTORS -1;
x<SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS -1; x++){
                        SFEnemy interceptor;
                        if (x>=(SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS) / 2
){

                                interceptor = new SFEnemy(SFEngine.TYPE_SCOUT,
SFEngine.ATTACK_RIGHT);
                        }else{
                                interceptor = new SFEnemy(SFEngine.TYPE_SCOUT,
SFEngine.ATTACK_LEFT);
                        }
                        enemies[x] = interceptor;
                }

        }

        private void initializeWarships(){

                for (int x = SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS -1;
x<SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS + SFEngine.TOTAL_WARSHIPS -1;
x++){
                        SFEnemy interceptor = new SFEnemy(SFEngine.TYPE_WARSHIP,
SFEngine.ATTACK_RANDOM);
                        enemies[x] = interceptor;
                }

        }




        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
                initializeInterceptors();
                initializeScouts();
                initializeWarships();

                gl.glEnable(GL10.GL_TEXTURE_2D);
                gl.glClearDepthf(1.0f);
                gl.glEnable(GL10.GL_DEPTH_TEST);
                gl.glDepthFunc(GL10.GL_LEQUAL);

                background.loadTexture(gl,SFEngine.BACKGROUND_LAYER_ONE,
SFEngine.context);
                background2.loadTexture(gl,SFEngine.BACKGROUND_LAYER_TWO,
SFEngine.context);
        }


}

Loading the Sprite Sheet

With the enemies[] array initialized, you can focus on the sprite sheet. Recall that you created a common texture method that will return the OpenGL assigned names of all textures assigned in an int array. This int array of OpenGL names is going to be held in the spriteSheets[] array.

Instantiate your textureLoader() method. After textureLoader() is instantiated, call the loadTexture() method, passing it the CHARACTER_SHEET, and assign the return to the spriteSheets[] array.

public class  SFGameRenderer implements Renderer{



        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
                initializeInterceptors();
                initializeScouts();
                initializeWarships();
                textureLoader = new SFTextures(gl);
                spriteSheets = textureLoader.loadTexture(gl, SFEngine.CHARACTER_SHEET,
SFEngine.context, 1);


                gl.glEnable(GL10.GL_TEXTURE_2D);
                gl.glClearDepthf(1.0f);
                gl.glEnable(GL10.GL_DEPTH_TEST);
                gl.glDepthFunc(GL10.GL_LEQUAL);

                background.loadTexture(gl,SFEngine.BACKGROUND_LAYER_ONE,
SFEngine.context);
                background2.loadTexture(gl,SFEngine.BACKGROUND_LAYER_TWO,
SFEngine.context);
        }

}

Your initialization of the enemies and their textures is complete. 3+39.′′′

+It is time to move on to the AI logic. Let's start with the interceptors.

Reviewing the AI

The description of the interceptor AI sounds complicated, but in reality, it is the easiest of the three enemies. The interceptor will start off moving in a straight line down the y axis. At some point along the y axis, it will lock on to the player's ship and fly directly at those coordinates in an effort to ram the player's ship.

The way you will accomplish this is by subtracting a predefined amount, INTERCEPTOR_SPEED, from the y axis position to slowly move the interceptor down the screen. Because the interceptor could be at any random point above the visible edge of the screen, you have to wait for it to be visible before it can lock on to the enemy. Once the interceptor has reached this point, you will then pass it the x and y coordinates of the player's ship at that moment. Finally, you will use a simple slope formula to move the Interceptor toward these coordinates.

Creating the moveEnemy() Method

The first step to adding some enemy AI is to create a moveEnemy() method that will hold all of the AI logic for your enemies. Just like the movePlayer1() method, the moveEnemy() method will be called by the game loop to update the positions of the enemy ships.

package com.proandroidgames;

import java.util.Random;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

        }




}

The moveEnemy() method will update all of your enemies in one call. Addressing all of your enemies in one call is always the best way to approach updating a large amount of nonplayable characters. Doing so can save you precious processor cycles.

Creating an enemies[] Array Loop

You want to create a for loop within the moveEnemy() method that will be able to cycle through each live enemy in the enemies[] array. By limiting the core of your process to only those enemies that have not been destroyed, you take care of two things in your game. First, you ensure that you are not drawing enemies that should no longer be on the screen. Second, you ensure that you are not wasting cycles on enemies that would not have any moves to be processed.

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                        }
                }

        }



}

NOTE: Don't worry too much about what actually sets the isDestroyed flag of the enemies. We will address this in a section about collision detection in the next chapter. You will also apply this logic to the player's character.

Now, you have a loop within your updating method that runs once for each enemy in your game and skips those enemies that have been destroyed.

Moving Each Enemy Using Its AI Logic

You have to run this loop for three different kinds of enemies, each with its own AI. The enemy class has an enemyType property that you set when you instantiated the enemy. Therefore, you will need to set up a switch on the enemyType so that you will know which AI to run for the enemy that is being updated.

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

                                        break;
                                case SFEngine.TYPE_SCOUT:

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

In the next section, you will create the interceptor AI, the logic that drives the interceptor enemies toward the player.

Creating the Interceptor AI

Let's now create the interceptor AI. The first thing that you are going to want to test for in this AI is whether or not the Interceptor has already moved off the screen. Recall that all of the enemies are going to move from the top to the bottom of the screen. Unless they are destroyed by the player, they will eventually reach the bottom of the screen.

You have a choice when you are designing a game like this. When an enemy reaches the bottom of the screen, you can either kill it to take it out of the rotation, or you can reset it to run again. For Star Fighter, you are going to reset the enemy to a random position above the top of the screen so that it will continue to attack the player until it is destroyed.

Test if the y axis position of the enemy is less than 0–below the bottom edge of the screen–and if so, reset its x and y positions to random positions. Also, you will want to clear any lock on positions and lock flags, just in case the enemy had previously locked onto the player.

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }

                                        break;
                                case SFEngine.TYPE_SCOUT:

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

In the next section, you will add the OpenGL code to the logic.

Adjusting the Vertices

The next step in the AI is some standard OpenGL work. You need to load the model matrix mode and adjust the vertices. This code should look very familiar to you, because it was already covered in the last chapter. In short, you are resizing the vertices so that the enemy ship is about the same size and the player—not the size of the entire screen.

package com.proandroidgames;

import java.util.Random;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);

                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);

                                        break;
                                case SFEngine.TYPE_SCOUT:

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

At this point, the AI for the interceptor is going to be split into two different clauses. The first describes what happens before the interceptor locks onto the player's position, and the second describes what happens after the interceptor locks on to the player's position.

Locking on to the Player's Position

Before the Interceptor locks onto the player's position, it will simply travel down the screen in straight line. Pick an arbitrary position on the y axis; this will be the point at which the Interceptor locks onto the player's position. For Star Fighter, the interceptor's y axis position when it locks onto the player's position is 3, meaning the interceptor will start at a random position on the y axis and move down in a straight line until it reaches 3.

;

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 3){

                                        }else{

                                        }

                                        break;
                                case SFEngine.TYPE_SCOUT:

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

Before the Interceptor reaches the lock on position, it will move in a straight line down the screen. This is accomplished by subtracting the INTERCEPTOR_SPEED value from the interceptor's current y axis position.

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){

                                case SFEngine.TYPE_INTERCEPTOR:

                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 3){
                                                enemies[x].posY -=
SFEngine.INTERCEPTOR_SPEED;
                                        }else{

                                        }

                                        break;
                                case SFEngine.TYPE_SCOUT:

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

Now, you can program the second half of the interceptor's AI logic.

Implementing a Slope Formula

First, set the interceptor to locked on, and get the current position of the player.

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){

                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 3){
                                                enemies[x].posY -=
SFEngine.INTERCEPTOR_SPEED;
                                        }else{
                                                if (!enemies[x].isLockedOn){
                                                        enemies[x].lockOnPosX =
SFEngine.playerBankPosX;
                                                        enemies[x].isLockedOn = true;

                                                }

                                        }

                                        break;
                                case SFEngine.TYPE_SCOUT:

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

Next, you are going to use a simple slope formula to determine the increments at which the interceptor needs to move to reach the player. Slope can be determined by using this formula:

(x1 - x2) / (y1 - y2)

Just to make things a little more interesting, you want the Interceptor to speed up once it locks onto the target. Therefore, replace y2 in the slope with INTERCEPTOR_SPEED. There is one modification you need to make to this formula before you can use it.

The formula as it is written will give you the full position directly to the player in one shot. However, you want to move in steady increments to the player. Therefore, you need to divide y1 by y2 rather than subtracting y2. This will give you an incremental value that you can keep adding to itself to move the interceptor along.

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 3){
                                                enemies[x].posY -=
SFEngine.INTERCEPTOR_SPEED;
                                        }else{
                                                if (!enemies[x].isLockedOn){
                                                        enemies[x].lockOnPosX =
SFEngine.playerBankPosX;
                                                        enemies[x].isLockedOn = true;
                                                        enemies[x].incrementXToTarget =
(float) ((enemies[x].lockOnPosX - enemies[x].posX )/ (enemies[x].posY /
(SFEngine.INTERCEPTOR_SPEED * 4)));
                                                }

                                        }

                                        break;

                                case SFEngine.TYPE_SCOUT:

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

Finish the logic by setting the x and y positions of the interceptor.

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 3){
                                                enemies[x].posY
-= SFEngine.INTERCEPTOR_SPEED;
                                        }else{
                                                if (!enemies[x].isLockedOn){
                                                        enemies[x].lockOnPosX =
SFEngine.playerBankPosX;
                                                        enemies[x].isLockedOn = true;

                                                        enemies[x].incrementXToTarget =
(float) ((enemies[x].lockOnPosX - enemies[x].posX )/ (enemies[x].posY /
(SFEngine.INTERCEPTOR_SPEED * 4)));
                                                }
                                                enemies[x].posY -=
(SFEngine.INTERCEPTOR_SPEED * 4);
                                                enemies[x].posX +=
enemies[x].incrementXToTarget;

                                        }

                                        break;
                                case SFEngine.TYPE_SCOUT:

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

Finally, you can update OpenGL with the position changes that you made to your interceptor. You need to move the vertices according to the new x and y axis positions of the Interceptor. Then, you need to push the texture matrix off the stack and set the texture to the interceptor sprite on the common sprite sheet.

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;

                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 3){
                                                enemies[x].posY -=
SFEngine.INTERCEPTOR_SPEED;
                                        }else{
                                                if (!enemies[x].isLockedOn){
                                                        enemies[x].lockOnPosX =
SFEngine.playerBankPosX;
                                                        enemies[x].isLockedOn = true;
                                                        enemies[x].incrementXToTarget =
(float) ((enemies[x].lockOnPosX - enemies[x].posX )/ (enemies[x].posY /
(SFEngine.INTERCEPTOR_SPEED * 4)));
                                                }
                                                enemies[x].posY -=
(SFEngine.INTERCEPTOR_SPEED * 4);
                                                enemies[x].posX +=
enemies[x].incrementXToTarget;
                                                gl.glTranslatef(enemies[x].posX,
enemies[x].posY, 0f);
                                                gl.glMatrixMode(GL10.GL_TEXTURE);
                                                gl.glLoadIdentity();
                                                gl.glTranslatef(0.25f, .25f , 0.0f);
                                        }

                                        break;
                                case SFEngine.TYPE_SCOUT:

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

Draw the interceptor, and you are ready to tackle the scout AI.

public class  SFGameRenderer implements Renderer{



        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 3){
                                                enemies[x].posY -=
SFEngine.INTERCEPTOR_SPEED;
                                        }else{
                                                if (!enemies[x].isLockedOn){
                                                        enemies[x].lockOnPosX =
SFEngine.playerBankPosX;
                                                        enemies[x].isLockedOn = true;
                                                        enemies[x].incrementXToTarget =
(float) ((enemies[x].lockOnPosX - enemies[x].posX )/ (enemies[x].posY /
(SFEngine.INTERCEPTOR_SPEED * 4)));
                                                }
                                                enemies[x].posY -=
(SFEngine.INTERCEPTOR_SPEED * 4);
                                                enemies[x].posX +=
enemies[x].incrementXToTarget;
                                                gl.glTranslatef(enemies[x].posX,
enemies[x].posY, 0f);
                                                gl.glMatrixMode(GL10.GL_TEXTURE);
                                                gl.glLoadIdentity();
                                                gl.glTranslatef(0.25f, .25f , 0.0f);
                                        }
                                        enemies[x].draw(gl, spriteSheets);
                                        gl.glPopMatrix();
                                        gl.glLoadIdentity();

                                        break;
                                case SFEngine.TYPE_SCOUT:


                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }




}

In the next section, you will create the AI logic for the scout enemy type.

Creating the Scout AI

Now that you have gotten your hands dirty with a little AI work, the remaining two enemies should be fairly easy. The only major difference between the scout and the interceptor is that the scout is going to move in a predefined pattern down the screen.

First, test to determine if the scout is off the bottom of the screen; if it is, reset it. In this same logic for the interceptor, you set both the x and y axis positions to random values. However, the scout will only attack from the extreme left or the extreme right of the screen. Therefore, set the x axis position to either 0 or 3 based on its direction of attack.

public class  SFGameRenderer implements Renderer{

...

        private void moveEnemy(GL10 gl){

                for (int x = 0; x < sfengine.TOTAL_INTERCEPTORS + sfengine.TOTAL_SCOUTS
+ sfengine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

...

                                        break;
                                case SFEngine.TYPE_SCOUT:
                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].posT = SFEngine.SCOUT_SPEED;

                                                enemies[x].lockOnPosX =
enemies[x].getNextScoutX();
                                                enemies[x].lockOnPosY =
enemies[x].getNextScoutY();
                                                if(enemies[x].attackDirection ==
SFEngine.ATTACK_LEFT){
                                                        enemies[x].posX = 0;
                                                }else{
                                                        enemies[x].posX = 3f;
                                                }
                                        }
gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }


...

}

Just like you did for the interceptor, you are going to slowly move the scout down the screen until it reaches the lock on point.

Setting a Random Point to Move the Scout

Since the action would look far too mechanical to the player if all of the enemies changed direction at the same point on the screen, you should set the lock-on point for the scout a little lower than that of the interceptor; otherwise, the code is the same.

public class  SFGameRenderer implements Renderer{

...

        private void moveEnemy(GL10 gl){

                for (int x = 0; x < sfengine.TOTAL_INTERCEPTORS + sfengine.TOTAL_SCOUTS
+ sfengine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();


                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

...

                                        break;
                                case SFEngine.TYPE_SCOUT:
                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].posT = SFEngine.SCOUT_SPEED;
                                                enemies[x].lockOnPosX =
enemies[x].getNextScoutX();
                                                enemies[x].lockOnPosY =
enemies[x].getNextScoutY();
                                                if(enemies[x].attackDirection ==
SFEngine.ATTACK_LEFT){
                                                        enemies[x].posX = 0;
                                                }else{
                                                        enemies[x].posX = 3f;
                                                }
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 2.75f){
                                                enemies[x].posY -= SFEngine.SCOUT_SPEED;
                                        }else{

                                        }

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }


...

}
In the next section, you will learn how to move the scout along a Bezier curve.

Moving Along a Bezier Curve

Luckily, you have already created methods to automatically supply you with the next x and y coordinates in a Bezier curve. All you have to do now is call the getNextScoutX() and getNextScoutY() methods to begin moving the scout along its curved path. Increment posT by the value of SCOUT_SPEED after you call these methods; otherwise, you will get the same values the next time you call them.

public class  SFGameRenderer implements Renderer{

...

        private void moveEnemy(GL10 gl){
                for (int x = 0; x < sfengine.TOTAL_INTERCEPTORS + sfengine.TOTAL_SCOUTS
+ sfengine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){
                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

...

                                        break;
                                case SFEngine.TYPE_SCOUT:
                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].posT = SFEngine.SCOUT_SPEED;
                                                enemies[x].lockOnPosX =
enemies[x].getNextScoutX();
                                                enemies[x].lockOnPosY =
enemies[x].getNextScoutY();
                                                if(enemies[x].attackDirection ==
SFEngine.ATTACK_LEFT){
                                                        enemies[x].posX = 0;
                                                }else{
                                                        enemies[x].posX = 3f;
                                                }
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 2.75f){
                                                enemies[x].posY -= SFEngine.SCOUT_SPEED;
                                        }else{
                                                enemies[x].posX =
enemies[x].getNextScoutX();
                                                enemies[x].posY =
enemies[x].getNextScoutY();

                                                enemies[x].posT += SFEngine.SCOUT_SPEED;
                                        }

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }


...

}

Believe it or not, that is all there is to the scout AI. Finish up this enemy's AI by performing your OpenGL procedure to translate the vertices and set the texture to the correct sprite for the scout.

public class  SFGameRenderer implements Renderer{

...

        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

...

                                        break;
                                case SFEngine.TYPE_SCOUT:
                                        if (enemies[x].posY <= 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].posT = SFEngine.SCOUT_SPEED;
                                                enemies[x].lockOnPosX =
enemies[x].getNextScoutX();
                                                enemies[x].lockOnPosY =
enemies[x].getNextScoutY();
                                                if(enemies[x].attackDirection ==
SFEngine.ATTACK_LEFT){
                                                        enemies[x].posX = 0;
                                                }else{

                                                        enemies[x].posX = 3f;
                                                }
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);
                                        if (enemies[x].posY >= 2.75f){
                                                enemies[x].posY -= SFEngine.SCOUT_SPEED;
                                        }else{
                                                enemies[x].posX =
enemies[x].getNextScoutX();
                                                enemies[x].posY =
enemies[x].getNextScoutY();
                                                enemies[x].posT += SFEngine.SCOUT_SPEED;
                                        }
                                        gl.glTranslatef(enemies[x].posX, enemies[x].posY, 0f);
                                        gl.glMatrixMode(GL10.GL_TEXTURE);
                                        gl.glLoadIdentity();
                                        gl.glTranslatef(0.75f, .25f , 0.0f);
                                        enemies[x].draw(gl, spriteSheets);
                                        gl.glPopMatrix();
                                        gl.glLoadIdentity();

                                        break;
                                case SFEngine.TYPE_WARSHIP

                                        break;

                                }

                        }
                }

        }


...

}

The last bit of AI that you need to add to your moveEnemy() method is the warship.

Creating the Warship AI

In the story for Star Fighter, the warship moves in a random direction toward the player. You will accomplish this by selecting a random position on the x axis and, using the same logic as the interceptor, moving the warship toward the random point, rather than directly to the position of the player.

Moving the warship toward a random position will make the game less predictable and make the enemy harder to fight. However, the AI for the warship will be almost identical to that of the interceptor, except that you need to replace the player's locked-on x position with a random number between 0 and 3.

public class  SFGameRenderer implements Renderer{

...

        private void moveEnemy(GL10 gl){

                for (int x = 0; x < SFEngine.TOTAL_INTERCEPTORS + SFEngine.TOTAL_SCOUTS
+ SFEngine.TOTAL_WARSHIPS - 1; x++){
                        if (!enemies[x].isDestroyed){

                                Random randomPos = new Random();

                                switch (enemies[x].enemyType){
                                case SFEngine.TYPE_INTERCEPTOR:

...

                                        break;
...

                                        break;
                                case SFEngine.TYPE_WARSHIP
                                        if (enemies[x].posY < 0){
                                                enemies[x].posY = (randomPos.nextFloat()
* 4) + 4;
                                                enemies[x].posX = randomPos.nextFloat()
* 3;
                                                enemies[x].isLockedOn = false;
                                                enemies[x].lockOnPosX = 0;
                                        }
                                        gl.glMatrixMode(GL10.GL_MODELVIEW);
                                        gl.glLoadIdentity();
                                        gl.glPushMatrix();
                                        gl.glScalef(.25f, .25f, 1f);

                                        if (enemies[x].posY >= 3){
                                                enemies[x].posY -=
SFEngine.WARSHIP_SPEED;
                                        }else{
                                                if (!enemies[x].isLockedOn){
                                                        enemies[x].lockOnPosX =
randomPos.nextFloat() * 3;
                                                        enemies[x].isLockedOn = true;
                                                        enemies[x].incrementXToTarget =
(float) ((enemies[x].lockOnPosX - enemies[x].posX )/ (enemies[x].posY /
(SFEngine.WARSHIP_SPEED * 4)));
                                                }
                                                enemies[x].posY -=
(SFEngine.WARSHIP_SPEED * 2);
                                                enemies[x].posX +=
enemies[x].incrementXToTarget;

                                        }
                                        gl.glTranslatef(enemies[x].posX,
enemies[x].posY, 0f);
                                        gl.glMatrixMode(GL10.GL_TEXTURE);
                                        gl.glLoadIdentity();
                                        gl.glTranslatef(0.50f, .25f , 0.0f);

                                        enemies[x].draw(gl,spriteSheets);
                                        gl.glPopMatrix();
                                        gl.glLoadIdentity();
                                        break;

                                }

                        }
                }

        }


...

}

Finish your game loop by calling your new moveEnemy() method from the onDrawFrame() method, right after you call movePlayer1().

Summary

In this chapter, you learned a great deal about creating three different, basic AI structures for your enemies. You also

  • Created the calls to load multiple textures
  • Created methods to move your enemies
  • Tested for the condition of your enemy before moving it
  • Created logic for moving your enemies along paths

In the next chapter, you will finish your game by developing some weapons and implementing collision detection.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.50.87