Browse Source

Updated MoveTo

This causes MoveTo to function based on position rather than time.  This
corrects problems with objects teleporting when they don't reach their
position before they are supposed to.  This was most commonly happening
because of the speed cap imposed by box2D.  The new method works the
same as before with the re-introduction of the margin as the optional
last parameter.  The new function is also more taxing on the engine
although it is unlikely that any loss in frame rate will be seen unless
hundreds of objects are being moved at the same time.
Peter Robinson 9 years ago
parent
commit
53177012cf

+ 0 - 1
engine/compilers/VisualStudio 2013/Torque 2D.vcxproj

@@ -707,7 +707,6 @@
     <ClInclude Include="..\..\source\2d\sceneobject\ParticlePlayer_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectList.h" />
-    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectMoveToEvent.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectRotateToEvent.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectSet_ScriptBinding.h" />

+ 0 - 3
engine/compilers/VisualStudio 2013/Torque 2D.vcxproj.filters

@@ -2471,9 +2471,6 @@
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject_ScriptBinding.h">
       <Filter>2d\sceneobject</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectMoveToEvent.h">
-      <Filter>2d\sceneobject</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectRotateToEvent.h">
       <Filter>2d\sceneobject</Filter>
     </ClInclude>

+ 120 - 28
engine/source/2d/sceneobject/SceneObject.cc

@@ -52,10 +52,6 @@
 #include "component/behaviors/behaviorTemplate.h"
 #endif
 
-#ifndef _SCENE_OBJECT_MOVE_TO_EVENT_H_
-#include "2d/sceneobject/SceneObjectMoveToEvent.h"
-#endif
-
 #ifndef _SCENE_OBJECT_ROTATE_TO_EVENT_H_
 #include "2d/sceneobject/SceneObjectRotateToEvent.h"
 #endif
@@ -131,6 +127,14 @@ SceneObject::SceneObject() :
     mRenderPosition( 0.0f, 0.0f ),
     mRenderAngle( 0.0f ),
     mSpatialDirty( true ),
+    mTargetPosition( 0.0f, 0.0f ),
+    mLastCheckedPosition( 0.0f, 0.0f ),
+    mTargetPositionActive( false ),
+    mDistanceToTarget( 0.0f ),
+    mTargetPositionMargin( 0.1f ),
+    mTargetPositionFound( false ),
+    mSnapToTargetPosition( true ),
+    mStopAtTargetPosition( true ),
 
     /// Body.
     mpBody(NULL),
@@ -193,7 +197,6 @@ SceneObject::SceneObject() :
     mEditorTickAllowed(true),
     mPickingAllowed(true),
     mAlwaysInScope(false),
-    mMoveToEventId(0),
     mRotateToEventId(0),
     mSerialId(0),
     mRenderGroup( StringTable->EmptyString )
@@ -607,6 +610,12 @@ void SceneObject::integrateObject( const F32 totalTime, const F32 elapsedTime, D
             
         // Update world proxy.
         mpScene->getWorldQuery()->update( this, tickAABB, tickDisplacement );
+
+        //have we arrived at the target position?
+        if (mTargetPositionActive)
+        {
+           updateTargetPosition();
+        }
     }
 
     // Update Lifetime.
@@ -656,6 +665,15 @@ void SceneObject::postIntegrate(const F32 totalTime, const F32 elapsedTime, Debu
         Con::executef(this, 1, "onUpdate");
     }
 
+    // Check to see if we're done moving.
+    if (mTargetPositionActive && mTargetPositionFound)
+    {
+       mTargetPositionActive = false;
+
+       PROFILE_SCOPE(SceneObject_onMoveToComplete);
+       Con::executef(this, 1, "onMoveToComplete");
+    }
+
 	// Check to see if we're done fading.
 	if ( mFadeActive && mBlendColor == mTargetColor )
 	{
@@ -1614,7 +1632,7 @@ void SceneObject::onEndCollision( const TickContact& tickContact )
 
 //-----------------------------------------------------------------------------
 
-bool SceneObject::moveTo( const Vector2& targetWorldPoint, const F32 speed, const bool autoStop, const bool warpToTarget )
+bool SceneObject::moveTo( const Vector2& targetWorldPoint, const F32 speed, const bool autoStop, const bool snapToTarget, const F32 margin )
 {
     // Check in a scene.
     if ( !getScene() )
@@ -1637,27 +1655,23 @@ bool SceneObject::moveTo( const Vector2& targetWorldPoint, const F32 speed, cons
         return false;
     }
 
-    // Cancel any previous event.
-    if ( mMoveToEventId != 0 )
-    {
-        Sim::cancelEvent( mMoveToEventId );
-        mMoveToEventId = 0;
-    }
+    // Set the target position
+    mLastCheckedPosition = getPosition();
+    mTargetPosition = targetWorldPoint;
+    mTargetPositionMargin = margin;
+    mTargetPositionActive = true;
+    mTargetPositionFound = false;
+    mSnapToTargetPosition = snapToTarget;
+    mStopAtTargetPosition = autoStop;
+    mDistanceToTarget = (mLastCheckedPosition - mTargetPosition).Length();
 
     // Calculate the linear velocity for the specified speed.
     Vector2 linearVelocity = targetWorldPoint - getPosition();
-    const F32 distance = linearVelocity.Normalize( speed );
-
-    // Calculate the time it will take to reach the target.
-    const U32 time = (U32)((distance / speed) * 1000.0f);
+    linearVelocity.Normalize(speed);
 
     // Set the linear velocity.
     setLinearVelocity( linearVelocity );
 
-    // Create and post event.
-    SceneObjectMoveToEvent* pEvent = new SceneObjectMoveToEvent( targetWorldPoint, autoStop, warpToTarget );
-    mMoveToEventId = Sim::postEvent(this, pEvent, Sim::getCurrentTime() + time );
-
     return true;
 }
 
@@ -1771,10 +1785,9 @@ bool SceneObject::growTo(const Vector2& targetSize, const Vector2& deltaSize)
 void SceneObject::cancelMoveTo( const bool autoStop )
 {
     // Only cancel an active moveTo event
-    if ( mMoveToEventId != 0 )
+    if ( mTargetPositionActive )
     {
-        Sim::cancelEvent( mMoveToEventId );
-        mMoveToEventId = 0;
+       mTargetPositionActive = false;
 
         // Should we auto stop?
         if ( autoStop )
@@ -4207,11 +4220,90 @@ void SceneObject::updateBlendColor(const F32 elapsedTime)
 
 void SceneObject::updateSize(const F32 elapsedTime)
 {
-	// Apply the size deltas to the area to move it toward the targetSize.
-	mSize.x = processEffect(mSize.x, mTargetSize.x, mDeltaSize.x * elapsedTime);
-	mSize.y = processEffect(mSize.y, mTargetSize.y, mDeltaSize.y * elapsedTime);
-
-	setSize(mSize);
+   // Apply the size deltas to the area to move it toward the targetSize.
+   mSize.x = processEffect(mSize.x, mTargetSize.x, mDeltaSize.x * elapsedTime);
+   mSize.y = processEffect(mSize.y, mTargetSize.y, mDeltaSize.y * elapsedTime);
+
+   setSize(mSize);
+}
+
+//-----------------------------------------------------------------------------
+
+void SceneObject::updateTargetPosition()
+{
+   F32 distance = (getPosition() - mTargetPosition).Length();
+   bool hasArrived = false;
+
+   // Is the current position within the target?
+   if ( distance <= mTargetPositionMargin )
+   {
+      hasArrived = true;
+   }
+   else // Did we pass through the target?
+   {
+      // Moving vertically
+      if (mLastCheckedPosition.x == getPosition().x)
+      {
+         if (((mLastCheckedPosition.y < mTargetPosition.y && mTargetPosition.y < getPosition().y) || (mLastCheckedPosition.y > mTargetPosition.y && mTargetPosition.y > getPosition().y)) &&
+         mFabs(getPosition().x - mTargetPosition.x) <= mTargetPositionMargin)
+         {
+            hasArrived = true;
+         }
+      }// Or moving horizontally
+      else if (mLastCheckedPosition.y == getPosition().y)
+      {
+         if (((mLastCheckedPosition.x < mTargetPosition.x && mTargetPosition.x < getPosition().x) || (mLastCheckedPosition.x > mTargetPosition.x && mTargetPosition.x > getPosition().x)) &&
+            mFabs(getPosition().y - mTargetPosition.y) <= mTargetPositionMargin)
+         {
+            hasArrived = true;
+         }
+      }// Or moving diagonally
+      else
+      {
+         //slopes of the two lines
+         Vector2 slope = (mLastCheckedPosition - getPosition());
+         Vector2 perpSlope = slope.getPerp();
+         F32 m1 = slope.y / slope.x;
+         F32 m2 = perpSlope.y / perpSlope.x;
+
+         //y-intercepts
+         F32 b1 = mLastCheckedPosition.y - (m1 * mLastCheckedPosition.x);
+         F32 b2 = mTargetPosition.y - (m2 * mTargetPosition.x);
+
+         //point of interception
+         F32 x = (b1 - b2) / (m2 - m1);
+         F32 y = (m1 * x) + b1;
+         Vector2 intercept = Vector2(x, y);
+
+         //Is the intercept point between the other two points and is the distance less than the margin?
+         if (((mLastCheckedPosition.x < intercept.x && intercept.x < getPosition().x) || (mLastCheckedPosition.x > intercept.x && intercept.x > getPosition().x)) &&
+            (intercept - mTargetPosition).Length() <= mTargetPositionMargin)
+         {
+            hasArrived = true;
+         }
+      }
+   }
+
+   if (hasArrived)
+   {
+      if (mSnapToTargetPosition)
+      {
+         setPosition(mTargetPosition);
+      }
+      if (mStopAtTargetPosition)
+      {
+         setLinearVelocity(Vector2::getZero());
+      }
+      mTargetPositionFound = true;
+   }
+   // Are we moving away from the target?
+   else if (distance > mDistanceToTarget)
+   {
+      // Then turn off the target. No need to keep checking.
+      mTargetPositionActive = false;
+   }
+   mLastCheckedPosition = getPosition();
+   mDistanceToTarget = distance;
 }
 
 //-----------------------------------------------------------------------------

+ 11 - 4
engine/source/2d/sceneobject/SceneObject.h

@@ -109,7 +109,6 @@ public:
     friend class ContactFilter;
     friend class WorldQuery;
     friend class DebugDraw;
-    friend class SceneObjectMoveToEvent;
     friend class SceneObjectRotateToEvent;
 
 protected:
@@ -156,6 +155,14 @@ protected:
     Vector2                 mRenderPosition;
     F32                     mRenderAngle;
     bool                    mSpatialDirty;
+    Vector2                 mLastCheckedPosition;
+    Vector2                 mTargetPosition;
+    bool                    mTargetPositionActive;
+    F32                     mDistanceToTarget;
+    F32                     mTargetPositionMargin;
+    bool                    mTargetPositionFound;
+    bool                    mSnapToTargetPosition;
+    bool                    mStopAtTargetPosition;
 
     /// Body.
     b2Body*                 mpBody;
@@ -228,7 +235,6 @@ protected:
     bool                    mEditorTickAllowed;
     bool                    mPickingAllowed;
     bool                    mAlwaysInScope;
-    U32                     mMoveToEventId;
     U32                     mRotateToEventId;
     U32                     mSerialId;
     StringTableEntry        mRenderGroup;
@@ -404,12 +410,13 @@ public:
     inline F32              getAngularDamping(void) const               { if ( mpScene ) return mpBody->GetAngularDamping(); else return mBodyDefinition.angularDamping; }
 
     /// Move/Rotate to.
-    bool                    moveTo( const Vector2& targetWorldPoint, const F32 speed, const bool autoStop = true, const bool warpToTarget = true );
+    bool                    moveTo(const Vector2& targetWorldPoint, const F32 speed, const bool autoStop = true, const bool snapToTarget = true, const F32 margin = 0.1f);
     bool                    rotateTo( const F32 targetAngle, const F32 speed, const bool autoStop = true, const bool warpToTarget = true );
     void                    cancelMoveTo( const bool autoStop = true );
     void                    cancelRotateTo( const bool autoStop = true );
-    inline bool             isMoveToComplete( void ) const              { return mMoveToEventId == 0; }
+    inline bool             isMoveToComplete( void ) const              { return !mTargetPositionActive; }
     inline bool             isRotateToComplete( void ) const            { return mRotateToEventId == 0; }
+    void                    updateTargetPosition( void );
 
 	// Fade to
 	bool					fadeTo( const ColorF& targetColor, const F32 deltaRed, const F32 deltaGreen, const F32 deltaBlue, const F32 deltaAlpha );

+ 0 - 75
engine/source/2d/sceneobject/SceneObjectMoveToEvent.h

@@ -1,75 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2013 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#ifndef _SCENE_OBJECT_MOVE_TO_EVENT_H_
-#define _SCENE_OBJECT_MOVE_TO_EVENT_H_
-
-#ifndef _SCENE_OBJECT_H_
-#include "2d/sceneobject/SceneObject.h"
-#endif
-
-//-----------------------------------------------------------------------------
-
-class SceneObjectMoveToEvent : public SimEvent
-{
-public:
-    SceneObjectMoveToEvent( const Vector2& targetWorldPoint, const bool autoStop, const bool warpToTarget  ) :
-        mAutoStop( autoStop ),
-        mWarpToTarget( warpToTarget ),
-        mTargetWorldPoint( targetWorldPoint ) {}
-    virtual ~SceneObjectMoveToEvent() {}
-
-    virtual void process(SimObject *object)
-    {
-        // Fetch scene object.
-        SceneObject* pSceneObject = (dynamic_cast<SceneObject*>(object));
-        if (pSceneObject == NULL )
-            return;
-
-        // Are we auto stopping?
-        if ( mAutoStop )
-        {
-            // Yes, so reset linear velocity.
-            pSceneObject->setLinearVelocity( Vector2::getZero() );
-        }
-
-        // Are we warping to target?
-        if ( mWarpToTarget )
-        {
-            // Yes, so set position to the target.
-            pSceneObject->setPosition( mTargetWorldPoint );
-        }
-
-        // Reset event Id.
-        pSceneObject->mMoveToEventId = 0;
-
-        // Script callback.
-        Con::executef( object, 2, "onMoveToComplete", mTargetWorldPoint.scriptThis() );
-    }
-
-private:
-    Vector2     mTargetWorldPoint;
-    bool        mAutoStop;
-    bool        mWarpToTarget;
-};
-
-#endif // _SCENE_OBJECT_MOVE_TO_EVENT_H_

+ 19 - 9
engine/source/2d/sceneobject/SceneObject_ScriptBinding.h

@@ -1861,15 +1861,17 @@ ConsoleMethodWithDocs(SceneObject, getAngularDamping, ConsoleFloat, 2, 2, ())
 //-----------------------------------------------------------------------------
 
 /*! Moves the object to the specified world point.
-    The point is moved by calculating the initial linear velocity required and applies it.
-    The object may never reach the point if it has linear damping applied or collides with another object.
-    @param worldPoint/Y The world point to move the object to.
+    Linear velocity is applied to the object at the given speed in the direction of the target world point.
+    The object may never reach the point if other forces act on the target such as collisions.
+    If the object moves away from the target the object will stop checking to see if it has arrived at the target.
+    @param worldPointX/Y The world point to move the object to.
     @param speed The speed (in m/s) to use to move to the specified point.
-    @param autoStop? Whether to automatically set the linear velocity to zero when time has elapsed or not
-    @param warpToTarget? Whether to move instantly to the target point after the specified time or not in-case the target was not quite reached.
+    @param autoStop? Whether to automatically set the linear velocity to zero when the object arrives at the target.
+    @param snapToTarget? Whether to snap the object to the target point when it is within the margin.
+    @param margin? The distance from the target that qualifies as reaching the target.
     @return Whether the move could be started or not.
 */
-ConsoleMethodWithDocs(SceneObject, moveTo, ConsoleBool, 4, 7, (worldPoint X/Y, speed, [autoStop = true], [warpToTarget = true]))
+ConsoleMethodWithDocs(SceneObject, moveTo, ConsoleBool, 4, 7, (worldPoint X/Y, speed, [autoStop = true], [snapToTarget = true], [margin = 0.1]))
 {
     // World point.
     const U32 worldPointElementCount = Utility::mGetStringElementCount(argv[2]);
@@ -1909,10 +1911,18 @@ ConsoleMethodWithDocs(SceneObject, moveTo, ConsoleBool, 4, 7, (worldPoint X/Y, s
         return object->moveTo( worldPoint, speed, autoStop );
     }
 
-    // Warp to target?
-    const bool warpToTarget = dAtob(argv[nextArg++]);
+    // Snap to target?
+    const bool snapToTarget = dAtob(argv[nextArg++]);
 
-    return object->moveTo( worldPoint, speed, autoStop, warpToTarget );
+    if ( argc <= nextArg )
+    {
+       return object->moveTo(worldPoint, speed, autoStop, snapToTarget);
+    }
+
+    // Margin.
+    const F32 margin = dAtof(argv[nextArg++]);
+
+    return object->moveTo( worldPoint, speed, autoStop, snapToTarget, margin );
 }
 
 //-----------------------------------------------------------------------------