Mouse 1 = Fire gattling guns at target (can hit air units)
Mouse 2 = Fire tank projectile at target (can only hit ground)
5 = leave FPS mode
Important variables if you change the camera position.
The observer unit (siege tank in my map) will have it's position and absolute height (ground height/air height + height of unit + camera Z offset above unit) adjusted every 0.0 seconds in a separate trigger from the traceline.
When you change position of your observer in the X or Y or both directions, the trigger will automatically handle it for you. However If you decided to change the height of the observer unit you need to change the camera Z offset by the same amount.
For instance G is currently set to 1.5 and the height of the siege tank is at 3. This means our observer sees himself 4.5 above the ground and 1.5 above the siege tank. If we change the siege tank back to ground height (or zero height above the ground) then we set our camera Z offset to 1.5 in our camera trigger, which is precisely the value of G. The value of G itself remains unchanged.
Making a 3-D traceline.
First we will start with a 2-D traceline. In order to make a 2-D traceline we need three pieces of information. The position of the observer, the Yaw of his camera and the Maximum Distance of his gun.
Let P = Position of the Observer
Let A = Yaw of Camera
Let M = Max Distance and M must be an integer.
Now we run a loop a M times. This loop will create M temporary points in the direction of the Yaw starting at P and ending at a distance of M from P. Let C be the temp point.
Pick each integer from 1 to M and do the actions:
Set K picked integer
Set C to P offset by K towards A degrees.
Now that we have created our temp points, we now need to create a circular region centered at the temp point with a radius of 1.25, and thus a diameter of 2.5. Remember that each adjacent temp point is a distance of 1 apart, such that each temp region overlaps the previous region and the one before it. The reason for this (and why we choose circular regions instead of rectangular regions) is given in Appendix A at the end of the post.
So we add this to our loop, let S be the temp Region:
Pick each integer from 1 to M and do the actions:
Set K picked integer
Set C to P offset by K towards A degrees.
Set S = CircleRegion(C, radius = 1.25)
Now we must check each consecutive region for units. We will create a temp unit group named U, we will introduce another Global unit group called FORBID which is a unit group consisting of units that we do want our traceline to detect (for instance we do not want our observer to target himself) and a Global Unit named Tracetarget.
Pick each integer from 1 to M and do the actions:
Set K picked integer
Set C to P offset by K towards A degrees.
Set S = CircleRegion(X of C, Y of X, radius = 1.25)
Set U = units in region S
Pick every unit in U and do actions
If U is not equal to "Empty Unit Group" and Picked Unit is not equal to "No Unit" and Picked unit is in group FORBID equal to false
Angle between P and Picked unit <= 10 degrees
then set Tracetarget = Picked Unit and Break Loop
else if K = M, then set Tracetarget = "No unit" , else do nothing
Our nested conditional on the Else statement makes it so that if our traceline has not detected a unit in the final loop, its sets the Global Tracetarget to No Unit, instead of leaving it as the previously detected unit.
So here we have a fully functioning 2-D traceline. With some modifications we can easily convert this to a 3-D traceline.
In Starcraft 2, the above traceline trigger could cause you to shoot a target high in the air that you cannot see, because its X and Y coordinate happen to be contained inside of the temp regions during the traceline check. In effect Starcraft 2 turns our circular regions into 3-D cylinders of infinite height above and below the observer. What we need to do is modify the trigger to check unit heights in accordance to the Pitch of our camera. To accomplish this, we need only some intuition and knowledge of the sine and cosine trigonometric functions.
Let B = Pitch of Camera.
We must remember that we are now tracing a 3-D line that extends in the direction of the polar coordinates (A , B). The length of this line remains the same as the pitch changes, namely the length of this line remains M. However we must realize that we are dealing with a right triangle now.
The hypotenuse of this triangle is the traceline itself. The main leg of the triangle is on the X-Y plane, this leg extends a distance of cos(B) towards angle A from the point P on the X-Y plane. Call the endpoint of this leg F. The other leg extends a distance of sin(B) at 90 degrees upwards from F. The endpoint of this leg is the edge of our hypotenuse.
So what do we know?
cos(b)^2 + sin(b)^2 = M, which tells us that
Length of first leg = cos(b)
Length of second leg = sin(b)
The first leg is on the X-Y plane and points in the direction of angle A.
The second leg (if we ignore the Yaw of the camera) in on the X-Z plane.
With this information we can modify our trigger that calculates the temp point C in the X-Y plane by scaling the line by cos(b). We will make it read the absolute value of cos(b) so that we don't result with negative distances.
Pick each integer from 1 to M and do the actions:
Set K picked integer
Set C to P offset by K*[Abs(cos(B)] towards A degrees.
We also want to read the height of this X-Y point when projected onto the hypotenuse. We know that similar triangles are proportional, so we can calculate the height of the projected point simply by the loop number and sin(b). Let H be a real variable that will be set to the height of the projected point.
Pick each integer from 1 to M and do the actions:
Set K picked integer
Set C to P offset by K*[Abs(cos(B)] towards A degrees.
Set H = sin(-b) + constant
Notice that we are calculated the sine of negative b instead of positive b. This is because some douche at Blizzard Entertainment decided to make the pitch = -90 when looking at the zenith and +90 when looking straight at the ground, instead of the other way around. If we do not change this to negative b in the sine argument, then we will be projecting a hypotenuse that is reflected over the X-Y plane from what we had originally intended.
We also added a constant here. The constant is the actual height of the observer. This is so that if our observer is in an aerial craft above the ground, our heights on the projected hypotenuse will all be adjusted in a uniform manner.
Ok, we're almost done! We now need to do one more thing. As of now, our cylinders still have an infinite height above and below the observer. We need to put bounds on these cylinders according the the Pitch of the Camera. The bounds of the cylinders will be within 1.25 units of the height of the temp point along the hypotenuse.
So now we add this to our loop.
Pick each integer from 1 to M and do the actions:
Set K picked integer
Set C to P offset by K*[Abs(cos(B)] towards A degrees.
Set H = K*sin(-b) + constant
Set S = CircleRegion(X of C, Y of X, radius = 1.25)
Set U = units in region S
Pick every unit in U and do actions
If U is not equal to "Empty Unit Group"
and Picked Unit is not equal to "No Unit"
and Picked unit is in group FORBID equal to false
and Height of Picked Unit is less than or equal to H + 1.25
and Height of Picked Unit is greter than or equal to H -1.25
then set Tracetarget = Picked Unit and Break Loop
else if K = M, then set Tracetarget = "No unit" , else do nothing
What is happening here is that any unit whose height is not within the bounds H - 1 to H + 1 within the temp region, is being rejected in the traceline detection, thus we have a fully functioning 3-D traceline that uses uniformly overlapping cylinders as its detection method.
Appendix A
The reason that circular regions are chosen instead of rectangular regions, is because rectangular regions cannot be rotated to correspond to the Yaw. Rectangular regions in Starcraft 2 have their sides parallel to the x and y axes of the map. This means that the overlapping of rectangular regions at different Yaws produce different overlap patterns, thus a mitigating factor would have to be added to increase the area of each region according to its Yaw. This would have to be handled with the Tan and Cotan functions and modular arithmetic in modulus 45. Also, even when dealt with, it still does not produce uniform overlapping patterns, but simply negates traceline failures, at the expense of creating traceline false positives at the Yaws near 45, 135, 225 and 315 degrees.
Circular regions on the other hand have the advantage of creating uniform overlap patterns at any Yaw values, thus the traceline failure rate and false positive rate can be controlled by simply changing the radius of our circles (assuming our observer knows how to aim) and are uniform in all directions. The amount of overlap is determined by the radius. A larger radius creates more overlap, preventing traceline errors, but a radius that is too large (take 100 units for example) would produce false positives, and the gun would shoot things that you do not appear to be aiming at.
EDIT:
False positives can be negated completely by setting a limit to the difference between the angles "Yaw of camera" and "angle from P to picked unit." Set this limit to something that is appropriate to your camera's field of view.
Also the arcsine of the Distance between P and picked unit/(Absolute height of picked unit - Absolute height of observer) returns the 3-D angle between P and picked unit. I also set this difference limit to 10. Remember that this is compared to the player camera's pitch.
Optimizing the traceline for performance. Notice that each time the loop runs we calculate sin(-b) and Abs[cos(b)]. Since these values are constant we can calculate them one time at the start of the trigger in the variables section. Make two real variables Q and R then:
Q = Abs[cos(B)]
R = sin(-b)
Now replace those expressions with their variables in the loop trigger.
Pick each integer from 1 to M and do the actions:
Set K picked integer
Set C to P offset by K*Qtowards A degrees.
Set H = K*R + constant
Set S = CircleRegion(X of C, Y of X, radius = 1.25)
Set U = units in region S
Pick every unit in U and do actions
If U is not equal to "Empty Unit Group"
and Picked Unit is not equal to "No Unit"
and Picked unit is in group FORBID equal to false
and Height of Picked Unit is less than or equal to H + 1.25
and Height of Picked Unit is greter than or equal to H -1.25
then set Tracetarget = Picked Unit and Break Loop
else if K = M, then set Tracetarget = "No unit" , else do nothing
Now our trigger doesn't have to run these calucations for each execution of the loop, saving memory. Also rename all your variables to 1 letter names, as this also saves memory. Always try to optimize memory on a trigger of this magnitude. Remember that it will run at least 4 times a second (mine is set to 8 times a second) and will often complete all 50 loops per execution when there is not target. This can be a whopping 400 loops a second overall, thus reducing everything to one letter, including the name of the trigger itself, frees a lot of memory.
EDIT:
In order to synchronize the Traceline and Collision (and other related triggers), I added an independent trigger called CVR for Continuous Variable Reset. This trigger executes every 0.125 seconds (or 8 times a second) in which it updates the values of 11 different arrays of size 12. The camera Yaw and Camera Pitch of each player is an example, as well as the positions and absolute height of the observers and their current Traceline Target.
Now the Traceline triggers and Collision triggers take the non-local data from the CVR instead of calculating it themselves, this alone attributed to a 0.5 - 1 degrees (Fahrenheit) drop in CPU temperature under the same test conditions and external environment (with all 12 tracelines and collisions running simultaneously, I simply executed it for Player 1 twelve times to test it).
EDIT 2:
Both the Traceline and the Collision triggers are executed when the player clicks either mouse 1 or mouse 2 to shoot their weapon, and it cannot be activated again for another 0.25 seconds (or 1/4 of a quarter). However your gun will continuing firing at a target in between this quarter second period, so there is no delay between shots. This attributed to substantial 2-3 degree cooldown in CPU under same conditions and nearly a 5-6 degrees cooldown with all 12 running. Overall we knocked about 7 degrees off of a quad-core 3.2 GZ processor with L3 cache (my processor).
EDIT 3:
Upon lifting Mouse 1 or Mouse 2, the observer unit is automatically ordered to stop attacking, this trigger does not have a cooldown. So your gun will stop firing at the tracetarget even if it within the 1/4 second timeframe. Did not effect temperatures, but made it aesthetically pleasing.
I was PM'ed that this traceline doesn't account for changing terrain heights. As my map was only testing a completely flat environment, I forgot to handle the issue of relative height vs absolute height. Here is the fix.
Let E = Height of the Ground at the position of the observer
Let F = Height of the Ground at the position of detected unit.
Compensate for the changing terrain height by modifying H with the following command:
Set H = H - ( F - E )
Your cylinders are now checking the absolute heights of the units. I'll upload the map again.
Adding memory efficient Terrain Collision as requested. Going to add Doodad collision soon.
About the Collision Check or the TCC trigger (for Terrain Collision Check):
The TCC trigger is independent of the Traceline trigger itself. The traceline already handles "unit to unit collision" thus we need not worry about shooting through units to reach our target. It should be mentioned that the Traceline trigger was also edited in this map to create dummy units at the end of the traceline if nothing is detected, allowing it to shoot at "nothing".
The TCC itself has no event, the trigger is run by other triggers, in our case the Attack Triggers. When a player presses mouse 1 or 2 to fire his laser gun, it will first run the TCC trigger. The TCC trigger creates a line of 100 partitions between the observer and the Traceline Target. If the terrain height is greater at any partition is greater than the absolute height of the unit, the TCC changes the Global Boolean Variable named "Terrain Collide" to true. If not, it changes it to false.
After the TCC has completed the Attack Trigger will resume, this is accomplished by the "Run Trigger and wait until it completes" action in the trigger GUI. The Attack Trigger reads the condition "If Terrain Collide is false then proceed to attack target, otherwise it will attack the point of terrain where collision first occurs. Also, if there is no unit in the traceline, the gun will still fire at the "dummy unit" at the end of the traceline and terrain collision between the observer and the dummy unit will also be detected by the TCC.
WARNING:
Some of the parameters in the Collision Check need to be changed according to the unit that is being checked for collision.
For instance, suppose there is a marine whose head we can see from above a hill. His absolute height (at his feet) is lower than the maximal ground height in our traceline. The result is that our TCC will register a terrain collision and will not shoot the unit.
To fix this we increase the absolute height of the targeted unit to the actual height of the model. So for marines we add a value (or a linear offset) of 1.15
For vikings we add 1.35 instead of 1.15. This ensures that the unit will pass the collision check.
The best way to go about this is to add custom values to units and create units on the map via triggers (this also saves memory). For instance every Marine that I will create will have a custom value 1 = 1.15 and vikings will have custom value 1 = 1.35. The collision check will read Custom value 1 of all units for its offset.
Your main problem is calling "Units in region" 50 times. Do you realize the engine goes through every unit in game when you call this function? That means for 500 units in game, 25 000 unit position test are run. That is the cause of FPS dropping to 1 in FP mode, even without any terrain.
What you should do is pick all units in a big region, that covers the range, and test for each unit, how far it is from the line of fire.
Problem is it's the only publicly available traceline (without downloading an entire mod/bundled crap). If someone has a better one (which I'm sure they do since you just told me), then make a standalone version and upload it, also provide the link here.
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
Commands: Backspace = Enters FPS mode
Mouse 1 = Fire gattling guns at target (can hit air units)
Mouse 2 = Fire tank projectile at target (can only hit ground)
5 = leave FPS mode
Important variables if you change the camera position.
The observer unit (siege tank in my map) will have it's position and absolute height (ground height/air height + height of unit + camera Z offset above unit) adjusted every 0.0 seconds in a separate trigger from the traceline.
When you change position of your observer in the X or Y or both directions, the trigger will automatically handle it for you. However If you decided to change the height of the observer unit you need to change the camera Z offset by the same amount.
For instance G is currently set to 1.5 and the height of the siege tank is at 3. This means our observer sees himself 4.5 above the ground and 1.5 above the siege tank. If we change the siege tank back to ground height (or zero height above the ground) then we set our camera Z offset to 1.5 in our camera trigger, which is precisely the value of G. The value of G itself remains unchanged.
@EdwardSolomon: Go
Making a 3-D traceline. First we will start with a 2-D traceline. In order to make a 2-D traceline we need three pieces of information. The position of the observer, the Yaw of his camera and the Maximum Distance of his gun.
Let P = Position of the Observer Let A = Yaw of Camera Let M = Max Distance and M must be an integer.
Now we run a loop a M times. This loop will create M temporary points in the direction of the Yaw starting at P and ending at a distance of M from P. Let C be the temp point.
Pick each integer from 1 to M and do the actions: Set K picked integer Set C to P offset by K towards A degrees.
Now that we have created our temp points, we now need to create a circular region centered at the temp point with a radius of 1.25, and thus a diameter of 2.5. Remember that each adjacent temp point is a distance of 1 apart, such that each temp region overlaps the previous region and the one before it. The reason for this (and why we choose circular regions instead of rectangular regions) is given in Appendix A at the end of the post.
So we add this to our loop, let S be the temp Region:
Pick each integer from 1 to M and do the actions: Set K picked integer Set C to P offset by K towards A degrees. Set S = CircleRegion(C, radius = 1.25)
Now we must check each consecutive region for units. We will create a temp unit group named U, we will introduce another Global unit group called FORBID which is a unit group consisting of units that we do want our traceline to detect (for instance we do not want our observer to target himself) and a Global Unit named Tracetarget.
Pick each integer from 1 to M and do the actions: Set K picked integer Set C to P offset by K towards A degrees. Set S = CircleRegion(X of C, Y of X, radius = 1.25) Set U = units in region S Pick every unit in U and do actions If U is not equal to "Empty Unit Group" and Picked Unit is not equal to "No Unit" and Picked unit is in group FORBID equal to false Angle between P and Picked unit <= 10 degrees then set Tracetarget = Picked Unit and Break Loop else if K = M, then set Tracetarget = "No unit" , else do nothing
Our nested conditional on the Else statement makes it so that if our traceline has not detected a unit in the final loop, its sets the Global Tracetarget to No Unit, instead of leaving it as the previously detected unit.
So here we have a fully functioning 2-D traceline. With some modifications we can easily convert this to a 3-D traceline.
In Starcraft 2, the above traceline trigger could cause you to shoot a target high in the air that you cannot see, because its X and Y coordinate happen to be contained inside of the temp regions during the traceline check. In effect Starcraft 2 turns our circular regions into 3-D cylinders of infinite height above and below the observer. What we need to do is modify the trigger to check unit heights in accordance to the Pitch of our camera. To accomplish this, we need only some intuition and knowledge of the sine and cosine trigonometric functions.
Let B = Pitch of Camera.
We must remember that we are now tracing a 3-D line that extends in the direction of the polar coordinates (A , B). The length of this line remains the same as the pitch changes, namely the length of this line remains M. However we must realize that we are dealing with a right triangle now.
The hypotenuse of this triangle is the traceline itself. The main leg of the triangle is on the X-Y plane, this leg extends a distance of cos(B) towards angle A from the point P on the X-Y plane. Call the endpoint of this leg F. The other leg extends a distance of sin(B) at 90 degrees upwards from F. The endpoint of this leg is the edge of our hypotenuse.
So what do we know? cos(b)^2 + sin(b)^2 = M, which tells us that
Length of first leg = cos(b) Length of second leg = sin(b)
The first leg is on the X-Y plane and points in the direction of angle A. The second leg (if we ignore the Yaw of the camera) in on the X-Z plane.
With this information we can modify our trigger that calculates the temp point C in the X-Y plane by scaling the line by cos(b). We will make it read the absolute value of cos(b) so that we don't result with negative distances.
Pick each integer from 1 to M and do the actions: Set K picked integer Set C to P offset by K*[Abs(cos(B)] towards A degrees.
We also want to read the height of this X-Y point when projected onto the hypotenuse. We know that similar triangles are proportional, so we can calculate the height of the projected point simply by the loop number and sin(b). Let H be a real variable that will be set to the height of the projected point.
Pick each integer from 1 to M and do the actions: Set K picked integer Set C to P offset by K*[Abs(cos(B)] towards A degrees. Set H = sin(-b) + constant
Notice that we are calculated the sine of negative b instead of positive b. This is because some douche at Blizzard Entertainment decided to make the pitch = -90 when looking at the zenith and +90 when looking straight at the ground, instead of the other way around. If we do not change this to negative b in the sine argument, then we will be projecting a hypotenuse that is reflected over the X-Y plane from what we had originally intended.
We also added a constant here. The constant is the actual height of the observer. This is so that if our observer is in an aerial craft above the ground, our heights on the projected hypotenuse will all be adjusted in a uniform manner.
Ok, we're almost done! We now need to do one more thing. As of now, our cylinders still have an infinite height above and below the observer. We need to put bounds on these cylinders according the the Pitch of the Camera. The bounds of the cylinders will be within 1.25 units of the height of the temp point along the hypotenuse.
So now we add this to our loop.
Pick each integer from 1 to M and do the actions: Set K picked integer Set C to P offset by K*[Abs(cos(B)] towards A degrees. Set H = K*sin(-b) + constant Set S = CircleRegion(X of C, Y of X, radius = 1.25) Set U = units in region S Pick every unit in U and do actions If U is not equal to "Empty Unit Group" and Picked Unit is not equal to "No Unit" and Picked unit is in group FORBID equal to false and Height of Picked Unit is less than or equal to H + 1.25 and Height of Picked Unit is greter than or equal to H -1.25 then set Tracetarget = Picked Unit and Break Loop else if K = M, then set Tracetarget = "No unit" , else do nothing
What is happening here is that any unit whose height is not within the bounds H - 1 to H + 1 within the temp region, is being rejected in the traceline detection, thus we have a fully functioning 3-D traceline that uses uniformly overlapping cylinders as its detection method.
Appendix A The reason that circular regions are chosen instead of rectangular regions, is because rectangular regions cannot be rotated to correspond to the Yaw. Rectangular regions in Starcraft 2 have their sides parallel to the x and y axes of the map. This means that the overlapping of rectangular regions at different Yaws produce different overlap patterns, thus a mitigating factor would have to be added to increase the area of each region according to its Yaw. This would have to be handled with the Tan and Cotan functions and modular arithmetic in modulus 45. Also, even when dealt with, it still does not produce uniform overlapping patterns, but simply negates traceline failures, at the expense of creating traceline false positives at the Yaws near 45, 135, 225 and 315 degrees.
Circular regions on the other hand have the advantage of creating uniform overlap patterns at any Yaw values, thus the traceline failure rate and false positive rate can be controlled by simply changing the radius of our circles (assuming our observer knows how to aim) and are uniform in all directions. The amount of overlap is determined by the radius. A larger radius creates more overlap, preventing traceline errors, but a radius that is too large (take 100 units for example) would produce false positives, and the gun would shoot things that you do not appear to be aiming at.
EDIT: False positives can be negated completely by setting a limit to the difference between the angles "Yaw of camera" and "angle from P to picked unit." Set this limit to something that is appropriate to your camera's field of view.
Also the arcsine of the Distance between P and picked unit/(Absolute height of picked unit - Absolute height of observer) returns the 3-D angle between P and picked unit. I also set this difference limit to 10. Remember that this is compared to the player camera's pitch.
@EdwardSolomon: Go
Optimizing the traceline for performance. Notice that each time the loop runs we calculate sin(-b) and Abs[cos(b)]. Since these values are constant we can calculate them one time at the start of the trigger in the variables section. Make two real variables Q and R then:
Q = Abs[cos(B)] R = sin(-b)
Now replace those expressions with their variables in the loop trigger. Pick each integer from 1 to M and do the actions: Set K picked integer Set C to P offset by K*Qtowards A degrees. Set H = K*R + constant Set S = CircleRegion(X of C, Y of X, radius = 1.25) Set U = units in region S Pick every unit in U and do actions If U is not equal to "Empty Unit Group" and Picked Unit is not equal to "No Unit" and Picked unit is in group FORBID equal to false and Height of Picked Unit is less than or equal to H + 1.25 and Height of Picked Unit is greter than or equal to H -1.25 then set Tracetarget = Picked Unit and Break Loop else if K = M, then set Tracetarget = "No unit" , else do nothing
Now our trigger doesn't have to run these calucations for each execution of the loop, saving memory. Also rename all your variables to 1 letter names, as this also saves memory. Always try to optimize memory on a trigger of this magnitude. Remember that it will run at least 4 times a second (mine is set to 8 times a second) and will often complete all 50 loops per execution when there is not target. This can be a whopping 400 loops a second overall, thus reducing everything to one letter, including the name of the trigger itself, frees a lot of memory.
EDIT: In order to synchronize the Traceline and Collision (and other related triggers), I added an independent trigger called CVR for Continuous Variable Reset. This trigger executes every 0.125 seconds (or 8 times a second) in which it updates the values of 11 different arrays of size 12. The camera Yaw and Camera Pitch of each player is an example, as well as the positions and absolute height of the observers and their current Traceline Target.
Now the Traceline triggers and Collision triggers take the non-local data from the CVR instead of calculating it themselves, this alone attributed to a 0.5 - 1 degrees (Fahrenheit) drop in CPU temperature under the same test conditions and external environment (with all 12 tracelines and collisions running simultaneously, I simply executed it for Player 1 twelve times to test it).
EDIT 2: Both the Traceline and the Collision triggers are executed when the player clicks either mouse 1 or mouse 2 to shoot their weapon, and it cannot be activated again for another 0.25 seconds (or 1/4 of a quarter). However your gun will continuing firing at a target in between this quarter second period, so there is no delay between shots. This attributed to substantial 2-3 degree cooldown in CPU under same conditions and nearly a 5-6 degrees cooldown with all 12 running. Overall we knocked about 7 degrees off of a quad-core 3.2 GZ processor with L3 cache (my processor).
EDIT 3: Upon lifting Mouse 1 or Mouse 2, the observer unit is automatically ordered to stop attacking, this trigger does not have a cooldown. So your gun will stop firing at the tracetarget even if it within the 1/4 second timeframe. Did not effect temperatures, but made it aesthetically pleasing.
@EdwardSolomon: Go
I was PM'ed that this traceline doesn't account for changing terrain heights. As my map was only testing a completely flat environment, I forgot to handle the issue of relative height vs absolute height. Here is the fix.
Let E = Height of the Ground at the position of the observer Let F = Height of the Ground at the position of detected unit.
Compensate for the changing terrain height by modifying H with the following command:
Set H = H - ( F - E )
Your cylinders are now checking the absolute heights of the units. I'll upload the map again.
@EdwardSolomon: Go
Adding memory efficient Terrain Collision as requested. Going to add Doodad collision soon.
About the Collision Check or the TCC trigger (for Terrain Collision Check):
The TCC trigger is independent of the Traceline trigger itself. The traceline already handles "unit to unit collision" thus we need not worry about shooting through units to reach our target. It should be mentioned that the Traceline trigger was also edited in this map to create dummy units at the end of the traceline if nothing is detected, allowing it to shoot at "nothing".
The TCC itself has no event, the trigger is run by other triggers, in our case the Attack Triggers. When a player presses mouse 1 or 2 to fire his laser gun, it will first run the TCC trigger. The TCC trigger creates a line of 100 partitions between the observer and the Traceline Target. If the terrain height is greater at any partition is greater than the absolute height of the unit, the TCC changes the Global Boolean Variable named "Terrain Collide" to true. If not, it changes it to false.
After the TCC has completed the Attack Trigger will resume, this is accomplished by the "Run Trigger and wait until it completes" action in the trigger GUI. The Attack Trigger reads the condition "If Terrain Collide is false then proceed to attack target, otherwise it will attack the point of terrain where collision first occurs. Also, if there is no unit in the traceline, the gun will still fire at the "dummy unit" at the end of the traceline and terrain collision between the observer and the dummy unit will also be detected by the TCC.
WARNING: Some of the parameters in the Collision Check need to be changed according to the unit that is being checked for collision.
For instance, suppose there is a marine whose head we can see from above a hill. His absolute height (at his feet) is lower than the maximal ground height in our traceline. The result is that our TCC will register a terrain collision and will not shoot the unit.
To fix this we increase the absolute height of the targeted unit to the actual height of the model. So for marines we add a value (or a linear offset) of 1.15
For vikings we add 1.35 instead of 1.15. This ensures that the unit will pass the collision check.
The best way to go about this is to add custom values to units and create units on the map via triggers (this also saves memory). For instance every Marine that I will create will have a custom value 1 = 1.15 and vikings will have custom value 1 = 1.35. The collision check will read Custom value 1 of all units for its offset.
This is so ineficient!!! I don't know where you got the idea for such a traceline, but it is just a wrong solution. Also rrowland already posted this same algotrithm here. By the way, here is how it's done : http://www.hiveworkshop.com/forums/starcraft-ii-editor-help-zone-647/simple-2d-traceline-function-167915/
Your main problem is calling "Units in region" 50 times. Do you realize the engine goes through every unit in game when you call this function? That means for 500 units in game, 25 000 unit position test are run. That is the cause of FPS dropping to 1 in FP mode, even without any terrain.
What you should do is pick all units in a big region, that covers the range, and test for each unit, how far it is from the line of fire.
how about just fire off projectiles with collision....
@M0rt1mer: Go
Problem is it's the only publicly available traceline (without downloading an entire mod/bundled crap). If someone has a better one (which I'm sure they do since you just told me), then make a standalone version and upload it, also provide the link here.