HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Particle System

09-03-2006, 05:33 PM#1
iNfraNe
This system is made for moving units as if they were particles. It is perfect for projectiles from weapons, or anything other needing to move physically.
It uses:
Anitarf's Vector System
My Linked Lists
both of which are, ofcourse, already included in the map. Be sure to download and read the documentation of the vector system, since you will need to activily use it too.

The beauty of the system is that you can add an unlimited amount of forces which can be different for each particle. You can also link a function to a condition which makes each particle have its own "triggers".

All of this is done by functions which you can very easily write yourself. In the post below this one I will send some of these functions which you can use yourself (some of which are already included in the map).

Be sure to read the documentation so you know exactly when I am talking about :)

Find any bugs? please tell me.

(P.S. It is gamecache free)
Attached Images
File type: jpgParticle System.jpg (7.8 KB)
Attached Files
File type: w3xParticleSystem.w3x (41.8 KB)
09-03-2006, 05:36 PM#2
iNfraNe
Forces:
Collapse JASS:
// Needs variable: forceGravity (integer)
function G takes nothing returns integer
  if udg_forceGravity == 0 then
    set udg_forceGravity = VectorCreate(0,0,-2500)
  endif
  return udg_forceGravity
endfunction

Conditions:
Collapse JASS:
// Needs variable conditionGroundHit (trigger)
function ParticleGroundHitCond takes nothing returns boolean
  local unit u = GetEnumUnit()
  local location loc = GetUnitLoc(u)
  local integer i = udg_clsParticlePlace[GetUnitUserData(u)]

  if GetLocationZ(loc) > udg_vectorZ[i] then
    call RemoveLocation(loc)
    set loc = null
    return true
  endif

  call RemoveLocation(loc)
  set loc = null
  return false
endfunction
function ParticleGroundHit takes nothing returns trigger
  if udg_conditionGroundHit == null then
    set udg_conditionGroundHit = CreateEvalTrig(function ParticleGroundHitCond)
  endif
  return udg_conditionGroundHit
endfunction

// Needs variable conditionDeath (trigger)
function ParticleDeathCond takes nothing returns boolean
  return GetWidgetLife(GetEnumUnit()) < 0.405
endfunction
function ParticleDeath takes nothing returns trigger
  if udg_conditionDeath == null then
    set udg_conditionDeath = CreateEvalTrig(function ParticleDeathCond)
  endif
  return udg_conditionDeath
endfunction

// needs an extra variable: udg_TUnit (unit), can hit allies to, add a condition to the group otherwise
function ParticleCollisionCond takes nothing returns boolean
  local group g = CreateGroup()
  local unit u = GetEnumUnit()
  local unit f
  local integer p = udg_clsParticlePlace[GetUnitUserData(u)]
  local integer tv
  local location l
  call GroupEnumUnitsInRange(g, udg_vectorX[p], udg_vectorY[p], 60, null)

  loop
    set f = FirstOfGroup(g)
    exitwhen f == null
    set l = GetUnitLoc(f)
    set tv = VectorCreate(GetLocationX(l), GetLocationY(l), GetLocationZ(l)+GetUnitFlyHeight(f))
    call RemoveLocation(l)

    if IsPointInSphere(tv, p, 60) then

      call VectorDestroy(tv)

      set udg_TUnit = f //This is a temp global to be used in the conditionfuncs

      set u = null
      set l = null
      set f = null

      return true
    endif
    call VectorDestroy(tv)
    call GroupRemoveUnit(g,f)
  endloop

  set u = null
  set l = null
  set f = null

  return false
endfunction
function ParticleCollision takes nothing returns trigger
  if udg_conditionCollision == null then
    set udg_conditionCollision = CreateEvalTrig(function ParticleCollisionCond)
  endif
  return udg_conditionCollision
endfunction

Actions (ConditionFuncs):
Collapse JASS:
// Bounces a particle back up. This function works on both flat and angled terrain.
function GroundHit_Bounce takes nothing returns nothing
  local unit u = GetEnumUnit()
  local integer i = GetUnitUserData(u)
  local integer p = udg_clsParticlePlace[i]
  local integer v = udg_clsParticleSpeed[i]
  local integer vector1 = VectorGetTerrainNormal(udg_vectorX[p], udg_vectorY[p], 25.0)
  local integer vector2 = VectorProjectVector(v,vector1)

  set udg_vectorX[p] = udg_vectorX[p] - udg_vectorX[v]*ParticlePeriod()
  set udg_vectorY[p] = udg_vectorY[p] - udg_vectorY[v]*ParticlePeriod()
  set udg_vectorZ[p] = udg_vectorZ[p] - udg_vectorZ[v]*ParticlePeriod()

  call VectorScale(vector2, -2)
  call VectorAdd(v, vector2)
  call VectorScale(v, 0.65)

  call VectorDestroy(vector1)
  call VectorDestroy(vector2)
  set u = null
endfunction


// damages the target (used in CollisionDamage condition) for 10 hp.
function Collision_Damage10 takes nothing returns nothing
  local unit u = GetEnumUnit()
  call UnitDamageTarget(u, udg_TUnit, 10, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
  call DestroyEffect(AddSpecialEffectTarget("bloodeffect?", udg_TUnit, "chest")) //replace "bloodeffect?" with the path of a blood effect, you can also leave out this part.
  call KillUnit(u)
  call RemoveParticle(u,true)
  set u = null
endfunction


More of these functions will be added later...
09-03-2006, 07:37 PM#3
karukef
OOOO I am so looking forward to rip... lend.. uh use some of these functions in my map. ;)

Don't have time to look at it right now, but I can certainly testify to the usefulness of such a system. Assuming it is working well, and I bet it does, great and useful work!
09-03-2006, 10:04 PM#4
PipeDream
Since all forces and the differential equation are constant it is practical to solve the diff eq exactly. Since you don't need any genericity you can do this by precomputing (when forces change):
W = acceleration*period
U = w*period/2
and at each time step:
pos_f = U + vel_i*period + pos_i
vel_f = W + vel_i
09-03-2006, 10:19 PM#5
iNfraNe
Yes, however the forces could change at any time. The system was ment to handle both constant forces and forces that are constantly changing.
09-03-2006, 10:34 PM#6
PipeDream
Your forces are constant over a time step, the interval in which this is relevant. Of course you would do even better (in terms of number of operations / size of time step) if you did take in to account the changing forces.
09-04-2006, 12:33 PM#7
blu_da_noob
I'm not sure exactly what you are meaning by time step there Pipe, but he may be accounting for an external thing changing the forces at some other time? (Like a spell causing missiles to slow down or something)
09-04-2006, 07:44 PM#8
PipeDream
In general using a better integration method lets you increase the time step. Two things limit you to a small step: visually smooth and numerical stability/precision. If you are currently limited by stability/precision, you switch to a more accurate method and larger time steps which reduces your total computational costs practically for free.

I say in free in contrast to the hard way that one would do this under non JASS circumstances: use separate loops for integration and display.
09-04-2006, 11:22 PM#9
iNfraNe
I dont see anyway reason to... It runs smoothly with 50 particles at a 0.025 timer, the changes with changing forces that happen when you higher the period would be non-noticable. And I dont see a reason to make it higher anyway, since you usually dont need more than 50 particles.

(tell me if im completely missing the point here...)
09-04-2006, 11:43 PM#10
PipeDream
It's just room for optimization. The demonstration with the current integrator is geared towards constant forces where things are not very curved. If you want to have things oscillating, moving in small circles or in general experiencing large periodic accelerations you'll have to start ratcheting the time step down to get under the stability threshold. Using the midpoint integrator is relatively cheap and reduces the error by a factor of four (instead of two) for every time you cut the time step in half. And it has the nice side bonus that the curves from constant acceleration are computed exactly.
09-05-2006, 03:25 PM#11
iNfraNe
But what you are saying is... s = s+v*dt+0.5a*dt² right? Isn't that exactly what im doing, only in 2 steps? I'm not quite sure how I should else do it...
09-05-2006, 06:52 PM#12
PipeDream
Almost. What you are doing is stepping to the end of the interval and computing acceleration there:
v1 = v0 + a dt
x1 = x0 + v1*dt = x0 + v0*dt + a*dt^2 <-- no factor of 1/2
Which has the same amount of error as doing it from the beginning, but depending on the direction of things it'll grow instead of decay.
09-06-2006, 12:29 PM#13
iNfraNe
Indeed!
I never realised that. How odd. I'll fix this and update asap.

Thanks once again.
09-18-2006, 05:36 AM#14
PipeDream
Still going to update it?
09-22-2006, 04:48 PM#15
iNfraNe
Yep, asap.