Usually Neuro Fuzzy is the one who provides the best answers for these mathematical problems; but here is a "simple man's" solution.
Its a generic system that use relative forces; and a visual with curved movement for smoothness. I use this kind of system in all of my projects because anything can have a position somewhere, be it a UI element, sprite or 3D object.
It needs work but with the right tweaks you should be able to get somewhere.
By using a PushPos() function, it pushes one position of the user defined type away from another.
For other situations;
The PullPos() does the opposite.
The ForcePos() causes the object follow the path of the object, however it moves.
The CurvePos() function smoothly animates movement and is actually used to cause a certain position to follow the mouse; and another to follow the follower; because sprite integer positions can get jerky.
The other functions speak for themselves.
The power# variables adjust the power of influence.
This code can be used in 3D with an additional Z property added for convinience.
type pos
x as float
y as float
z as float
oldX as float
oldY as float
oldZ as float
endtype
dim _pos(4) as pos
global wavePos as pos
global mousePos as pos
global targetPos as pos
global visualPos as pos
hide mouse
global i# = 0
sync on
sync rate 60
_pos(1).x = mousex()
_pos(1).y = mousey()
do
i# = wrapvalue( i# + 0.5 )
updateMouse()
updateTarget()
updateWave()
updateVisual()
updateScreen()
loop
function updateMouse()
rem Set as the mouse location
mousePos = _pos(1)
mousePos.oldX = mousePos.x
mousePos.oldy = mousePos.y
mousePos.x = mousex()
mousePos.y = mousey()
_pos(1) = mousePos
endfunction
function updateTarget()
targetPos = _pos(2)
if atSamePosition( 1, 2 ) = 0
power# = 5
curvePos( 2, 1, 2000, power# )
targetPos = _pos(2)
endif
endfunction
function updateWave()
rem Orbit the area
wavePos = _pos(3)
wavePos.oldX = wavePos.x
wavePos.oldY = wavePos.y
wavePos.x = (240 + (SIN(i#) * 150))
wavePos.y = (240 + (COS(i#) * 150))
_pos(3) = wavePos
rem either push or pull and force the target, adjust the power accordingly
forcePos( 3, 2, 50, 5.0 )
pullPos( 3, 2, 50, 0.0 )
pushPos( 3, 2, 50, 3.0 )
pushPos( 3, 2, 150, 1.0 )
endfunction
function updateVisual()
if atSamePosition( 2, 4 ) = 0
power# = 8
curvePos( 4, 2, 2000, power# )
visualPos = _pos(4)
endif
endfunction
rem Force one position along the motion of another if in range
function forcePos( forcerId, targetId, range, power# )
if isNear( forcerId, targetId, range )
dec _pos(targetId).x, (_pos(forcerId).x - _pos(forcerId).oldX) * power#
dec _pos(targetId).y, (_pos(forcerId).y - _pos(forcerId).oldY) * power#
dec _pos(targetId).z, (_pos(forcerId).z - _pos(forcerId).oldZ) * power#
endif
endfunction
function curvePos( targetId, destinationId, range, power# )
if isNear( pusherId, targetId, range )
_pos( targetId ).x = curveValue( _pos( destinationId ).x, _pos( targetId ).x, power# )
_pos( targetId ).y = curveValue( _pos( destinationId ).y, _pos( targetId ).y, power# )
_pos( targetId ).z = curveValue( _pos( destinationId ).z, _pos( targetId ).z, power# )
endif
endfunction
rem Pull one position towards the other if in range, at the rate of power
function pullPos( pullerId, targetId, range, power# )
if isNear( pullerId, targetId, range )
if _pos(targetId).x > _pos(pullerId).x
dec _pos(targetId).x, power#
else
if _pos(targetId).x < _pos(pullerId).x
inc _pos(targetId).x, power#
endif
endif
if _pos(targetId).y > _pos(pullerId).y
dec _pos(targetId).y, power#
else
if _pos(targetId).y < _pos(pullerId).y
inc _pos(targetId).y, power#
endif
endif
if _pos(targetId).z > _pos(pullerId).z
dec _pos(targetId).z, power#
else
if _pos(targetId).z < _pos(pullerId).z
inc _pos(targetId).z, power#
endif
endif
endif
endfunction
rem Pull one position towards the other if in range, at the rate of power
function pushPos( pushPos, targetId, range, power# )
if isNear( pushPos, targetId, range )
if _pos(targetId).x > _pos(pushPos).x
inc _pos(targetId).x, power#
else
if _pos(targetId).x <= _pos(pushPos).x
dec _pos(targetId).x, power#
endif
endif
if _pos(targetId).y > _pos(pushPos).y
inc _pos(targetId).y, power#
else
if _pos(targetId).y <= _pos(pushPos).y
dec _pos(targetId).y, power#
endif
endif
if _pos(targetId).z > _pos(pushPos).z
inc _pos(targetId).z, power#
else
if _pos(targetId).z <= _pos(pushPos).z
dec _pos(targetId).z, power#
endif
endif
endif
endfunction
function isNear( a, b, range )
if _pos( a ).x >= _pos( b ).x - range
if _pos( a ).x <= _pos( b ).x + range
if _pos( a ).y >= _pos( b ).y - range
if _pos( a ).y <= _pos( b ).y + range
if _pos( a ).z >= _pos( b ).z - range
if _pos( a ).z <= _pos( b ).z + range
print a; " near "; b
exitfunction 1
endif
endif
endif
endif
endif
endif
endfunction 0
function atSamePosition( a, b)
result = ( _pos( a ).x = _pos( b ).x ) and ( _pos( a ).y = _pos( b ).y ) and ( _pos( a ).z = _pos( b ).z )
endfunction result
function updateScreen()
for i = 2 to 3
circle _pos(i).x, _pos(i).y, 7*i
next i
box visualPos.x - 10, visualPos.y - 10, visualPos.x + 10, visualPos.y + 10, rgb( 0, 255, 0 ), rgb( 128, 255, 0 ), rgb( 0, 255, 255 ), rgb( 0, 255, 128 )
sync
cls
endfunction