- Joined
- Jan 21, 2006
- Messages
- 2,552
Since I had absolutely nothing to do last night, I started coding a physics engine. It is in early stages right now, and it doesn't have many of the features that I would like to include; but that won't stop me from posting some preliminary code and gaining some feedback.
The strength of this (as opposed to a large system such as Simple Entity Engine) is primarily the
Well, whatever, I'll answer any questions if there are any. If anybody has ideas as to what I can add I'd also be happy to hear your thoughts.
The strength of this (as opposed to a large system such as Simple Entity Engine) is primarily the
field
struct, which allows users to relatively easily create force-fields that attract particles (types of particles that are attracted can be filtered). It also has some basic interface methods for applying a force to a particle.Well, whatever, I'll answer any questions if there are any. If anybody has ideas as to what I can add I'd also be happy to hear your thoughts.
JASS:
library Physics initializer init requires Vector
//############################################################################################
globals
// Config Constants
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Constants that are frequently used by the engine and can be considered user preference
// variables. They can be adjusted to create a variation of results.
//
// Period it takes to update each particle. The particles are updated on a stack that is
// iterated through each "core__refTime" period.
private constant real core__refTime = 0.03
//
// When particles are created, they are appointed vectors to store necessary data to
// calculate movement patterns.
private vector def__velocity
private vector def__acceleration
//
private constant real def__absorbRadius = 80
private constant real def__frictionFactor = 0.8
//
// Core Engine Components
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Core engine components.
private timer core__tmr = CreateTimer()
//
private hashtable core__table = InitHashtable()
//
// Dynamic/Temporary Data
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// The following variables are used for quick-reference data storage for performance
// reasons. It is faster to reference globals than allocate new local data.
private constant location core__tempLoc = Location(0, 0)
private real core__tempLocZ
//
endglobals
//############################################################################################
function GetUnitZ takes unit u returns real
//Returns the terrain-height beneath the specified unit along with it's flying height
call MoveLocation(core__tempLoc, GetUnitX(u), GetUnitY(u))
set core__tempLocZ = GetLocationZ(core__tempLoc)
return core__tempLocZ+GetUnitFlyHeight(u)
endfunction
function SetUnitZ takes unit u, real z returns nothing
//Changes a unit's absolute height value
call MoveLocation(core__tempLoc, GetUnitX(u), GetUnitY(u))
set core__tempLocZ = GetLocationZ(core__tempLoc)
call SetUnitFlyHeight(u, z-core__tempLocZ, 0)
endfunction
//############################################################################################
private interface fieldinterface
method onAbsorb takes particle p returns nothing defaults nothing
method particleFilter takes particle p returns boolean defaults true
endinterface
//############################################################################################
private interface particleinterface
method onGroundEvent takes nothing returns nothing defaults nothing
method onGroundLeaveEvent takes nothing returns nothing defaults nothing
endinterface
//############################################################################################
// Field
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// The field data struct is used to create a force-field which applies a force to affected
// particles on each loop iteration.
//
struct field extends fieldinterface
readonly real x
readonly real y
readonly real z
readonly real magnitude = 0
readonly real radius
public boolean absorbActive = false
readonly real absorbRange = def__absorbRadius
private static timer core__tmr = CreateTimer()
private static group core__enum = CreateGroup()
private static thistype core__temp
private static thistype array core__stack
private static integer core__stackSize = 0
private integer core__stackDex
//========================================================================================
method setpos takes real x, real y, real z returns nothing
set this.x = x
set this.y = y
set this.z = z
endmethod
//========================================================================================
method onDestroy takes nothing returns nothing
set thistype.core__stackSize = thistype.core__stackSize-1
set thistype.core__stack[core__stackDex] = thistype.core__stack[thistype.core__stackSize]
set thistype.core__stack[core__stackDex].core__stackDex = core__stackDex
if(thistype.core__stackSize == 0) then
call PauseTimer(thistype.core__tmr)
endif
endmethod
//========================================================================================
private static method loopEnumFunc takes nothing returns boolean
local particle p = particle[GetFilterUnit()]
local thistype f = core__temp
local vector v
local real m
local real d
if (p != 0) and (f.particleFilter(p)) then
set d = SquareRoot((f.x-p.x)*(f.x-p.x) + (f.y-p.y)*(f.y-p.y) + (f.z-p.z)*(f.z-p.z))
if(f.absorbActive) and(d <= f.absorbRange) then
call f.onAbsorb(p)
endif
set m = (f.radius - d) * (f.magnitude/f.radius)
set v = vector.create(f.x-p.x, f.y-p.y, f.z-p.z)
call v.setLength(m)
call p.applyForce(v)
call v.destroy()
endif
return false
endmethod
private static method loopFunc takes nothing returns nothing
local integer i = core__stackSize-1
local thistype f
loop
exitwhen(i < 0)
set f = core__stack[i]
if(f != 0) then
set core__temp = f
call GroupEnumUnitsInRange(core__enum, f.x, f.y, f.radius,/*
*/Filter(function thistype.loopEnumFunc))
endif
set i = i-1
endloop
endmethod
//========================================================================================
static method create takes real x, real y, real z, real magnitude, real radius returns thistype
local thistype f = allocate()
set f.magnitude = magnitude
set f.radius = radius+0.001
set f.x = x
set f.y = y
set f.z = z
if(core__stackSize == 0) then
call TimerStart(thistype.core__tmr, core__refTime, true, function thistype.loopFunc)
endif
set f.core__stackDex = core__stackSize
set core__stack[core__stackSize] = f
set core__stackSize = core__stackSize+1
return f
endmethod
endstruct
//############################################################################################
// Particle
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// The primary data struct for the physics engine, contains all necessary data for
// controlling the position and physical motion of any created particles.
//
struct particle extends particleinterface
readonly unit self = null
private vector core__pos = 0
private vector core__vel = 0
private vector core__acc = 0
private boolean flag__ground = false
private real ref__timescale = 1.0
private real ref__mass
private static thistype array core__stack
private static integer core__stackSize = 0
private integer core__stackDex
//========================================================================================
method setpos takes real x, real y, real z returns nothing
set core__pos.x = x
set core__pos.y = y
set core__pos.z = z
endmethod
//========================================================================================
static method operator [] takes unit u returns thistype
return thistype(LoadInteger(core__table, GetHandleId(u), 1))
endmethod
method operator x takes nothing returns real
return core__pos.x
endmethod
method operator y takes nothing returns real
return core__pos.y
endmethod
method operator z takes nothing returns real
return core__pos.z
endmethod
//========================================================================================
method reflect takes vector normalVector returns nothing
call core__vel.rotate(normalVector, bj_PI)
call core__vel.setLength(-core__vel.getLength())
endmethod
//========================================================================================
method onDestroy takes nothing returns nothing
//Destroy vectors
call core__pos.destroy()
call core__vel.destroy()
call core__acc.destroy()
//Remove from stack
set thistype.core__stackSize = thistype.core__stackSize-1
set thistype.core__stack[core__stackDex] = thistype.core__stack[thistype.core__stackSize]
set thistype.core__stack[core__stackDex].core__stackDex = core__stackDex
if(thistype.core__stackSize == 0) then
call PauseTimer(core__tmr)
endif
call RemoveSavedInteger(core__table, GetHandleId(self), 1)
endmethod
//========================================================================================
method applyForce takes vector forceVector returns nothing
local vector v = forceVector.copy()
call v.scale(core__refTime)
call core__vel.add(v)
call v.destroy()
endmethod
//========================================================================================
debug method debug takes nothing returns nothing
debug //call BJDebugMsg(R2S(core__acc.getLength()))
debug //call BJDebugMsg(R2S(core__vel.getLength()))
debug endmethod
//========================================================================================
//Iterates through particles that are included in the data stack.
private static method loopFunc takes nothing returns nothing
local integer i = thistype.core__stackSize-1
local thistype p
local vector acc
local vector vel
local vector pos
loop
exitwhen(i < 0)
set p = thistype.core__stack[i]
if(p != 0)then
//Update vectors
set acc = p.core__acc
set vel = p.core__vel
set pos = p.core__pos
set vel.x = vel.x + acc.x * p.ref__timescale
set vel.y = vel.y + acc.y * p.ref__timescale
set vel.z = vel.z + acc.z * p.ref__timescale
set pos.x = pos.x + vel.x * p.ref__timescale
set pos.y = pos.y + vel.y * p.ref__timescale
set pos.z = pos.z + vel.z * p.ref__timescale
//Debug output
debug call p.debug()
//Update unit position
call SetUnitX(p.self, p.core__pos.x)
call SetUnitY(p.self, p.core__pos.y)
call SetUnitZ(p.self, p.core__pos.z)
call SetUnitLookAt(p.self, "BONE_HEAD", p.self, vel.x, vel.y, vel.z)
if(p.core__pos.z <= core__tempLocZ) and not(p.flag__ground) then
//The particle has made contact with the terrain
set p.flag__ground = true
call p.onGroundEvent()
elseif(p.core__pos.z > core__tempLocZ) and(p.flag__ground) then
//The particle has escaped from being in contact with the terrain
set p.flag__ground = false
call p.onGroundLeaveEvent()
endif
endif
set i = i-1
endloop
endmethod
//========================================================================================
//Constructs a particle given a specific unit. In addition if "change" is flagged then the
//Locust and Chaos abilities will be added to it so that it is unselectable.
static method create takes unit u, boolean change returns thistype
local thistype p = allocate()
set p.self = u
set p.core__pos = vector.create(GetUnitX(u), GetUnitY(u), GetUnitZ(u))
set p.core__vel = def__velocity.copy()
set p.core__acc = def__acceleration.copy()
//Scale velocity/acceleration vectors to match the loop period
call p.core__acc.scale(core__refTime*core__refTime)
call p.core__vel.scale(core__refTime)
//Save the data to the unit in a hashtable so it can be quickly referenced externally
call SaveInteger(core__table, GetHandleId(u), 1, p)
if(core__stackSize == 0) then
call TimerStart(core__tmr, core__refTime, true, function thistype.loopFunc)
endif
set core__stack[core__stackSize] = p
set p.core__stackDex = core__stackSize
set core__stackSize = core__stackSize+1
if(change) then
call UnitAddAbility(u, 'Aloc')
call UnitAddAbility(u, 'Srtt')
call UnitRemoveAbility(u, 'Aloc')
endif
call UnitAddAbility(u, 'Amrf')
call UnitRemoveAbility(u, 'Amrf')
return p
endmethod
endstruct
//############################################################################################
private function init takes nothing returns nothing
//Setup default vector references
set def__velocity = vector.create(0, 0, 0)
set def__acceleration = vector.create(0, 0, 0)
endfunction
endlibrary