HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Forumla please?

09-07-2009, 12:01 AM#1
Zandose
I don't know what to ask for. It's a math formula. Basically I'm trying to make a ship move in space, but I don't know the formula. Remember, there is nothing to stop a moving object, so if the ship was already moving and you gave it a new target it would have to take into account the angle and thrust needed to change directions.

For instance, you start moving a ship from the center of the map to the center-right and along the way to change the direction to the bottom-center of the map, you now need to rotate the ship and apply thrust to slow the motion of the ship (stop is moving to the right) and at the same time make it go the new direction.

Hope this makes sense. BTW, please make this a layman as possible. The highest I know is BEDMAS. Well I use to know a little more but I forgot it.
09-07-2009, 02:26 AM#2
Zerzax
This seems to deal with some basic physics if I'm correct -velocity, applied force, and acceleration. When I get home I'll try to elaborate.

Seems there is torque also...

So we have a system in which there are no frictional forces acting on the spaceship. We apply a force in some direction and the ship gains speed in that direction (velocity). In order to slow the ship down, we first need to apply a new force to the ship in the direction that you want to go in. This force generates acceleration in this new direction that in this case is slowing the horizontal (x) velocity and "decreasing" (because the y direction is negative) the y velocity so that ship is moving a certain way in both dimensions. This is pretty easy to explain in terms of vectors. If you don't understand 2D vectors, then you won't really understand this. But the good thing is that they are Very simple. I'll just lay it out quickly.

A vector is made up of two components - magnitude and direction. The magnitude is in this case base speed, and the direction is the angle in which this speed is directed. I'm going to omit the force vector in this case because then you have to use mass and translate that to WC3 terms, it just seems pointless. So we are left with two vector values - velocity (the speed in a certain direction) and acceleration (the rate of change of velocity with its own direction).

A vector can be shown as Magnitude * (Cos(theta) + Sin(theta)), where theta is the direction of the vector. The magnitude can be found by taking the square root of the x and y components of the vector - It's just like a triangle, it's the pythagorean theorem.

After you set this space ship in motion and it is just floating in that same direction, it has no acceleration - just a constant velocity. You need to store that velocity so that when we begin applying acceleration, we can slowly change the velocity value over time.

Velocity is in units of Distance/Time (meters/second usually) and acceleration is in units of Distance/(Time Squared). You just add this new acceleration value (knowing the magnitude of the thrust and the angle you need to go in) times the timer interval you are using (you'll need a timer) to the velocity. You then add the new velocity times the timer interval for each x and y component to the current x and y values. You keep applying this acceleration until the velocity is what you want - speed in the new direction - in which case you can stop applying acceleration.
09-13-2009, 01:15 AM#3
Zandose
Sorry it has taken me so long to reply, I've been very busy.

You lost me there a little. Could you show that in JASS or a formula with what each variable stands for?

Another formula please. Knowing where you want a ship to go (using the above formula), can you plot a point beforehand to turn the ship around and decelerate?
09-13-2009, 01:50 AM#4
Zerzax
Alright, lets see if this makes sense

I will represent most of the variables as struct members within a "particle" struct

Collapse JASS:

vector position
vector acceleration
vector velocity
unit u

real curVelocity
real destVelocity

static method timerUpdate takes nothing returns nothing
    local particle this = GetTimerData(GetExpiredTimer())

    set .velocity.x  = .velocity.x + acceleration.x * TIMER_INTERVAL
    set .velocity.y = .velocity.y + acceleration.y * TIMER_INTERVAL
   
    set .position.x = .position.x + .velocity.x * TIMER_INTERVAL
    set .position.y = .position.y + velocity.y * TIMER_INTERVAL

    call SetUnitX(.u, position.x)
    call SetUnitY(.y, position.y)

endmethod


Editing now, just wanted to get the post up

Okay, so I use a "vector" type (seen in Anitarf's Vectorlib) - all there is to this vector is the storing of three values - x, y, and z. We don't use z here because it seems you don't need it at the moment. In this case, the position vector is actually the point in 2d space that the ship occupies. You can visualize this by putting the tail of the vector (like the end of the line segment) at the origin (0,0). You can visualize the arrowhead (that represents direction) as the actual point (x,y). When we add the components of one vector to another, we get a new vector in this new position. The order of events goes like this: since acceleration impacts velocity in a certain direction, we first add acceleration ( times the timer period of course, this cancels out one of the time units (seconds) of the acceleration vector, yielding velocity (units/second)) to the velocity. Then we add this new velocity vector to the position vector - by of course first taking apart each vector into its x and y components - and adding those components. Because we take into account a small timer interval, we are nudging the ship unit in the direction we want. However, notice that the ship never completely stops - it maintains its speed. This will create the wonky motion that slows the ship down in the former direction and starts speeding it up in the new direction.

Remember that x and y components are: Magnitude of vector times the cosine of the direction angle (x vector component), Magnitude of vector times the sine of the direction angle (y vector component).

The magnitude of acceleration is probably going to be constant in your map - like 200 units/second/second. The magnitude of velocity is speed in a certain direction - like miles per hour in a car. We call these magnitudes scalar quantities because they merely increase (or decrease) the size of x and y vectors to scale.

I guess to find out if current velocity equals the destination velocity (after you target a point), you need to compare the scalar values of each vector - and add them in the timer interval as well.

EDIT:
Collapse JASS:
set acceleration.x = Acceleration_Base * Cos(direction)
set acceleration.y = Acceleration_Base * Sin(direction)

This might help as well: http://en.wikipedia.org/wiki/Euclidean_vector
09-16-2009, 06:09 AM#5
Zandose
Is this correct or totally wrong?
Collapse JASS:
struct particle
vector position
vector acceleration
vector velocity
unit u

static method timerUpdate takes nothing returns nothing
    local particle this = GetTimerData(GetExpiredTimer())

    set .acceleration.x = 10 * TIMER_INTERVAL * Cos(direction)
    set .acceleration.y = 10 * TIMER_INTERVAL * Sin(direction)

    set .velocity.x = .velocity.x + .acceleration.x * TIMER_INTERVAL 
    set .velocity.y = .velocity.y + .acceleration.y * TIMER_INTERVAL
    
    set .position.x = .position.x + .velocity.x * TIMER_INTERVAL
    set .position.y = .position.y + .velocity.y * TIMER_INTERVAL

    call SetUnitX(.u, position.x)
    call SetUnitY(.y, position.y)

endmethod
endstruct
09-16-2009, 07:18 AM#6
TheWye
So your base acceleration is 10*TIMER_INTERVAL ? Why don't you just set it to a constant number instead? Your code should work fine, although this line seems mathematically awkward.

And um.. I'm not sure about this myself, but isn't it better to make the timerUpdate method a normal method instead of a static method? This way, you don't need to GetTimerData everytime the timer expires.
09-16-2009, 05:34 PM#7
Zandose
Quote:
Originally Posted by TheWye
So your base acceleration is 10*TIMER_INTERVAL ? Why don't you just set it to a constant number instead?
It use to be "Acceleration_Base" instead of 10, but I thought it looked better this way.

Quote:
Your code should work fine, although this line seems mathematically awkward
How would you do it?

Quote:
Originally Posted by TheWye
And um.. I'm not sure about this myself, but isn't it better to make the timerUpdate method a normal method instead of a static method? This way, you don't need to GetTimerData everytime the timer expires.
This was the way Zerzax showed me, so I didn't want to change it.
09-16-2009, 08:01 PM#8
Zerzax
Whatever, you still need to use a timer - doesn't matter if you do it in a static method (required at first), or branch off into a regular method. If acceleration is constant (your ship speeds up at a constant speed) then you only set acceleration on initialization. There is some other stuff - you are looking for a destination velocity, you need to increment an independent variable (curVelocity) until it is greater than or equal to destVelocity (determined when your ship changes direction). this might help:

Collapse JASS:

    method changeDirection takes real direction returns nothing // in radians...
        set .acceleration.x = BASE_ACCELERATION * Cos(direction) * TIMER_PERIOD
        set .acceleration.y = BASE_ACCELERATION * Sin(direction) * TIMER_PERIOD
        set .curVelocity = 0
    endmethod

    static method timerUpdate
        local particle this = GetTimerData(GetExpiredTimer())

        if .curVelocity < SHIP_MAX_SPEED then
            set .curVelocity = .curVelocity + BASE_ACCELERATION*TIMER_PERIOD
            set .velocity.x = .velocity.x + acceleration.x
            set .velocity.y = .velocity.y + .acceleration.y
        endif

        set .velocity.x = .velocity.x + .acceleration.x * TIMER_INTERVAL 
        set .velocity.y = .velocity.y + .acceleration.y * TIMER_INTERVAL
    
        set .position.x = .position.x + .velocity.x * TIMER_INTERVAL
        set .position.y = .position.y + .velocity.y * TIMER_INTERVAL

        call SetUnitX(.u, position.x)
        call SetUnitY(.y, position.y)


    endmethod



Sorry, hastily done, will update tonight.

EDIT: Something is up with the curVelocity, it needs to be more precise
09-25-2009, 12:09 AM#9
Zandose
This is what I have, doesn't work though. Just trying to get the ship/object to move normally.

Collapse JASS:
library aaa initializer Init requires TimerUtils

globals
    private constant real TIMER_PERIOD      = .3
    private constant real BASE_ACCELERATION = .01
endglobals

struct vector
    real x = 0
    real y = 0
endstruct

struct particle
    unit   u
    timer  t
    vector target
    vector position
    vector velocity
    vector acceleration

    static method Create takes nothing returns nothing
        local particle this = particle.create()
        
        set .u = CreateUnit(Player(0), 'hgyr', 0, 0, 0)
        
        call SetUnitUserData(.u, this)
        
        set .t = NewTimer()
        call SetTimerData(.t, this)
        call TimerStart(.t, TIMER_PERIOD, true, function particle.timerUpdate)
        
        debug call BJDebugMsg("Create")
    endmethod
    
    static method timerUpdate takes nothing returns nothing
        local particle this = GetTimerData(GetExpiredTimer())
                
        set .velocity.x = .velocity.x + .acceleration.x * TIMER_PERIOD //Does this increase the speed of the unit over time?
        set .velocity.y = .velocity.y + .acceleration.y * TIMER_PERIOD
    
        set .position.x = .position.x + .velocity.x * TIMER_PERIOD
        set .position.y = .position.y + .velocity.y * TIMER_PERIOD
                
        call SetUnitX(.u, .position.x)
        call SetUnitY(.u, .position.y)
        
        if .position.x == .target.x and .position.y == .target.y and .target.x != 0 then
            debug call BJDebugMsg("TargetReached")
        endif
    endmethod
    
    static method changeDirection takes nothing returns nothing // in radians...
        local particle this = GetUnitUserData(GetTriggerUnit())
        
        local real angle = 180.0/3.14159 * Atan2(GetOrderPointY() - .position.y, GetOrderPointX() - .position.x)
        set .acceleration.x = BASE_ACCELERATION * Cos(angle) * TIMER_PERIOD
        set .acceleration.y = BASE_ACCELERATION * Sin(angle) * TIMER_PERIOD
                
        set .target.x = GetOrderPointX()
        set .target.y = GetOrderPointY()
        
        debug call BJDebugMsg("OrderPoint")
    endmethod
    
endstruct

private function DoTrue takes nothing returns boolean
    return true
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, Condition(function DoTrue))
    call TriggerAddAction(t, function particle.changeDirection)

    call particle.Create()
    debug call BJDebugMsg("Init")
endfunction

endlibrary
Attached Files
File type: w3xwc3c v1.w3x (95.8 KB)
09-25-2009, 02:19 AM#10
Zerzax
That is a mighty tiny acceleration (.01 wc3 units per second squared, you should try faster, like 10) and you probably will want a much lower timer interval, perhaps .025, .03, or .04. Make sure the unit can barely move on its own (1 move speed might work so it can still be moved by the engine but won't show by itself).
09-25-2009, 02:39 AM#11
Zandose
Quote:
Originally Posted by Zerzax
That is a mighty tiny acceleration (.01 wc3 units per second squared, you should try faster, like 10) and you probably will want a much lower timer interval, perhaps .025, .03, or .04. Make sure the unit can barely move on its own (1 move speed might work so it can still be moved by the engine but won't show by itself).
I've tried higher speeds before. I wanted to use a slow speed to see everything happening. Anyways, the unit is jumping and/or just going too fast across the screen. Is something wrong with the math?

Collapse JASS:
library aaa initializer Init requires TimerUtils

globals
    private constant real TIMER_PERIOD      = .25
    private constant real BASE_ACCELERATION = 1
endglobals

struct vector
    real x = 0
    real y = 0
endstruct

struct particle
    unit   u
    timer  t
    vector target
    vector position
    vector velocity
    vector acceleration

    static method Create takes nothing returns nothing
        local particle this = particle.create()
        
        set .u = CreateUnit(Player(0), 'hgyr', 0, 0, 0)
        
        call SetUnitUserData(.u, this)
        
        set .t = NewTimer()
        call SetTimerData(.t, this)
        call TimerStart(.t, TIMER_PERIOD, true, function particle.timerUpdate)
        
        debug call BJDebugMsg("Create")
    endmethod
    
    static method timerUpdate takes nothing returns nothing
        local particle this = GetTimerData(GetExpiredTimer())
                
        set .velocity.x = .velocity.x + .acceleration.x * TIMER_PERIOD //Does this increase the speed of the unit over time?
        set .velocity.y = .velocity.y + .acceleration.y * TIMER_PERIOD
    
        set .position.x = .position.x + .velocity.x * TIMER_PERIOD
        set .position.y = .position.y + .velocity.y * TIMER_PERIOD
                
        call SetUnitX(.u, .position.x)
        call SetUnitY(.u, .position.y)
        
        if .position.x == .target.x and .position.y == .target.y and .target.x != 0 then
            debug call BJDebugMsg("TargetReached")
        endif
    endmethod
    
    static method changeDirection takes nothing returns nothing // in radians...
        local particle this = GetUnitUserData(GetTriggerUnit())
        
        local real angle = 180.0/3.14159 * Atan2(GetOrderPointY() - .position.y, GetOrderPointX() - .position.x)
        set .acceleration.x = BASE_ACCELERATION * Cos(angle) * TIMER_PERIOD
        set .acceleration.y = BASE_ACCELERATION * Sin(angle) * TIMER_PERIOD
                
        set .target.x = GetOrderPointX()
        set .target.y = GetOrderPointY()
        
        debug call BJDebugMsg("OrderPoint")
    endmethod
    
endstruct

private function DoTrue takes nothing returns boolean
    return true
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, Condition(function DoTrue))
    call TriggerAddAction(t, function particle.changeDirection)

    call particle.Create()
    debug call BJDebugMsg("Init")
endfunction

endlibrary
09-25-2009, 02:01 PM#12
Zerzax
You need to create the vectors. Testing now

EDIT: I set the movespeed to one. The cosine and sine functions take radians so you don't need to convert.
You can only really check to see if you're finished if your speed (magnitude of velocity) is what you want. I'll first fix the bugs then we need to add a speed increment.

EDIT2: Well creating the vectors helped a lot, but something is bringing it back for some reason.

EDIT3: This is quite perplexing, so I will come back to it in a bit.

EDIT4: The velocity needs to complement the current position as opposed to the stored position values - we can get rid of the position vector and use GetUnitX and GetUnitY for the current position.

EDIT5: So, we were multiplying acceleration by the timer period twice. But we also need to get rid of the use of movement of the unit - we need to cast a spell to designate a point, set the unit's animation, and turn its facing properly.

Okay, so it's working pretty well now. One problem is the terrain - I used the carrion swarm spell to designate the target location without having to move there. Since the abyss texture isn't pathable, we can't use that specific texture if we are going to use a spell to designate the target spot. I changed it to dirt and it works fine. I'm going to add a BoundSentinel library so the ship can't move off the pathing map.
09-26-2009, 06:31 AM#13
Zandose
Mind posting what you have so far?

Quote:
You need to create the vectors. Testing now
I assumed the vectors where allocated when I created the particle struct.

Quote:
EDIT: I set the movespeed to one. The cosine and sine functions take radians so you don't need to convert.
You can only really check to see if you're finished if your speed (magnitude of velocity) is what you want. I'll first fix the bugs then we need to add a speed increment.
Collapse Guess your refering to this::
        if .curVelocity < SHIP_MAX_SPEED then
            set .curVelocity = .curVelocity + BASE_ACCELERATION*TIMER_PERIOD
            set .velocity.x = .velocity.x + acceleration.x
            set .velocity.y = .velocity.y + .acceleration.y
        endif

Quote:
EDIT2: Well creating the vectors helped a lot, but something is bringing it back for some reason.
what?

Quote:
EDIT3: This is quite perplexing, so I will come back to it in a bit.
n/a

Quote:
EDIT4: The velocity needs to complement the current position as opposed to the stored position values - we can get rid of the position vector and use GetUnitX and GetUnitY for the current position.
I thought using GetUnitX/GetUnitY would cause problems because the ship does have some speed and I'd throw off the math. Also, its two function calls opposed to variables and math.

Quote:
EDIT5: So, we were multiplying acceleration by the timer period twice. But we also need to get rid of the use of movement of the unit - we need to cast a spell to designate a point, set the unit's animation, and turn its facing properly.
Not sure about the first part, but the second, unit animation and facing are secondary things, movement is first for me.

Quote:
Okay, so it's working pretty well now. One problem is the terrain - I used the carrion swarm spell to designate the target location without having to move there. Since the abyss texture isn't pathable, we can't use that specific texture if we are going to use a spell to designate the target spot. I changed it to dirt and it works fine. I'm going to add a BoundSentinel library so the ship can't move off the pathing map.
I assumed the terrain textures were just images overlayed on the games tiles, having nothing to do with pathing. Also, theres that channeling spell you can use I think.
09-27-2009, 04:36 PM#14
Zerzax
Sorry I should have reposted, I can post up the map. The texture actually did make the spell not able to click on the terrain. The turning radius is fine, and yes the channel idea would work really well. Here is the map. I will work on it a bit and repost later.

I'm going to implement the max speed now - otherwise it's impossible to control the thing.

EDIT: I worked on it a bit some strange things are happening with the acceleration - but I also realized that we were adding a small fraction of the velocity - that did not need to be multiplied by the timer interval, only the acceleration. I'm having trouble figuring out whether the speed is at its target speed - but I can work on that as well. As long as you understand the process in general well enough, you should see if you can tweak it so it works. Doing the math out on paper also helps a lot. I will definitely work on this, but if we pool our efforts I think we can come up with the solutions - it's only like 6 lines of code... Meanwhile, here's a new current map.

EDIT2: I found a way to check for speed (by finding the magnitude of the velocity, square root of (xvelocity squared + yvelocity squared ) but there are still problems cropping up. The good news is the movement seems to work quite well. I think our final solution will involve jerk (change in acceleration over change in time) so that the ship can slow itself to basically a halt by using a huge burst of acceleration, then use a smaller acceleration to get into a more controlled movement.

Collapse JASS:
library aaa initializer Init requires TimerUtils

globals
    private constant real TIMER_PERIOD      = .05
    private constant real BASE_ACCELERATION = 10
    private constant real MAX_SPEED = 500.00
endglobals

struct vector
    real x = 0
    real y = 0
endstruct

struct particle
    unit   u
    timer  t
    vector velocity
    vector acceleration
    boolean speedReached = true

    static method Create takes nothing returns nothing
        local particle this = particle.create()
        
        set .u = CreateUnit(Player(0), 'hgyr', 0, 0, 0)
        call SetUnitUserData(.u, this)
        
        set .velocity = vector.create()
        set .acceleration = vector.create()
        
        set .t = NewTimer()
        call SetTimerData(.t, this)
        call TimerStart(.t, TIMER_PERIOD, true, function particle.timerUpdate)
    endmethod
    
    static method timerUpdate takes nothing returns nothing
        local particle this = GetTimerData(GetExpiredTimer())
                
        if not .speedReached then
            set .velocity.x = .velocity.x + .acceleration.x 
            set .velocity.y = .velocity.y + .acceleration.y
            if SquareRoot(.velocity.x*.velocity.x + .velocity.y*.velocity.y) >= MAX_SPEED * TIMER_PERIOD then
                set .speedReached = true
            endif
        else
            call BJDebugMsg("Speed reached")
        endif
        
        call SetUnitX(.u, GetUnitY(.u) + .velocity.x )
        call SetUnitY(.u, GetUnitX(.u) + .velocity.y )
        
    endmethod
    
    static method changeDirection takes nothing returns nothing
        local particle this = GetUnitUserData(GetTriggerUnit())
        local real angle = Atan2(GetOrderPointY() - GetUnitX(GetTriggerUnit()), GetOrderPointX() - GetUnitY(GetTriggerUnit()))
        
        set .speedReached = false
        
        set .acceleration.x = BASE_ACCELERATION * Cos(angle) * TIMER_PERIOD
        set .acceleration.y = BASE_ACCELERATION * Sin(angle) * TIMER_PERIOD
        //set .baseSpeed = 0
    endmethod
    
endstruct

private function DoTrue takes nothing returns boolean
    return true
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, Condition(function DoTrue))
    call TriggerAddAction(t, function particle.changeDirection)

    call particle.Create()
endfunction

endlibrary

Attached Files
File type: w3xwc3c v1.w3x (100.6 KB)