Sign in to follow this  
BUnzaga

Combat and various effect distance checks, etc

Recommended Posts

BUnzaga    267
I am writing up a simple 'space shooter' game, and I came across a situation where I figured there had to be a better way to manage code than what I was doing. Originally, I was just checking for distance between each ship and the player ship. If a threshold was breached, damage would occur. Now that I am adding more things, like invulnerability shield, forcefield to take one shot, etc, this simple system doesn't work. For example, if an enemy gets too close to the player, it does damage, but it then has to have a small delay, like .5 seconds, before the next damage 'tic' to occur, otherwise the player would instantly die every time it got close to the enemies (rather than go through 3 shields first). The next problem, is the invulnerability shield. If the enemy gets close to the player, instead of doing damage to the player, the invulnerability shield does damage to the enemy. A good example would be like the pac man super pill. Originally the ghosts do damage to pac man, but then he does damage to them. For bosses, or enemies with forcefields, the invulnerability shield should only do one damage every tic, rather than instantly kill the boss because of doing damage every update cycle. The most obvious idea I thought of trying, was to check for distance in both enemies, and the invulnerability shield. This works, but now I am doubling the number of times I am calculating distance. It seems like a waste. The next problem, is how do I handle each units 'tic' separately? Such as if one enemy is close, and so it does damage, then .25 seconds later another enemy comes inside range, it should do damage to enemy2, but not do damage to enemy1 until 1 second has passed. How do I track each enemies damage tics individually? I hope someone out there can point me in the right direction.

Share this post


Link to post
Share on other sites
jyk    2094
First, regarding the duplicated distance checks, this can be avoided by structuring your collision loop so that each unique pair of objects is only tested once. Note that this assumes a brute-force check, which doesn't scale particularly well as the number of objects in your simulation increases; if you plan on having lots of different entities active simultaneously, you may need to use a spatial partitioning scheme of some sort.

Ideally (IMO), your collision system should identify pairs of intersecting entities and invoke a callback for each, passing in any relevant data, e.g.:
void collision_callback(
ref Entity entity1,
ref Entity entity2,
Vec3 collisionPoint,
Vec3 collisionNormal)
{
// ...
}
Within the function body you would then take the appropriate action based on the types of the two entities.

Regarding damage effects, it seems there would be at least a couple of cases to consider: 'instantaneous' collisions (where the two objects bounce off of each other), and 'sustained' collisions.

In the former case, I can imagine a fixed damage amount, perhaps scaled according to the relative velocity of the two objects. For the latter, it seems it would make sense to think in terms of damage per second rather than 'x amount of damage every n seconds'. (If you went with the damage per second approach, you'd need to know the current time step in the callback shown above; the amount of damage to apply would then be the damage per second scaled by the current time step.)

Share this post


Link to post
Share on other sites
Zipster    2365
Another helpful hint when doing distance checks is to remember that for all non-negative values (which is what distances are), a2 < b2 implies a < b. This means that you only need to compare squared values to know if something is within a certain distance of something else, and can completely avoid doing the square roots unless you actually need that distance for something.

To answer your other question, if you give the player an additional field which holds "last time damaged", whenever more damage is applied to the player you can check to see if enough time has passed between now and the last time, and only apply the damage if that time exceeds a certain interval.

Share this post


Link to post
Share on other sites
BUnzaga    267
That is a great idea jyk. I kept trying to visualize how to handle it, and I kept on taking one side or another. I hadn't thought of doing BOTH at the same time.

So in the collision checks, if two are overlapping, call the collision functions for both objects, and then handle the appropriate timers/whatever that way. I don't know why I didn't think of that. ;P

I was considering a space partition system, but for this particular game, there will only be a max of around 6-7 enemies on screen at one time, so nothing too major.

I appreciate the hint about having a double for loop to check collisions one time.

Regarding the damage per second approach, in this particular situation it wouldn't work. The life of the ships are not high numbers, but small like '1', '2', or '11'. Which is just the number of times it can get hit with a bullet, collide with the player, get hit with a bomb, etc.

So working with the damage over time method, would I store the collision times in some kind of lookup table stored on the damage source?

timeTable[entity.id] = timeOfDamage;

Then on the next callback, I check if currentTime-timeTable[entity.id] >= perSecond time, do damage.

Then I'll have to 'clean up' the table when the entity dies or goes out of range.

Think of a situation like a RPG or something where there could be 2-3 players with different 'area effect auras', and there are any number of random enemies in the area that could be in range of these auras.

How would something like that be managed?

Share this post


Link to post
Share on other sites
BUnzaga    267
Zipster, I remember reading that trick about the squared values, but in my specific situation, I am just using a built in engine function that handles it for me. 'VectorMath.distance(v1, v2)'. I did do testing and the built in function was faster than anything I tried to implement on my own. :)

I like the idea for the 'time last damaged' approach. The problem is, I want to figure out the time last damaged, for each source of potential damage individually, not as a whole.

For example, enemy1 comes up, I want to track the time last damaged from enemy1. Then if enemy2 comes up, I want to track the time last damaged from enemy2. Then if yet another enemy comes up, I want this to have its own time last damaged value.

With just one player, it is simple, I could just put a variable to keep track of this for the static player.

I am trying to push beyond my current understanding though, and handle a situation where there could be 3 players, and any number of random enemies. How would each enemy and each player be able to handle the same situation?

Share this post


Link to post
Share on other sites
jyk    2094
Quote:
I was considering a space partition system, but for this particular game, there will only be a max of around 6-7 enemies on screen at one time, so nothing too major.
Yup, for 6-7 objects, just brute-force it! :-)

For the double loop, make sure not to use a simple 'naive' nested loop, or you'll end up performing redundant checks and invoking callbacks multiple times for single pairs. (How to structure the loop so as to avoid this is fairly straightforward, but feel free to ask if you're not sure how to do it.)

For tracking 'damage times', how about a 2-d square array whose dimensions are the number of entities in the game? Entry i, j would represent the amount of time that had elapsed since entity i last damaged entity j. Every update, you would add the current time step to each entry in the array (since the number of total entities is small, this process shouldn't be too costly). Then, in the callback, if (for example) the first entity should damage the second entity (pseudocode):
int i = entity1->get_index();
int j = entity2->get_index();
if (time_since_last_damage[i][j] >= entity1->damage_period()) {
entity2->apply_damage(entity1->damage_amount());
time_since_last_damage[i][j] = 0;
}
[Edit: If non-player entities can't damage each other, then you probably don't need the full 2-d array and can use a more compact data structure instead.]

Share this post


Link to post
Share on other sites
Burnt_Fyr    1665
Sorry to hijack,but jyk, wouldn't storing *time* of the of the last damage, then testing if(currtime - timeoflastdamage >= threshold) on collision avoid the cost of updating the whole table each dt?

Share this post


Link to post
Share on other sites
jyk    2094
Quote:
Sorry to hijack,but jyk, wouldn't storing *time* of the of the last damage, then testing if(currtime - timeoflastdamage >= threshold) on collision avoid the cost of updating the whole table each dt?
Yup, I think you're right - a time stamp would probably make more sense :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this