HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

The Mechanics Behind a Wave

03-28-2010, 04:37 PM#1
Panto
Greets!

First, a confession: This isn't really for any particular project. I'm just trying to get a solid handle on the process involved in this trigger for my own education. Any help you can provide is fantastic, and I'm looking for the theoretical best answer more than the easy one. I speak jass if it is useful to answer in code.

So, to it.

Blizzard has a number of "wave" spells, like Carrion Swarm, Shockwave, Impale, Breath of Fire, Breath of Frost, Breath of Lemmings. Mostly these spells are pretty unfortunate: There's no way to easy "get" who the spell affected, they frequently have hardcoded targets or buffs, they lag through doodads and so on.

So I'd like to understand how these spells can be coded. Firstly, one must be able to get all the units/doodads/structures or whathaveyou inside the area that you want to affect. Let's suppose that we have:
  • The caster's X/Y
  • The angle between the caster and the target X/Y or unit
  • The full distance of the wave
  • The angle from either side of the casting direction that should also be affected
How do we get what widgets are in that space? It looks like this:

Zoom (requires log in)

Then there's another level of complexity. This is a wave, so it should affect the closest targets first, then farther out et cetera. How do we get the widgets in slices of a certain depth of the original area, so that we may affect them in stages? It might look like this:

Zoom (requires log in)

I ought to tell you that my geometry isn't very fresh, so I welcome any amount of detail you wish to give.

Thanks!
Attached Images
File type: jpgWave Diagram 2.jpg (103.2 KB)
03-28-2010, 05:16 PM#2
Earth-Fury
Enum units in incrementally larger circles. Cull units who's squared distance from the caster is equal to or less than the square of the previous circle's radius. (You use squared distances to avoid needlessly computing squared roots)

This gives you units in an initial circle then in rings.

To cull units outside of the cone, you have to ensure the angle between the caster and target is within a certain distance (the width of the code) of the cast angle.

Likely horribly suboptimal way: (My maths is subpar in this area)

RAD_TO_DEG * Atan2(by-ay, bx-ax) = angle of the line ax->bx in degrees

c = angle of the line caster->cast target point
t = angle of the line caster->target

if t - c <= 180 then
d = t - c
else
d = (c + 360) - t
endif

if d <= ConeWidth / 2 then
// Unit is in cone
endif
03-28-2010, 05:17 PM#3
Kueken
Blizzard waves can be approximated by a missile with a collision size (the start area), starting at a polar projection from the caster by (start area) in the direction the spell was casted, then increase the collision size of the projectile dynamically while it travels up to the end area size.
I made a testmap for this recently

Note that you can see, that blizzard's waves use an update intervall significantly less than 0.025, which xecollider uses :) (I found this to be pretty interesting when testing the map).

This map was originally a test for my GroupEnumUnitsInCone function, however it proved, there was almost no use for such a function, because you can hardly tell any difference using this method or a hardcoded wave.
Attached Files
File type: w3xCarrionSwarm.w3x (91.1 KB)
03-28-2010, 05:21 PM#4
Ammorth
I would do an emum units in circle for each "slice", determine if the angle between the caster and the target is within the bounds of the wave, check if the unit is within the specified distance from the caster (in the proper slice) and then deal damage to it.

To handle the time delay between slice damages, I would check the previous slice as well, for new units within the slice.
03-28-2010, 05:30 PM#5
Anitarf
The wave is most likely not shaped like a slice of a circle. First of all, wave spells can have a starting width, so it would be a slice of a ring rather than full circle or, more likely, an isosceles trapezoid (in the case of shockwave which has the same start and end width, a rectangle).

Furthermore, the units affected are not grouped instantly, but as the spell projectile passes them. This can be easily demonstrated by fireing a slow wave spell towards a unit and then moving the unit out of the way and moving another unit that was outside of the affected area into the way.

As such, line/cone spells are best simulated by a moving projectile which affects units in a circular area (I never tested the spells in enough detail to know if this is exactly how they work, but at worst it is still a reasonable approximation while also being the fastest method to code it) with diameter equal to the spell's width (when simulating spells that have different starting and ending width, the diamater changes as the projectile moves). xecollider, for example, can be used to simulate such spells this way.

Edit: Damn it, I'm really slow at typing this, three people beat me to it.

Quote:
Originally Posted by Earth-Fury
Enum units in incrementally larger circles. Cull units who's squared distance from the caster is equal to or less than the square of the previous circle's radius. (You use squared distances to avoid needlessly computing squared roots)
Or you store the already hit units in a group and then use a IsUnitInGroup check instead of a distance check, which also allows you to avoid missing the units moving towards the caster and hitting the units moving away from the caster twice.
03-28-2010, 05:34 PM#6
Kueken
Quote:
Originally Posted by Anitarf
The wave is most likely not shaped like a slice of a circle. First of all, wave spells can have a starting width, so it would be a slice of a ring rather than full circle or, more likely, an isosceles trapezoid (in the case of shockwave which has the same start and end width, a rectangle).

Furthermore, the units affected are not grouped instantly, but as the spell projectile passes them. This can be easily demonstrated by fireing a slow wave spell towards a unit and then moving the unit out of the way and moving another unit that was outside of the affected area into the way.

As such, line/cone spells are best simulated by a moving projectile which affects units in a circular area (I never tested the spells in enough detail to know if this is exactly how they work, but at worst it is still a reasonable approximation while also being the fastest method to code it) with diameter equal to the spell's width (when simulating spells that have different starting and ending width, the diamater changes as the projectile moves). xecollider, for example, can be used to simulate such spells this way.

Yes, this is exactly what I have done in the testmap, and it matches the hardcoded carrion swarm almost perfectly (for appropiate values, of course)

€ Sorry, Anitarf. ;) At least you linked xecollider, which I even forgot to mention ;)

Oh btw, I am using onLoop for this, but I heard, this would be bad, because those interface methods are really slow (onUnitHit can/should be used, because it is not used every interval but onLoop should be avoided because its like 20 times slower than a function call or more)
Is this significant enough to require special handling? (like run another timer just to do the periodic stuff)

Quote:
Or you store the already hit units in a group and then use a IsUnitInGroup check instead of a distance check, which also allows you to avoid missing the units moving towards the caster and hitting the units moving away from the caster twice.
Hardcoded spells CAN hit multipe times, if you want to simulate them exactly, you might want to consider this. xecollider can hit multiple times, too, if a unit leaves the collision radius and re-enters it.
03-28-2010, 05:47 PM#7
Panto
It occurs to me that there's another dimension to this idea that I want to cover. It sounds like the methods discussed are good for finding and affecting target units. I am also interested in defining an area for each slice to play spell effects within that area, should it be desirable to do so. Thoughts on this?

EDIT: Earth-Fury's method touches on finding units in an area, which can be adapted to finding random points in an area for spell effects; however, is there a way to do this without first enumerating a lot of unnecessary units and then culling them out?
03-28-2010, 05:51 PM#8
Kueken
I am not sure, if I got you right, you want to create special effects alongside with the wave? Like a trail of effects or something? Well, probably I misunderstood, because this would be very easy when picking units in range of coordinates anyway...
03-28-2010, 05:57 PM#9
Panto
That would work, but I believe it would have some overlap as the size of the picked area increased. In other words, as the wave progresses, it will leave behind more effects in the previous area before the pick circle moves out of it. Or, perhaps I'm missing something, here.
03-28-2010, 06:01 PM#10
Kueken
If you watch my testmap, I am using xecollider with a constant speed, so the coordinates change with a constant distance. Just the area around this increases.

If you spawn effects periodically, you would get a constant trail of effects alongside with the wave.

You can always use counter variables to vary the effect spawning rate, or effects with different sizes, or whatever.

Also, xecollider uses enumerations of more units, too, and sorts them out (to consider unit collision size properly). This is not a big deal usually, because every unit filtered out runs like one if statement and some calculations or, in case of xecollider the IsUnitInRangeXY native. YOu do not want to enum more units than you have to, but if you have to enumerate some more units, it is not that bad.
03-28-2010, 06:05 PM#11
Anitarf
Again, for a reasonable approximation, you could create some effects in the onLoop method at random points inside the damage area, and increase the number of effects created this way as the damage area radius increases.

Quote:
Originally Posted by Kueken
Hardcoded spells CAN hit multipe times, if you want to simulate them exactly, you might want to consider this. xecollider can hit multiple times, too, if a unit leaves the collision radius and re-enters it.
That is correct, thanks for the clarification. The spell will not continue hitting the unit as the projectile passes over it, but if the unit blinks in front of the projectile after it has passed over that unit then it will be hit again.
03-29-2010, 07:41 PM#12
Panto
The map works great; you really can't tell the difference between the xecollider wave and the original.

I don't speak vjass very well at all. I think this is a solution that I could implement from jass if I can sort out what's happening in the xecollider script. Is that correct?

EDIT: Is it necessary to have a "moving projectile"? Can I calculate several points along the path and simply get all units near those points, inside a radius that expands from point to point?
03-29-2010, 08:39 PM#13
Anitarf
Quote:
Originally Posted by Panto
EDIT: Is it necessary to have a "moving projectile"? Can I calculate several points along the path and simply get all units near those points, inside a radius that expands from point to point?
It is necessary if you want a wave-type spell, since not having a projectile would no longer be the same thing.
03-29-2010, 08:47 PM#14
Panto
If the wave doesn't have a projectile but instead uses those spawned effects, does that change your answer?
03-29-2010, 08:53 PM#15
Kueken
You don't need, you can just calculate points, if you want to.