Explore Courses Blog Tutorials Interview Questions
0 votes
in AI and Deep Learning by (50.2k points)

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 a 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 derives 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 component takes over and starts giving instructions based on its 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 difficult 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 the 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 the 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.

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

1 Answer

0 votes
by (108k points)

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 possession of a FlightPath instance. EnemyShip checks where it should be in the path every frame.

EnemyGroup then controls the production of each EnemyShip. If you have 8 EnemyShips in one EnemyGroup, all possess the same FlightPath type, then you can imagine that EnemyGroup would produce 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.

You have to organize the system for the FlightPaths being strictly paths, give the EnemyShip class more functionality and also give EnemyGroup control over the overall group behavior instead of it just creating and storing enemies. Maybe split up the AI so that the ships control their own movement based on the flightPath, speed, and cue given by the EnemyGroup and the EnemyGroup keeps track of the group as a whole so that it can check when to give the ships a new instruction. 

Browse Categories