Game Development Community

Get World Coordinates by Mouse

by Kiyaku · in Torque Game Engine · 07/25/2008 (5:15 am) · 6 replies

Hi,
i just have a small question.

When you use the World Editor, you can ask world position with your mouse to select objects, paint on Terrains, etc.
Is it possible to get the same mouse while playing? (not in the world editor).

I just want to be able to show the mouse cursor, click on a terrain and let an AIPlayer move to this position.

I saw the several "mouse movement", "object selection" etc articles but they are sometimes too old and i can't get them working.

Though i also can't understand why i have to change so much sourcecode, if there is already a mouse function like this in the world editor.

So does someone know if i can use this for my games too?

Thanks in advance!

#1
07/25/2008 (8:19 am)
EditTSCtrl has on3DMouse[etc] callbacks in C++ and I think some in script, but they do not pass the 3D position on the terrain clicked into script. You can figure it out with the 3D position of the camera, the forward vector of the camera, and a raycast against the terrain. That is essentially what EditTSCtrl is doing in C++ in "build3DMouseEvent" or some method like that.
#2
07/25/2008 (8:39 am)
If you want to do the AIPlayer moving to the point you clicked, then you are going to have to make C++ changes, but it's not too difficult. I can provide you with some code to get you started if you like...
#3
07/25/2008 (8:44 am)
That would be great!
#4
07/25/2008 (9:08 am)
OK, easiest thing is to extend the EditTSCtrl, so you'll need to add a class:

class MyTSControl : public EditTSCtrl
{
	private:
		typedef EditTSCtrl Parent;

	public:
		struct CollisionInfo
		{
			SceneObject *obj;
			Point3F           pos;
			VectorF           normal;
			Point3F           start;
		};

		SceneObject * getControlObject();
		bool collide(const Gui3DMouseEvent & event, CollisionInfo & info);

		MyTSControl ();
		~MyTSControl ();

		// SimObject
		bool onWake();
		void onSleep();

		// EditTSCtrl
		void on3DMouseMove(const Gui3DMouseEvent & event);
		void on3DMouseDown(const Gui3DMouseEvent & event);
		void on3DMouseUp(const Gui3DMouseEvent & event);
		void on3DMouseDragged(const Gui3DMouseEvent & event);
		void on3DMouseEnter(const Gui3DMouseEvent & event);
		void on3DMouseLeave(const Gui3DMouseEvent & event);
		void on3DRightMouseDown(const Gui3DMouseEvent & event);
		void on3DRightMouseUp(const Gui3DMouseEvent & event);

		void updateGuiInfo();

		//
		void onRender(Point2I offset, const RectI &updateRect);
		void renderScene(const RectI & updateRect);

		static void initPersistFields();

		DECLARE_CONOBJECT(MyTSControl );
};
#endif
#5
07/25/2008 (9:32 am)
Ok, relevent implementation. Obvious this is not a cut and paste implementation, but should give you all the important parts:
SceneObject * MyTSControl::getControlObject()
{
	GameConnection * connection = GameConnection::getLocalClientConnection();
   if(connection)
      return(dynamic_cast<SceneObject*>(connection->getControlObject()));
   return(0);
}
Note: This code is TGEA, so some calls may need to be changed to work in TGE, especially in regards to the above method. With a quick search, you should be able to find similar code and update to work in TGE.
bool MyTSControl::collide(const Gui3DMouseEvent & event, CollisionInfo & info)
{
   SceneObject * controlObj = getControlObject();
   if(controlObj)
      controlObj->disableCollision();

   Point3F startPnt = event.pos;
   Point3F endPnt   = event.pos + event.vec * 500.f;

   RayInfo ri;
   bool hit;
   hit = gServerContainer.castRay(startPnt, endPnt, TerrainObjectType | PlayerObjectType | StaticShapeObjectType, &ri);
   if(controlObj)
      controlObj->enableCollision();

   if(hit)
   {
		info.pos    = ri.point;
      info.obj    = ri.object;
      info.normal = ri.normal;
      AssertFatal(info.obj, "FF_GUI_BattleControl::collide - client container returned non SceneObject");
      AssertFatal(info.obj->isServerObject(), "Non server object!");
   }
   else
	{
		hit = false;
	}

   return(hit);
}

void MyTSControl ::on3DMouseDown(const Gui3DMouseEvent & event)
{
   CollisionInfo info;
   if(collide(event, info))
   {
      if(info.obj)
      {
			  AIPlayer *unit = dynamic_cast<AIPlayer*>(info.obj);;
				if(unit)
				{
					//we are over a unit, so make it selected, if you are expecting the user
					//to click on the unit before moving it
				}
				else
				{
					//We are over terrain, so move the unit you want moved
					//For example:
					//selected_unit->setMoveDestination(info.pos, true);
					//selected_unit->setMoveSpeed(1);
				}//end else over terrain
      }//end if obj
   }//end if collide
}

Ok, final thing is to update the PlayGUI... Change the first control in the playGui.gui file to be this new class:

%guiContent = new MyTSControl (PlayGui) {
   canSaveDynamicFields = "0";
   Enabled = "1";
   isContainer = "1";
   Profile = "GuiContentProfile";
   HorizSizing = "right";
   VertSizing = "bottom";
   Position = "0 0";
   Extent = "1024 768";
   MinExtent = "8 8";
   canSave = "1";
   Visible = "1";
   ...

That should be it... Once other thing you may need to account for, is objects in the PlayGui that will take your mouse input away from you. FirstResponders and such...

This is pretty bear bones I know, so just let me know if you have any troubles and I'll keep you pointed in the right direction.
#6
07/27/2008 (9:07 am)
This is very kind of you. My knowledge about the C++ part isn't that great yet but i will give it a try and see how far i come with your informations. Thanks a lot!