Ooops
November 25, 2020, 08:10:10 AM

Author Topic: [bb] Smooth Movement for Entities by Rob the Great [ 1+ years ago ]  (Read 616 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
Title : Smooth Movement for Entities
Author : Rob the Great
Posted : 1+ years ago

Description : Please see the comments in my code for the complete description. Essentially, you can include this code to gradually move an entity from point A to point B, including rotation. The entity starts off with zero velocity, picks up speed to 100% velocity (based on distance and time traveled), and then slows down back to zero velocity at the end of the movement. The math is contained in a single function, you just simply need to create a starting pivot and an ending pivot oriented to the beginning and final position for the entity to be moved.

I designed this for my purposes, but it turned out well, so I decided to share it with the community. Let me know if any major bugs or glitches occur (I did some testing, but not thorough testing).

Enjoy!

Code was last edited on 06-10-2013 (removed a small bug).


Code :
Code: BlitzBasic
  1. ;====================================================================
  2. ; Project: Smooth Movement Function
  3. ; Version: 1.0
  4. ; Author: Rob the Great
  5. ; Email: robmerrick181@gmail.com
  6. ; Copyright: No Copyright, free for public use (commercial and non-commericial)
  7. ; Description:    This is a single function that will apply smooth 3D movement
  8. ;                                 on an entity from point A to point B. The function can loop
  9. ;                                 on its own until the movement is complete, or it can be
  10. ;                                 iterated within an external loop. The function returns 0 if
  11. ;                                 the movement is still going, or 1 if the movement is complete.
  12. ;====================================================================
  13.  
  14. ;Everything in this block needs to be copied in order to use it in your project.
  15. ;BEGIN BLOCK
  16. ;===============================================================================
  17. ;I chose to use a type for ease and flow. This also allows for more than one entity to move at the same time, but looping must be disabled for that to work.
  18.  
  19. Type smoothmovement
  20.  
  21.         Field entity
  22.         Field movevalue#
  23.         Field amplitude#
  24.         Field pitchamplitude#
  25.         Field yawamplitude#
  26.         Field rollamplitude#
  27.         Field snapped
  28.  
  29. End Type
  30.  
  31. ;This function will move an entity from point A to point B in a smooth manner.
  32. ;It will begin with a velocity of 0 and will accelerate until it is half-way
  33. ;to the end point, and then decelerate until it reaches the end point at a
  34. ;velocity of 0. This is just like PositionEntity, only without the snapping.
  35. ;This function will also rotate that same entity from rotation A to rotation
  36. ;B in a smooth manner.
  37. ;
  38. ;This function requires the creation of two pivots, one for point A and the
  39. ;other for point B. I used pivots to cut back on the number of parameters
  40. ;needed to make this function work. Simply create the two pivots and position
  41. ;and rotate them to represent the starting and ending points for the entity
  42. ;to be moved. Don't forget to free the pivots once you are done, because the
  43. ;function won't do this for you (unless you add it in yourself above the
  44. ;Return 1 line in the SmoothMove() function.)
  45. ;
  46. ;The function has two modes: internal loop or external loop. The internal loop
  47. ;is more for my purposes, but I went ahead and included it in this demo. It will
  48. ;loop on its own and automatically end when it reaches its destination. This is
  49. ;especially useful if you wish to move the camera, but not so much for other
  50. ;entities. The other mode is the external loop, which just means that you have
  51. ;to continously call this function in a loop until it returns 1. The second
  52. ;option is more likely what you want out of this function, and is thus the default.
  53. ;
  54. ;Parameters: entity
  55. ;                                       -the handle of the entity to be moved from point A to point B.
  56. ;                        startpiv
  57. ;                                       -the handle of a pivot created to store the starting point of
  58. ;                                       -the entity to be moved. Make sure it is positioned at the
  59. ;                                       -starting coordinates and that it is rotated to the starting values.
  60. ;                                       -Note that the entity will automatically snap to the startpiv's
  61. ;                                       -beginning position and rotation to ensure the math is right.
  62. ;                        endpiv
  63. ;                                       -the handle of a pivot created to store the ending point of the
  64. ;                                       -entity to be moved. Make sure that it is positioned at the
  65. ;                                       -ending coordinates and that it is rotated to the ending values.
  66. ;                        framecount# = 120.0
  67. ;                                       -the number of iterations to make until the entity has moved from
  68. ;                                       -point A to point B. The default is 120 frames (a floating point
  69. ;                                       -number is used for math reasons, but this is untested with values
  70. ;                                       -other than integers for arguments). If needed, set this to 0 or 1 to
  71. ;                                       -instantly snap the entity to the new position and rotation. This is
  72. ;                                       -the same thing as calling PoisitionEntity() and RotateEntity()
  73. ;                                       -except that the math will be a little more intense on the
  74. ;                                       -processor using this method. It's your call.
  75. ;                        loop = 0
  76. ;                                       -set this parameter to 0 to return after each iteration, or set it
  77. ;                                       -to 1 to have the function loop on its own until it is finished. The
  78. ;                                       -default is set to 0
  79. ;
  80. ;Returns 0 if the entity is not done moving (and loop is off), or 1 if the movement is
  81. ;complete.
  82. ;
  83. ;NOTES: This function will rotate an entity as well as position it. The rotation works for
  84. ;all Blitz rotation values, but keep in mind that if it passes 180 degrees, it will rotate
  85. ;more than once before reaching the final destination. This function will snap the entity
  86. ;to the starting rotation of the startpiv to ensure the math works as it should. Because
  87. ;of this, the entity can accidently snap into it's starting position if this is different
  88. ;from its current position. Also note that this function is only designed to handle Global
  89. ;position and rotation values, so keep than in mind when you set up your start and end pivots.
  90.  
  91. Function SmoothMove(entity,startpiv,endpiv,framecount# = 120.0,loop = 0)
  92.  
  93.  
  94.         If entity = 0 Or startpiv = 0 Or endpiv = 0 ;check for valid handles
  95.                 RuntimeError "Please specificy a valid handle for entity, startpiv, and endpiv."
  96.         EndIf
  97.         If EntityDistance(entity,endpiv) <= 0.01 ;bail if entity is already at the end point (useful for multiple entity movements)
  98.                 If Abs(EntityPitch(entity,True) - EntityPitch(endpiv,True)) <= 0.01
  99.                         If Abs(EntityYaw(entity,True) - EntityYaw(endpiv,True)) <= 0.01
  100.                                 If Abs(EntityRoll(entity,True) - EntityRoll(endpiv,True)) <= 0.01
  101.                                         Return 1
  102.                                 EndIf
  103.                         EndIf
  104.                 EndIf
  105.         EndIf
  106.         ;make variables to store the entity's current rotation
  107.         Local temppitch#
  108.         Local tempyaw#
  109.         Local temproll#
  110.        
  111.         ;define a name for the smoothmovement type
  112.         Local s.smoothmovement
  113.        
  114.         ;make a variable to know whether or not the type has already been created (only applies if looping is enabled)
  115.         Local typemade = 0
  116.        
  117.         ;even though this is a loop, the function will return if looping is disabled
  118.         Repeat
  119.                
  120.                 ;here's a panic button. remove if needed.
  121.                 If KeyDown(1) Then End
  122.                
  123.                 If (typemade) Or (Not loop) ;if the type has already been made or looping is disabled
  124.                         For s.smoothmovement = Each smoothmovement
  125.                                 If sentity = entity ;find the type with the entity to be moved. Note that this function can handle more than one movement if looping is disabled.
  126.                                         temppitch# = EntityPitch#(sentity,True) ;store the rotation values
  127.                                         tempyaw# = EntityYaw#(sentity,True) ;store the rotation values
  128.                                         temproll# = EntityRoll#(sentity,True) ;store the rotation values
  129.                                         PointEntity sentity,endpiv ;aim the entity at the end point
  130.                                         Local newvalue#
  131.                                         newvalue = Sin#(smovevalue#/framecount#) ;This only needs to be calculated once per iteration, but is used multiple times.
  132.                                         ;the next line is a very simplified form of a very complex equation. See the comments at the end for complete details.
  133.                                         ;Local radianvalue# ;OLD VALUE! See below for more info.
  134.                                         ;radianvalue# = (Pi/framecount#)*(smovevalue#) ;OLD VALUE! See below for more info.
  135.                                         ;MoveEntity sentity,0,0,(Sin#(RADIAN_TO_DEGREE#*(radianvalue#)))*(samplitude#) ;OLD VALUE! See below for more info.
  136.                                         MoveEntity sentity,0,0,newvalue#*(samplitude#) ;SIMPLIFIED VALUE! See below for more info.
  137.                                         RotateEntity sentity,temppitch#+newvalue#*spitchamplitude#,tempyaw#+newvalue#*syawamplitude,temproll#+newvalue#*s
  138. ollamplitude#,True
  139.                                         ;turn the entity back to how it was facing and then apply a gradual turn to slowly orient toward the final rotation.
  140.                                         smovevalue# = smovevalue# + 180.0 ;this increases by 1*180 each iteration. Think of it as counting the number of frames that have passed
  141.                                                                                                                 ;the *180 comes from simplifying the equation above.
  142.                                         If smovevalue# >= (framecount#*180) ;If we go above the frame count (*180 from the simplified equation above), we are done!
  143.                                                 PositionEntity sentity,EntityX#(endpiv,True),EntityY#(endpiv,True),EntityZ#(endpiv,True),True ;should be there, but let's be smart
  144.                                                 RotateEntity sentity,EntityPitch#(endpiv,True),EntityYaw(endpiv,True),EntityRoll#(endpiv,True),True ;Same as above
  145.                                                 Delete s ;get ride of the smoothmovement type
  146.                                                 Return 1 ;and we're done!
  147.                                         EndIf
  148.                                         If Not loop ;if we're not looping
  149.                                                 Return 0 ;we're not done
  150.                                         Else ;if we are looping
  151.                                                 Exit ;get out of the For...Next loop (no need to cycle through everything once we have our type modified)
  152.                                         EndIf
  153.                                 EndIf
  154.                         Next
  155.                 EndIf
  156.                 If Not typemade ;if this is the first iteration...
  157.                                                 ;note that typemade changes to 1 after this block and will stay that way if looping is enabled. If looping is disabled,
  158.                                                 ;the function won't make it to this point anyway because it returns before it hits this point if the type has already been made.
  159.                         PositionEntity entity,EntityX(startpiv,True),EntityY(startpiv,True),EntityZ(startpiv,True),True ;Make sure we are starting at the right spot
  160.                         RotateEntity entity,EntityPitch(startpiv,True),EntityYaw(startpiv,True),EntityRoll(startpiv,True),True ;same as above
  161.                         s.smoothmovement = New smoothmovement ;make a new smoothmovement type
  162.                         sentity = entity ;inheret the entity
  163.                         smovevalue# = 0.0 ;initialize the movement value (think of this as a counter for the number of iterations. It increases by 1*180 each iteration).
  164.                         ;integraldistance = ((2*framecount)/Pi)*(amplitude of sin wave), yay calculus! See below for more info on how I got this equation.
  165.                         samplitude# = ((EntityDistance#(startpiv,endpiv))*Pi)/(2.0*framecount#) ;amplitude is solved from the integral, using entitydistance for the distance
  166.                         spitchamplitude# = (EntityPitch#(endpiv,True) - EntityPitch#(startpiv,True))*Pi/(2.0*framecount#) ;This tells us how fast to rotate on the x-axis
  167.                         syawamplitude# = (EntityYaw#(endpiv,True) - EntityYaw#(startpiv,True))*Pi/(2.0*framecount#) ;This tells us how fast to rotate on the y-axis
  168.                         s
  169. ollamplitude# = (EntityRoll#(endpiv,True) - EntityRoll#(startpiv,True))*Pi/(2.0*framecount#) ;This tells us how fast to rotate on the z-axis
  170.                         typemade = 1 ;let the program know we have made the smoothmovement type so we don't make it again (only applies if looping is enabled)
  171.                         If Not loop ;if we are not looping
  172.                                 Return 0 ;we're not done at this point
  173.                         EndIf
  174.                 EndIf
  175.                
  176.                 UpdateWorld ;on the loop, we need these lines, feel free to modify as you wish
  177.                 RenderWorld
  178.                 Flip
  179.                
  180.         Forever ;Don't freak out about this line. There is a panic button (ESC) to get out if needed.
  181.  
  182.  
  183. End Function
  184. ;END BLOCK
  185. ;============================================================================================================================================
  186.  
  187. ;3D Example - This shows how the process works. I won't comment this part as strongly.
  188. ;Essentially, I am making two spheres and a start and end point for each sphere.
  189. Graphics3D 1024,768,0,2
  190.  
  191. SetBuffer BackBuffer()
  192.  
  193. Global camera = CreateCamera()
  194. MoveEntity camera,0,20,-15
  195. RotateEntity camera,60,0,0
  196.  
  197. Local light = CreateLight(2)
  198. PositionEntity light,80,30,-5
  199.  
  200. Local sphere = CreateSphere()
  201. MoveEntity sphere,-15,0,0
  202. Local front = CreateCube()
  203. PositionEntity front,-15,0,1.5
  204. ScaleEntity front,0.75,0.75,0.75
  205. EntityParent front,sphere
  206. Local pointA
  207. Local pointB
  208. pointA = CreateCube()
  209. pointB = CreateCube()
  210. PositionEntity pointA,EntityX(sphere),EntityY(sphere),EntityZ(sphere)
  211. PositionEntity pointB,EntityX(sphere) + 30,EntityY(sphere),EntityZ(sphere)
  212. ScaleEntity pointA,0.1,0.1,0.1
  213. ScaleEntity pointB,0.1,0.1,0.1
  214. RotateEntity pointA,0,0,0
  215. RotateEntity pointB,0,90,0
  216. EntityColor pointA,255,0,0
  217. EntityColor pointB,0,0,255.0
  218.  
  219. Local sphere2 = CreateSphere()
  220. MoveEntity sphere2,0,0,-15
  221. Local front2 = CreateCube()
  222. PositionEntity front2,0,0,1.5-15
  223. ScaleEntity front2,0.75,0.75,0.75
  224. EntityParent front2,sphere2
  225. Local pointC
  226. Local pointD
  227. pointC = CreateCube()
  228. pointD = CreateCube()
  229. PositionEntity pointC,EntityX(sphere2),EntityY(sphere2),EntityZ(sphere2)
  230. PositionEntity pointD,EntityX(sphere2),EntityY(sphere2),EntityZ(sphere2) + 30
  231. ScaleEntity pointC,0.1,0.1,0.1
  232. ScaleEntity pointD,0.1,0.1,0.1
  233. RotateEntity pointC,90,180,180
  234. RotateEntity pointD,-90,-180,-90
  235. EntityColor pointC,0,255.0,0
  236. EntityColor pointD,255.0,255.0,0
  237.  
  238. Local atpoint$ = "A"
  239. Local framevalue = 120
  240. Local option = 0
  241. Local moved = 0
  242. Local moved2 = 0
  243.  
  244. While Not KeyDown(1)
  245.  
  246.         If KeyHit(57) ;Spacebar
  247.                 ;Option 0 (EXTERNAL LOOP, DEFAULT)
  248.                 If Not option
  249.                        
  250.                         While (moved = 0) Or (moved2 = 0)
  251.                                
  252.                                 If KeyDown(1) Then End
  253.                                
  254.                                 If atpoint$ = "A"
  255.                                         moved = SmoothMove(sphere,pointA,pointB,framevalue,option) ;Here is the SmoothMove() Function for the first sphere, and the second is below.
  256.                                         moved2 = SmoothMove(sphere2,pointC,pointD,120,option) ;I left the framevalue constant so you can see different rates of movement.
  257.                                 Else
  258.                                         moved = SmoothMove(sphere,pointB,pointA,framevalue,option)
  259.                                         moved2 = SmoothMove(sphere2,pointD,pointC,120,option) ;same as above
  260.                                 EndIf
  261.                                
  262.                                 UpdateWorld
  263.                                 RenderWorld
  264.                                 Text 0,0,"MOVING! Note that this text will not appear if option is set to 1."
  265.                                 Flip
  266.                                
  267.                         Wend
  268.                        
  269.                 ;Option 1 (INTERNAL LOOP)
  270.                 Else
  271.                         If atpoint$ = "A"
  272.                                 SmoothMove(sphere,pointA,pointB,framevalue,option)
  273.                                 SmoothMove(sphere2,pointC,pointD,120,option)
  274.                         Else
  275.                                 SmoothMove(sphere,pointB,pointA,framevalue,option)
  276.                                 SmoothMove(sphere2,pointD,pointC,120,option)
  277.                         EndIf
  278.                 EndIf
  279.                
  280.                 ;Reset from above, doesn't demonstrate the concepts of this function.
  281.                 FlushKeys()
  282.                 If atpoint$ = "A"
  283.                         atpoint$ = "B"
  284.                 Else
  285.                         atpoint$ = "A"
  286.                 EndIf
  287.                 moved = 0
  288.                 moved2 = 0
  289.         EndIf
  290.        
  291.         If KeyHit(28) ;Enter
  292.                 option = Not option
  293.         EndIf
  294.        
  295.         If KeyDown(200) ;Up arrow
  296.                 framevalue = framevalue + 1
  297.         EndIf
  298.        
  299.         If KeyDown(208) ;Down arrow
  300.                 framevalue = framevalue - 1
  301.         EndIf
  302.        
  303.         UpdateWorld
  304.         RenderWorld
  305.         Text 0,0,"Hit the space bar to move the spheres."
  306.         Text 0,15,"The first sphere is currently at point " + atpoint$ + "."
  307.         Text 0,30,"It will take " + framevalue + " frames to move from one point to the next. More frames makes the effect more pronounced."
  308.         Text 0,45,"Push the up and down arrows to increase and decrease the number of frames required for movement. More frames takes longer."
  309.         Text 0,60,"Push Enter to change the method of the function. Currently, it is set to option " + option + "."
  310.         Text 0,75,"Option 0 (default) will not loop on its own, while option 1 will loop on its own."
  311.         Text 0,90,"Option 0 supports multiple entity movement at the same time, but option 1 does not."
  312.         Flip
  313.        
  314. Wend
  315.  
  316. End
  317. ;The essentials to this equation:
  318. ;
  319. ;If you're wondering how I got the equation MoveEntity sentity,0,0,(Sin#(smovevalue#/framecount#))*(samplitude#), read this.
  320. ;
  321. ;I used a Sin function because it's periodic and because the values gradually go from 0 to 1 and back to 0.
  322. ;A typical Sin(x) function has a period of 2pi, but I didn't want it to go backwards, so I cut the period
  323. ;in half to make it pi. This means that I needed to limit my domain to be from 0 to pi*time. I ended up with
  324. ;a very basic function of Sin(pi*elapsedframes) for values to move the entity "forward". I needed this curve to
  325. ;stretch based on the total number of frames for the whole function, and the math makes this turn into a reciprocal.
  326. ;My function became Sin((pi/framecount)*(elapsedframe)). During testing, I saw that these values weren't giving
  327. ;me the right distances, so I needed to modify the amplitude of the sin wave in order to increase or decrease
  328. ;total distance travelled. The problem was that I had no idea what to set the amplitude to in order to get the
  329. ;exact distance. Looking at a Sin graph, I could then see that I had to use Calculus to get this answer.
  330. ;So, I let f(x) = Sin((pi/framecount)*(elapsedframe))*amplitude. EntityDistance would have to give me the
  331. ;same thing as the integral of this equation, so I went ahead and set up an integral.
  332. ;
  333. ;Let x = elapsedframes, c = framecount, amplitude = a, d = Total Distance Travelled
  334. ;Then the integral from 0 to c of ( (sin(pi*x/c)*a dx ) = d
  335. ;Let u = -cos(pi*x/c)
  336. ;du = sin(pi*x/c)*pi/c dx
  337. ;Producing (c/pi) times the integral from -1 to 1 of ( a*du ) = d
  338. ;Evaluating to 2*a*c/pi = d.
  339. ;
  340. ;From here, I solved for a (amplitude) to get
  341. ;a = d*pi/(2*c)
  342. ;Meaning
  343. ;amplitude = EntityDistance(startpiv,endpiv)*pi/(2*framecount)
  344. ;
  345. ;From here, I needed to convert from Radians to Degrees because the Blitz Sin function only handles degrees.
  346. ;This is what ended up happening:
  347. ;Local radianvalue#
  348. ;radianvalue# = (Pi/framecount#)*(smovevalue#) ;smovevalue# would increase by 1 each iteration, representing the passage of 1 frame
  349. ;RADIAN_TO_DEGREE# = 180/pi ;a constant to convert from radians to degrees
  350. ;MoveEntity sentity,0,0,(Sin#(RADIAN_TO_DEGREE#*(radianvalue#)))*(samplitude#)
  351. ;
  352. ;This equation was so nasty that I went to clean it up a little bit. Notice that the pis cancel, making the
  353. ;equation of MoveEntity sentity,0,0,(Sin#(180*smovevalue#/framecount#))*(samplitude#).
  354. ;
  355. ;Rather than mutliply by 180 each iteration, I felt that it would be slightly better to add 180 to smovevalue instead.
  356. ;I could then disregard the radian to degree conversion, making the final equation
  357. ;MoveEntity sentity,0,0,(Sin#(smovevalue#/framecount#))*(samplitude#)
  358. ;
  359. ;I applied the same technique to the rotation values.
  360. ;
  361. ;I hope that was more helpful that confusing. If none if it made sense, just trust that it works.


Comments : none...

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal