Selection is performed by mouse dragging. So we have to record one point at the beginning of the drag, and another one at the end. While dragging, we'll indicate the selection area by drawing it on the terrain. When drag is complete, we'll put the selection into grid, from where the entities can see if they are selected.
We'll gave a grid that can hold it's "selected" status. A ready-made one and utilities is in Selector.h, so we'll put that into CMyGame. At near top of MyGame.h:
#include "../engine/Selector.h" // selection sector and utilities #include "../engine/Grid.h" // the grid
public: typedef CGrid<SSelectionSector,32,32> TSelectionGrid; public: TSelectionGrid mSelGrid; bool mDragging; // dragging at the moment bool mDragValid; // drag is still valid bool mSelectionHappened; // drag just ended D3DXVECTOR3 mDragPointBegin; D3DXVECTOR3 mDragPointEnd;
The entity itself is responsible for "selecting" - it consults a grid about selection, and acts accordingly. We'll make a IDecider for this - create file src/game/SelDecider.h and insert into game project:
#ifndef __SEL_DECIDER_H #define __SEL_DECIDER_H #include "Decider.h" #include "MyGame.h" class CSelectionDecider : public IDecider { public: virtual void decideFor( CMyEntity& e ) const { // get my sector float x = e.getPosition().x; float y = e.getPosition().y; const CMyGame::TSelectionGrid& grid = CMyGame::getInstance().mSelGrid; const CMyGame::TSelectionGrid::TSector& s = grid.getSectorByPosition( x, y ); // see if i'm selected if( s.isSelected( x, y ) ) { // change my color (alpha at maximum) e.setColor( 0xFF000000 | (e.getColor() + 0x0073A93B) ); } // decider gets called every update e.mDeciderCounter = 1; }; }; #endif
Everything goes into CMyGame here.
Put includes at near top of MyGame.cpp:
#include "../engine/TerrainPicker.h" // ray-terrain intersection utility #include "SelDecider.h" // our decider
// not dragging mDragging = false; mDragValid = false; // one shared decider IDecider* decider = new CSelectionDecider();
e.mDeciderCounter = rand()&15;
e.mDecider = NULL;
e.mDeciderCounter = 1;
e.mDecider = decider;
Again, everything is in CMyGame (MyGame.cpp). Put some additional logic in CMyGame::onUpdate() - insert at the beginning of the method:
// clear selection grid int n = mSelGrid.getSecsCount(); for( int i = 0; i < n; ++i ) { mSelGrid.getSectorByIndex( i ).beginFrame(); } // if we have just got a selection - put it into grid if( mSelectionHappened ) { CSelector<TSelectionGrid>::putSelection( mSelGrid, mDragPointBegin.x, mDragPointBegin.y, mDragPointEnd.x, mDragPointEnd.y ); mSelectionHappened = false; } // update entities - this one is already present
Now we'll respond to mouse clicks and movements. In CMyGame::onMouseLChange():
// respond to mouse left clicks D3DXVECTOR3 origin = getCamera().getPosition(); D3DXVECTOR3 dir = getCamera().getWorldRay( getMouse().getMouseX(), getMouse().getMouseY() ); if( pressed ) { // mouse pressed - begin drag mDragging = false; mDragValid = false; mSelectionHappened = false; CTerrainPick pick = CTerrainPicker::pick( getTerrain(), origin, dir ); if( pick.isValid() ) { mDragging = true; mDragValid = true; mDragPointBegin = mDragPointEnd = pick.getLocation(); } } else { // mouse released - end drag mDragging = false; mSelectionHappened = true; }
// mouse move for dragging if( mDragging ) { D3DXVECTOR3 origin = getCamera().getPosition(); D3DXVECTOR3 dir = getCamera().getWorldRay( getMouse().getMouseX(), getMouse().getMouseY() ); CTerrainPick pick = CTerrainPicker::pick( getTerrain(), origin, dir ); if( pick.isValid() ) { mDragValid = true; mDragPointEnd = pick.getLocation(); } }
Now there's one last thing left: while dragging, indicate the selection are by drawing it over the terrain. The terrain class has helper methods for drawing parts of terrain above itself, so in CMyGame::onRenderAfterAll():
// draw selection over terrain if( mDragging && mDragValid ) { getTerrain().prepareQuad( mDragPointBegin.x, mDragPointBegin.y, mDragPointEnd.x, mDragPointEnd.y, 4.0f ); getTerrain().renderQuad( 0x60FF0000 ); // slightly transparent, red color }
That's all. Selection should now work.
Prev: Complete game example.
Next: Details: Using the Grid.
1.2.17