7
\$\begingroup\$

My game's a simple 2D one, but this probably applies to many types of scenarios.

Suppose my player has a sword, or a gun that shoots a projectile that can pass through and hit multiple enemies.

While the sword is swinging, there is a duration where I am checking for the sword making contact with any enemy on every frame. But once an enemy is hit by that sword, I don't want him to continue getting hit over and over as the sword follows through. (I do want the sword to continue checking whether it is hitting other enemies.)

I've thought of a couple different approaches (below), but they don't seem like good ones to me. I'm looking for a way that doesn't force cross-referencing (I don't want the enemy to have to send a message back to the sword/projectile). And I'd like to avoid generating/resetting multiple array lists with every attack.

  • Each time the sword swings it generates a unique id (maybe by just incrementing a global static long). Every enemy keeps a list of id's of swipes or projectiles that have already hit them, so the enemy knows not to get hurt by something multiple times. Downside is that every enemy may have a big list to compare to. So projectiles and sword swipes would have to broadcast their end-of-life to all enemies and cause a search and remove on every enemy's array list. Seems kind of slow.
  • Each sword swipe or projectile keeps its own list of enemies that it has already hit so it knows not to apply damage. Downsides: Have to generate a new list (probably pull from a pool and clear one) every time a sword is swung or a projectile shot. Also, this breaks down modularity, because now the sword has to send a message to the enemy, and the enemy has to send a message back to the sword. Seems to me that two-way streets like this are a great way to create very difficult-to-find bugs.
\$\endgroup\$

4 Answers 4

8
\$\begingroup\$

The resolution for this could be the same you'd use for processing held keys on the keyboard.

colliding = checkCollision(this, otherObject);
if(colliding && !previousFrameColliding) {
    //trigger hit with otherObject
} else if(colliding && previousFrameColliding) {
    //still in contact, do what you will here, or nothing
} else if(!colliding && previousFrameColliding) {
    //we just finished a collision
} else {
    //not colliding
}
previousFrameColliding = colliding;

If you're likely to only be colliding with one object at a time, this will work. If you're going to be colliding with multiple objects at the same time, you'll need to track the "currently colliding with" objects and check against that.

\$\endgroup\$
3
  • \$\begingroup\$ Unfortunately, I have projectiles flying around all over the place from multiple sources, so this method sends me back to my first idea above. \$\endgroup\$
    – TenFour04
    Oct 25, 2012 at 18:37
  • \$\begingroup\$ The above would be kept in the projectile or sword. But the idea is the same however you implement it. It's about detecting when you change from not colliding to colliding. It's not about checking "am I colliding right now" \$\endgroup\$
    – House
    Oct 25, 2012 at 18:40
  • \$\begingroup\$ OK, I understand now. Each enemy keeps a list of things that have previously hit it, but it can remove an object from the list itself once that object stops colliding with it. Doesn't need that object to broadcast its death. \$\endgroup\$
    – TenFour04
    Oct 25, 2012 at 18:54
4
\$\begingroup\$

With something like a sword swing, you don't actually care that much about the question "who am I colliding with right now?" What you care about is not state, but rather, change of state, that is, you're interested in knowing when the sword/enemy collision state changes from false to true. The only way you can measure changes in state is to compare your current state against your previous state.

So have each object maintain two sets: the set of what I'm colliding with now (i.e., this frame), and the set of what I was colliding with previously (i.e., last frame). The difference of these two sets provides you with the set of things I am colliding with now that I was not previously colliding with.

I see no reason why this would necessarily involve passing messages back and forth between the colliding entities. Your sword knows that it hit something; it doesn't need to get approval from the thing it hit! Make the sword tell the enemy that it was hit, and have the enemy react appropriately, or whatever makes sense in your scenario.

\$\endgroup\$
2
\$\begingroup\$

You don't necessarily need a huge list. For example, if you were limited to just four players, you could just use four small timers on each enemy (one for each possible player). These timers could be as small as a few bits, where all timers are decremented every 1/5th of a second or so.

If you have a larger number of players, you can again only worry about a small number of hits at a time. Even if you have 1,000 players, it's unlikely that all 1,000 will be attacking the same enemy. The enemy can keep a small fixed-size ring buffer of recent attackers and timestamps. If by some chance too many players do attack a particular enemy, you can just ignore the attacks that would "overflow" the buffer (it's unlikely to be a big deal to the players).

Yet another option is to keep a single global set of attack-to-enemy timers. If you have a small number of actively engaged enemies at any given time, this approach can keep your memory usage way down, have an extremely minimal impact on performance (might even be better due to keeping things in cache better; profile to be sure, of course), and is pretty simple to implement. This is how many of the Minecraft-like games keep track of transitory per-block state (like mining "damage," for example), since storing state in blocks is not feasible for memory reasons and since there's generally only a tiny handful of blocks in a transitory state at any given point in time despite there being something like 2x10^17 blocks.

For larger highly-multiplayer worlds, you could use the shared attack-to-enemy timer set, but instead of making it global store it per-zone (or chunk or area or whatever processing-oriented spatial subdivision you use).

Remember that with games, you don't need to be 100% correct. You only need to be correct enough to keep players happy. You don't actually need to allow an enemy to take damage from all attacks but only once; you only need to ensure that the combat feels fun and enjoyable. If you run into an algorithmic problem, in games you often just change the game play slightly to eliminate the problem entirely or rescope it to an easier to solve problem, rather than trying to actually solve something which quite possibly has no acceptable solutions.

\$\endgroup\$
0
\$\begingroup\$

One variable on the sword:

When you swing the sword you clear it.

When it hits something you store the ID of the object that it hit.

When you find that it's colliding with the last object that it hit you disregard the hit.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .