| 02-06-2009, 03:02 PM | #1 |
I've been working on some sort of physics system/map but I'm currently stuck with this problem: I already made it possible to apply a force on the object i want to accelerate but usually forces aren't permanent so i added this method "applyforcetimed": JASS:method applyforcetimed takes real amount, real horAngle, real verAngle, real duration returns nothing call this.applyforce(amount, horAngle, verAngle) call TriggerSleepAction(duration) call this.applyforce(-amount, horAngle, verAngle) endmethod As you might have already noticed the problem with this is that if the struct instance is destroyed and a new one is created, that new one will take over this old instance. If that happens during the TriggerSleepAction part the newly created struct will be accelerated by a force that wasn't supposed to be applied to that instance. So somehow I have to stop that method to work when the struct is destroyed. Adding a timer as a struct member that will be paused onDestroy might be possible but that would mean that i can only apply one force at a time to the object. Here's the rest of the struct if needed: JASS:struct object unit ent real vx = 0 real vy = 0 real vz = 0 real ax = 0 real ay = 0 real az = 0 real m timer mov = CreateTimer() private method onDestroy takes nothing returns nothing call RemoveUnit(this.ent) call FlushHandleLocals(this.mov) call PauseTimer(this.mov) call DestroyTimer(this.mov) endmethod private method applyforcevectorial takes real fx, real fy, real fz returns nothing set this.ax = this.ax+fx/this.m set this.ay = this.ay+fy/this.m set this.az = this.az+fz/this.m endmethod method applyforce takes real amount, real horAngle, real verAngle returns nothing local real fz = Sin(bj_DEGTORAD*verAngle)*amount local real fhor = Cos(bj_DEGTORAD*verAngle)*amount local real fx = Cos(bj_DEGTORAD*horAngle)*fhor local real fy = Sin(bj_DEGTORAD*horAngle)*fhor call this.applyforcevectorial(fx, fy, fz) endmethod method applyforcetimed takes real amount, real horAngle, real verAngle, real duration returns nothing call this.applyforce(amount, horAngle, verAngle) call TriggerSleepAction(duration) call this.applyforce(-amount, horAngle, verAngle) endmethod private static method move takes nothing returns nothing local object movo = GetHandleInt(GetExpiredTimer(), "object") set movo.vx = movo.vx+movo.ax set movo.vy = movo.vy+movo.ay set movo.vz = movo.vz+movo.az call SetUnitX(movo.ent, GetUnitX(movo.ent)+movo.vx) call SetUnitY(movo.ent, GetUnitY(movo.ent)+movo.vy) call SetUnitZ(movo.ent, GetUnitZ(movo.ent)+movo.vz) endmethod static method create takes player whichPlayer, integer unitId, real m, real x, real y, real z, real face returns object local object creo = object.allocate() set creo.m = m set creo.ent = CreateUnit(whichPlayer, unitId, 0, 0, face) call UnitAddAbility(creo.ent, 'Arav') call UnitRemoveAbility(creo.ent, 'Arav') call SetUnitPathing(creo.ent, false) call SetUnitX(creo.ent, x) call SetUnitY(creo.ent, y) call SetUnitZ(creo.ent, z) call SetHandleInt(creo.ent, "object", creo) call SetHandleInt(creo.mov, "object", creo) call TimerStart(creo.mov, 0.01, true, function object.move) return creo endmethod endstruct P.P.S: Any additional criticism may be welcome. |
| 02-06-2009, 04:30 PM | #2 |
You can make a list of forces, and to everyone you save how long it should be there. On every tick(move) you always decrease the time and test if it is <= 0. If it is <= 0 you remove it. A smarter version version is to use the time of a timer that runs all the time. So you calculate everything with that time, that you have not to decrease anything. @code: You should make it use only a single timer. So you have a array of all objects. Every 0.035 (or whatever) seconds you iterate over all of them an call their .move (in that case it is not necessary to make move static). You will have to inform you about how to manage all the data. But it is pretty easy if you know how. Just look over some resources. |
| 02-06-2009, 06:47 PM | #3 |
Alright... thanks very much for your help My current code looks like this now and it's working: JASS:library Object initializer Init globals private timer Timer = CreateTimer() private integer ObjectCount = 0 private object array Object endglobals struct phforce real fx real fy real fz real tleft static method create takes real fx, real fy, real fz, real duration returns phforce local phforce f = phforce.allocate() set f.fx = fx set f.fy = fy set f.fz = fz set f.tleft = duration return f endmethod endstruct struct object unit ent real vx = 0 real vy = 0 real vz = 0 //real ax = 0 //real ay = 0 //real az = 0 real m real r phforce array f[31] integer ForceCount = 0 //timer mov = CreateTimer() method onDestroy takes nothing returns nothing local integer i = 0 call RemoveUnit(this.ent) loop exitwhen Object[i] == this set i = i+1 endloop loop exitwhen i == ObjectCount set Object[i] = Object[i+1] set i = i+1 endloop set ObjectCount = ObjectCount-1 //call FlushHandleLocals(this.mov) //call PauseTimer(this.mov) //call DestroyTimer(this.mov) endmethod //method applyforcevectorial takes real fx, real fy, real fz returns nothing // set this.ax = this.ax+fx/this.m // set this.ay = this.ay+fy/this.m // set this.az = this.az+fz/this.m //endmethod //method applyforce takes real magnitude, real horAngle, real verAngle returns nothing // local real fz = Sin(bj_DEGTORAD*verAngle)*magnitude // local real fhor = Cos(bj_DEGTORAD*verAngle)*magnitude // local real fx = Cos(bj_DEGTORAD*horAngle)*fhor // local real fy = Sin(bj_DEGTORAD*horAngle)*fhor // // call this.applyforcevectorial(fx, fy, fz) //endmethod method applyforcetimed takes real magnitude, real horAngle, real verAngle, real duration returns nothing local real fz = Sin(bj_DEGTORAD*verAngle)*magnitude local real fhor = Cos(bj_DEGTORAD*verAngle)*magnitude local real fx = Cos(bj_DEGTORAD*horAngle)*fhor local real fy = Sin(bj_DEGTORAD*horAngle)*fhor set this.f[this.ForceCount] = phforce.create(fx, fy, fz, duration) set this.ForceCount = this.ForceCount+1 //call this.applyforce(magnitude, horAngle, verAngle) //call TriggerSleepAction(duration) //call this.applyforce(-magnitude, horAngle, verAngle) endmethod //static method move takes nothing returns nothing // local object movo = GetHandleInt(GetExpiredTimer(), "object") // // set movo.vx = movo.vx+movo.ax // set movo.vy = movo.vy+movo.ay // set movo.vz = movo.vz+movo.az // // call SetUnitX(movo.ent, GetUnitX(movo.ent)+movo.vx) // call SetUnitY(movo.ent, GetUnitY(movo.ent)+movo.vy) // call SetUnitZ(movo.ent, GetUnitZ(movo.ent)+movo.vz) //endmethod //method move takes nothing returns nothing // set this.vx = this.vx+this.ax // set this.vy = this.vy+this.ay // set this.vz = this.vz+this.az // // call SetUnitX(this.ent, GetUnitX(this.ent)+this.vx) // call SetUnitY(this.ent, GetUnitY(this.ent)+this.vy) // call SetUnitZ(this.ent, GetUnitZ(this.ent)+this.vz) //endmethod static method create takes player whichPlayer, integer unitId, real m, real r, real x, real y, real z, real face returns object local object creo = object.allocate() set creo.m = m set creo.r = r set Object[ObjectCount] = creo set ObjectCount = ObjectCount+1 set creo.ent = CreateUnit(whichPlayer, unitId, 0, 0, face) call UnitAddAbility(creo.ent, 'Arav') call UnitRemoveAbility(creo.ent, 'Arav') call SetUnitPathing(creo.ent, false) call SetUnitX(creo.ent, x) call SetUnitY(creo.ent, y) call SetUnitZ(creo.ent, z) call SetHandleInt(creo.ent, "object", creo) //call SetHandleInt(creo.mov, "object", creo) //call TimerStart(creo.mov, 0.01, true, function object.move) return creo endmethod endstruct private function LoopThroughObjects takes nothing returns nothing local integer i = 0 local integer j //Looping through objects call BJDebugMsg("--total object count: "+I2S(ObjectCount)) loop exitwhen i >= ObjectCount set j = 0 //Looping through Forces call BJDebugMsg("----working on object "+I2S(i)) loop exitwhen j >= Object[i].ForceCount call BJDebugMsg("------total force count: "+I2S(Object[i].ForceCount)) if Object[i].f[j].tleft > 0 then //Accelerating call BJDebugMsg("--------accelerating object "+I2S(i)+" using force "+I2S(j)) set Object[i].vx = Object[i].f[j].fx / Object[i].m + Object[i].vx set Object[i].vy = Object[i].f[j].fy / Object[i].m + Object[i].vy set Object[i].vz = Object[i].f[j].fz / Object[i].m + Object[i].vz set Object[i].f[j].tleft = Object[i].f[j].tleft - 0.01 else //Removing Force from the list call BJDebugMsg("--------destroying force "+I2S(j)+" of object "+I2S(i)) call Object[i].f[j].destroy() loop exitwhen j >= Object[i].ForceCount set Object[i].f[j] = Object[i].f[j+1] set j = j+1 endloop set Object[i].ForceCount = Object[i].ForceCount-1 endif set j = i+1 endloop //Moving call SetUnitX(Object[i].ent, GetUnitX(Object[i].ent)+Object[i].vx) call SetUnitY(Object[i].ent, GetUnitY(Object[i].ent)+Object[i].vy) call SetUnitZ(Object[i].ent, GetUnitZ(Object[i].ent)+Object[i].vz) set i = i+1 endloop endfunction private function Init takes nothing returns nothing call TimerStart(Timer, 0.01, true, function LoopThroughObjects) endfunction endlibrary It's just really laggy at already 3-5 objects featuring one force each... Ofc I could reduce the timer frequency but when I think about what I was actually going to add to this system (electric fields + charged objects, elastic collision) I guess it's a better idea to scrap that whole idea. Nature is a hell of an algorithm... wow |
| 02-06-2009, 07:04 PM | #4 |
You should really use 0.03125 (I think this is the smallest possible) or 0.04 for the timer... Anything smaller than that is way too fast. You might even be able to get away with 0.05 or 0.06, it all depends... |
| 02-06-2009, 07:16 PM | #5 |
Well still it gets laggy at about 10 objects on my comp and that one's not too old. Adding multiple forces or even collision (periodic 3D-distance checking yawrr) would totally fuck any compy... WC3 just isn't a 3D game... |
| 02-06-2009, 07:24 PM | #6 | |
Quote:
HINDYhat also made a less awesome engine, but actually released it: http://wc3campaigns.net/showthread.php?t=98089 Drop by the WC3C IRC server (use the java chat applet because im too lazy to find the IP because the URL doesn't work) if you want to talk fast and hard about how that's possible in WC3. |
| 02-06-2009, 09:17 PM | #7 |
This is why we don't use TriggerSleepAction. Well, one of the many reasons why we don't use it anyway. If you want a large number of physics objects running smoothly you'll probably have to write unreadable code. |
| 02-06-2009, 09:24 PM | #8 | ||
Quote:
A thread sleep is not the proper way to implement sane time-based code Quote:
Now, hard to understand, yes. But what physics simulator isn't? :) If you're smart enough to understand the math needed to implement it, you're likely smart enough to understand the logic. |
| 02-07-2009, 11:24 AM | #9 |
Well thanks all :> I never liked TriggerSleepAction either and I knew that code wouldn't work but basically it helped me to demonstrate what I was aiming at [/excuses]. Anyway thanks for your help. I might take a look at the SEE and try to learn something from it :) |
| 02-07-2009, 12:25 PM | #10 | |||
Quote:
Quote:
Quote:
|
| 02-07-2009, 08:31 PM | #11 |
You need to flag the struct so that if it is destroyed and recreated you will know. One possibility is to add a location variable called flag, which is removed and nulled when destroy is called, and created when the struct is created. When you run your method, assign a local to the flag location, wait, and compare it to the current flag location. If it differs, the struct has been destroyed since you last looked at it. |
| 02-07-2009, 09:15 PM | #12 |
Or you could just use an incrementing counter int, starting at one and set ++ each time it changes (NB: may break horribly if you have 2^31 instances of the struct throughout the course of the game!!!). |
| 02-07-2009, 11:51 PM | #13 |
Or, if you use a timer, you could store it in the struct itself and release it in the onDestroy method. |
| 02-08-2009, 01:50 AM | #14 | |
Quote:
That could cause problems if the same struct is used in a spell twice, since one would overwrite the other. The safest bet is a value which is constant for exactly the object's lifetime, such as a flag handle or the destruction counter griffen suggested. ... This is why I liked garbage collectors. |
| 02-08-2009, 06:46 PM | #15 | |
Quote:
|
