so say I have a agent I want to attach an object to, and I don’t want the object to teleport or ghost into position,
I was thinking that the object will float to the agent, and once in a radius rotate itself around the agent inside this radius until it was lined up with the attachment point and then float in and attach it would be a neat effect.
I have the float into radius code, but am having a bit of trouble thinking of how to rotate the object around the agent.
import bge
from mathutils import Vector
def main():
cont = bge.logic.getCurrentController()
own = cont.owner
#Target object
object = own.scene.objects['Target']
#Desired attachment point in local space
local = [2,-2,2]
#convert local space to world space
targetPos1 = object.worldPosition+(object.worldOrientation*Vector(local))
#get vector to point
v2 = own.getVectTo(targetPos1)
if v2[0]>5:
#float to radius
own.applyForce(v2[1]*15*v2[0],0)
own.applyForce([0,0,own.mass*9.8],0)
own.worldLinearVelocity*=.9
else:
#hover at radius for now
own.worldLinearVelocity*=.005
own.worldPosition = targetPos1+v2[1]*-5
own.applyForce((0,0,9.8*own.mass),0)
main()
any ideas?
perhaps rotate on 1 axis at a time?
starting with local Z to the target agent?
Attachments
FloatToRadius.blend (417 KB)
sdfgeoff
(sdfgeoff)
August 6, 2017, 8:55am
2
Transformation matrices are amazing:
difference = obj1.worldTransform.inverted() * obj2.worldTransform
Where difference is a 4x4 matrix representing the transformations that will need to be made to obj1 to move it to the location of obj2.
From there you can decompose it into a translation and a rotation:
diff_linear = difference.translation
diff_angular = difference.to_3x3().to_euler()
From there you can feed it into whatever control system you like: applying forces, lerping, anything.
That is only the start. With the help of transformation matrices, rotating around arbitary points in space is easy enough, as are many other things.
Just don’t try put numbers in them yourself. The 3x3 portion has to be homogeneous to prevent scaling.
You have to multiply in the right order too, or you get unexpected results.
I usually just try several different combinations until I find one that works.
well this is what I ended up with
import bgefrom mathutils import Vector
def main():
cont = bge.logic.getCurrentController()
own = cont.owner
if not own.parent:
Target = own.scene.objects['Target']
local = [5,5,0]
r = Vector(local).magnitude
own['Desired']=str(local)
targetPos1 = Target.worldPosition+(Target.worldOrientation*Vector(local))
if 'p' not in Target:
Target['p']= own.scene.addObject('point',Target,0)
Target['p'].worldPosition=targetPos1
Target['p'].setParent(Target,0,1)
v2 = own.getVectTo(Target)
l2 = own.worldPosition - Target.worldPosition
if v2[0]>r:
#float to radius
own.applyForce(v2[1]*15*v2[0],0)
own.applyForce([0,0,own.mass*9.8],0)
own.worldLinearVelocity*=.9
else:
diff = l2 - Vector(local)
own['Current'] =str(diff)
rot = Target.worldOrientation.to_euler()
vex = l2.copy()
#hover at radius for now
own.worldLinearVelocity*=.005
own.worldPosition = Target.worldPosition+v2[1]*-r
own.applyForce((0,0,9.8*own.mass),0)
if diff.magnitude>.5:
if diff.x>0:
if diff.y>0:
add = own.scene.addObject('point',Target,0)
rot.z-=.005
add.worldOrientation = rot
own.worldPosition = add.worldPosition +own.worldOrientation*Vector(vex)
else:
add = own.scene.addObject('point',Target,0)
rot.z+=.005
add.worldOrientation = rot
own.worldPosition = add.worldPosition +add.worldOrientation*Vector(vex)
else:
if diff.y>0:
add = own.scene.addObject('point',Target,0)
rot.z-=.005
add.worldOrientation = rot
own.worldPosition = add.worldPosition +add.worldOrientation*Vector(vex)
else:
add = own.scene.addObject('point',Target,0)
rot.z+=.005
add.worldOrientation = rot
own.worldPosition = add.worldPosition +add.worldOrientation*Vector(vex)
else:
own.worldPosition = targetPos1
own.setParent(Target)
own.state =1
own['On']=False
main()
it almost works, but if you rotate the ‘Target’ it produces strange results
any idea what is wrong?
Attachments
FloatToRadius_And_Rotate_to_Place.blend (423 KB)
Not sure what you wanted exactly, but you could try setting a variable to check the initial position of the cube in some way, and then using that to ‘stop’ the circling.
Here’s a start: FloatToRadius_And_Rotate_to_Place.blend (482 KB)
Of course you may never exactly reach the initial position again, but you can check for a margin of error.
update- removed much complexity * 2
import bgefrom mathutils import Vector
def main():
cont = bge.logic.getCurrentController()
own = cont.owner
if not own.parent:
Target = own.scene.objects['Target']
local = [5,5,0]
r = Vector(local).magnitude
own['Desired']=str(local)
targetPos1 = Target.worldPosition+(Target.worldOrientation*Vector(local))
if 'p' not in Target:
Target['p']= own.scene.addObject('point',Target,0)
Target['p'].worldPosition=targetPos1
Target['p'].setParent(Target,0,1)
v2 = own.getVectTo(Target)
l2 = own.worldPosition - Target.worldPosition
if v2[0]>r:
#float to radius
own.applyForce(v2[1]*15*v2[0],0)
own.applyForce([0,0,own.mass*9.8],0)
own.worldLinearVelocity*=.9
else:
diff = l2 - Vector(local)
own['Current'] =str(diff)
rot = Target.worldOrientation.to_euler()
vex = l2.copy()
#hover at radius for now
own.worldLinearVelocity*=.005
own.worldPosition = Target.worldPosition+v2[1]*-r
own.applyForce((0,0,9.8*own.mass),0)
if diff.magnitude>.5:
if diff.x>0:
if diff.y>0:
rot.z-=.005
b = rot.to_matrix()
own.worldPosition = Target.worldPosition +b*Vector(vex)
else:
rot.z+=.005
b = rot.to_matrix()
own.worldPosition = Target.worldPosition +b*Vector(vex)
else:
if diff.y>0:
rot.z-=.005
b = rot.to_matrix()
own.worldPosition = Target.worldPosition +b*Vector(vex)
else:
rot.z+=.005
b = rot.to_matrix()
own.worldPosition = Target.worldPosition +b*Vector(vex)
else:
own.worldPosition = targetPos1
own.setParent(Target)
own.state =1
own['On']=False
main()
Attachments
FloatToRadius_And_Rotate_to_Place_2.blend (423 KB)
ok, used angle2_signed and it’s almost working, but there are some issues I could use help with
sometimes it seems the angle value is inverted potentially?
import bge
from mathutils import Vector
def main():
cont = bge.logic.getCurrentController()
own = cont.owner
Target = own.scene.objects['Target']
if 'Helper' not in own:
own['Helper'] = own.scene.addObject('Helper',Target,0)
local = eval(own['local'])
r = Vector(local).magnitude
own['Desired']=str(local)
targetPos1 = Target.worldPosition+(Target.worldOrientation*Vector(local))
if 'p' not in own:
own['p']= own.scene.addObject('point',Target,0)
own['p'].worldPosition=targetPos1
own['p'].setParent(Target,0,1)
v2 = own.getVectTo(Target)
l2 = own.worldPosition - Target.worldPosition
D = l2.copy()
D = D.normalized()
D*=-r
l2 = Target.worldOrientation.inverted()*l2
if v2[0]>r:
#float to radius
own.applyForce(v2[1]*15*v2[0],0)
own.applyForce([0,0,own.mass*9.8],0)
own.worldLinearVelocity*=.5
else:
own.applyForce([0,0,own.mass*9.8],0)
own.worldLinearVelocity*=0
if 'rotMe' not in own:
v2t = Target.getVectTo(targetPos1)
v2o = Target.getVectTo(own)
v2dt = Vector([v2t[1][0],v2t[1][1]])
v2do = Vector([v2o[1][0],v2t[1][1]])
a2d = v2dt.angle_signed(v2do, 5)
print(a2d)
own['rotMe']=a2d
own['Helper'].alignAxisToVect([0,0,1],2,1)
own['Helper'].alignAxisToVect(v2o[1],0,1)
own.setParent(own['Helper'])
else:
if abs(own['rotMe'])>.005:
if own['rotMe']>0:
own['Helper'].applyRotation((0,0,-.005),1)
own['rotMe']-=.005
else:
own['Helper'].applyRotation((0,0,.005),1)
own['rotMe']+=.005
else:
own.worldPosition=targetPos1
own.setParent(Target)
main()
Attachments
FloatToRadius_And_Rotate_to_Place_4.blend (457 KB)
sdfgeoff
(sdfgeoff)
August 13, 2017, 11:53am
8
If you can avoid using euler angles, do so. Instead use either quaternions or a 3x3 orientation matrix. Euler angles suffer from gimbal lock and singularities - and are unclear about what reference frame they work in.
More specifically in this case: an angle between two vectors has no inherent direction. It is simply an angular difference. So instead, try representing the angular difference as a rotation axis. You can do this using the cross product between two vectors (either the axis’ of the objects you want to align if you want it to rotate on the spot or vectors between positions of the spot you want to rotate it around). This can then be fed straight into applyRotation.
Quick note: things like
Target.worldPosition+(Target.worldOrientation*Vector(local))
Can be replaced with:
Target.worldTransform * Vector(local)
because transformation matrices are amazing.
how does one get the angle between quats?
can you pop up an edited example?
edit: this works but I still want to know more about quat math
Attachments
FloatToRadius_And_Rotate_to_Place_5.blend (457 KB)
ok now I have
import bge
from mathutils import Vector
def main():
cont = bge.logic.getCurrentController()
own = cont.owner
Target = own.scene.objects['Target']
if 'Helper' not in own:
own['Helper'] = own.scene.addObject('Helper',Target,0)
own['Helper2'] = own.scene.addObject('Helper',Target,0)
local = eval(own['local'])
r = Vector(local).magnitude
own['Desired']=str(local)
targetPos1 = Target.worldPosition+(Target.worldOrientation*Vector(local))
if 'p' not in own:
own['p']= own.scene.addObject('point',Target,0)
own['p'].worldPosition=targetPos1
own['p'].setParent(Target,0,1)
v2 = own.getVectTo(Target)
v2t = Target.getVectTo(targetPos1)
if v2[0]>r:
#float to radius
own.applyForce(v2[1]*15*v2[0],0)
own.applyForce([0,0,own.mass*9.8],0)
own.worldLinearVelocity*=.5
else:
own.applyForce([0,0,own.mass*9.8],0)
own.worldLinearVelocity*=0
if 'rotMe' not in own:
v2o = Target.getVectTo(own)
own['Helper'].alignAxisToVect([0,0,1],2,1)
own['Helper'].alignAxisToVect(v2t[1],0,1)
own['Helper2'].alignAxisToVect([0,0,1],2,1)
own['Helper2'].alignAxisToVect(v2o[1],0,1)
v2t = own['Helper'].worldOrientation.to_quaternion()
v20 = own['Helper2'].worldOrientation.to_quaternion()
diff = v2t.rotation_difference(v20)
print(diff)
own['rotMe']=diff
own['Helper'].alignAxisToVect([0,0,1],2,1)
own['Helper'].alignAxisToVect(v2o[1],0,1)
own.setParent(own['Helper'])
else:
#use rotMe to rotate into place?
pass
main()
what do I do with the difference between the quats? (how do I rotate the object using it?)
edit: attached file
Attachments
FloatToRadius_And_Rotate_to_Place_6_a.blend (457 KB)
...
else:
Target.worldOrientation = own['rotMe']
...
Is this what you want?
EDIT: Nevermind, disregard the above.
EDIT 2:
Oh, interesting. Do you already have the ‘rotate to desired point’ mechanism in place?
...
else:
ori = Target.worldOrientation.to_quaternion().copy()
ori.w += 0.1
Target.worldOrientation = ori
...
Hope this was what you were looking for. The ‘w’ value defines how much to rotate the quaternion by.
got it
I needed .slerp(other, rate)
but rotating each axis over x frames would be cool,
https://docs.blender.org/api/blender_python_api_current/mathutils.html?highlight=track#mathutils.Vector.to_track_quat
There’s a built in “track to” function. Use .to_3d() to make it compatible with a world orientation matrix.
Then you can lerp them.