Introduction
This is a bullet shooting game. During the time making this project, I have encountered many problems in creating this game.
In this How-To page, I will cover some topics that may be useful for you.
- Process input from user
- Display a background using TiledLayer
- Making bullet pattern
- Enemy move against wall
- Vector normalization
- Remove any sprite from layer manager
When you should apply this?
You can apply any solution above to enhance your game. However, making bullet pattern is optional.
Advantages and disadvantages, Requirement, Step and ScreenShot
public void getPlayerKeyInput() {
int keyState = getKeyStates();
int characterXPosition = character.getX();
int characterYPosition = character.getY();
if ((keyState & UP_PRESSED) != 0 && characterYPosition > 0) {
moveCharacter(0, -CHARACTER_MOVEMENT_SPEED);
}
if ((keyState & DOWN_PRESSED) != 0 && characterYPosition < HEIGHT - character.getHeight()) {
moveCharacter(0, CHARACTER_MOVEMENT_SPEED);
}
if ((keyState & LEFT_PRESSED) != 0 && characterXPosition > 0) {
moveCharacter(-CHARACTER_MOVEMENT_SPEED, 0);
}
if ((keyState & RIGHT_PRESSED) != 0 && characterXPosition < WIDTH - character.getWidth()) {
moveCharacter(CHARACTER_MOVEMENT_SPEED, 0);
}
}
Note: moveCharacter() is a method defined by programmer. It moves character. For example, character.move(dx, dy). The second Boolean logic is to make character to move inside the screen. WIDTH and HEIGHT are the width and height of the GameCanvas.
Advantages: Easy to control and edit the code
Disadvantage: Make the code longer
- Display background using TiledLayer
private void displayBackground() {
int[] map = { 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 4, 3, 3, 4, 3, 4, 3, 4, 2, 4, 2, 1, 2, 1, 1, 2, 1, 2, 1, 3, 1, 3, 4, 3, 4, 3, 3, 4, 3, 4, 2, 4, 2, 4, 2, 1, 2, 1, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 4, 3, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 1, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1 };
int count = 0;
for (int i = 0; i < background.getRows(); i++)
{ for (int j = 0; j < background.getColumns(); j++) { background.setCell(j, i, map[count]); count++; }
}
}
Note: there are many ways to setCell, but in my game, I use nested-loop.
I experience that when you setCell by TiledLayer. If you setCell(col, row, 0), the cell will be transparent.
Advantage: TiledLayer will help you make a customized background image. Its size is much smaller than you use a Graphics to draw the whole image.
Disadvantage: The TiledLayer has limitation comparing to draw an image in the quality and the scenery.
public void createCharacterBullet() {
if (!characterAbleToFire) {
return;
}
int characterXPosition = character.getRefPixelX();
int characterYPosition = character.getRefPixelY();
//bullet pattern
characterBulletLayerManage.append(
getCharacterBullet(characterBulletImage, 0, -6, characterXPosition, characterYPosition - 10));
if (characterBulletDamage > 15) {
characterBulletLayerManage.append(
getCharacterBullet(characterBulletImage, 0, -6, characterXPosition - 15, characterYPosition - 10));
characterBulletLayerManage.append(
getCharacterBullet(characterBulletImage, 0, -6, characterXPosition + 15, characterYPosition - 10));
}
if (characterBulletDamage > 30) {
characterBulletLayerManage.append(
getCharacterBullet(characterBulletImage, -2, -6, characterXPosition - 20, characterYPosition - 10));
characterBulletLayerManage.append(
getCharacterBullet(characterBulletImage, 2, -6, characterXPosition + 20, characterYPosition - 10));
}
characterAbleToFire = false;
}
private BulletSprite getCharacterBullet(Image image, int dx, int dy, int xRefPosition, int yRefPosition) {
BulletSprite characterBullet = new BulletSprite(new Sprite(image, 10, 15), characterBulletDamage, dx, dy);
characterBullet.defineReferencePixel(5, 7);
characterBullet.setRefPixelPosition(xRefPosition, yRefPosition);
return characterBullet;
}
For some reason, I cannot insert image in this post. Sorry for any inconvenience.
Advantage: It looks awesome.
Disadvantage: None
- Enemy move against the wall
//move action
if (enemyXPosition < 0 || enemyXPosition > WIDTH) {
enemySprite.setFlag();
}
if (enemySprite.isFlag()) {
enemySprite.move(3, 0);
} else {
enemySprite.move(-3, 0);
}
Create a class called EnemySprite, and initial a boolean type called “flag”
enemySprite.setFlag() method does (this.flag = !flag;)
Advantage: It is easy to control character move
public int[] getVectorNormalization(int x1, int y1, int x2, int y2) {
int[] coordinates = new int[2];
int a = x2 - x1;
int b = y2 - y1;
double length = Math.sqrt(a * a + b * b);
coordinates[0] = (int) (BULLET_SPEED * a / length);
coordinates[1] = (int) (BULLET_SPEED * b / length);
return coordinates;
}
The formular for vector is (x2 – x1, y2 – y1)
The length of the vector is with a = x2 – x1, b = y2 – y1. To calculate normalized vector, use the formular . You can multiple with BULLET_SPEED to increase the speed of the bullet.
- Remove any sprite from LayerManager
public void itemDropDown() {
for (int i = itemLayerManager.getSize() - 1; i >= 0; i--) {
Sprite item = (Sprite) itemLayerManager.getLayerAt(i);
int itemXPosition = item.getRefPixelX();
int itemYPosition = item.getRefPixelY();
if (itemXPosition < 0 || itemXPosition > WIDTH || itemYPosition < 0 || itemYPosition > HEIGHT) {
itemLayerManager.remove(item);
} else {
item.move(0, 4);
}
}
}
I experience that your program is easily catched Exception if you use normal for-loop.
On the reason that when you remove a layer, the size of the LayerManger reduce; however, in the for-loop the variable “i” still stores the old value and the LayerManager will catch ArrayIndexOutOfBound. Thus, it is better to use a for loop going from the last element to the first element.
private void bossBulletPattern1(int enemyXPosition, int enemyYPosition, boolean doubleStrike) {
Image image = null;
switch (random.nextInt(3) + 1) {
case EnemySprite.ENEMY_1:
image = getImage("/image/Bullet1.PNG");
break;
case EnemySprite.ENEMY_2:
image = getImage("/image/Bullet2.PNG");
break;
case EnemySprite.ENEMY_3:
image = getImage("/image/Bullet3.PNG");
break;
}
final int NUMBER_OF_BULLET = 5;
int[] x = new int[NUMBER_OF_BULLET];
int[] y = new int[NUMBER_OF_BULLET];
final int SPACE = 5;
final int RADIUS = SPACE * (NUMBER_OF_BULLET + 1);
for (int i = 0; i < NUMBER_OF_BULLET; i++) {
x[i] = SPACE * (i + 1);
y[i] = (int) Math.sqrt(RADIUS * RADIUS - x[i] * x[i]);
}
for (int i = 0; i < NUMBER_OF_BULLET; i++) {
pattern1Handler(enemyXPosition, enemyYPosition, enemyXPosition + x[i], enemyYPosition + y[i], image, doubleStrike);
pattern1Handler(enemyXPosition, enemyYPosition, enemyXPosition - x[i], enemyYPosition + y[i], image, doubleStrike);
pattern1Handler(enemyXPosition, enemyYPosition, enemyXPosition - x[i], enemyYPosition - y[i], image, doubleStrike);
pattern1Handler(enemyXPosition, enemyYPosition, enemyXPosition + x[i], enemyYPosition - y[i], image, doubleStrike);
}
}
private void pattern1Handler(int enemyXPosition, int enemyYPosition, int newEnemyXPosition, int newEnemyYPosition,
Image image, boolean doubleStrike) {
int[] vector = getVectorNormalization(enemyXPosition, enemyYPosition, newEnemyXPosition, newEnemyYPosition);
enemyBulletLayerManger.append(getBulletSprite(new Sprite(image, 20, 20), 10, vector[0], vector[1], enemyXPosition, enemyYPosition));
if (doubleStrike) {
enemyBulletLayerManger.append(getBulletSprite(new Sprite(image, 20, 20), 10, vector[0], vector[1], newEnemyXPosition, newEnemyYPosition));
}
}
private void bossBulletPattern2(int xPosition, int yPosition) {
final int NUMBER_OF_BULLET = 5;
final int SPACEX = WIDTH / NUMBER_OF_BULLET;
final int SPACEY = HEIGHT / NUMBER_OF_BULLET;
int[] x = new int[NUMBER_OF_BULLET * 4];
int[] y = new int[NUMBER_OF_BULLET * 4];
int[] vector;
Image image = null;
switch (random.nextInt(3) + 1) {
case EnemySprite.ENEMY_1:
image = getImage("/image/Bullet1.PNG");
break;
case EnemySprite.ENEMY_2:
image = getImage("/image/Bullet2.PNG");
break;
case EnemySprite.ENEMY_3:
image = getImage("/image/Bullet3.PNG");
break;
}
for (int i = 0; i < NUMBER_OF_BULLET * 4; i++) {
if (i < NUMBER_OF_BULLET) {
x[i] = i * SPACEX;
y[i] = 0;
} else if (i < NUMBER_OF_BULLET * 2) {
x[i] = WIDTH;
y[i] = (NUMBER_OF_BULLET * 2 - i) * SPACEY;
} else if (i < NUMBER_OF_BULLET * 3) {
x[i] = (NUMBER_OF_BULLET * 3 - i) * SPACEX;
y[i] = HEIGHT;
} else {
x[i] = 0;
y[i] = (NUMBER_OF_BULLET * 4 - i) * SPACEY;
}
}
for (int i = 0; i < NUMBER_OF_BULLET * 4; i++) {
vector = getVectorNormalization(x[i], y[i],
xPosition, yPosition);
enemyBulletLayerManger.append(
getBulletSprite(new Sprite(image, 20, 20), 10, vector[0], vector[1], x[i], y[i]));
}
}
Reference
- J2ME Game Programming (Game Development) By Matin J.Wells
- JavaME < http://www.codeotaku.com/blog/2010-08/java-me-games/index>