Hello all,
I've been hanging out for a while using SC2Mapster's resources and sharing the progress of one of my maps with the community. One of the questions I get asked the most is how to detect where the player is looking via MouseLook. The answer is: I use a custom-made traceline function. I want to take the time to explain to the community what a traceline is and how to make one. Please note that although I wrote my code for Reaper Madness in pure script (no triggers via galaxy editor) this tutorial will be explaining how to set it up in Galaxy Editor.
Setting Up Variables and Triggers
First, because Galaxy does not support bulk copy or pointers, and because Galaxy Editor does not support structs, we will need to set up the values that our traceline function calculates as global variables. We want to calculate the three following variables:
Current Target - We will want our function to return which unit is currently targeted.
Current Targeted Point - We will want our function to return the point the player is targeting, whether it is a unit or terrain. We will use this point to position our impact effects.
Current Targeted Height - When our player is targeting a unit, we want to know the height at which the traceline intersected the unit. We will use this point to position our impact effects.
We also need to set them up as arrays because we will need a different set of value for each player. As a lesson in good development practices, I would like to show you how to set up Constant variables, however Galaxy doesn't support setting array lengths by anything except raw numbers so we'll have to do it the hard way.
Make a new folder called "Traceline" so we can separate this function from the rest of your project. Inside the new folder, create another folder named "Variables". Inside the variables folder, make our three variables: traceline_currentTarget (unit), traceline_currentTargetPoint (point) and traceline_currentTargetHeight (real). Turn them into an array by checking the array box. Set the size to the maximum amount of players who can play your game (We'll use 8 as the example):
Now we need a function to write our traceline into. Right click on the "Traceline" folder, hover over "New" and click "New Action Definition". Name it "Traceline":
Our function will need to be passed certain variables as parameters to perform its function, so let's set the parameters up. Our function needs to know:
Camera Position - The position of the camera (XY).
Camera Height - The height of the camera (Z).
Player - The player we're calculating a traceline for.
To create a new parameter, right click on "Parameters", hover over "New" and click "New Parameter". We want to add three: "Camera Position" (point), "Camera Height" (real) and "Player" (integer):
We will also be using Custom Script, so add a new Action of type "Custom Script":
Now we get to the meat of our function. Click on the new custom script action to be greeted with a nice empty box, ready to be filled with juicy target-finding script. While following the tutorial, keep in mind that the editor will change the name of your parameters behind-the-scenes. For instance, the variable "Camera Height" will be renamed "lp_cameraHeight" when referred to in Galaxy Script. The lp_ stands for "Local Parameter", the first letter of our variable is shifted to lowercase and all spaces are removed.
Explanation of Traceline Function
A short intermission to explain what our traceline function will do: Our traceline function is, surprise, going to trace an imaginary line. The beginning of the line is the camera's position and height, the end of the line is 50.0 units in the direction the player is facing. To "trace" the line, we will be checking the position of each point in the line in small increments. For example, if you're at 0,0 at height 0 [0, 0, 0] and you're looking straight east the end of the imaginary line would be at 50, 0 at height 0 [50, 0, 0]. Every 1.0 unit of the line, we will check to see if we've hit anything. This means we run a loop 50 times: First we check [1,0,0], if we find nothing we check at [2,0,0] etc until we hit something.
Inside Our Function
First we need to get the angles of the players camera: The pitch and yaw. First we'll need to create two variables (real) to hold the angles, so create two variables inside the function and call them "Pitch" and "Yaw". For pitch, set the initial value to Camera Pitch of Player (Player) and for yaw set the initial value to Camera Yaw of Player (Player), where the "Player" inside the parenthesis refers to our parameter named "Player":
Last thing before we dive into code: We'll need to set our main unit to a global variable so that we don't end up targeting it in our traceline. Create a variable ON THE LEFT in our trigger list, OUTSIDE of our function named "Main Unit" (unit). In a multiplayer game this variable will need to be an array, as all players will have a different main unit. However, for this example I will only be using 1 main unit.
Next we'll want to start in on our code. As explained earlier, we will be checking each point in small intervals in a loop. So, we need to set up the loop. Create a variable named "Trace Distance" (real) and set its initial value to 0.0. This variable will keep track of how far along the imaginary line we are. Now we'll set up our loop:
while(lv_traceDistance<50.0){// While Trace Distance is under our max range, 50, we want to run everything between the squiggly brackets: {}// This is where all of our future code is going to go.lv_traceDistance+=0.5;// At the end of the loop we increment our distance from the camera by 0.5 units. Since the maximum distance is 50.0, this loop will run up to 100 times.}
The first things we'll need to do inside our loop is calculate the height and position of our current "step" in the trace. Create a variable called "Trace Height" (real). Inside our loop, we'll calculate the height of the current "step":
It's not so important that you understand what's going on in the last code segment, but we're finding the Tangent of the angle of lv_Pitch which returns a value between 0 and 1. We multiply that number by the distance we travel every step. For example if we're looking directly toward the sky (90 degree angle) and our trace distance is 0.5 units, the resulting tangent will be 1, and the relative height will be 1 * 0.5 = 0.5, meaning the height at this step in our traceline is 0.5 units higher than the camera's height. As another example, if we're looking forward and up equally (45 degree angle), the resulting tangent will be 0.5 and the relative height will be 0.5 * 0.5 = 0.25, meaning the height at this step in our traceline is 0.25 units higher than the camera's height. If you don't understand all that, it's fine, just continue reading.
Next we need to get the point (XY) where our current step will be located. This one is less complex because Blizzard gave us a function to do the dirty work for us. Create a variable named "Trace Point" (point). We'll get the trace point by calculating it as a polar offset of the camera position. A polar offset means it is a certain distance (offset) away from a point at a given angle (polar). Here's how we call it:
We will need the World Height at the current position of our traceline step, so create a variable named "Trace World Height" (real). This is how we get the height of the world at the current step position:
Now that we have the exact information about the current point in our imaginary line, we're going to do a few things to determine if a unit is in our way:
Create a small region with a radius of 1.5 at the current traceline point
Grab the closest unit to the center of that region, that is within the region
Create a region at the unit we found, the size of the unit
Get the world height at the position of the unit
So create four variables: "Trace Region" (region), "Closest Unit" (unit), "Unit Region" (region) and "Unit World Height" (real). We are going to use the unit's default radius as the size of the unit, however you can replace it with a custom size if you want. We are also going to assume that the unit is a ground unit and therefore uses the c_heightMapGround heightmap, and that the bottom of their "hitbox" is the worldheight (which means where the terrain they're standing on is):
Now we have all the information we need to decide whether we're: Looking at a unit, looking at terrain or aren't looking at anything (yet). So, we're going to test which of those 3 is the case. First we're going to check if it's a unit. We will make sure:
The unit is not null (A unit was actually selected)
The unit is not the player's main hero
The traceline point's location (XY) is inside the unit's hitbox
The traceline point's height (Z) is above the bottom of the unit (We're using 0 because we assume it's ground)
The traceline point's height (Z) is below the top of the unit (We're going to use the radius of the unit as a baseline, however this can be VERY inaccurate on many units and should be replaced with CUSTOM values on a unit-by-unit basis.
And those checks will look like this:
if(lv_closestUnit!=null&&lv_closestUnit!=gv_mainUnit&&RegionContainsPoint(lv_unitRegion,lv_tracePoint)&&lp_cameraHeight+lv_traceHeight-lv_unitWorldHeight>=0.0&&lp_cameraHeight+lv_traceHeight-lv_unitWorldHeight<=UnitGetPropertyFixed(lv_closestUnit,c_unitPropRadius,true)){// This is where our response will go if all of the conditions are met.// If all of the conditions are met, it means we are looking at the unit stored in lv_closestUnit.}
If those conditions are met, which means we're looking at a unit, we want to set those three variables we created earlier to the unit's info and then exit our traceline function (This goes inside the brackets {} of the if statement):
However, if those conditions aren't met, then we aren't looking at a unit (at this step in the traceline) so we need to check whether we're looking at terrain or still haven't collided with anything yet. The way to check if we've hit terrain is very simple, just check if the current height of the traceline step is under the height of the terrain:
So now if we've hit either a unit or terrain, our function has ended and we have the information of the unit and/or terrain that the player is looking at. This is the end of our loop, so if it runs this test and finds neither a unit nor terrain then it will run again, 0.5 units further down the imaginary line. If it runs 100 times and finds nothing, it will exit the loop and continue through the function. Since it's important that we know when it intersects with NOTHING (Say they're looking up at the sky) we're going to add the following AFTER the loop. To reiterate, this goes OUTSIDE of the while(){} statement that we have been putting our code in:
This just clears out all of the values if we're not looking at anything to make sure that if we're not looking at anything we don't use old, outdated values. As for the traceline, that's it! Any time you want to see what the user is looking at, just call the action using the trigger editor like any other action, it's under the "- General" tab:
We're Done!
Two places you can put this function are:
In a recurring function (Every 0.0 seconds, etc). This is useful if you want to use the current target information to display info to the player about the unit they're looking at. You can see an example of this in my Reaper Madness videos. You can then use the SAME information (You don't have to run the traceline twice) to determine which unit or piece of terrain to shoot when the player hits the attack key (such as left mouse click).
In a trigger responding to left mouse click, or any other attack key. Just run the traceline() function to populate the three variables (Current target, current target position and current target height) and use those variables to process your attack. For instance, you could run traceline, deal 10.0 damage to traceline_currentTarget and spawn a blood splatter effect at position traceline_currentTargetPosition and height traceline_currentTargetHeight.
Tips:
After running the traceline function, you can check if the unit is targeting a unit by checking whether or not traceline_currentTarget is null. If it's null, there's no target. If it's not null, then it holds the unit info of the target.
After running the traceline function, you can check if the unit is targeting terrain by checking whether or not traceline_currentTarget is null AND whether or not traceline_currentTargetPosition is null. If both are null, then the player isn't looking at a unit nor is he looking at terrain.
You can change the values within the traceline such as our max distance, 50.0, to better suit your needs. For example, if the player is holding a sniper rifle you will probably want him to shoot further than 50.0, so just change the 50.0 to however far you want them to see. On the biggest map, 250.0 should be long enough to target anybody on the entire map. Remember that the bigger this number is the more work needs to be processed and it could end up slowing down your game.
Check Your Work!
Lastly, here's a picture of how your trigger editor should look if you followed my tutorial exactly:
And here's how your code should look if you followed my tutorial exactly:
Thanks for reading everybody! I know it's a bit advanced and a lot of people probably won't get it, but I tried to explain it as best as possible! Feel free to leave your feedback and/or questions and I'll do my best to answer them.
But i have a question about trigger - how to use complete Traceline in the trigger editor? I don't understand the parameters :)
I create Traceline in my trigger editor with this parameters:
Traceline
Camera Position: traceline_currentTargetPoint
Index 1: 1
Camera Height: 2.0
Player: 1
And in-game I have an error, when I click the mouse button on target unit:
Trigger Error in 'gt_MouseAimStart_Func': Could not get 'p' from parameter in 'PointWithOffsetPolar' (value: 65535)
Is that correct? If so, the problem is you're plugging in the point of the current target, "traceline_currentTargetPoint", instead of the point of your camera. If your camera is following your main unit, then your function call should look like this:
This is assuming that your camera is set to follow your unit and that your camera is set at a 2.0 height offset. If your camera is not set at a 2.0 offset you should replace 2.0 with the actual offset of your camera.
To Rrowland: Good tutorial/read... not sure if you saw my WoW RPG map/tutorial I put out yesterday, but I was having this sort of issue since I do custom movement/camera, the user doesn't have their hero selected (WASD moves hero). So to attack units I had to do a trigger for right mouse click, then move a region of size 1.75 to their target point (X point of mouse clicked and Y point of mouse clicked), then check if an enemy unit was within the region, then order hero to attack said unit. The issue with this though is that if your camera is almost parallel to the ground and you click the top end of a unit, the game would pick the X and Y point to be a far-off terrain point behind the unit where it thinks you clicked and thus it wouldn't find anything.
It looks like the stuff in this tutorial would fix my issue... because it would trace everywhere along that line... the issue then becomes though whether or not 50 is too big or too little, and this would be more CPU-intensive to do this...
Quote:
Can you enlighten me about how to implement this? I'm trying to make it so that effect spawns on the position where my mouse clicks...
Refer to the map...
Without custom scripting, you could just make a trigger with event of UI - mouse pressed or w/e... then make a local variable called Point Clicked... first action is to set the position of the point to XY, and pick the X value to be mouse clicked position in X, and Y to be mouse clicked position in Y (search for these, I believe they are under Unit category for Reals). This wouldn't give you the Z position though... but anyway at this point you can do an Actor model creation action to make an effect at the point.
Hey, great tut...
Though' A thing about Camera position and camera height.
How on earth are you going to the XY an Z position of the camera if you are not attached to a unit?
When i do it i do it pased on the Pitch and camera distance, but since camera-distance is relative in this game it usually screw up my function.
Can you enlighten me about how to implement this? I'm trying to make it so that effect spawns on the position where my mouse clicks...
Refer to the map...
The traceline function is really only necessary for aiming at the middle of a player's camera view. As OneTwoSC said, you can achieve what you're trying to do much more simply by checking for "Point Clicked" events.
Hey, great tut... Though' A thing about Camera position and camera height.
How on earth are you going to the XY an Z position of the camera if you are not attached to a unit? When i do it i do it pased on the Pitch and camera distance, but since camera-distance is relative in this game it usually screw up my function.
I'm not exactly sure because I haven't run into that scenario myself. The pitch and yaw would remain the same, but you'd have to calculate the position and height of the camera. I'd play with CameraGetTarget() and CameraInfoGetValue(x, c_cameraValueHeightOffset ).
K, I tried that it works, in fact I don't need the traceline. So what exactly is the point of the traceline?
The traceline function is used for 3D aiming, like you'd see in a First Person Shooter. Since you can't just click on the ground you have to calculate what the player is aiming at in the middle of his screen, where the crosshairs are.
Thanks for this. It helps getting it working Rrowland I am working on adding physics to this because this can be used as a vector for trajectories. So I can add to my library for all map makers I am making. Cause right now if you use this to fire it directly hits the target which is good but it doesn't account for what if the target is moving and you have to lead the target. This is important because that is physically how things should work. I found a guy who wrote some code that uses projectiles to hit targets the dilemma he has is the formula incorrect so the patterns that they use are alright. I intend on take both, also Rrowland I hope your fine with me using this traceline function in my library I am making for the mapping community. I talked to you a little about it when I contacted you on YouTube.
Well the traceline will tell you what is in front of you right now, that's the point. So you don't need to take into account "What if the unit moves?" because you should be dealing damage to the unit you get from the traceline instantly. If you wanted to apply the same logic to projectiles, you could do the same thing except change the loop to take one step every 0.0 game seconds and update the position of a projectile with each iteration. That way, you actually have a moving projectile that will check for collision at every step.
However, if you're coding a physics library this wouldn't be the way to go. You will want to treat a missile like any other object. Give it a starting location of the user's viewpoint and a 0 vector: [0, 0, 0]. Then, apply a force of great magnitude to the object in the direction the unit is facing, which should change the object's directional vector to something like [1.0, 1.0, 0.0] meaning it moves +1.0 east, +1.0 north per iteration (Every 0.0 game seconds). At every iteration you should also apply the forces of gravity, wind resistance and friction to slow the projectile down and create the arc that brings every object that goes up back to the ground.
On my map the camera rotates around the unit, so the target point is always the position of the unit, which of course does not work for this.
I'm not seeing a way to attach the camera object to a unit without it being the target. If I attach it to a region attached to a unit the region becomes the camera target (and the camera jitters). It just seems to be the nature of the mouse rotation trigger.
Unless I'm doing something terribly wrong (which I sort of feel like I am).
I agree but I was also thinking of using it to draw a line for a projectile to follow, I was going to factor in gravity, and I have done some research already and Gravity isn't the same due to scaling of the game. I figure If the average person is about 2 m high it means that a ghost is about .6 of the map scale. this to me means that that is approximately equivalent to 2 meters. That means gravity is closer to a -1 unit per second. As far as wind this might be good in a golf game or if you were sniping at a long distance, but for something fairly close this wont matter. I was also thinking of using this to display information about units and am having no luck with that aspect.
Turn on both mouselook flags, make a tiny region at your unit and set your camera's unit bounds to that region every 0.0 seconds.
The camera target is still constant (unless the unit moves). The actual camera object changes position of course, but it doesn't allow the traceline function to work as intended.
I'm not getting something. I don't understand how to change the camera target without moving the entire camera with mouselook enabled. It would work great if the camera target would change but the object was constant, but it's the opposite.
Hey Kanaru take a look at my map it works on there its nothing special but it works. Everything is fully implemented. I have also currently set up a way to alternate between 3rd person to top view. Alot of the stuff is going to be used in my Library I am making and some of it is not being used cause I have modified it. All that you need to know to test it is this hit esc to put the game in 3rd person shooter, then turn till you aim at one of the four units. When you actually target one it will pull up showing their information on a boss bar. The exact way it was shown in reaver madness. If you decide to fire. I was using my own version of a modified x,y coordinate to aim so its not 100% accurate.
I think i also know your problem. If you read the code he is setting the Array up using the playernumber instead of using playernumber-1 if your trying so you need to account for this when you try to pull the data from the global variables that are set up. if you want it to start at zero just do the math and your good.
I agree but I was also thinking of using it to draw a line for a projectile to follow, I was going to factor in gravity, and I have done some research already and Gravity isn't the same due to scaling of the game. I figure If the average person is about 2 m high it means that a ghost is about .6 of the map scale. this to me means that that is approximately equivalent to 2 meters. That means gravity is closer to a -1 unit per second. As far as wind this might be good in a golf game or if you were sniping at a long distance, but for something fairly close this wont matter. I was also thinking of using this to display information about units and am having no luck with that aspect.
By wind resistance I'm referring to the friction caused by any object attempting to move through open air. This affects objects of all sizes moving in any direction, regardless of whether there is wind or not. The scale of its effect is dictated usually by mass and surface area of the object in the direction it's traveling, but for emulation purposes you can give it an absolute value on an object-by-object basis.
Thanks for sharing that map, now I can learn how to implement the traceline.
The traceline function never works. Focus on the TargetedUnit, it iterates every 0s, but as you can see the traceline_currentTargetUnit is alwayts null...
Introduction
Hello all, I've been hanging out for a while using SC2Mapster's resources and sharing the progress of one of my maps with the community. One of the questions I get asked the most is how to detect where the player is looking via MouseLook. The answer is: I use a custom-made traceline function. I want to take the time to explain to the community what a traceline is and how to make one. Please note that although I wrote my code for Reaper Madness in pure script (no triggers via galaxy editor) this tutorial will be explaining how to set it up in Galaxy Editor.
Setting Up Variables and Triggers
First, because Galaxy does not support bulk copy or pointers, and because Galaxy Editor does not support structs, we will need to set up the values that our traceline function calculates as global variables. We want to calculate the three following variables:
We also need to set them up as arrays because we will need a different set of value for each player. As a lesson in good development practices, I would like to show you how to set up Constant variables, however Galaxy doesn't support setting array lengths by anything except raw numbers so we'll have to do it the hard way.
Make a new folder called "Traceline" so we can separate this function from the rest of your project. Inside the new folder, create another folder named "Variables". Inside the variables folder, make our three variables: traceline_currentTarget (unit), traceline_currentTargetPoint (point) and traceline_currentTargetHeight (real). Turn them into an array by checking the array box. Set the size to the maximum amount of players who can play your game (We'll use 8 as the example):
Now we need a function to write our traceline into. Right click on the "Traceline" folder, hover over "New" and click "New Action Definition". Name it "Traceline":
Our function will need to be passed certain variables as parameters to perform its function, so let's set the parameters up. Our function needs to know:
To create a new parameter, right click on "Parameters", hover over "New" and click "New Parameter". We want to add three: "Camera Position" (point), "Camera Height" (real) and "Player" (integer):
We will also be using Custom Script, so add a new Action of type "Custom Script":
Now we get to the meat of our function. Click on the new custom script action to be greeted with a nice empty box, ready to be filled with juicy target-finding script. While following the tutorial, keep in mind that the editor will change the name of your parameters behind-the-scenes. For instance, the variable "Camera Height" will be renamed "lp_cameraHeight" when referred to in Galaxy Script. The lp_ stands for "Local Parameter", the first letter of our variable is shifted to lowercase and all spaces are removed.
Explanation of Traceline Function
A short intermission to explain what our traceline function will do: Our traceline function is, surprise, going to trace an imaginary line. The beginning of the line is the camera's position and height, the end of the line is 50.0 units in the direction the player is facing. To "trace" the line, we will be checking the position of each point in the line in small increments. For example, if you're at 0,0 at height 0 [0, 0, 0] and you're looking straight east the end of the imaginary line would be at 50, 0 at height 0 [50, 0, 0]. Every 1.0 unit of the line, we will check to see if we've hit anything. This means we run a loop 50 times: First we check [1,0,0], if we find nothing we check at [2,0,0] etc until we hit something.
Inside Our Function
First we need to get the angles of the players camera: The pitch and yaw. First we'll need to create two variables (real) to hold the angles, so create two variables inside the function and call them "Pitch" and "Yaw". For pitch, set the initial value to Camera Pitch of Player (Player) and for yaw set the initial value to Camera Yaw of Player (Player), where the "Player" inside the parenthesis refers to our parameter named "Player":
Last thing before we dive into code: We'll need to set our main unit to a global variable so that we don't end up targeting it in our traceline. Create a variable ON THE LEFT in our trigger list, OUTSIDE of our function named "Main Unit" (unit). In a multiplayer game this variable will need to be an array, as all players will have a different main unit. However, for this example I will only be using 1 main unit.
Next we'll want to start in on our code. As explained earlier, we will be checking each point in small intervals in a loop. So, we need to set up the loop. Create a variable named "Trace Distance" (real) and set its initial value to 0.0. This variable will keep track of how far along the imaginary line we are. Now we'll set up our loop:
The first things we'll need to do inside our loop is calculate the height and position of our current "step" in the trace. Create a variable called "Trace Height" (real). Inside our loop, we'll calculate the height of the current "step":
It's not so important that you understand what's going on in the last code segment, but we're finding the Tangent of the angle of lv_Pitch which returns a value between 0 and 1. We multiply that number by the distance we travel every step. For example if we're looking directly toward the sky (90 degree angle) and our trace distance is 0.5 units, the resulting tangent will be 1, and the relative height will be 1 * 0.5 = 0.5, meaning the height at this step in our traceline is 0.5 units higher than the camera's height. As another example, if we're looking forward and up equally (45 degree angle), the resulting tangent will be 0.5 and the relative height will be 0.5 * 0.5 = 0.25, meaning the height at this step in our traceline is 0.25 units higher than the camera's height. If you don't understand all that, it's fine, just continue reading.
Next we need to get the point (XY) where our current step will be located. This one is less complex because Blizzard gave us a function to do the dirty work for us. Create a variable named "Trace Point" (point). We'll get the trace point by calculating it as a polar offset of the camera position. A polar offset means it is a certain distance (offset) away from a point at a given angle (polar). Here's how we call it:
We will need the World Height at the current position of our traceline step, so create a variable named "Trace World Height" (real). This is how we get the height of the world at the current step position:
Now that we have the exact information about the current point in our imaginary line, we're going to do a few things to determine if a unit is in our way:
So create four variables: "Trace Region" (region), "Closest Unit" (unit), "Unit Region" (region) and "Unit World Height" (real). We are going to use the unit's default radius as the size of the unit, however you can replace it with a custom size if you want. We are also going to assume that the unit is a ground unit and therefore uses the c_heightMapGround heightmap, and that the bottom of their "hitbox" is the worldheight (which means where the terrain they're standing on is):
Now we have all the information we need to decide whether we're: Looking at a unit, looking at terrain or aren't looking at anything (yet). So, we're going to test which of those 3 is the case. First we're going to check if it's a unit. We will make sure:
And those checks will look like this:
If those conditions are met, which means we're looking at a unit, we want to set those three variables we created earlier to the unit's info and then exit our traceline function (This goes inside the brackets {} of the if statement):
However, if those conditions aren't met, then we aren't looking at a unit (at this step in the traceline) so we need to check whether we're looking at terrain or still haven't collided with anything yet. The way to check if we've hit terrain is very simple, just check if the current height of the traceline step is under the height of the terrain:
So now if we've hit either a unit or terrain, our function has ended and we have the information of the unit and/or terrain that the player is looking at. This is the end of our loop, so if it runs this test and finds neither a unit nor terrain then it will run again, 0.5 units further down the imaginary line. If it runs 100 times and finds nothing, it will exit the loop and continue through the function. Since it's important that we know when it intersects with NOTHING (Say they're looking up at the sky) we're going to add the following AFTER the loop. To reiterate, this goes OUTSIDE of the while(){} statement that we have been putting our code in:
This just clears out all of the values if we're not looking at anything to make sure that if we're not looking at anything we don't use old, outdated values. As for the traceline, that's it! Any time you want to see what the user is looking at, just call the action using the trigger editor like any other action, it's under the "- General" tab:
We're Done!
Two places you can put this function are:
Tips:
Check Your Work!
Lastly, here's a picture of how your trigger editor should look if you followed my tutorial exactly:
And here's how your code should look if you followed my tutorial exactly:
Thanks for Reading!
Thanks for reading everybody! I know it's a bit advanced and a lot of people probably won't get it, but I tried to explain it as best as possible! Feel free to leave your feedback and/or questions and I'll do my best to answer them.
Thanks for great tutorial rrowland!
But i have a question about trigger - how to use complete Traceline in the trigger editor? I don't understand the parameters :) I create Traceline in my trigger editor with this parameters:
Traceline Camera Position: traceline_currentTargetPoint Index 1: 1 Camera Height: 2.0 Player: 1
And in-game I have an error, when I click the mouse button on target unit:
Trigger Error in 'gt_MouseAimStart_Func': Could not get 'p' from parameter in 'PointWithOffsetPolar' (value: 65535)
In-game Screenshot: http://img40.imageshack.us/img40/2755/screenshot000lg.jpg
This looks like:
Is that correct? If so, the problem is you're plugging in the point of the current target, "traceline_currentTargetPoint", instead of the point of your camera. If your camera is following your main unit, then your function call should look like this:
This is assuming that your camera is set to follow your unit and that your camera is set at a 2.0 height offset. If your camera is not set at a 2.0 offset you should replace 2.0 with the actual offset of your camera.
Awesome tutorial and it works magic.
Props to you.
I honestly don't know how to impement this.
Can you enlighten me about how to implement this? I'm trying to make it so that effect spawns on the position where my mouse clicks...
Refer to the map...
To Rrowland: Good tutorial/read... not sure if you saw my WoW RPG map/tutorial I put out yesterday, but I was having this sort of issue since I do custom movement/camera, the user doesn't have their hero selected (WASD moves hero). So to attack units I had to do a trigger for right mouse click, then move a region of size 1.75 to their target point (X point of mouse clicked and Y point of mouse clicked), then check if an enemy unit was within the region, then order hero to attack said unit. The issue with this though is that if your camera is almost parallel to the ground and you click the top end of a unit, the game would pick the X and Y point to be a far-off terrain point behind the unit where it thinks you clicked and thus it wouldn't find anything.
It looks like the stuff in this tutorial would fix my issue... because it would trace everywhere along that line... the issue then becomes though whether or not 50 is too big or too little, and this would be more CPU-intensive to do this...
Without custom scripting, you could just make a trigger with event of UI - mouse pressed or w/e... then make a local variable called Point Clicked... first action is to set the position of the point to XY, and pick the X value to be mouse clicked position in X, and Y to be mouse clicked position in Y (search for these, I believe they are under Unit category for Reals). This wouldn't give you the Z position though... but anyway at this point you can do an Actor model creation action to make an effect at the point.
Hey, great tut... Though' A thing about Camera position and camera height.
How on earth are you going to the XY an Z position of the camera if you are not attached to a unit? When i do it i do it pased on the Pitch and camera distance, but since camera-distance is relative in this game it usually screw up my function.
@OneTwoSC: Go
K, I tried that it works, in fact I don't need the traceline. So what exactly is the point of the traceline?
Thanks for the positive feedback, guys. :)
The traceline function is really only necessary for aiming at the middle of a player's camera view. As OneTwoSC said, you can achieve what you're trying to do much more simply by checking for "Point Clicked" events.
I'm not exactly sure because I haven't run into that scenario myself. The pitch and yaw would remain the same, but you'd have to calculate the position and height of the camera. I'd play with CameraGetTarget() and CameraInfoGetValue(x, c_cameraValueHeightOffset ).
The traceline function is used for 3D aiming, like you'd see in a First Person Shooter. Since you can't just click on the ground you have to calculate what the player is aiming at in the middle of his screen, where the crosshairs are.
@rrowland: Go
How do you do that? Do I have to use the periodic method instead and check where the point is?
Thanks for this. It helps getting it working Rrowland I am working on adding physics to this because this can be used as a vector for trajectories. So I can add to my library for all map makers I am making. Cause right now if you use this to fire it directly hits the target which is good but it doesn't account for what if the target is moving and you have to lead the target. This is important because that is physically how things should work. I found a guy who wrote some code that uses projectiles to hit targets the dilemma he has is the formula incorrect so the patterns that they use are alright. I intend on take both, also Rrowland I hope your fine with me using this traceline function in my library I am making for the mapping community. I talked to you a little about it when I contacted you on YouTube.
@sherardt: Go
Well the traceline will tell you what is in front of you right now, that's the point. So you don't need to take into account "What if the unit moves?" because you should be dealing damage to the unit you get from the traceline instantly. If you wanted to apply the same logic to projectiles, you could do the same thing except change the loop to take one step every 0.0 game seconds and update the position of a projectile with each iteration. That way, you actually have a moving projectile that will check for collision at every step.
However, if you're coding a physics library this wouldn't be the way to go. You will want to treat a missile like any other object. Give it a starting location of the user's viewpoint and a 0 vector: [0, 0, 0]. Then, apply a force of great magnitude to the object in the direction the unit is facing, which should change the object's directional vector to something like [1.0, 1.0, 0.0] meaning it moves +1.0 east, +1.0 north per iteration (Every 0.0 game seconds). At every iteration you should also apply the forces of gravity, wind resistance and friction to slow the projectile down and create the arc that brings every object that goes up back to the ground.
On my map the camera rotates around the unit, so the target point is always the position of the unit, which of course does not work for this.
I'm not seeing a way to attach the camera object to a unit without it being the target. If I attach it to a region attached to a unit the region becomes the camera target (and the camera jitters). It just seems to be the nature of the mouse rotation trigger.
Unless I'm doing something terribly wrong (which I sort of feel like I am).
@Kanaru: Go
Turn on both mouselook flags, make a tiny region at your unit and set your camera's unit bounds to that region every 0.0 seconds.
I agree but I was also thinking of using it to draw a line for a projectile to follow, I was going to factor in gravity, and I have done some research already and Gravity isn't the same due to scaling of the game. I figure If the average person is about 2 m high it means that a ghost is about .6 of the map scale. this to me means that that is approximately equivalent to 2 meters. That means gravity is closer to a -1 unit per second. As far as wind this might be good in a golf game or if you were sniping at a long distance, but for something fairly close this wont matter. I was also thinking of using this to display information about units and am having no luck with that aspect.
The camera target is still constant (unless the unit moves). The actual camera object changes position of course, but it doesn't allow the traceline function to work as intended.
I'm not getting something. I don't understand how to change the camera target without moving the entire camera with mouselook enabled. It would work great if the camera target would change but the object was constant, but it's the opposite.
Hey Kanaru take a look at my map it works on there its nothing special but it works. Everything is fully implemented. I have also currently set up a way to alternate between 3rd person to top view. Alot of the stuff is going to be used in my Library I am making and some of it is not being used cause I have modified it. All that you need to know to test it is this hit esc to put the game in 3rd person shooter, then turn till you aim at one of the four units. When you actually target one it will pull up showing their information on a boss bar. The exact way it was shown in reaver madness. If you decide to fire. I was using my own version of a modified x,y coordinate to aim so its not 100% accurate.
I think i also know your problem. If you read the code he is setting the Array up using the playernumber instead of using playernumber-1 if your trying so you need to account for this when you try to pull the data from the global variables that are set up. if you want it to start at zero just do the math and your good.
By wind resistance I'm referring to the friction caused by any object attempting to move through open air. This affects objects of all sizes moving in any direction, regardless of whether there is wind or not. The scale of its effect is dictated usually by mass and surface area of the object in the direction it's traveling, but for emulation purposes you can give it an absolute value on an object-by-object basis.
@sherardt: Go
Thanks for sharing that map, now I can learn how to implement the traceline.
The traceline function never works. Focus on the TargetedUnit, it iterates every 0s, but as you can see the traceline_currentTargetUnit is alwayts null...