| 09-07-2009, 12:01 AM | #1 |
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 |
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 |
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 |
Alright, lets see if this makes sense I will represent most of the variables as struct members within a "particle" struct 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: 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 |
Is this correct or totally wrong? 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 |
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 | |||
Quote:
Quote:
Quote:
|
| 09-16-2009, 08:01 PM | #8 |
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: 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 |
This is what I have, doesn't work though. Just trying to get the ship/object to move normally. 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 |
| 09-25-2009, 02:19 AM | #10 |
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 | |
Quote:
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 |
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 | |||||||
Mind posting what you have so far? Quote:
Quote:
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:
Quote:
Quote:
Quote:
Quote:
|
| 09-27-2009, 04:36 PM | #14 |
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. 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 |
