Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android shoot-em-up game. Robust enemy patterns for complex group behaviour

I'm working on an arcade shoot-em-up game for Android similar to Ikaruga. The problem I'm facing is that it's proving quite difficult to robustly create move and shoot patterns for the enemies. At the moment I've created two abstract classes EnemyShip and FlightPath from which each different enemy and move pattern derive from respectively. When the World is created it instantiates a LevelManager which stores level info in the form of:

waveInfos.add(new WaveInfo(3, 3f)); // new WaveInfo(NumberOfGroups, spawn interval)
enemyGroups.add(new EnemyGroup(8, EnemyGroup.TYPE_SCOUT_SHIP, EnemyGroup.F_PATH_INVADERS));
enemyGroups.add(new EnemyGroup(1, EnemyGroup.TYPE_QUAD_SPHERE, EnemyGroup.F_PATH_QUAD_SPHERE_L, World.BLACK));
enemyGroups.add(new EnemyGroup(8, EnemyGroup.TYPE_SCOUT_SHIP, EnemyGroup.F_PATH_INVADERS));
// new EnemyGroup(NumberOfEnemies, EnemyType, FlightPathType)
// new EnemyGroup(NumberOfEnemies, EnemyType, FlightPathType, ShipColour)

waveInfos.add(new WaveInfo(2, 0.33f));
enemyGroups.add(new EnemyGroup(1, EnemyGroup.TYPE_QUAD_SPHERE, EnemyGroup.F_PATH_QUAD_SPHERE_L, World.WHITE));
enemyGroups.add(new EnemyGroup(1, EnemyGroup.TYPE_QUAD_SPHERE, EnemyGroup.F_PATH_QUAD_SPHERE_R, World.WHITE));

totalWaves = waveInfos.size();

The levels are split into waves of groups of enemies and right now the EnemyGroup class takes care instantiating, adding the specified FlightPath to the newly created enemy and passing that enemy to the ArrayList in LevelManager for storage until spawned into the world at the time needed.

Once spawned the FlightPath componant takes over and starts giving instructions based on it's own stateTime and since each FlightPath has a reference field to its EnemyShip owner it can access the ship's functions and members it's controlling.

The EnemyShip class has a few functions for easy instruction such as moveTo(float x, float y, float duration) and shoot() but even with these the FlightPath derivatives are diffcult to make especially when I want different enemies in the same group to have slightly different paths and slightly different time arrivals.

I created a few fields in the FlightPath to keep track of keyFrames:

public int currentKeyFrame = 0;
public int totalKeyFrames;
public KeyFrame[] keyFrames; // Stores duration of instruction to be done, the spreadTime, totalFrameTime and enemyIntervalTime
public int shipNumber; // Stores which ship out of group this FlightPath is attached to
public int totalShips; // Stores total number of ships in this EnemyShip's group
public float stateTime = 0;

KeyFrame.spreadTime is my attempt to control the time between the first enemy in group to begin moving/shooting and the last.

KeyFrame.totalFrameTime = KeyFrame.duration + KeyFrame.spreadTime

KeyFrame.enemyIntervalTime = KeyFrame.spreadTime / Number of enemies in this group

While this setup works great for very simple linear movement, it feels quite cumbersome.

Thanks for reading this far. My question is how do I implement a more streamlined pattern control which would allow for complex movement without hordes of if() statements to check what other enemies in the group are doing and the like.

I hope I've provided enough information for you to understand how the enemies are handled. I'll provide any source code to anyone interested. Thanks in advance for any light you can shed on the subject.

Marios Kalogerou

EDIT: I found a page which very much describes the kind of system which would be perfect for what I want but I'm unsure how to correctly implement it with regards to overall group keyFrames

http://www.yaldex.com/games-programming/0672323699_ch12lev1sec3.html

like image 818
MazK Avatar asked Apr 10 '12 16:04

MazK


2 Answers

There are different approaches:

  1. You can add random intervals before shooting, and set slightly random arrival times. Like currentEnemyArrivalTime += (X - rand(2*X)).

  2. You can control movement of group of enemies. Each enemy in the group tries to maintain it's position relative to the center of the group.

  3. For really complex patterns may be better to develop some simple scripting engine. It can be very simple (like array of coefficients for spline), or something more complex. I believe, in such games behavior is done by scripts.

like image 28
werewindle Avatar answered Nov 10 '22 00:11

werewindle


FlightPath should not control any objects. It's a path, not a manager. However, it should be able to give coordinates given any keyframe or time. For example: flightPath.getX(1200) -> where should I be in the X-coordinate at 1200ms?

Each EnemyShip should maintain a possession of a FlightPath instance. EnemyShip checks where it should be in the path every frame.

EnemyGroup then controls the spawning of each EnemyShip. If you have 8 EnemyShips in one EnemyGroup, all possess the same FlightPath type, then you can imagine that EnemyGroup would spawn each ship around 500ms apart to create the wave.

Finally, you translate all the EnemyShip coordinates relative to the world/screen coordinate, which traditionally moves slowly in the vertical direction.

like image 189
garbagecollector Avatar answered Nov 09 '22 23:11

garbagecollector