| 08-09-2009, 09:50 PM | #1 |
Hi guys, I am making another sample for my newest system CMS and I want to demonstrate how a user can easily create his/her own movement method for the projectile. For this case I want to make a fire bolt move in a Sin like pattern. I tried studying Anitarf's submission on a spell contest but my map gives a fatal error and dies ... Can anyone please help me with this movement formula? |
| 08-09-2009, 11:33 PM | #2 |
Wait, I'm still testing if my formula works. EDIT: nope it doesn't lol. |
| 08-09-2009, 11:48 PM | #3 |
I'll tell you everything, lemme just edit the post: Okay, so you want to move a projectile in the pattern of a sin wave. The first object is to move linearly between your start and end point (or in the direction of the end point). The easiest way I know of that works with the sin wave is using polar projections. This way, once you've reached a certain point on this line, you can then add or subtract 90 from the projected angle and then project by your distance. If you look at a sin graph, it will show you that the height of the wave is equal to the amplitude of the wave times the sine of the angle. This angle at any time can be determined by dividing your accumulated distance along the line by the wavelength of the wave, then multiplying this value as a factor by 2pi or 360. Once you have the distance, you just add that times the sin / cos of your angle + 90. Here is a formula : JASS:set linearx = currentx + dist * cos(theta) set lineary = currenty + dist * sin(theta) set sinx = linearx + sin((current_distance / wavelength) * 2*bj_PI) * amplitude * cos(theta + bj_PI/2) set siny = lineary + sin((current_distance / wavelength) * 2*bj_PI) * amplitude * sin(theta + bj_PI/2) |
| 08-10-2009, 09:58 AM | #4 |
Zerzax, about your formula: JASS:set linearx = currentx + dist * cos(theta) dist = total distance that needs to be done? Or speed of the projectile? The amplitude is is Radians right? EDIT EDIT EDIT Ok, I used your formula, the result is better (the map doesn't crash any longer) but the movement is at least ... disturbing ... Here is the code of the method: JASS://this demonstrates how you can make your own move method! method costumMoveMissile takes nothing returns nothing local real x = GetUnitX(.currentTarget)-.missile.x local real y = GetUnitY(.currentTarget)-.missile.y local real distance = SquareRoot(x*x + y*y) local real angle1 = Atan2(y, x) local real linearX = .missile.x + .speed * Cos(angle1) local real linearY = .missile.y + .speed * Sin(angle1) local real sinX = linearX + Sin((.missileCurrentDist / .targetDistance) * 2*bj_PI) * 25 * Cos(angle1 + bj_PI/2) local real sinY = linearY + Sin((.missileCurrentDist / .targetDistance) * 2*bj_PI) * 25 * Sin(angle1 + bj_PI/2) set .missileCurrentDist = .missileCurrentDist + distance set .missile.xyangle = angle1 set .missile.x = sinX set .missile.y = sinY endmethod If you need to know more, here is the code of the spell: JASS:scope CrazyFire initializer Init //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A001' private constant string FXPATH = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl" //the path of the missile model private constant string EXP_PATH = "Abilities\\Spells\\Demon\\DemonBoltImpact\\DemonBoltImpact.mdl" //path of the explosion private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC //the attack type the fire will cause private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL //the damage type the fire will do endglobals private constant function Range takes integer level returns real //the range of the missile. The missile will look for new targets inside this range only return 600. endfunction private constant function Damage takes integer level returns real //the damage the missile will cause to the victims return 50.*level endfunction private constant function JumpNumber takes integer level returns integer //the number of times the fire will jump return 12 endfunction private constant function GrenadeHeight takes integer level returns real return 600. endfunction private function Targets takes unit caster, unit target returns boolean //the targets the fire will affect return (GetWidgetLife(target) > .405) and IsUnitEnemy(target, GetOwningPlayer(caster)) and IsUnitType(target, UNIT_TYPE_GROUND) endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== globals private unit tmpCaster private boolexpr chooseTargets private group targets endglobals //=========================================================================== private function PickTargets takes nothing returns boolean return Targets(tmpCaster, GetFilterUnit()) endfunction //=========================================================================== private struct CrazyFire extends ChainMissile unit caster integer level unit currentTarget real missileCurrentDist real targetDistance //this demonstrates how you can make your own move method! method costumMoveMissile takes nothing returns nothing local real x = GetUnitX(.currentTarget)-.missile.x local real y = GetUnitY(.currentTarget)-.missile.y local real distance = SquareRoot(x*x + y*y) local real angle1 = Atan2(y, x) local real linearX = .missile.x + .speed * Cos(angle1) local real linearY = .missile.y + .speed * Sin(angle1) local real sinX = linearX + Sin((.missileCurrentDist / .targetDistance) * 2*bj_PI) * 25 * Cos(angle1 + bj_PI/2) local real sinY = linearY + Sin((.missileCurrentDist / .targetDistance) * 2*bj_PI) * 25 * Sin(angle1 + bj_PI/2) set .missileCurrentDist = .missileCurrentDist + distance set .missile.xyangle = angle1 set .missile.x = sinX set .missile.y = sinY endmethod method searchTarget takes unit currentTarget returns unit local unit f = null local unit ret = null //the position of the current target local real cX = GetUnitX(currentTarget) local real cY = GetUnitY(currentTarget) //the position of our new target local real nX local real nY //by saving the minimal distance, this will help us choose //the closest new target to our current target //note we start it with the biggest value possible, so //we can find the minimum after //Also note that I am calculating minDist^2 so I can avoid //the use of a squareroot which will save speed. Know that if //minDist <, >, <=, >= d then minDis^2 <, >, <=, >= d^2 and vice-versa local real minDist = Range(.level)*Range(.level) local real d //this will be a temporary variable for the distances we will calculate //here we pick all units near the current target set tmpCaster = .caster call GroupEnumUnitsInRange(targets, cX, cY, Range(.level), chooseTargets) loop set f = FirstOfGroup(targets) exitwhen(f == null) call GroupRemoveUnit(targets, f) if (IsUnitInGroup(f, .pickedUnits) == false) then set nX = GetUnitX(f) set nY = GetUnitY(f) //now we calculate the distance between f and our current target //note that: d^2 = (x2-x1)^2 + (y2-y1)^2 //I avoid using a square to make this faster set d = (nX - cX)*(nX - cX) + (nY - cY)*(nY - cY) //if this new distance is the smallest, then we update our target if (d < minDist) then set minDist = d set ret = f endif endif endloop //we need to know our current target in order to move the missile towards him! set .currentTarget = ret //here we clear the picked units group, so we can hit our first target again! //NOTE: we will only hit the 1st unit again ONLY if that unit is the closest one! call GroupClear(.pickedUnits) return ret endmethod method onTarget takes unit target returns nothing //this is what we are going to do to the target! //we will blow him up! call UnitDamageTarget(.caster, target, Damage(.level), true, false, ATTACK_TYPE, DAMAGE_TYPE, null) //the BOOOM effect call DestroyEffect(AddSpecialEffect(EXP_PATH, GetUnitX(target), GetUnitY(target))) endmethod // static method create takes unit aCaster, unit aTarget returns CrazyFire static method create takes unit aCaster, unit aTarget, real angle returns thistype local CrazyFire this = CrazyFire.allocate(FXPATH, aCaster, aTarget, JumpNumber(GetUnitAbilityLevel(aCaster, AID)), true) local real x = GetUnitX(aTarget)- GetUnitX(aCaster) local real y = GetUnitY(aTarget)- GetUnitY(aCaster) local real distance = SquareRoot(x*x + y*y) //in this case I don't set any variables, so I will use the default values //and now we set the other stuff set .caster = aCaster set .level = GetUnitAbilityLevel(.caster, AID) set .currentTarget = aTarget set .missileCurrentDist = 0. set .targetDistance = distance //we need to know our current target in order to move the missile towards him! return this endmethod endstruct //=========================================================================== private function Conditions takes nothing returns boolean local unit caster local unit target if GetSpellAbilityId() == AID then set caster = GetTriggerUnit() set target = GetSpellTargetUnit() call CrazyFire.create(caster, target, Atan2(GetUnitY(target) - GetUnitY(caster), GetUnitX(target) - GetUnitX(caster))) endif set caster = null set target = null return false endfunction //=========================================================================== private function Init takes nothing returns nothing local trigger CrazyFireTrg = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(CrazyFireTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(CrazyFireTrg, Condition(function Conditions)) //setting our globals set targets = CreateGroup() set chooseTargets = Condition(function PickTargets) //Preloading our effects call Preload(EXP_PATH) endfunction endscope Can some one please tell me what I am doing wrong ? |
| 08-10-2009, 12:58 PM | #5 | |
can you post a screenshot when it's moving Quote:
cause I had a similiar problem in the first version of my contest spell |
| 08-10-2009, 03:26 PM | #6 | ||
Quote:
Quote:
Also, JASS:set .missileCurrentDist = .missileCurrentDist + distance |
| 08-10-2009, 03:31 PM | #7 |
The mystery here is, if you want it to know like Sin function, how about you use Sin ? |
| 08-10-2009, 07:19 PM | #8 | |
Saturn: 1 - So first I need set missile.x = linearX and then missile.x = sinX? 2 - I don't get it, why is it wrong? That is the current distance between the target and the missile ...Mmmm, I will think about it Quote:
|
| 08-10-2009, 07:26 PM | #9 |
...and you're retarded. JASS:set data.dist = data.dist+data.inc set data.x = data.startx + data.dist*data.cos set data.y = data.starty + data.dist*data.sin set data.sindist = Sin(data.dist/data.period)*data.amp set data.x = data.x + data.sindist*data.vertcos set data.y = data.y + data.sindist*data.vertsin You could even condense it further: JASS:set data.dist = data.dist+data.inc set data.sindist = Sin(data.dist/data.period)*data.amp set data.x = data.startx + data.dist*data.cos + data.sindist*data.vertcos set data.y = data.starty + data.dist*data.sin + data.sindist*data.vertsin |
| 08-10-2009, 09:17 PM | #10 | |
Hi Pyro, I want you to meet my friend Mr.Nice: Quote:
Anyway, thx for the post ... I think I got it ... JASS://this demonstrates how you can make your own move method! method costumMoveMissile takes nothing returns nothing local real x = GetUnitX(.currentTarget)-.missile.x local real y = GetUnitY(.currentTarget)-.missile.y local real distance = SquareRoot(x*x + y*y) local real angle1 = Atan2(y, x) local real linearX = .missile.x + .speed * Cos(angle1) local real linearY = .missile.y + .speed * Sin(angle1) local real sinX = linearX + Sin((.missileCurrentDist / .targetDistance) * 2*bj_PI) * .amplitude * Cos(angle1 + bj_PI/2) local real sinY = linearY + Sin((.missileCurrentDist / .targetDistance) * 2*bj_PI) * .amplitude * Sin(angle1 + bj_PI/2) set .missileCurrentDist = .missileCurrentDist + .speed set .missile.xyangle = angle1 set .missile.x = sinX set .missile.y = sinY endmethod Spell: JASS:scope CrazyFire initializer Init //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A001' private constant string FXPATH = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl" //the path of the missile model private constant string EXP_PATH = "Abilities\\Spells\\Demon\\DemonBoltImpact\\DemonBoltImpact.mdl" //path of the explosion private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC //the attack type the fire will cause private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL //the damage type the fire will do endglobals private constant function Range takes integer level returns real //the range of the missile. The missile will look for new targets inside this range only return 600. endfunction private constant function Damage takes integer level returns real //the damage the missile will cause to the victims return 50.*level endfunction private constant function JumpNumber takes integer level returns integer //the number of times the fire will jump return 12 endfunction private constant function GrenadeHeight takes integer level returns real return 600. endfunction private function Targets takes unit caster, unit target returns boolean //the targets the fire will affect return (GetWidgetLife(target) > .405) and IsUnitEnemy(target, GetOwningPlayer(caster)) and IsUnitType(target, UNIT_TYPE_GROUND) endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== globals private unit tmpCaster private boolexpr chooseTargets private group targets endglobals //=========================================================================== private function PickTargets takes nothing returns boolean return Targets(tmpCaster, GetFilterUnit()) endfunction //=========================================================================== private struct CrazyFire extends ChainMissile unit caster integer level unit currentTarget real missileCurrentDist real targetDistance real amplitude //this demonstrates how you can make your own move method! method costumMoveMissile takes nothing returns nothing local real x = GetUnitX(.currentTarget)-.missile.x local real y = GetUnitY(.currentTarget)-.missile.y local real distance = SquareRoot(x*x + y*y) local real angle1 = Atan2(y, x) local real linearX = .missile.x + .speed * Cos(angle1) local real linearY = .missile.y + .speed * Sin(angle1) local real sinX = linearX + Sin((.missileCurrentDist / .targetDistance) * 2*bj_PI) * .amplitude * Cos(angle1 + bj_PI/2) local real sinY = linearY + Sin((.missileCurrentDist / .targetDistance) * 2*bj_PI) * .amplitude * Sin(angle1 + bj_PI/2) set .missileCurrentDist = .missileCurrentDist + .speed set .missile.xyangle = angle1 set .missile.x = sinX set .missile.y = sinY endmethod method searchTarget takes unit currentTarget returns unit local unit f = null local unit ret = null //the position of the current target local real cX = GetUnitX(currentTarget) local real cY = GetUnitY(currentTarget) //the position of our new target local real nX local real nY //by saving the minimal distance, this will help us choose //the closest new target to our current target //note we start it with the biggest value possible, so //we can find the minimum after //Also note that I am calculating minDist^2 so I can avoid //the use of a squareroot which will save speed. Know that if //minDist <, >, <=, >= d then minDis^2 <, >, <=, >= d^2 and vice-versa local real minDist = Range(.level)*Range(.level) local real d //this will be a temporary variable for the distances we will calculate //here we pick all units near the current target set tmpCaster = .caster call GroupEnumUnitsInRange(targets, cX, cY, Range(.level), chooseTargets) loop set f = FirstOfGroup(targets) exitwhen(f == null) call GroupRemoveUnit(targets, f) if (IsUnitInGroup(f, .pickedUnits) == false) then set nX = GetUnitX(f) set nY = GetUnitY(f) //now we calculate the distance between f and our current target //note that: d^2 = (x2-x1)^2 + (y2-y1)^2 //I avoid using a square to make this faster set d = (nX - cX)*(nX - cX) + (nY - cY)*(nY - cY) //if this new distance is the smallest, then we update our target if (d < minDist) then set minDist = d set ret = f endif endif endloop //we need to know our current target in order to move the missile towards him! set .currentTarget = ret set .targetDistance = d //here we clear the picked units group, so we can hit our first target again! //NOTE: we will only hit the 1st unit again ONLY if that unit is the closest one! call GroupClear(.pickedUnits) return ret endmethod method onTarget takes unit target returns nothing //this is what we are going to do to the target! //we will blow him up! call UnitDamageTarget(.caster, target, Damage(.level), true, false, ATTACK_TYPE, DAMAGE_TYPE, null) //the BOOOM effect call DestroyEffect(AddSpecialEffect(EXP_PATH, GetUnitX(target), GetUnitY(target))) endmethod // static method create takes unit aCaster, unit aTarget returns CrazyFire static method create takes unit aCaster, unit aTarget, real angle returns thistype local CrazyFire this = CrazyFire.allocate(FXPATH, aCaster, aTarget, JumpNumber(GetUnitAbilityLevel(aCaster, AID)), true) local real x = GetUnitX(aTarget)- GetUnitX(aCaster) local real y = GetUnitY(aTarget)- GetUnitY(aCaster) local real distance = SquareRoot(x*x + y*y) //in this case I don't set any variables, so I will use the default values //and now we set the other stuff set .caster = aCaster set .level = GetUnitAbilityLevel(.caster, AID) set .currentTarget = aTarget set .missileCurrentDist = 0. set .targetDistance = distance set .amplitude = 7 //amplitude of Sin wave //we need to know our current target in order to move the missile towards him! return this endmethod endstruct //=========================================================================== private function Conditions takes nothing returns boolean local unit caster local unit target if GetSpellAbilityId() == AID then set caster = GetTriggerUnit() set target = GetSpellTargetUnit() call CrazyFire.create(caster, target, Atan2(GetUnitY(target) - GetUnitY(caster), GetUnitX(target) - GetUnitX(caster))) endif set caster = null set target = null return false endfunction //=========================================================================== private function Init takes nothing returns nothing local trigger CrazyFireTrg = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(CrazyFireTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(CrazyFireTrg, Condition(function Conditions)) //setting our globals set targets = CreateGroup() set chooseTargets = Condition(function PickTargets) //Preloading our effects call Preload(EXP_PATH) endfunction endscope I can also post testmap is you are curious =D |
| 08-10-2009, 09:35 PM | #11 |
Instead of targetDistance I would use the wave period. Sorry I didn't post further, I've been in class all day. EDIT: I guess the distance you divide by depends on the frequency or period of the wave. Pyro's code block looks fine. |
| 08-10-2009, 11:25 PM | #12 |
I'm chobo, the sc2 noob lol. |
| 08-11-2009, 08:32 AM | #13 | |||
Quote:
and I believe the theta I should use us the original angle between targets ? I think so. I will give a try today. Quote:
Quote:
|
| 08-11-2009, 12:24 PM | #14 |
Sine and Cosine of the vertical angle... Really, if you understood what it was you were doing you'd understand why the code is what it is and what exactly each part does. The easiest way to do this is with a double-projection approach where you project the position of the object once first along a line towards the target point, and then use the value of the Sin() function to tell you a vertical distance away from that line. |
| 08-11-2009, 12:52 PM | #15 | |
Quote:
Your problem is not that you don't understand. No, that is the least of your problems. Your problem, Phoenix, is that you are virtually immune to education. One can spend entire threads explaining something to you and yet you can retain conviction that their explanation is somehow wrong or incorrect or flawed. There are examples all over the forums, so I refuse to spend time finding them. WC3C has a reputation of being the best at helping people; however, that reputation comes at a price. First, you must be able to help yourself. If you cannot help yourself, then God only knows what you expect us to do for you. This question that you ask has been solved thousands of times over the course of WC3C's existence in so many ways that even you can find one that makes sense to you. That this thread exists and that it still hasn't sunk in how to do it are examples of your willingness to waste our time and your lack of desire to learn anything on your own, but rather only learn by having it spoon-fed to you. And that is the last I'm going to hear of your Mr. Nice crap. If what I just said doesn't make sense to you, angers you, saddens you, or you just don't care about it, then tough luck. Don't act persecuted, because the only one at fault for how you are treated is yourself. |
