02092007, 01:58 PM  #1 
Kelani Mage

Deducting formulas (JASS)
1. Introduction
The biggest problem I’ve noticed that many JASS users have is the fact that they fail to improve their code (and thus, make it work faster), as they are not organized enough to get formulas rolling. I am not talking about super complicated ones, but in certain cases (such as working with coordinates), it is very important to reduce the number of calculations the processor must make. You may say that it is no noticeable, but spamming certain triggers (in big maps… but not necessarily) can cause extreme lag. I will assume that you are already a decent JASS coder. The example based on which I will explain in this tutorial will be well known in the coding world, but it works with coordinates (so no more locations for you). That is because graphical patterns are usually the most formuladependant. Some GUI users may learn certain things from this tutorial, but I wouldn’t put my bet on it (as code will be in JASS). Okay then, let’s get started! 2. Important steps Well, it is very important to identify the steps of the problem and keep it organized if you want to get a clean deduction and therefore, a correct and reliable formula. Don’t be afraid if you don’t know all mathematical concepts. There are a lot of websites (such as mathworld.com) which can help you greatly when you do not know certain important aspects. Yes, it may be difficult at the beginning, but in time, you will see that things can get so much easier. Google is your friend! So use it well, as in, use it every time you need something! Identifying the problem Now, the most important step is identifying the problem. For this tutorial, let’s say that you want to simulate a spell like Death Coil. Generally it is very simple, but the one problem we are facing is simulating the missile (no, it’s not efficient to rely on other spells to do the whole effect for you as you can’t control the spell that well). There are some tutorials out there that teach you how to move a unit from a point to another, but they clearly aren’t very efficient as they use a lot of trigonometric functions. They are time consuming! Those calculations could be spent somewhere else. Therefore, it would be so much better if we could use as few functions as possible. The calculations should be made every time we want to move the unit! This is extremely important, to think when you actually want to apply the “formula”. Getting the input data So we have the problem, let’s see what we actually “have”. The input data is very important, every time you get to such a problem, try to think clearly every element you could use to solve the problem. Two elements are already quite clear: the coordinates of the missile, respectively the target. Since we want to move the missile to the unit, we know these objects! Another thing we should know is the speed of the missile (how fast it moves). A last parameter is the frequency of the movement. You should know that this “moving” is done by repeatedly changing the position of the unit. How fast you change it, that’s the frequency (and it is obviously a constant, usually defined when starting the timer which does the trajectory). Output data Yes, we now have to see which output data we require. We want to move this unit repeatedly, very fast, and we only know a few stuff (where it is, when we want to go and how fast it can move). Therefore, we have to determine where the unit should find itself at the next step (it should come “forward” towards the target). If we can determine how much it should advance on the OX, respectively OY axis, we’ve solved the problem. Actual calculations Here comes the tricky part, as you have to use your input data (and possibly some other math formulas) to determine the next position of the unit. A simple alternative would be to use polar coordinates, but as I said before, they use trigonometric functions which are quite slow. Therefore, we need to manipulate input data in order to obtain output data. You will need a lot of practice to do this easily. Let’s see… 3. Solving the problem It is not an uncommon fact that we need to work a lot with variables (hell… we usually work with them when deducting formulas even at math, physics, chemistry etc.). Therefore, let’s transfer the input data in “JASS” language. Sometimes it is important to make some notes on paper, usually with single letters and maybe indexes representing the input data. Here are my data: v – missile speed μ – missile frequency (timer speed) d – initial distance x1,y1 – missile coordinates x2,y2 – target coordinates  Of course, we also need intermediate values (obtained from initial values through some calculations), and for this problem, they are (we will see in a few moments how I got to them): t – total time tr – time left Δx – distance between missile and target on OX axis Δy – distance between missile and target on OY axis Note: I’ve actually simplified the problem a little so that the missile always reaches its target in the same amount of time it would reach if the target did not move from the moment it is launched. Usually we start the “thinking” by reducing the problem to an abstract level. For this problem, we want to determine the formula (where to move the missile next) regardless of the missile’s position, or that of the target. Here is a quick drawing (of course, the points could be anywhere): To interpret it, I first need to explain you some elements. The triangles represent the missile (red), the target (blue), and the following position of the missile (cyan). Δx and Δy are quite obvious, as they are the distance between the two objects. And you can also clearly see that the coordinates of the next position are those of the initial, to which I added a value on the respective coordinate (on the X, respectively Y axis). Ok, so we clearly know what we have to determine: those mysterious x+ and y+ stuff. Now, another thing you needed to figure out (yes, a bit difficult at first, I know, believe me, I do) is that you can actually determine how much time you have left until your missile much reach the target. How is that? Simple! Since we came to the conclusion (by simplifying the problem) that the time in which the missile must reach its target is constant, no matter how much it moves. So if the missile moves with 800 pixels per second and the initial distance between the caster and the target were 400 pixels, then the missile would reach its target in half a second (no matter how much the target moves). Therefore, we have out total time variable. How we can detect the time left? Well, at each repetition of the timer (and therefore, each movement) we could reduce from the total time, how much it passed since the last movement (which is μ). And voila, we actually have the time left, at a certain moment. Now, let’s look again at the drawing. We know the distance, we know the total time, and we have to find a part of the distance for a certain moment. And here come proportions. Think about it like this: “if the target stood in place and the missile moved a repeated number of times, how many movements would it need to make in order to reach its target, knowing the total time and the frequency of the repetitions?”. I’d say it is that by dividing the time to the frequency, we actually get the number of movements. Note: You need to initialize tr with the total time. To determine it, it’s enough to divide the initial distance between the caster and the target and divide it to the speed of the missile. Example: Let’s say that you have to move a missile in two seconds, with a frequency of 0.05 seconds between the movements. Well, you would need two split those two seconds into small periods of two seconds. The number of periods would actually be the number of times you need to move your missile. In this example, that value would be 40. I will name this variable Q (number of movements), and therefore, we have the following relation: Q=tr/ μ. Why I based it off the time left and not the initial time? Because we are interested in the number of repetitions required at a certain moment, not at the beginning. Generalization And now we go back to the graphic. If the total distance depends on the time left, then so does the current distance modification (x+ respectively y+) depend on the frequency. I suspect many of you learned proportions at math, and so, know about a nice rule which would look like this (it is the same for Δy): Δx.......................................... tr x+.......................................... μ From here, x+ = Δx * μ / tr But μ / tr = 1/Q Therefore x+ = Δx/Q (and in the same manner y+ = Δy/Q) However, we still have some problems about Δx (and therefore similar to Δy). Consider different situations for the two points (when one is to the left or right of another). We should study it on cases and see how the formula behaves. Case 1 x1<x2 => missile is in the left of the target => we need to increase the x coordinate => x+ > 0 => Δx>0 => x1x2>0 (where a is the absolute value of a) => x1x2=x2x1 Therefore: Δx=x2x1 Case 2 x1>x2 => missile is in the right of the target => we need to decrease the x coordinate => x+ < 0 => Δx<0 => x1x2<0 => x1x2=x2x1 Therefore: Δx=x2x1 Case 3 x1=x2 => Δx=x2x1=x1x2=0 (sign does not matter as absolute value is 0) From these three we can conclude that Δx=x2x1, no matter the position of the two points. Similarly, Δy=y2y1. Note: μ / tr is always greater than 0. Therefore the sign of x+ and y+ always depends on Δx or Δy. And done, having finalized our problem, here is how our solution would look (brief, with no explanations): t = √[(x2x1)²+(y2y1)²]/v tr – initialized with t, reduced at each timer loop with μ Δx = x2x1 Δy = y2y1 Q = tr/ μ x+ = Δx/Q y+ = Δy/Q Exercise: Improve the formula so that you do not need to calculate Q every time we move the missile (at every repetition). Think that Q depends on μ (which is a constant), and tr, which also depends on μ and decreases every step. Tip You may sometimes have to rely on logical solutions, as you cannot always “catch” the idea mathematically. The proportions I presented when solving x+/y+ have a logical fundament in basis, and in this case, it is correct universally. Be careful though, that in some cases, you may not be able to fully rely on a logical hunch, and that’s why your formula may be wrong. It takes time and practice to get how these things work. 4. Implementing the formula This is very simple. I shall spend though a few minutes for this problem too, as some of you might find it confusing. First thing to do is transform the data names into correct variable names. μ = u Δx = dx Δy = dy x+ = xp y+ = yp Tip If you have been using indexes (I personally wrote on the paper tr with r as an index, same for x1,x2,y1,y2… for x+ and y+ I placed the ‘+’ above the letter), just put them in front of the word. This way, you should be able to avoid any confusions of data (especially when there is a large number). In the beginning, declare your constants into specific functions to avoid any problems later. The constants here are μ and v. JASS: constant function Missile_Speed takes nothing returns real return 800.00 endfunction constant function Missile_Frequency takes nothing returns real return 0.035 endfunction First of all, we create our first function which generates the missile and begins the movement. I split it into two functions to make it look good. JASS: function Missile_BeginMovement takes unit missile, unit target returns nothing local real xx = GetUnitX(missile)GetUnitX(target) //simplify distance function local real yy = GetUnitY(missile)GetUnitY(target) // accordingly local real d = SquareRoot(xx*xx+yy*yy) //initial distance local real tr = d/Missile_Speed() //time left initialized with the total time local timer t = CreateTimer() //our timer call SetHandleHandle(t, "missile", missile) //I am using call SetHandleHandle(t, "target", target) //Kattana’s HandleVars cache call SetHandleHandle(t, "timeleft", tr) //you may use your own storing system call TimerStart(t, Missile_Frequency(), true, function Missile_StartTimer) call PolledWait(tr) //get out of the function only after the missile has reached its target set t = null endfunction function Missile_Main takes nothing returns nothing local unit cast = GetTriggerUnit() local unit targ = GetSpellTargetUnit() local unit missile = CreateUnit(GetOwningPlayer(cast), 'h000', GetUnitX(cast), GetUnitY(cast), 0.00) call Missile_BeginMovement(missile,target) //continue spell accordingly endfunction Note: All input data are either linked to the timer or constants (which can be reached through functions). Other data can be easily determined using specific functions. Okie dokie, things are quite clear. I used some helpful variables to determine the original distance so that I didn’t call coordinate determination functions eight times (only four). You may do so if you wish, as the efficiency problem is minimum, but I’m using to doing it how you’ve seen it above. Now let’s see that pesky timer function. I’ll explain after how I’ve actually implemented the formula (as this is the place where we do it!). JASS: function Missile_Move takes timer t returns nothing local unit missile = GetHandleUnit(t, "missile") local unit target = GetHandleUnit(t, "target") local real tr = GetHandleReal(t, "timeleft") //INPUT DATA END local real dx = GetUnitX(target)GetUnitX(missile) // Δx=x2x1 local real dy = GetUnitY(target)GetUnitY(missile) // Δy=y2y1 local real xp = dx*Missile_Frequency()/tr local real yp = dy*Missile_Frequency()/tr call SetUnitX(missile, GetUnitX(missile)+xp) call SetUnitY(missile, GetUnitY(missile)+yp) set tr = tr Missile_Frequency() //the reduction of time left I was telling you about if tr< Missile_Frequency() //in case Q is not an integer number call SetUnitX(missile, GetUnitX(target)) call SetUnitY(missile, GetUnitY(target)) call PauseTimer(t) call FlushHandleLocals(t) call DestroyTimer(t) else call SetHandleReal(t, "timeleft", tr) endif set missile = null set target = null endfunction function Missile_StartTimer takes nothing returns nothing call Missile_Move(GetExpiredTimer()) //let’s avoid any timer nulling problems endfunction Note That if/then/else stuff refers to the fact that if Q is not an integer number (there is not an integer number of segments), the number of repetitions would be screwed. Therefore, if we still have something like 0.032 left in tr, we have almost another repetition, but we can skip it as the difference is unnoticeable, and instantly move the unit to the target. If there is an even number of repetitions, once tr reaches 0, it is clear that its value is less than the frequency. Therefore, we destroy the timer and end the movement. There, problem done! Test it and convince yourself that it works! 5. Mathematical concepts Okay, this example was probably a bit simple, but I personally found it extremely relevant for the topic… took me a while to figure it out too, hehe. You will notice that math is really important if you want to optimize your code, because you can simplify your algorithm and/or expressions to make things easier. Here are some math elements that might help you. Boolean expressions Learn to simplify Boolean (logical) expressions, made out of 1 and 0 (which is translated into the language through true and false). The computer will strictly do what you tell him to do, so you will need to simplify them on paper. Here are some important relations which might help you (all variables are of type boolean): Graph Theory Though not with huge applicability in Warcraft at this point, as it hasn’t been exploited enough, graph theory is extremely useful for determining fast roads between units, roads of minimum cost (an optimal chain spell for example) and other things. Generally the nodes are units, and the edges are the path from one to another. Of course, because units actually move, the graphs are considered dynamic if calculations are made on a period of time and not in a certain moment. You can read more about graph theory on Wikipedia or here. Analytical geometry Spells work a lot with unit positions. In an abstract way, they are points in an XOYZ plane, as they have coordinates. Therefore many formulas may require that you have some solid knowledge into analytical geometry. Without studying this area you can’t really expect to optimize your code much (when it comes to positions and coordinates). You can read about analytical geometry on Wikipedia or Mathworld. Some interesting problems can also be studied here. These are only a couple of areas you need to study. Of course, there are many more, but these are in my opinion the most important. Good luck and have fun developing and optimizing your JASS codes. And of course, if you have any comments or complaints about the tutorial, don’t hesitate to post them here. They will all be taken into consideration. Last edited by Daelin : 02132007 at 04:29 AM. 
Sponsored Links  Login to hide this ad! 

02092007, 02:15 PM  #2 
Free Software Terrorist
Technical Director

Whatever text editor you used it is making your quotes 'smart' , it converts "missile" into “missile” ...
__________________ 
02092007, 02:46 PM  #3 
Kelani Mage

Ooops... sorry about that. The editor would be Microsoft Word. Fixed!
__________________Daelin 
03112007, 10:11 PM  #4 
Alcopops
Tools & Tutorials Moderator

Approved, of course.
__________________ 
03112007, 10:51 PM  #5  
Moderator
Code Moderator
Join Date: Feb 2006
Posts: 1,405



03132007, 03:33 PM  #6 
Kelani Mage

I know that the period is the inverse of the frequency. I also study physics in school. frequency*period = 1 :P
__________________Crap... I always thought that it was deducting. Well, graph theory may help in a couple of cases (some complicated chain spells could require some of it) and I thought I should mention it as it is one of the most important areas in structural programming (and not only of course ;)). The tip was something like "you can't expect your formula to always work just because it is 'logical'... a mathematical demonstration is required to make sure that it will work"  like I presented in this tutorial. Thanks for the nice ending words. :) Daelin 
03132007, 03:45 PM  #7 
Free Software Terrorist
Technical Director

I think that pipe means that graph algorithms might be needed but graph theory itself (all the math and theoresm) isn't so required.
__________________ 
03262007, 02:49 AM  #8 
User

JASS: call PolledWait(tr) //get out of the function only after the missile has reached its target set t = null Why do you use a polled wait and then null the timer? You could forgo that completely and do: JASS: call DestroyTimer(t) set t = null Then you'd have one less timer going  right? (Also, doesn't polled wait leak a timer?) The explination was good, the code was very good (Besides the nonconstant xp/yp, but that was the 'project' right?). Anyways, this is far better than using PolarProjection every time you want to move the projectile. 
03262007, 12:08 PM  #9 
Kelani Mage

Actually, the PolledWait is essential! Destroying the timer just after being created will not move the missile one bit. The trick is to wait until the missile reaches its destination point. Because the duration is fixed, you don't actually have to detect when that happens and get out of the function, but instead, use PolledWait.
__________________And yeah yeah, PolledWait leaks a timer. You can replace it with your own version, a secondary timer or wutever you wish. The idea was to understand the code, and such secondary "wait" methods can prove confusing. :) ~Daelin 
03262007, 02:37 PM  #10 
Free Software Terrorist
Technical Director

Make a function that doesn't leak a timer, call it polledwait2 and add a comment about it, not too confusing I think...
__________________ 
03262007, 10:07 PM  #11  
BuranX

Quote:
Quote:
tutor is nice... gj ! 

03272007, 05:22 AM  #12 
Kelani Mage

The PolledWait is universal, as it holds the thread as much as required. If you have a chain spell, and you want to move the missile multiple times, and call the missile movement function for each loop, then you need to hold the thread in place using a wait.
__________________You are emphasizing on an unsignificant detail (such as a leak in PolledWait) rather than considering the purpose of this tutorial. Thanks for the nice comment ToadCop. ~Daelin 
03272007, 01:04 PM  #13  
BuranX

Quote:
Quote:


03272007, 01:12 PM  #14 
Free Software Terrorist
Technical Director

I don't see much sense in not using TriggerSleepAction when timing doesn't have to be totally accurate...
__________________ 
03272007, 05:16 PM  #15  
BuranX

Quote:


Thread Tools  Search this Thread 

