• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Math - Cos() or Sin() curve

Status
Not open for further replies.
Level 6
Joined
Jul 25, 2005
Messages
221
Hey, I've been trying to figure out how to make a unit move in a wave-like pattern.. like

p1_.-'¨'-._´´´´´_.-'¨'-._p2
_________'-._.-'

And if I remember from my math lessons, offset*Cos(Amplitude*Duration) and offset*Sin(Amplitude*Duration) where Amplitude is the span from up and down and duration is how many degrees (1-360)

but I don't know how to add this to a coordinate system, and then rotate it. I tried something like this
JASS:
local real x = GetUnitX(spawn)+10*Cos(facing*bj_DEGTORAD)+Cos(5*counter*bj_DEGTORAD)
local real y = GetUnitY(spawn)+10*Sin(facing*bj_DEGTORAD)+Cos(5*counter*bj_DEGTORAD)
Where counter is a counter going from 1-360 -1-360 until it reaches the target. But this only works at a 0-90 degree angle.
Anyone done this before? Any suggestions?

I'd look it up in my 'blue book' (where almost all important functions are listed) but I can't seem to find it...

EDIT: hmm, maybe the 'Duration' is the 'distance' between the two points

EDIT: right, sorry, forgot to convert to radians (pi/180.0), my calculator has two modes, radian and degrees, so I don't have to add it myself

EDIT: still working on it, the solution is often very silly, but I sure as hell can't see it. Let me know if you know... or something :D
 
Last edited:
Level 6
Joined
Jul 25, 2005
Messages
221
Ok, so now I've tested alot of different formulas, but here's one that worked the best. Anyone have any suggestions/improvements on it or a different one?

JASS:
local real x = GetUnitX(spawn)+10*Cos(facing*bj_DEGTORAD)+5*Cos(Dist*bj_DEGTORAD)
local real y = GetUnitY(spawn)+10*Sin(facing*bj_DEGTORAD)+5*Sin(Dist*bj_DEGTORAD)
-x,+y.| +x,+y
........|
------P-----
-x,-y.| +x, -y
........|
 
Level 8
Joined
Apr 30, 2009
Messages
338
if you want it to be more stretched out in sin or cos, divide the argument by a number - the bigger the number, the more stretched out it is
 
Level 8
Joined
Apr 30, 2009
Messages
338
the argument of sin(x) is x

so say the graphs are

sin(x) = /'''\.../'''\.../

sin(x/2) = /''''''\....../''''''\....../

sin(4x) = /\/\/

basically there are two ways to affect the "shape" of a sin or cos curve:

a coefficient in front of the function: asin(x)
large a: the maximum distance from the neutral axis is big
small a: the maximum distance from the neutral axis is small

a coefficient in the argument of the function: sin(bx)

large b (b>1): graph is compressed
small b (0<b<1): graph is stretched
 
Level 6
Joined
Jul 25, 2005
Messages
221
Hm, well, alot of this I already knew, in fact I think I stated this earlier, but thanks for clarifying.

Now, how would YOU implement the function into the game?
I've already shown you my way, and I found it to be... buggy if a is bigger than 6.
 
Level 14
Joined
Nov 18, 2007
Messages
816
JASS:
library Movement // maybe you need an initializer and requirements; add them here

    globals
        private constant real TICK = 1./32
    endglobals

    public struct Data
        unit u
        real s
        real f
        real d
        real p
        real c=0
        real x
        real y
        real tx
        real ty
        real dist

        private integer i

        private static thistype array Structs
        private static timer T=CreateTimer()
        private static integer Count=0

        method onDestroy takes nothing returns nothing
            set .u=null
            // clean your struct here
            set thistype.Count=thistype.Count-1
            set thistype.Structs[.i]=thistype.Structs[thistype.Count]
            set thistype.Structs[.i].i=.i
            if thistype.Count==0 then
                call PauseTimer(thistype.T)
            endif
        endmethod

        private static method Callback takes nothing returns nothing
        local integer i=thistype.Count-1
        local thistype s
        local real x
        local real y
            loop
                exitwhen i<0
                set s=thistype.Structs[i]
                set s.c=s.c+s.s
                set y=s.d*Sin(s.c/s.p)
                
                set s.x=s.x+(s.s*Cos(s.f))
                set s.y=s.y+(s.s*Sin(s.f))
                
                set x=s.x+(y*Cos(s.f+bj_PI/2))
                set y=s.y+(y*Sin(s.f+bj_PI/2))

                call SetUnitX(s.u, x)
                call SetUnitY(s.u, y)
                // you may want to remove the following two lines.
                call AddSpecialEffect("Abilities\\Spells\\Human\\FlameStrike\\FlameStrikeDamageTarget.mdl", x, y)
                call AddSpecialEffect("Abilities\\Spells\\Human\\FlameStrike\\FlameStrikeDamageTarget.mdl", s.x, s.y)
                
                if s.dist-s.c<(s.s*TICK)/2 then
                    call s.destroy()// do your things here, dont forget to call s.destroy() somewhen
                endif
                set i=i-1
            endloop
        endmethod

        static method create takes unit u, real angle, real distance, real deviation, real speed, real periods returns Data
        local thistype s=thistype.allocate()
            set periods=R2I((periods+0.25)/0.5)*0.5 // Round to next 0.5, so we can finish at tx/ty
            set s.u=u
            set s.s=speed*TICK
            set s.f=angle
            set s.d=deviation
            set s.p=distance/(periods*2*bj_PI)
            set s.x=GetUnitX(u)
            set s.y=GetUnitY(u)
            set s.tx=GetUnitX(u)+Cos(s.f)*distance
            set s.ty=GetUnitY(u)+Sin(s.f)*distance
            set s.dist=distance
            // initialize the struct here
            set thistype.Structs[thistype.Count]=s
            set s.i=thistype.Count
            if thistype.Count==0 then
                call TimerStart(thistype.T, TICK, true, function thistype.Callback)
            endif
            set thistype.Count=thistype.Count+1
            return s
        endmethod
    endstruct

endlibrary

JASS:
library Test initializer Init uses Movement

    private function Actions takes nothing returns nothing
    local unit u=CreateUnit(Player(0), 'hfoo', 0,0,0)
        call Movement_Data.create(u, bj_PI/4, 500, 200, 50, 1)
        call UnitApplyTimedLife(u, 'BTLF', 11)
        set u=null
    endfunction
    
    private function Init takes nothing returns nothing
    local trigger t=CreateTrigger()
        call TriggerRegisterPlayerChatEvent(t, Player(0), "-test", true)
        call TriggerAddAction(t, function Actions)
    endfunction
    
endlibrary
that might work, if i didnt screw up and made some errors.

Edit: I screwed up, expect a new version soon.

Edit2: Done. Working correctly now.
 
Last edited:
Level 6
Joined
Jul 25, 2005
Messages
221
Deaod, I didn't ask for a really huge trigger on everything, I just wanted to see your equation with a cos/sin curve. I'll try your equation and see the difference.

Also, this is an interesting library, and a good method to create multi-instanceability, I'll remember it when I come to libraries. So far I'm making scopes for specific spells.
Your approach, 1 global timer, is the same as in my scope, so I'm quite happy that I use a similiar method. I didn't know about thistype, this makes things easier.
 
Level 8
Joined
Apr 30, 2009
Messages
338
Well the maximum value of sin(x) or cos(x) is 1, and 1 unit of distance is impossible to notice in the game. You would need at least 100 to even begin to notice the curve.
 
Level 6
Joined
Jul 25, 2005
Messages
221
Thanks Deaod for your very thorough trigger, it gave me some advice on how to effeciently write MUI inside a struct entirely. Though I personally think you make it hard to read when you divide each calculation in bits and then put them together. I find it easier to read the whole equation in one line, don't you?

And thanks to tml616;
Reply:
Which is why you add an 'offset' in front of it, just like with polar projection. Though the offset can't be more than 8, because then the 'amplitude' gets too great and it just goes to 0. It's hard to explain, but, I will upload my spell soon, and you can play around with the numbers and see what I mean.

Final equation used in the spell:
JASS:
local real x = GetUnitX(UNIT)+SPEED*Cos(facing*bj_DEGTORAD)+AMPLITUDE*Cos((.SpawnDist()*bj_DEGTORAD)/INTERVAL)
local real y = GetUnitY(UNIT)+SPEED*Sin(facing*bj_DEGTORAD)+AMPLITUDE*Sin((.SpawnDist()*bj_DEGTORAD)/INTERVAL)

You can continue posting better equations, but keep in mind to keep them simple (like the one above) so that it can be easily implemented and tested.
 
Status
Not open for further replies.
Top