1
\$\begingroup\$

I have been working on a simple arcade shooter in Java in the vein of missile command/space invaders. I've made a base Projectile class and have been extending other Projectile types from it. This has been a nice way so far for me to keep the same basic behavior of the projectile, while experimenting with new ideas, like a missile projectile, electric projectile, bursting, etc. Before I went further though, I started to realize I needed to think about this in a better organized way because of this problem: what if I wanted to make a projectile that has features of more than one type?

Like, for example, if I wanted to make a projectile that is both seeking as well as bursting, or seeking, and shocking, etc... or a projectile that is all of the types. It seems repetitive then for me to have to create a class for each combination type. If I tried to do this on my own now, I'd essentially be copying n' pasting code from one type into another to make the hybrid type.

What's the best way to organize this type of design? It seems as though I need multiple inheritance. I don't understand how that works exactly in runtime though and how to ensure that the functions that I'm required to implement will be called and perform the behaviors I want, given any projectile.

Maybe in code it looks something like this?

Projectile p = new Projectile(loc, vel);
p.attachBehavior('electric');
p.attachBehavior('bursting');

and Projectile already inherits all the types possible? (It seems like it will make the Projectile class really huge though) and so perhaps attachBehavior() is essentially a loop that cycles through all the interfaces that the Projectile has?

However, looking at this post on Java and multiple inheritance (https://stackoverflow.com/questions/21824402/java-multiple-inheritance?lq=1), it seems as though even the base Projectile would also be an inherited thing. So I'm a bit confused about how to design such behaviors. (Even more confused when I think that some projectiles like lazers/beams behave instantaneously compared to gun bullets)

Also, when I'm doing things like this, I'm a bit wary about how to code in such a way that I'm not running more for loops than I should. Like for example, the missile projectile type needs to loop through the enemies to find the closest enemy, but the electric type, which shoots out an arc to a badguy will also need to loop through the enemies to see if its close to it. Instead of having one for loop, by having these two behaviors, haven't I now made the game run potentially slower because I'm going through two for loops now as opposed to just making a distinct hybrid projectile class that checks for both conditions in the same for loop?

\$\endgroup\$
1
\$\begingroup\$

I encountered a similar problem while working on a project of mine. The player was able to pick up a certain variety of weapons, which shoot different type of bullets. I'll help you to figure out a solution for you by explaining the solution I found for my game.

In order to take advantage of inheritance and polymorphism, I managed to work out all the possible bullets my weapons were supposed to shoot, and made a list of their properties:

  • Travel speed
  • Impact damage
  • Splash damage
  • Homing
  • Stickyness
  • Graphic appearance
  • Trail appearance
  • Affected by gravity

First thing I did was to create the main parent class Bullet, through which I could track and deal with any bullet object in my side-scrolling 2D shooter game.

Travel speed

Does the bullet physically travel in the space (projectile), or is supposed to reach the target instantaneously (hitscan)? Here we have two different behaviors: given starting point A and ending point B, our bullet may travel and get to B after a certain amount of time (remember the equation of motion), or get there after a single in-game timestep, eventually stopping before B because of a collision with an object. Do I need two different classes HitscanBullet and ProjectileBullet who extends the parent class Bullet?

Answer was no. Reason was: even though hitscan bullets reach their final position instantaneously, the algorithm I used to compute such position was based on the idea behind the binary search on arrays. Simply, I check if there are collisions with any instance of, for example, objects BipedEnemy; if no, then I found the final position (the second point of my line); else, the middle point of trajectory is taken in account, then I verify if the first middle segment intersects any object: if yes, iterate; else, the collision occurs in the further segment, and I iterate anyway.

This algorithm requested two arguments: a starting position (x,y), a direction θ, and a range, which are fields in my parent class, so I can edit them on need (and, we'll see I need to do so...). Because of the range being a finite number, I realized I could set this value as big as I liked to simulate HitscanBullets (the bullet would eventually reach its target outside the screen, so no problem), and if target was visible by the player the effect was immediate.

On the other hand, a ProjectileBullet could simply use a very smaller value, taking care of iterating such algorithm every timestep in order to give the idea of non-instant motion.

So, no separated classes here, I just pass a valid value for bullet speed (the range argument) and get any motion effect I desire. Motion is done, and is put in the parent class Bullet along with fields to track position, direction, and speed.

Impact damage

Nothing special to talk about here. Impact damage is another field (we may call damage) in the parent class Bullet, and stores a value which is subtracted to the object's health it collide with when moving. If that has health, obviously. Enemies have health, floor and walls don't.

Splash damage

Splash damage occurs when a bullet impacts, and delivers area or volume damage to nearby objects. In my game, I decided not to let a Bullet deal with splash damage itself, instead it creates an object SplashDamage, or maybe DamageExplosion, which is in charge of delivering damage to objects closer than a suitable damage_range distance.

Anyway, if you want your Bullet to deliver splash damage on his own, you can consider a maximum distance for it. At this point, your Bullet will have a impact_damage (previously simply damage), and two more fields, splash_damage and splash_damage_range. Make sure to deliver less splash damage as distance increase from the bullet collision point.

If you implement splash damage as creation of a new object in charge of it, you can do a lot of different things: you may create explosions for missiles, minor explosions for energy-based bullets, or an electric area damage (as you mentioned). You could even burst new bullets at random directions (as you mentioned, I guess), from this point on.

Homing

Homing is the capability of a bullet to adjust its direction to automatically move towards an enemy. Here I questioned what were the right criteria to move the bullet. Should it move to the closest enemy continuously? Or it should check for the closest enemy at creation and then just follow that particular one, regardless of others as it travels? I opted for the latter, as it was easier to implement.

Homing requires two operations: (1) identify the target, and (2) adjust the direction accordingly to its relative position. Can homing be implemented as a function, or is it better creating a subclass HomingBullet which extends parent class Bullet? Your choice. In my game, I extended my parent class with a subclass, because there was more than one bullet type capable of homing towards an enemy.

Stickyness

Does the bullet need to be destroyed at the impact, or the bullet can stick into walls, so it would be nice to see it? A rifle bullet is the first type, but if you fire a nail gun you expect to see nails sticking to walls or even enemies. I found it simple to implement this feature as a timer i called bullet_life, being decreased every timestep, and when it reaches the 0 then I can destroy the bullet object. If you already set it at 0 the bullet is never seen once impacted a target or a wall.

Graphic and tail appearance

You may want to store the drawing sprite or model associated to a given Bullet in a variable, or you can take advantage of inheritance for advanced drawing hierarchy. Anyway, drawing can be performed in many ways, so I'm not taking it in account for this answer.

Gravity

This property can be implemented in the physics update functions, and I don't believe you need an entire class to make distinctions between bullets affected by gravity or not.

This is a possible way to realize your bullet hierarchy, but it isn't the only possible solution. Don't try to find the best solution at all, but the best one which fits your game: you don't really need a bullet capable of hitscanning, splash damage, bursting, electric... Well, it would be OP, ahah. Anyway, you can have a couple of subclasses to differentiate the unique properties, and by unique I mean that feature is not available to other bullets. If you want properties available to any projectile, then it would be a property of the parent class.

\$\endgroup\$
  • \$\begingroup\$ Thank you for the detailed post; to hone on "how do i have more than one type of behavior to a projectile", are you saying that essentially the best way is to make a parent class have all the properties and then create subclasses that are from the parent? Does that mean each Parent bullet object becomes huge? Would that affect performance at all, or should it not matter? Is it something like, the Parent Bullet class is an abstract class that leaves its child class to implement the details? Perhaps it would be too OP to have all characterstics, but having two could be kinda cool I think. \$\endgroup\$ – user3772547 May 27 '16 at 23:31
  • \$\begingroup\$ It depends, if there are few properties to deal with, there's no problem in having two or three children for same parent class. Anyway, the idea behind inheritance is to give the parent all common behaviors, for them to be inherited by the children. If you want to create all possible combinations of bullets, I think it'd be better having one object only and activate/deactivate bullet properties by setting the proper flags. \$\endgroup\$ – liggiorgio May 31 '16 at 13:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.