I have previously written an article about the collision detection tool class implementation in ogre. The console program that wrote the * Algorithm in the previous article. So this article is a combination of two. Here, we will use the * algorithm to automatically find the path after the robot in the map is indicated.
The basic functions are as follows: 1. Use the mouse to show the destination.
2. After receiving the command, the robot searches for the optimal path.
3. Robots need to bypass obstacles and switch their own directions along with the path.
Below are several images.
The path coordinates output by the console.
The following is an implementation:
First, write the framework. Use baseapplication for quick setup. I am using a map generated by my own map editor. You can build obstacles in ogre at will.
The required tool is the collision detection tool: collisiontools class, and the automatic path finding tool: astarpathfinder class.
The following functions are required in the class that inherits baseapplicaiton:
View plaincopy to clipboardprint?
Void createrobot ();
Void robotidle (float dtime );
Void robotmove (float dtime );
Void rotatebody (Ogre: vector3 DIR );
Void setpath ();
Void createrobot ();
Void robotidle (float dtime );
Void robotmove (float dtime );
Void rotatebody (Ogre: vector3 DIR );
Void setpath ();
Variable:
View plaincopy to clipboardprint?
Ogre: rayscenequery * mscenequery;
Collisiontools * mcollisiontools;
Astarpathfinder * mpathfinder;
Ogre: scenenode * mrobotnode;
Ogre: entity * mrobotent;
Ogre: vector3 mnextposisition;
Ogre: vector3 mdirection;
Bool mmovenext;
Float mmovespeed;
Float mdistance;
Ogre: vector3 mstart;
Ogre: vector3 mdest;
STD: deque <ogre: vector3> mpathdeque;
Ogre: rayscenequery * mscenequery;
Collisiontools * mcollisiontools;
Astarpathfinder * mpathfinder;
Ogre: scenenode * mrobotnode;
Ogre: entity * mrobotent;
Ogre: vector3 mnextposisition;
Ogre: vector3 mdirection;
Bool mmovenext;
Float mmovespeed;
Float mdistance;
Ogre: vector3 mstart;
Ogre: vector3 mdest;
STD: deque <ogre: vector3> mpathdeque;
Create our robot and collision detection tools and path search tools in createscene, and set the robot as a non-collision query so that the first Ray will not touch the robot itself. Other objects in the scenario need to be set as collision (including the ground, and the target point needs to be clicked for Ray query)
View plaincopy to clipboardprint?
Void gameapp: createscene ()
{
Mcamera-> setposition (193,268,222 );
Mcamera-> lookat (0, 0, 0 );
Mscenemgr-> setambientlight (colourvalue: Black );
Mscenemgr-> setshadowtechnique (shadowtype_texture_modulative );
Maploader ("./MAP/map_maze.map", objectfactory: getsingletonptr ());
Maploader. loadmap ();
Createlight ();
Creategui ();
Mscenequery = mscenemgr-> createrayquery (Ray ());
Mcollisiontools = new collisiontools (mscenequery );
Mpathfinder = new astarpathfinder;
Createrobot ();
}
Void gameapp: createframelistener ()
{
Baseapplication: createframelistener ();
}
Void gameapp: createrobot ()
{
Mrobotent = mscenemgr-> createentity ("robotai", "robot. mesh ");
Mrobotnode = mscenemgr-> getrootscenenode ()-> createchildscenenode ("robotainode ");
Mrobotnode-> attachobject (mrobotent );
Mrobotnode-> setscale (0.3, 0.3, 0.3 );
Mrobotent-> setqueryflags (noncollision );
}
Void gameapp: createscene ()
{
Mcamera-> setposition (193,268,222 );
Mcamera-> lookat (0, 0, 0 );
Mscenemgr-> setambientlight (colourvalue: Black );
Mscenemgr-> setshadowtechnique (shadowtype_texture_modulative );
Maploader ("./MAP/map_maze.map", objectfactory: getsingletonptr ());
Maploader. loadmap ();
Createlight ();
Creategui ();
Mscenequery = mscenemgr-> createrayquery (Ray ());
Mcollisiontools = new collisiontools (mscenequery );
Mpathfinder = new astarpathfinder;
Createrobot ();
}
Void gameapp: createframelistener ()
{
Baseapplication: createframelistener ();
}
Void gameapp: createrobot ()
{
Mrobotent = mscenemgr-> createentity ("robotai", "robot. mesh ");
Mrobotnode = mscenemgr-> getrootscenenode ()-> createchildscenenode ("robotainode ");
Mrobotnode-> attachobject (mrobotent );
Mrobotnode-> setscale (0.3, 0.3, 0.3 );
Mrobotent-> setqueryflags (noncollision );
}
The mask is as follows:
View plaincopy to clipboardprint?
Enum collision_mask
{
Collision = 1 <0,
Noncollision = 1 <1
};
Enum collision_mask
{
Collision = 1 <0,
Noncollision = 1 <1
};
Then write the following code at the place where the mouse clicks.
View plaincopy to clipboardprint?
Bool gameapp: mousepressed (const ois: mouseevent & Arg, ois: mousebuttonid ID)
{
If (ID = ois: mb_left)
{
Cegui: Point mousepos = cegui: mousecursor: getsingleton (). getposition ();
Ray mouseray = mcamera-> getcameratoviewportray (mousepos. D_x/float (Arg. state. width ),
Mousepos. d_y/float (Arg. state. Height ));
Mscenequery-> setray (mouseray );
Rayscenequeryresult & Result = mscenequery-> execute ();
Rayscenequeryresult: iterator itr = result. Begin ();
If (itr! = Result. End () & itr-> movable)
{
Mnextposisition = mouseray. getpoint (itr-> distance );
// Convert coordinates into Integers
Mdest. x = STD: floor (mnextposisition. x + 0.5 );
Mdest. Y = 0;
Mdest. z = STD: floor (mnextposisition. Z + 0.5 );
Mstart. x = STD: floor (mrobotnode-> getposition (). x + 0.5 );
Mstart. Y = 0;
Mstart. z = STD: floor (mrobotnode-> getposition (). Z + 0.5 );
Mpathfinder-> clear ();
Mpathfinder-> findpath (mstart. X, mstart. Z, mdest. X, mdest. z );
Setpath ();
}
Mmovenext = true;
}
Return true;
}
Bool gameapp: mousepressed (const ois: mouseevent & Arg, ois: mousebuttonid ID)
{
If (ID = ois: mb_left)
{
Cegui: Point mousepos = cegui: mousecursor: getsingleton (). getposition ();
Ray mouseray = mcamera-> getcameratoviewportray (mousepos. D_x/float (Arg. state. width ),
Mousepos. d_y/float (Arg. state. Height ));
Mscenequery-> setray (mouseray );
Rayscenequeryresult & Result = mscenequery-> execute ();
Rayscenequeryresult: iterator itr = result. Begin ();
If (itr! = Result. End () & itr-> movable)
{
Mnextposisition = mouseray. getpoint (itr-> distance );
// Convert coordinates into Integers
Mdest. x = STD: floor (mnextposisition. x + 0.5 );
Mdest. Y = 0;
Mdest. z = STD: floor (mnextposisition. Z + 0.5 );
Mstart. x = STD: floor (mrobotnode-> getposition (). x + 0.5 );
Mstart. Y = 0;
Mstart. z = STD: floor (mrobotnode-> getposition (). Z + 0.5 );
Mpathfinder-> clear ();
Mpathfinder-> findpath (mstart. X, mstart. Z, mdest. X, mdest. z );
Setpath ();
}
Mmovenext = true;
}
Return true;
}
First, obtain the point of intersection with the ground after the mouse clicks. The query does not need to be sorted, because the last queried object --> the ground is required. Save the target point position to mdest. Here, we need to approximate the coordinates to Integers and use the floor function of library C. The objective of approximation is to make path search easy to implement. (The floating point operation is slow and difficult to determine the target point) click the mouse to obtain the path of the robot and complete it in setpath.
The setpath code is as follows:
View plaincopy to clipboardprint?
Void gameapp: setpath ()
{
Mpathdeque. Clear ();
STD: vector <point> Path = mpathfinder-> getpath ();
If (! Path. Empty ())
{
For (STD: vector <point >:: reverse_iterator itr = path. rbegin (); itr! = Path. rend (); ++ itr)
{
Mpathdeque. push_front (Ogre: vector3 (itr-> X, 0, itr-> Z ));
}
Mpathfinder-> clear ();
}
}
Void gameapp: setpath ()
{
Mpathdeque. Clear ();
STD: vector <point> Path = mpathfinder-> getpath ();
If (! Path. Empty ())
{
For (STD: vector <point >:: reverse_iterator itr = path. rbegin (); itr! = Path. rend (); ++ itr)
{
Mpathdeque. push_front (Ogre: vector3 (itr-> X, 0, itr-> Z ));
}
Mpathfinder-> clear ();
}
}
The code above mainly transfers the data of the vector storing the path to a deque structure. You can directly Save the path to deque in the path search tool. After each path is set, it needs to be cleared for the next search path. Clear is used to clear open, closed, and path.
Robot Movement is mainly completed in robotmove. The Code is as follows:
View plaincopy to clipboardprint?
Void gameapp: robotmove (float dtime)
{
Ogre: animationstate * anim = mrobotent-> getanimationstate ("walk ");
Anim-> setloop (true );
Anim-> setenabled (true );
Anim-> addtime (dtime );
If (! Mpathdeque. Empty ())
{
STD: cout <mpathdeque. back (). x <"," <mpathdeque. back (). Y <"," <mpathdeque. back (). z <STD: Endl;
Ogre: vector3 dir = mpathdeque. Back ()-mrobotnode-> getposition ();
Rotatebody (DIR );
Mrobotnode-> translate (dir * dtime * mmovespeed );
If (dir. normalise () <0.1f ))
{
Mrobotnode-> setposition (mpathdeque. Back ());
Mpathdeque. pop_back ();
}
}
Else
{
Mmovenext = false;
}
}
Void gameapp: rotatebody (Ogre: vector3 DIR)
{
Ogre: vector3 src = mrobotnode-> getorientation () * ogre: vector3: unit_x;
If (1.0f + SRC. dotproduct (mdirection) <0.0001f)
{
Mrobotnode-> yaw (Ogre: Degree (180 ));
}
Else
{
Ogre: quaternion qua = SRC. getrotationto (DIR );
Mrobotnode-> rotate (qua );
}
}
Void gameapp: robotmove (float dtime)
{
Ogre: animationstate * anim = mrobotent-> getanimationstate ("walk ");
Anim-> setloop (true );
Anim-> setenabled (true );
Anim-> addtime (dtime );
If (! Mpathdeque. Empty ())
{
STD: cout <mpathdeque. back (). x <"," <mpathdeque. back (). Y <"," <mpathdeque. back (). z <STD: Endl;
Ogre: vector3 dir = mpathdeque. Back ()-mrobotnode-> getposition ();
Rotatebody (DIR );
Mrobotnode-> translate (dir * dtime * mmovespeed );
If (dir. normalise () <0.1f ))
{
Mrobotnode-> setposition (mpathdeque. Back ());
Mpathdeque. pop_back ();
}
}
Else
{
Mmovenext = false;
}
}
Void gameapp: rotatebody (Ogre: vector3 DIR)
{
Ogre: vector3 src = mrobotnode-> getorientation () * ogre: vector3: unit_x;
If (1.0f + SRC. dotproduct (mdirection) <0.0001f)
{
Mrobotnode-> yaw (Ogre: Degree (180 ));
}
Else
{
Ogre: quaternion qua = SRC. getrotationto (DIR );
Mrobotnode-> rotate (qua );
}
}
The code above first checks whether the path queue is empty. If it is not empty, the robot is allowed to walk every point in the queue. The vertex is removed from the queue after it is finished. A distance value is used to determine whether the robot has reached the target point.
Then, add the collision detection judgment in the path search tool:
View plaincopy to clipboardprint?
Void astarpathfinder: updatenode (pathnode * bestnode, int Sx, int SZ, int dx, int Dz)
{
Pathnode * child = new pathnode;
Child-> H = (DX-SX) * (DX-SX) + (Dz-sz) * (Dz-sz );
Child-> G = bestnode-> G + tilesize;
Child-> F = Child-> G + child-> h;
Child-> point. x = SX; child-> point. z = SZ;
// Skip nodes in the closed table and nodes that cannot pass through
//////////////////////////////////////// //////////////////////////////////
Ogre: vector3 dir = ogre: vector3 (sx, 0, SZ)-Ogre: vector3 (bestnode-> point. X, 0, bestnode-> point. z );
If (findnodeinclosed (child) |
Gameapp: getsingleton (). getcollisiontools ()-> collisionwithmovable (Ogre: vector3 (bestnode-> point. X, 5, bestnode-> point. Z), DIR ))
Return;
//////////////////////////////////////// //////////////////////////////////
Else if (! Findnodeinclosed (child )&&! Findnodeinopen (child ))
{
// If it is not in the open and closed tables, set the parent node of this node as the current node.
Child-> parent = bestnode;
// Add the node to the Open Table
Open. insert (child );
}
Else if (findnodeinopen (child ))
{
// In the Open table, compare the G value + tilesize of the current node with the original G value of the current node. If the new G value is small, update
// G value, and set the parent node of this node to the current node
If (bestnode-> G + tilesize) <child-> G)
{
Child-> G = bestnode-> G + tilesize;
Child-> parent = bestnode;
}
}
}
Void astarpathfinder: updatenode (pathnode * bestnode, int Sx, int SZ, int dx, int Dz)
{
Pathnode * child = new pathnode;
Child-> H = (DX-SX) * (DX-SX) + (Dz-sz) * (Dz-sz );
Child-> G = bestnode-> G + tilesize;
Child-> F = Child-> G + child-> h;
Child-> point. x = SX; child-> point. z = SZ;
// Skip nodes in the closed table and nodes that cannot pass through
//////////////////////////////////////// //////////////////////////////////
Ogre: vector3 dir = ogre: vector3 (sx, 0, SZ)-Ogre: vector3 (bestnode-> point. X, 0, bestnode-> point. z );
If (findnodeinclosed (child) |
Gameapp: getsingleton (). getcollisiontools ()-> collisionwithmovable (Ogre: vector3 (bestnode-> point. X, 5, bestnode-> point. Z), DIR ))
Return;
//////////////////////////////////////// //////////////////////////////////
Else if (! Findnodeinclosed (child )&&! Findnodeinopen (child ))
{
// If it is not in the open and closed tables, set the parent node of this node as the current node.
Child-> parent = bestnode;
// Add the node to the Open Table
Open. insert (child );
}
Else if (findnodeinopen (child ))
{
// In the Open table, compare the G value + tilesize of the current node with the original G value of the current node. If the new G value is small, update
// G value, and set the parent node of this node to the current node
If (bestnode-> G + tilesize) <child-> G)
{
Child-> G = bestnode-> G + tilesize;
Child-> parent = bestnode;
}
}
}
In this case, OK. The demo of searching for the Optimal Path is complete. The optimization problem mentioned in the previous article is not solved in this article. For example, the G value of the diagonal line is actually greater than the G value of the linear neighbor, and cannot be searched on the Y axis.
I hope this article will help you.
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/pizzazhang/archive/2011/03/20/6262265.aspx