Browse Source

Path Feature

-Added start of pathing features(path node, path object and path)
-Created PathToy
marauder2k7 5 years ago
parent
commit
ff1feff8d9

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

@@ -272,6 +272,7 @@
     <ClCompile Include="..\..\source\2d\gui\SceneWindow.cc" />
     <ClCompile Include="..\..\source\2d\gui\SceneWindow.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\CompositeSprite.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\CompositeSprite.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\ParticlePlayer.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\ParticlePlayer.cc" />
+    <ClCompile Include="..\..\source\2d\sceneobject\Path.cpp" />
     <ClCompile Include="..\..\source\2d\sceneobject\SceneObject.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\SceneObject.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectList.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectList.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectSet.cc" />
     <ClCompile Include="..\..\source\2d\sceneobject\SceneObjectSet.cc" />
@@ -719,6 +720,8 @@
     <ClInclude Include="..\..\source\2d\sceneobject\CompositeSprite_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\CompositeSprite_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\ParticlePlayer.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\ParticlePlayer.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\ParticlePlayer_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\ParticlePlayer_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\2d\sceneobject\Path.h" />
+    <ClInclude Include="..\..\source\2d\sceneobject\Path_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectList.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectList.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectMoveToEvent.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectMoveToEvent.h" />

+ 9 - 0
engine/compilers/VisualStudio 2017/Torque 2D.vcxproj.filters

@@ -1462,6 +1462,9 @@
     <ClCompile Include="..\..\source\2d\sceneobject\ShadowMap.cc">
     <ClCompile Include="..\..\source\2d\sceneobject\ShadowMap.cc">
       <Filter>2d\sceneobject</Filter>
       <Filter>2d\sceneobject</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="..\..\source\2d\sceneobject\Path.cpp">
+      <Filter>2d\sceneobject</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\source\audio\audio.h">
     <ClInclude Include="..\..\source\audio\audio.h">
@@ -3252,6 +3255,12 @@
     <ClInclude Include="..\..\source\2d\sceneobject\ShadowMap_ScriptBinding.h">
     <ClInclude Include="..\..\source\2d\sceneobject\ShadowMap_ScriptBinding.h">
       <Filter>2d\sceneobject</Filter>
       <Filter>2d\sceneobject</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\..\source\2d\sceneobject\Path.h">
+      <Filter>2d\sceneobject</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\2d\sceneobject\Path_ScriptBinding.h">
+      <Filter>2d\sceneobject</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">

+ 238 - 0
engine/source/2d/sceneobject/Path.cpp

@@ -0,0 +1,238 @@
+#include "graphics/dgl.h"
+#include "2d/sceneobject/Path.h"
+
+#include "2d/sceneobject/Path_ScriptBinding.h"
+
+IMPLEMENT_CONOBJECT(Path);
+
+PathObject::PathObject():
+   mObj(NULL),
+   mPath(NULL)
+{
+   mCurrNode = 0;
+   mPrevNode = 0;
+   mLoop = true;
+   mOrient = false;
+   mSnapToNode = false;
+   mLoopCount = 0;
+   mMaxLoop = -1;
+   mMaxSpeed = 1.0f;
+   mAngOff = 0.0f;
+}
+
+void PathObject::setCurrNode(S32 node)
+{
+}
+
+void PathObject::setNextNode(S32 node)
+{
+}
+
+void PathObject::setPrevNode(S32 node)
+{
+}
+
+
+Path::Path()
+{
+
+   // Use a static body by default.
+   mBodyDefinition.type = b2_staticBody;
+
+   VECTOR_SET_ASSOCIATION(mObjs);
+   VECTOR_SET_ASSOCIATION(mNodes);
+}
+
+Path::~Path()
+{
+
+}
+
+void Path::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+void Path::preIntegrate(const F32 totalTime, const F32 elapsedTime, DebugStats * pDebugStats)
+{
+   Parent::preIntegrate(totalTime, elapsedTime, pDebugStats);
+
+   Vector<PathObject*>::iterator i;
+   for (i = mObjs.begin(); i != mObjs.end(); i++)
+   {
+      bool stop = false;
+      PathObject &pObj = *(*i);
+
+      Vector2 cPos = pObj.mObj->getPosition();
+      Node &cNode = mNodes[pObj.mCurrNode];
+      Vector2 cDst = mNodes[pObj.mCurrNode].position;
+      F32 distance = (cDst - cPos).Length();
+
+      if (distance < (pObj.mMaxSpeed * elapsedTime) || distance <= cNode.distance)
+      {
+         S32 nCount = mNodes.size();
+         S32 end = nCount - 1;
+         if (pObj.mCurrNode == end)
+         {
+            if (pObj.mLoop)
+            {
+               pObj.mLoopCount++;
+               if ((pObj.mMaxLoop > 0) && (pObj.mLoopCount >= pObj.mMaxLoop))
+               {
+                  Con::printf("why we stopping?");
+                  stop = true;
+               }
+               else
+               {
+                  Con::printf("back to 0");
+                  pObj.mPrevNode = pObj.mCurrNode;
+                  pObj.mCurrNode = 0;
+                  pObj.mNextNode = pObj.mCurrNode;
+               }
+            }
+            else
+            {
+               stop = true;
+            }
+         }
+         else
+         {
+            pObj.mPrevNode = pObj.mCurrNode;
+            pObj.mCurrNode = pObj.mCurrNode + 1;
+            pObj.mNextNode = pObj.mCurrNode;
+         }
+
+         if (pObj.mCurrNode >= nCount)
+         {
+            pObj.mCurrNode = 0;
+            pObj.mNextNode = pObj.mCurrNode;
+         }
+         else if (pObj.mCurrNode < 0)
+         {
+            pObj.mCurrNode = 0;
+            pObj.mNextNode = pObj.mCurrNode;
+         }
+
+      }
+
+      if (!stop)
+      {
+         moveObject(pObj);
+      }
+
+      else
+      {
+         pObj.mObj->setLinearVelocity(Vector2(0.0f, 0.0f));
+      }
+
+   }
+
+}
+
+void Path::integrateObject(const F32 totalTime, const F32 elapsedTime, DebugStats * pDebugStats)
+{
+   Parent::integrateObject(totalTime, elapsedTime, pDebugStats);
+}
+
+S32 Path::addNode(Vector2 pos, F32 distance, F32 weight)
+{
+   S32 nodeCount = mNodes.size();
+
+   mNodes.push_back(Node(pos, distance, weight));
+
+   return nodeCount;
+}
+
+void Path::attachObject(SceneObject * object, F32 speed, bool orientToPath, F32 angleOff, bool snapToNode, S32 startNode, bool loop, S32 maxLoop)
+{
+   if (snapToNode)
+   {
+      if ((startNode >= 0) && (startNode < mNodes.size()))
+         object->setPosition(mNodes[startNode].position);
+   }
+
+   object->setLinearVelocity(Vector2::getZero());
+
+   deleteNotify(object);
+
+   PathObject *pObj  = new PathObject();
+   pObj->mPath       = this;
+   pObj->mObj        = object;
+   pObj->mObjId      = object->getId();
+   pObj->mOrient     = orientToPath;
+   pObj->mAngOff     = angleOff;
+   pObj->mLoop       = loop;
+   pObj->mMaxLoop    = maxLoop;
+   pObj->mMaxSpeed   = speed;
+   pObj->mCurrNode   = startNode;
+   pObj->mNextNode   = startNode;
+   
+
+   mObjs.push_back(pObj);
+}
+
+void Path::detachObject(SceneObject * object)
+{
+   if (!object)
+      return;
+
+   Vector<PathObject*>::iterator i;
+
+   for (i = mObjs.begin(); i != mObjs.end(); i++)
+   {
+      PathObject *pObj = (*i);
+      if(object == pObj->mObj)
+      {
+         if (!pObj->mObj.isNull())
+         {
+            pObj->mObj->setLinearVelocity(Vector2(0, 0));
+            clearNotify(pObj->mObj);
+         }
+
+         delete pObj;
+
+         mObjs.erase_fast(i);
+         
+         break;
+      }
+   }
+}
+
+void Path::moveObject(PathObject& obj)
+{
+   Vector2 cDest = mNodes[obj.mNextNode].position;
+   Vector2 oPos = obj.mObj->getPosition();
+   Vector2 dir = cDest - oPos;
+   dir.Normalize();
+
+   obj.mObj->setLinearVelocity(dir * obj.mMaxSpeed);
+
+   if (obj.mOrient)
+   {
+      F32 rot = mRadToDeg(mAtan(dir.x, dir.y));
+      rot = rot - obj.mAngOff;
+      F32 ang = mDegToRad(rot);
+      F32 speed = obj.mMaxSpeed;
+      obj.mObj->rotateTo(ang, speed);
+   }
+
+}
+
+void Path::onDeleteNotify(SimObject* object)
+{
+   Vector<PathObject*>::iterator i;
+
+   SimObjectId objId = object->getId();
+
+   for (i = mObjs.begin(); i != mObjs.end(); i++)
+   {
+      if ((*i)->mObjId == objId)
+      {
+         delete (*i);
+
+         mObjs.erase_fast(i);
+
+         break;
+      }
+   }
+}

+ 108 - 0
engine/source/2d/sceneobject/Path.h

@@ -0,0 +1,108 @@
+#ifndef _PATH_H_
+#define _PATH_H_
+
+#include "2d/sceneobject/SceneObject.h"
+
+class Path;
+
+class PathObject
+{
+private:
+   friend class Path;
+   void setCurrNode(S32 node);
+   void setNextNode(S32 node);
+   void setPrevNode(S32 node);
+
+   Path* mPath;
+   SimObjectPtr<SceneObject> mObj;
+   SimObjectId mObjId;
+   F32 mMaxSpeed;
+   F32 mAngOff;
+   S32 mCurrNode;
+   S32 mPrevNode;
+   S32 mNextNode;
+   bool mLoop; 
+   bool mOrient;
+   bool mSnapToNode;
+   S32 mLoopCount;
+   S32 mMaxLoop;
+
+public:
+   PathObject();
+   ~PathObject() {};
+
+};
+
+class Path : public SceneObject
+{
+   typedef SceneObject Parent;
+
+public:
+   struct Node
+   {
+      Node(Vector2 pos, F32 dst, F32 wght)
+      {
+         position = pos;
+         distance = dst;
+         weight = wght;
+      };
+
+      Vector2 position;
+      F32 distance;
+      F32 weight;
+   };
+
+   Path();
+   ~Path();
+   virtual void onDeleteNotify(SimObject* object);
+   static void initPersistFields();
+
+   virtual void preIntegrate(const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats);
+   virtual void integrateObject(const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats);
+
+   S32 addNode(Vector2 pos, F32 distance, F32 weight);
+
+   S32 getNodeCount() const { return mNodes.size(); }
+
+   inline Node& getNode(S32 index)
+   {
+      if (isValidNode(index)) return mNodes[index];
+      return mNodes[0];
+   }
+
+   inline bool isValidNode(S32 index)
+   {
+      if (mNodes.empty()) return false;
+      if((index >= 0) && (index < mNodes.size())) return true;
+   }
+
+   void attachObject(SceneObject* object, F32 speed, bool orientToPath, F32 angleOff, bool snapToNode, S32 startNode, bool loop, S32 maxLoop);
+
+   void detachObject(SceneObject* object);
+
+   S32 getAttachedObjectCount() { return mObjs.size(); }
+
+   SceneObject* getPathObject(U32 index) { if (index < mObjs.size()) return mObjs[index]->mObj; return NULL; }
+   
+   inline PathObject* getAttachedObject(const SceneObject* obj)
+   {
+      if (obj == NULL)
+         return NULL;
+
+      Vector<PathObject*>::iterator i;
+      for (i = mObjs.begin(); i != mObjs.end(); i++)
+         if ((*i)->mObj == obj) return *i;
+   }
+
+   DECLARE_CONOBJECT(Path);
+
+private:
+
+   void moveObject(PathObject& obj);
+
+   Vector<PathObject*> mObjs;
+   Vector<Node> mNodes;
+
+};
+
+#endif

+ 121 - 0
engine/source/2d/sceneobject/Path_ScriptBinding.h

@@ -0,0 +1,121 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+ConsoleMethodWithDocs(Path, attachObject, ConsoleVoid, 4, 10, (sceneObject, float speed, [bool orient], [float angOff], [bool snapToNode],[integer startNode],[bool loop],[integer maxLoop]))
+{
+   // Set Group.
+   SceneObject* pSceneObject = dynamic_cast<SceneObject*>(Sim::findObject(argv[2]));
+   // Did we find the object?
+   if (!pSceneObject)
+   {
+      // No, so warn.
+      Con::warnf("Path::attachObject() - Could not find the specified object '%s'.", argv[2]);
+      return;
+   }
+
+   F32 speed = dAtof(argv[3]);
+
+   bool orient = false;
+   if (argc > 4)
+   {
+      orient = dAtob(argv[4]);
+   }
+
+   F32 angleOff = 0.0f;
+   if (argc > 5)
+   {
+      angleOff = dAtof(argv[5]);
+   }
+
+   bool snapToNode = false;
+   if (argc > 6)
+   {
+      snapToNode = dAtob(argv[6]);
+   }
+
+   S32 startNode = 0;
+   if (argc > 7)
+   {
+      startNode = dAtoi(argv[7]);
+   }
+
+   bool loop = true;
+   if (argc > 8)
+   {
+      loop = dAtob(argv[8]);
+   }
+
+   S32 maxLoop = 0;
+   if (argc > 9)
+   {
+      maxLoop = dAtoi(argv[9]);
+   }
+
+   object->attachObject(pSceneObject, speed, orient, angleOff, snapToNode, startNode, loop, maxLoop);
+
+}
+
+ConsoleMethodWithDocs(Path, detachObject, ConsoleVoid, 3, 3, (sceneObject))
+{
+   // Set Group.
+   SceneObject* pSceneObject = dynamic_cast<SceneObject*>(Sim::findObject(argv[2]));
+
+   if (pSceneObject)
+      object->detachObject(pSceneObject);
+   else
+      Con::warnf("Path::detachObject() - Could not find the specified object '%s'.", argv[2]);
+}
+
+ConsoleMethodWithDocs(Path, addNode, ConsoleVoid, 3, 6, (float x, float y, [float distance], [float weight]))
+{
+   Vector2 position;
+   // Elements in the first argument.
+   U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+
+   // ("x y")
+   if ((elementCount == 2) && (argc >= 3))
+      position = Utility::mGetStringElementVector(argv[2]);
+
+   // (x, y)
+   else if ((elementCount == 1) && (argc == 4))
+      position.Set(dAtof(argv[2]), dAtof(argv[3]));
+
+   // Invalid
+   else
+   {
+      Con::warnf("Path::addNode() - Invalid number of parameters!");
+      return;
+   }
+
+   F32 distance = 0.0f;
+   if (argc > 4)
+   {
+      distance = dAtof(argv[4]);
+   }
+
+   F32 weight = 0.0f;
+   if(argc > 5)
+   {
+      weight = dAtof(argv[5]);
+   }
+
+   object->addNode(position, distance, weight);
+}

+ 1 - 1
engine/source/2d/sceneobject/SceneObject.cc

@@ -581,7 +581,7 @@ void SceneObject::preIntegrate( const F32 totalTime, const F32 elapsedTime, Debu
    // Finish if nothing is dirty.
    // Finish if nothing is dirty.
     if ( !mSpatialDirty )
     if ( !mSpatialDirty )
         return;
         return;
-
+    
     // Reset spatial changed.
     // Reset spatial changed.
     mSpatialDirty = false;
     mSpatialDirty = false;
 
 

+ 0 - 10
engine/source/2d/sceneobject/ShadowMap.cc

@@ -220,16 +220,6 @@ void ShadowMap::OnUnregisterScene(Scene* mScene)
    Parent::OnUnregisterScene(mScene);
    Parent::OnUnregisterScene(mScene);
 }
 }
 
 
-void ShadowMap::processObject(SceneObject *obj)
-{
-
-}
-
-void ShadowMap::renderShadow(const Vector<RayList>& verts, const Vector2& lightPos)
-{
-
-}
-
 S32 QSORT_CALLBACK sortRays(const void* a, const void* b)
 S32 QSORT_CALLBACK sortRays(const void* a, const void* b)
 {
 {
    RayList* ray_a = (RayList*) a;
    RayList* ray_a = (RayList*) a;

+ 12 - 10
engine/source/2d/sceneobject/ShadowMap.h

@@ -14,12 +14,14 @@ public:
    F32 l;
    F32 l;
    F32 ang;
    F32 ang;
    bool operator == (const RayList& t) const { return ((mFabs(t.x - x) < 0.01) && (mFabs(t.y - y) < 0.01)); }
    bool operator == (const RayList& t) const { return ((mFabs(t.x - x) < 0.01) && (mFabs(t.y - y) < 0.01)); }
+
 };
 };
+
 class RaysCastCallback : public b2RayCastCallback
 class RaysCastCallback : public b2RayCastCallback
 {
 {
 public:
 public:
-   RaysCastCallback() : m_fixture(NULL) {
-   }
+
+   RaysCastCallback() : m_fixture(NULL) {}
 
 
    float32 ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction) {
    float32 ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction) {
       m_fixture = fixture;
       m_fixture = fixture;
@@ -42,10 +44,12 @@ class ShadowMap : public SceneObject
    typedef SceneObject Parent;
    typedef SceneObject Parent;
 
 
 protected:
 protected:
+
    F32                     mLightRadius;
    F32                     mLightRadius;
    U32                     mLightSegments;
    U32                     mLightSegments;
 
 
 public:
 public:
+
    ShadowMap();
    ShadowMap();
    ~ShadowMap();
    ~ShadowMap();
 
 
@@ -61,12 +65,9 @@ public:
    //virtual bool validRender(void) const {}
    //virtual bool validRender(void) const {}
    virtual bool shouldRender(void) const { return true; }
    virtual bool shouldRender(void) const { return true; }
 
 
-   void processObject(SceneObject *obj);
-   void renderShadow(const Vector<RayList>& verts, const Vector2& lightPos);
-
    /// Light segments.
    /// Light segments.
-   inline void             setLightSegments(const U32 lightSegments) { mLightSegments = lightSegments; };
-   inline U32              getLightSegments(void) const { return mLightSegments; }
+   inline void setLightSegments(const U32 lightSegments) { mLightSegments = lightSegments; };
+   inline U32 getLightSegments(void) const { return mLightSegments; }
 
 
    /// Light Radius.
    /// Light Radius.
    inline void setLightRadius(const F32 lightRadius) { mLightRadius = lightRadius; }
    inline void setLightRadius(const F32 lightRadius) { mLightRadius = lightRadius; }
@@ -76,10 +77,12 @@ public:
 
 
 
 
 protected:
 protected:
-      virtual void OnRegisterScene(Scene* mScene);
-      virtual void OnUnregisterScene(Scene* mScene);
+
+   virtual void OnRegisterScene(Scene* mScene);
+   virtual void OnUnregisterScene(Scene* mScene);
 
 
 protected:
 protected:
+
    static bool setLightRadius(void* obj, const char* data) { static_cast<ShadowMap*>(obj)->setLightRadius(dAtof(data)); return false; }
    static bool setLightRadius(void* obj, const char* data) { static_cast<ShadowMap*>(obj)->setLightRadius(dAtof(data)); return false; }
    static bool writeLightRadius(void* obj, StringTableEntry pFieldName) { return static_cast<ShadowMap*>(obj)->getLightRadius() > 0.0f; }
    static bool writeLightRadius(void* obj, StringTableEntry pFieldName) { return static_cast<ShadowMap*>(obj)->getLightRadius() > 0.0f; }
 
 
@@ -88,7 +91,6 @@ protected:
 
 
 };
 };
 
 
-
 #endif //_SHADOWMAP_H_
 #endif //_SHADOWMAP_H_
 
 
 S32 QSORT_CALLBACK sortRays(const void * a, const void * b);
 S32 QSORT_CALLBACK sortRays(const void * a, const void * b);

+ 70 - 0
modules/PathToy/1/main.cs

@@ -0,0 +1,70 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+function PathToy::create( %this )
+{        
+    // Reset the toy initially.
+    PathToy.reset();
+}
+
+//-----------------------------------------------------------------------------
+
+function PathToy::destroy( %this )
+{
+}
+
+//-----------------------------------------------------------------------------
+
+function PathToy::reset( %this )
+{
+	// Clear the scene.
+    SandboxScene.clear();
+	
+	%this.createPath();
+	
+	%this.createTarget();
+      
+}
+
+//-----------------------------------------------------------------------------
+
+function PathToy::createPath(%this)
+{
+	%path = new Path(SquarePath);
+	%path.addNode(10.0,10.0);
+	%path.addNode(-10.0,10.0);
+	%path.addNode(-10.0,-10.0);
+	%path.addNode(10.0,-10.0);
+	
+	SandboxScene.add(%path);
+}
+
+function PathToy::createTarget(%this)
+{
+	%object = new Sprite();
+	%object.Image = "ToyAssets:hollowArrow";
+    %object.Size = 5;
+    %object.setBodyType( dynamic );
+    SandboxScene.add( %object );
+	
+	SquarePath.attachObject(%object, 20.0, true, 90.0);
+}

+ 10 - 0
modules/PathToy/1/module.taml

@@ -0,0 +1,10 @@
+<ModuleDefinition
+	ModuleId="PathToy"
+	VersionId="1"
+	Description="Path object toy"
+	Dependencies="ToyAssets=1"
+	Type="toy"
+	ToyCategoryIndex="3"
+	ScriptFile="main.cs"
+	CreateFunction="create"
+	DestroyFunction="destroy"/>