فهرست منبع

Adds Gamepad class to gameplay.
Stores Gamepad objects in Game.
Adds Gamepad* createGamepad(const char* gamepadFormPath) to Game.
Adds Gamepad* getGamepad(unsigned int playerIndex=0) to Game.
Adds void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int index) event callback to Game.
Adds const char* getType() on Control and has each child of Control implement the function, returning the name of the class as a string in camel-casing.
Adds concept of contact index at the Control level. This allows for more than one Control to be active/in focus at once. Had to refactor some Control's touchEvents to handle this. Setting the contactIndex on a TOUCH_PRESS event, and then resetting it to INVALID_CONTACT_INDEX on TOUCH_RELEASE.
Fixes memory leak in FrameBuffer.
Code cleanup, removes some warnings.

Kieran Cunney 13 سال پیش
والد
کامیت
e137e948bb

+ 2 - 4
gameplay/gameplay.vcxproj

@@ -35,7 +35,6 @@
     <ClCompile Include="src\Curve.cpp" />
     <ClCompile Include="src\DebugNew.cpp" />
     <ClCompile Include="src\DepthStencilTarget.cpp" />
-    <ClCompile Include="src\DropDownList.cpp" />
     <ClCompile Include="src\Effect.cpp" />
     <ClCompile Include="src\FileSystem.cpp" />
     <ClCompile Include="src\FlowLayout.cpp" />
@@ -44,6 +43,7 @@
     <ClCompile Include="src\FrameBuffer.cpp" />
     <ClCompile Include="src\Frustum.cpp" />
     <ClCompile Include="src\Game.cpp" />
+    <ClCompile Include="src\Gamepad.cpp" />
     <ClCompile Include="src\gameplay-main-android.cpp" />
     <ClCompile Include="src\gameplay-main-qnx.cpp" />
     <ClCompile Include="src\gameplay-main-win32.cpp" />
@@ -53,7 +53,6 @@
     <ClCompile Include="src\Label.cpp" />
     <ClCompile Include="src\Layout.cpp" />
     <ClCompile Include="src\Light.cpp" />
-    <ClCompile Include="src\List.cpp" />
     <ClCompile Include="src\Material.cpp" />
     <ClCompile Include="src\MeshBatch.cpp" />
     <ClCompile Include="src\Pass.cpp" />
@@ -129,7 +128,6 @@
     <ClInclude Include="src\Curve.h" />
     <ClInclude Include="src\DebugNew.h" />
     <ClInclude Include="src\DepthStencilTarget.h" />
-    <ClInclude Include="src\DropDownList.h" />
     <ClInclude Include="src\Effect.h" />
     <ClInclude Include="src\FileSystem.h" />
     <ClInclude Include="src\FlowLayout.h" />
@@ -138,6 +136,7 @@
     <ClInclude Include="src\FrameBuffer.h" />
     <ClInclude Include="src\Frustum.h" />
     <ClInclude Include="src\Game.h" />
+    <ClInclude Include="src\Gamepad.h" />
     <ClInclude Include="src\gameplay.h" />
     <ClInclude Include="src\Image.h" />
     <ClInclude Include="src\Joint.h" />
@@ -146,7 +145,6 @@
     <ClInclude Include="src\Label.h" />
     <ClInclude Include="src\Layout.h" />
     <ClInclude Include="src\Light.h" />
-    <ClInclude Include="src\List.h" />
     <ClInclude Include="src\Material.h" />
     <ClInclude Include="src\MathUtil.h" />
     <ClInclude Include="src\MeshBatch.h" />

+ 2 - 8
gameplay/gameplay.vcxproj.filters

@@ -279,10 +279,7 @@
     <ClCompile Include="src\Joystick.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\DropDownList.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
-    <ClCompile Include="src\List.cpp">
+    <ClCompile Include="src\Gamepad.cpp">
       <Filter>src</Filter>
     </ClCompile>
   </ItemGroup>
@@ -563,10 +560,7 @@
     <ClInclude Include="src\MathUtil.h">
       <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\DropDownList.h">
-      <Filter>src</Filter>
-    </ClInclude>
-    <ClInclude Include="src\List.h">
+    <ClInclude Include="src\Gamepad.h">
       <Filter>src</Filter>
     </ClInclude>
   </ItemGroup>

+ 16 - 3
gameplay/src/Button.cpp

@@ -5,11 +5,14 @@ namespace gameplay
 {
 
 Button::Button()
+    : _gamepadButtonIndex(NULL)
 {
 }
 
 Button::~Button()
 {
+    if (_gamepadButtonIndex)
+        SAFE_DELETE(_gamepadButtonIndex);
 }
 
 Button* Button::create(const char* id, Theme::Style* style)
@@ -33,9 +36,7 @@ Button* Button::create(Theme::Style* style, Properties* properties)
 bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!isEnabled())
-    {
         return false;
-    }
 
     switch (evt)
     {
@@ -43,8 +44,12 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
             y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
+            _contactIndex = (int) contactIndex;
+
             setState(Control::ACTIVE);
+
             notifyListeners(Listener::PRESS);
+
             return _consumeInputEvents;
         }
         else
@@ -54,12 +59,15 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         break;
 
     case Touch::TOUCH_RELEASE:
+        _contactIndex = INVALID_CONTACT_INDEX;
         notifyListeners(Listener::RELEASE);
         if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
             y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
             setState(Control::FOCUS);
+
             notifyListeners(Listener::CLICK);
+
             return _consumeInputEvents;
         }
         else
@@ -75,4 +83,9 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return false;
 }
 
-}
+const char* Button::getType() const
+{
+    return "button";
+}
+
+}

+ 9 - 1
gameplay/src/Button.h

@@ -32,6 +32,7 @@ namespace gameplay
 class Button : public Label
 {
     friend class Container;
+    friend class Gamepad;
 
 public:
 
@@ -81,14 +82,21 @@ protected:
      */
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
 private:
 
     /**
      * Constructor.
      */
     Button(const Button& copy);
+
+    int* _gamepadButtonIndex;
 };
 
 }
 
-#endif
+#endif

+ 5 - 0
gameplay/src/CheckBox.cpp

@@ -171,4 +171,9 @@ void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     spriteBatch->draw(pos.x, pos.y, size.x, size.y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _viewportClipBounds);
 }
 
+const char* CheckBox::getType() const
+{
+    return "checkBox";
+}
+
 }

+ 6 - 1
gameplay/src/CheckBox.h

@@ -76,6 +76,11 @@ public:
      */
     const Vector2& getImageSize() const;
 
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
     /**
      * Add a listener to be notified of specific events affecting
      * this control.  Event types can be OR'ed together.
@@ -166,4 +171,4 @@ private:
 
 }
 
-#endif
+#endif

+ 8 - 8
gameplay/src/Container.cpp

@@ -11,7 +11,6 @@
 #include "Slider.h"
 #include "TextBox.h"
 #include "Joystick.h"
-#include "List.h"
 #include "Game.h"
 
 namespace gameplay
@@ -158,10 +157,6 @@ void Container::addControls(Theme* theme, Properties* properties)
         {
             control = Joystick::create(controlStyle, controlSpace);
         }
-        else if (controlName == "LIST")
-        {
-            control = List::create(controlStyle, controlSpace);
-        }
         else
         {
             GP_ERROR("Failed to create control; unrecognized control name '%s'.", controlName.c_str());
@@ -338,6 +333,11 @@ Animation* Container::getAnimation(const char* id) const
     return NULL;
 }
 
+const char* Container::getType() const
+{
+    return "container";
+}
+
 void Container::update(const Control* container, const Vector2& offset)
 {
     // Update this container's viewport.
@@ -578,7 +578,7 @@ bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
     return false;
 }
 
-bool Container::isContainer()
+bool Container::isContainer() const
 {
     return true;
 }
@@ -975,7 +975,7 @@ bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
 
         Control::State currentState = control->getState();
         if ((control->isContainer() && currentState == Control::FOCUS) ||
-            currentState != Control::NORMAL ||
+            (currentState != Control::NORMAL && control->_contactIndex == data) ||
             ((evt == Touch::TOUCH_PRESS ||
               evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
               evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
@@ -1001,7 +1001,7 @@ bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
 
     if (!eventConsumed && _scroll != SCROLL_NONE)
     {
-        if (mouse && mouseEventScroll((Mouse::MouseEvent)evt, x - xPos, y - yPos, data) ||
+        if ((mouse && mouseEventScroll((Mouse::MouseEvent)evt, x - xPos, y - yPos, data)) ||
             (!mouse && touchEventScroll((Touch::TouchEvent)evt, x - xPos, y - yPos, (unsigned int)data)))
         {
             _dirty = true;

+ 7 - 2
gameplay/src/Container.h

@@ -177,6 +177,11 @@ public:
      */
     Animation* getAnimation(const char* id = NULL) const;
 
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
     /**
      * @see AnimationTarget#getAnimationPropertyComponentCount
      */
@@ -284,10 +289,10 @@ protected:
 
     /**
      * Returns whether this control is a container.
-     * 
+     *
      * @return true if this is a container, false if not.
      */
-    bool isContainer();
+    bool isContainer() const;
 
     /**
      * Returns whether this container or any of its controls have been modified and require an update.

+ 18 - 7
gameplay/src/Control.cpp

@@ -6,8 +6,9 @@ namespace gameplay
 {
 
 Control::Control()
-    : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
-    _dirty(true), _consumeInputEvents(true), _listeners(NULL), _styleOverridden(false), _skin(NULL), _clearBounds(Rectangle::empty())
+    : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
+    _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _listeners(NULL), _contactIndex(INVALID_CONTACT_INDEX),
+    _styleOverridden(false), _skin(NULL)
 {
 }
 
@@ -706,9 +707,7 @@ void Control::addSpecificListener(Control::Listener* listener, Listener::EventTy
 bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!isEnabled())
-    {
         return false;
-    }
 
     switch (evt)
     {
@@ -719,17 +718,24 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
             y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
+            _contactIndex = (int) contactIndex;
+
             notifyListeners(Listener::PRESS);
+
             return _consumeInputEvents;
         }
         else
         {
             // If this control was in focus, it's not any more.
             _state = NORMAL;
+            _contactIndex = INVALID_CONTACT_INDEX;
         }
         break;
             
     case Touch::TOUCH_RELEASE:
+
+        _contactIndex = INVALID_CONTACT_INDEX;
+
         // Always trigger Listener::RELEASE
         notifyListeners(Listener::RELEASE);
 
@@ -738,8 +744,9 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
             // Leave this control in the FOCUS state.
-            notifyListeners(Listener::CLICK);
+            notifyListeners(Listener::CLICK);
         }
+
         return _consumeInputEvents;
     }
 
@@ -991,7 +998,7 @@ bool Control::isDirty()
     return _dirty;
 }
 
-bool Control::isContainer()
+bool Control::isContainer() const
 {
     return false;
 }
@@ -1035,6 +1042,11 @@ Theme::ThemeImage* Control::getImage(const char* id, State state)
     return imageList->getImage(id);
 }
 
+const char* Control::getType() const
+{
+    return "control";
+}
+
 // Implementation of AnimationHandler
 unsigned int Control::getAnimationPropertyComponentCount(int propertyId) const
 {
@@ -1122,7 +1134,6 @@ void Control::setAnimationPropertyValue(int propertyId, AnimationValue* value, f
         break;
     case ANIMATE_OPACITY:
         _dirty = true;
-    default:
         break;
     }
 }

+ 19 - 1
gameplay/src/Control.h

@@ -25,6 +25,7 @@ class Control : public Ref, public AnimationTarget
     friend class AbsoluteLayout;
     friend class VerticalLayout;
     friend class FlowLayout;
+    friend class Gamepad;
 
 public:
 
@@ -709,6 +710,13 @@ public:
      */
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
+    /**
+     * Gets the type of the Control and returns it as a string.
+     *
+     * @return The string of the Control type, all in lower-case.
+     */
+    virtual const char* getType() const;
+
     /**
      * @see AnimationTarget#getAnimationPropertyComponentCount
      */
@@ -726,6 +734,11 @@ public:
 
 protected:
 
+    /**
+     *  Constant value representing an unset or invalid contact index.
+     */
+    static const int INVALID_CONTACT_INDEX = -1;
+
     /**
      * Constructor.
      */
@@ -843,7 +856,7 @@ protected:
      *
      * @return true if this object is of class Container, false otherwise.
      */
-    virtual bool isContainer();
+    virtual bool isContainer() const;
 
     /**
      * Returns whether this control has been modified and requires an update.
@@ -976,6 +989,11 @@ protected:
      */
     int _zIndex;
 
+    /**
+     * The contact index assigned to this control.
+     */
+    int _contactIndex;
+
     /**
      * The focus order of the control.
      */

+ 6 - 0
gameplay/src/Form.cpp

@@ -57,6 +57,7 @@ Form* Form::create(const char* id, Theme::Style* style, Layout::Type layoutType)
         break;
     default:
         GP_ERROR("Unsupported layout type '%d'.", layoutType);
+        break;
     }
 
     Form* form = new Form();
@@ -521,6 +522,11 @@ void Form::draw()
     }
 }
 
+const char* Form::getType() const
+{
+    return "form";
+}
+
 void Form::initializeQuad(Mesh* mesh)
 {
     // Release current model.

+ 6 - 1
gameplay/src/Form.h

@@ -156,6 +156,11 @@ public:
      */
     void draw();
 
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
 private:
     
     /**
@@ -237,4 +242,4 @@ private:
 
 }
 
-#endif
+#endif

+ 1 - 0
gameplay/src/FrameBuffer.cpp

@@ -86,6 +86,7 @@ FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned in
     frameBuffer->_renderTargets = renderTargets;
 
     frameBuffer->setRenderTarget(renderTarget, 0);
+    SAFE_RELEASE(renderTarget);
 
     __frameBuffers.push_back(frameBuffer);
 

+ 76 - 2
gameplay/src/Game.cpp

@@ -20,7 +20,8 @@ Game::Game()
     : _initialized(false), _state(UNINITIALIZED), 
       _frameLastFPS(0), _frameCount(0), _frameRate(0), 
       _clearDepth(1.0f), _clearStencil(0), _properties(NULL),
-      _animationController(NULL), _audioController(NULL), _physicsController(NULL), _audioListener(NULL)
+      _animationController(NULL), _audioController(NULL), _physicsController(NULL), _audioListener(NULL), 
+      _gamepadCount(0), _gamepads(NULL)
 {
     GP_ASSERT(__gameInstance == NULL);
     __gameInstance = this;
@@ -95,7 +96,7 @@ bool Game::startup()
     setViewport(Rectangle(0.0f, 0.0f, (float)_width, (float)_height));
     RenderState::initialize();
     FrameBuffer::initialize();
-
+    
     _animationController = new AnimationController();
     _animationController->initialize();
 
@@ -105,6 +106,8 @@ bool Game::startup()
     _physicsController = new PhysicsController();
     _physicsController->initialize();
 
+    loadGamepad();
+    
     _state = RUNNING;
 
     return true;
@@ -122,6 +125,15 @@ void Game::shutdown()
         Platform::signalShutdown();
         finalize();
 
+        if (_gamepads)
+        {
+            for (unsigned int i = 0; i < _gamepadCount; i++)
+            {
+                SAFE_DELETE(_gamepads[i]);
+            }
+            SAFE_DELETE_ARRAY(_gamepads);
+        }
+
         _animationController->finalize();
         SAFE_DELETE(_animationController);
 
@@ -207,6 +219,12 @@ void Game::frame()
         // Update the physics.
         _physicsController->update(elapsedTime);
 
+        if (_gamepads)
+        {
+            for (unsigned int i = 0; i < _gamepadCount; i++)
+                _gamepads[i]->update();
+        }
+
         // Application Update.
         update(elapsedTime);
 
@@ -215,6 +233,12 @@ void Game::frame()
 
         // Graphics Rendering.
         render(elapsedTime);
+        
+        if (_gamepads)
+        {
+            for (unsigned int i = 0; i < _gamepadCount; i++)
+                _gamepads[i]->render();
+        }
 
         // Update FPS.
         ++_frameCount;
@@ -305,6 +329,10 @@ void Game::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactI
 {
 }
 
+void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int index)
+{
+}
+
 void Game::schedule(float timeOffset, TimeListener* timeListener, void* cookie)
 {
     GP_ASSERT(_timeEvents);
@@ -386,4 +414,50 @@ bool Game::TimeEvent::operator<(const TimeEvent& v) const
     return time > v.time;
 }
 
+Gamepad* Game::createGamepad(const char* gamepadFormPath)
+{
+    GP_ASSERT(gamepadFormPath);
+
+    Gamepad* gamepad = new Gamepad(gamepadFormPath);
+    GP_ASSERT(gamepad);
+
+    if (!_gamepads)
+    {
+        _gamepadCount++;
+        _gamepads = new Gamepad*[_gamepadCount];
+        _gamepads[0] = gamepad;
+    }
+    else
+    {
+        int oldSize = _gamepadCount;
+        _gamepadCount++;
+        Gamepad** tempGamepads = new Gamepad*[_gamepadCount];
+        memcpy(tempGamepads, _gamepads, sizeof(Gamepad*) * oldSize);
+        tempGamepads[oldSize] = gamepad;
+        
+        SAFE_DELETE_ARRAY(_gamepads);
+        _gamepads = tempGamepads;
+    }
+
+    return gamepad;
+}
+
+void Game::loadGamepad()
+{
+    if (_properties)
+    {
+        // Check if there is a virtual keyboard included in the .config file.
+        // If there is, try to create it and assign it to "player one".
+        Properties* gamepadProperties = _properties->getNamespace("gamepad", true);
+        if (gamepadProperties && gamepadProperties->exists("form"))
+        {
+            const char* gamepadFormPath = gamepadProperties->getString("form");
+            GP_ASSERT(gamepadFormPath);
+
+            Gamepad* gamepad = createGamepad(gamepadFormPath);
+            GP_ASSERT(gamepad);
+        }
+    }
+}
+
 }

+ 33 - 0
gameplay/src/Game.h

@@ -13,6 +13,7 @@
 #include "Rectangle.h"
 #include "Vector4.h"
 #include "TimeListener.h"
+#include "Gamepad.h"
 
 namespace gameplay
 {
@@ -22,6 +23,7 @@ namespace gameplay
  */
 class Game
 {
+
 public:
 
     /**
@@ -267,6 +269,15 @@ public:
      */
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @param evt The gamepad event that occured.
+     * @param gamepad the gamepad the event occured on
+     * @param index The joystick or button index that triggered the event.
+     */
+    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int index);
+
     /**
      * Sets muli-touch is to be enabled/disabled. Default is disabled.
      *
@@ -299,6 +310,21 @@ public:
      */
     void schedule(float timeOffset, TimeListener* timeListener, void* cookie = 0);
 
+    /** 
+     * Creates a Gamepad object from a .form file.
+     *
+     * @param playerIndex
+     * @param formPath
+     */
+    Gamepad* createGamepad(const char* gamepadFormPath);
+
+    /**
+     * Gets the gamepad for the specified player index.
+     *
+     * @param playerIndex The player index to get the gamepad for (0 <= playerIndex <= 3) 
+     */
+    inline Gamepad* getGamepad(unsigned int playerIndex = 0);
+
 protected:
 
     /**
@@ -405,6 +431,11 @@ private:
      */
     void loadConfig();
 
+    /**
+     * Loads a gamepad from the configuration file.
+     */
+    void loadGamepad();
+
     bool _initialized;                          // If game has initialized yet.
     State _state;                               // The game state.
     static double _pausedTimeLast;              // The last time paused.
@@ -423,6 +454,8 @@ private:
     AudioController* _audioController;          // Controls audio sources that are playing in the game.
     PhysicsController* _physicsController;      // Controls the simulation of a physics scene and entities.
     AudioListener* _audioListener;              // The audio listener in 3D space.
+    unsigned int _gamepadCount;
+    Gamepad** _gamepads;
     std::priority_queue<TimeEvent, std::vector<TimeEvent>, std::less<TimeEvent> >* _timeEvents; // Contains the scheduled time events.
 
     // Note: Do not add STL object member variables on the stack; this will cause false memory leaks to be reported.

+ 11 - 1
gameplay/src/Game.inl

@@ -72,4 +72,14 @@ inline void Game::displayKeyboard(bool display)
     Platform::displayKeyboard(display);
 }
 
-}
+inline Gamepad* Game::getGamepad(unsigned int playerIndex)
+{
+    GP_ASSERT(playerIndex < _gamepadCount);
+
+    if (_gamepads)
+        return _gamepads[playerIndex];
+    else
+        return NULL;
+}
+
+}

+ 181 - 0
gameplay/src/Gamepad.cpp

@@ -0,0 +1,181 @@
+#include "Base.h"
+#include "Gamepad.h"
+#include "Joystick.h"
+#include "Game.h"
+
+namespace gameplay
+{
+
+Gamepad::Gamepad()
+    : _playerIndex(-1), _joystickValues(NULL), _joystickCount(0), _buttonStates(NULL), _buttonCount(0), _gamepadForm(NULL)
+{
+}
+
+Gamepad::Gamepad(const char* formPath)
+    : _playerIndex(-1), _joystickValues(NULL), _joystickCount(0), _buttonStates(NULL), _buttonCount(0), _gamepadForm(NULL)
+{
+    GP_ASSERT(formPath);
+
+    _gamepadForm = Form::create(formPath);
+    GP_ASSERT(_gamepadForm);
+
+    _gamepadForm->setConsumeInputEvents(false);
+
+    getGamepadControls(_gamepadForm);
+
+    if (_joystickCount > 0)
+    {
+        _joystickValues = new Vector2*[_joystickCount];
+        for (unsigned int i = 0; i < _joystickCount; i++)
+        {
+            _joystickValues[i] = new Vector2();
+        }
+    }
+    if (_buttonCount > 0)
+    {
+        _buttonStates = new ButtonState*[_buttonCount];
+        for (unsigned int i = 0; i < _buttonCount; i++)
+        {
+            _buttonStates[i] = new ButtonState();
+        }
+    }
+}
+
+void Gamepad::getGamepadControls(Form* form)
+{
+    std::vector<Control*> controls = form->getControls();
+    std::vector<Control*>::iterator itr = controls.begin();
+
+    for (; itr != controls.end(); itr++)
+    {
+        Control* control = *itr;
+        GP_ASSERT(control);
+
+        if (std::strcmp("container", control->getType()) == 0)
+        {
+            getGamepadControls((Form*) control);
+        }
+        else if (std::strcmp("joystick", control->getType()) == 0)
+        {
+            control->addListener(this, Control::Listener::PRESS | Control::Listener::VALUE_CHANGED | Control::Listener::RELEASE);
+            Joystick* joystick = (Joystick*) control;
+            
+            if (!joystick->_gamepadJoystickIndex)
+                joystick->_gamepadJoystickIndex = new int[1];
+            
+            *joystick->_gamepadJoystickIndex = _joystickCount;
+            _joystickCount++;
+        }
+        else if (std::strcmp("button", control->getType()) == 0)
+        {
+            control->addListener(this, Control::Listener::PRESS | Control::Listener::RELEASE);
+            Button* button = (Button*) control;
+            
+            if (!button->_gamepadButtonIndex)
+                button->_gamepadButtonIndex = new int[1];
+
+            *button->_gamepadButtonIndex = _buttonCount;
+            _buttonCount++;
+        }
+    }
+}
+
+Gamepad::~Gamepad()
+{
+    if (_joystickValues)
+    {
+        for (unsigned int i = 0; i < _joystickCount; i++)
+            SAFE_DELETE(_joystickValues[i]);
+        SAFE_DELETE_ARRAY(_joystickValues);
+    }
+
+    if (_buttonStates)
+    {
+        for (unsigned int i = 0; i < _buttonCount; i++)
+           SAFE_DELETE(_buttonStates[i]);
+        SAFE_DELETE_ARRAY(_buttonStates);
+    }
+
+    if (_gamepadForm)
+        SAFE_RELEASE(_gamepadForm);
+}
+
+void Gamepad::update()
+{
+    if (_gamepadForm && _gamepadForm->isEnabled())
+    {
+        _gamepadForm->update();
+    }
+}
+
+void Gamepad::render()
+{
+    if (_gamepadForm && _gamepadForm->isEnabled())
+    {
+        _gamepadForm->draw();
+    }
+}
+
+void Gamepad::controlEvent(Control* control, Control::Listener::EventType evt)
+{
+    if (_gamepadForm && _gamepadForm->isEnabled())
+    {
+        if (std::strcmp("joystick", control->getType()) == 0)
+        {
+            int joystickIndex = *(((Joystick*) control)->_gamepadJoystickIndex);
+            switch(evt)
+            {
+                case Control::Listener::PRESS:
+                case Control::Listener::VALUE_CHANGED:
+                    _joystickValues[joystickIndex]->set(((Joystick*)control)->getValue());
+                    break;
+                case Control::Listener::RELEASE:
+                    _joystickValues[joystickIndex]->set(0.0f, 0.0f);
+                    break;
+            }
+            Game::getInstance()->gamepadEvent(JOYSTICK_EVENT, this, joystickIndex);
+        }
+        else if (std::strcmp("button", control->getType()) == 0)
+        {
+            int buttonIndex = *(((Button*) control)->_gamepadButtonIndex);
+            switch(evt)
+            {
+                case Control::Listener::PRESS:
+                    *_buttonStates[buttonIndex] = BUTTON_PRESSED;
+                    break;
+                case Control::Listener::RELEASE:
+                    *_buttonStates[buttonIndex] = BUTTON_RELEASED;
+                    break;
+            }
+            Game::getInstance()->gamepadEvent(BUTTON_EVENT, this, buttonIndex);
+        }
+    }
+}
+
+Gamepad::ButtonState Gamepad::getButtonState(unsigned int buttonId) const
+{
+    GP_ASSERT(buttonId < _buttonCount);
+
+    return *_buttonStates[buttonId];
+}
+
+bool Gamepad::isJoystickActive(unsigned int joystickId) const
+{
+    GP_ASSERT(joystickId < _joystickCount);
+
+    return !(_joystickValues[joystickId]->isZero());
+}
+
+const Vector2& Gamepad::getJoystickValue(unsigned int joystickId) const
+{
+    GP_ASSERT(joystickId < _joystickCount);
+
+    return *_joystickValues[joystickId];
+}
+
+bool Gamepad::isVirtual() const
+{
+    return (_gamepadForm && _gamepadForm->isEnabled());
+}
+
+}

+ 113 - 0
gameplay/src/Gamepad.h

@@ -0,0 +1,113 @@
+#ifndef GAMEPAD_H_
+#define GAMEPAD_H_
+
+#include "Form.h"
+#include "Button.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines an interface for handling gamepad support.
+ */
+class Gamepad : public Control::Listener
+{
+    friend class Game;
+
+public:
+
+    enum GamepadEvent
+    {
+        JOYSTICK_EVENT,
+        BUTTON_EVENT
+    };
+
+    enum ButtonState
+    {
+        BUTTON_PRESSED = gameplay::Button::Listener::PRESS, 
+        BUTTON_RELEASED = gameplay::Button::Listener::RELEASE
+    };
+    
+    /** 
+     * Gets the current state of the specified button.
+     */
+    ButtonState getButtonState(unsigned int buttonId) const;
+
+    /**
+     * Gets whether the specified joystick's state is active or not.
+     * 
+     * @param joystickId The unique integer ID of the joystick to set.
+     * @return Whether the given joystick is active or not.
+     */
+    bool isJoystickActive(unsigned int joystickId) const;
+
+    /**
+     * Returns the specified joystick's value as a Vector2.
+     *
+     * @param joystickId The unique integer ID of the joystick to set.
+     * @return A Vector2 of the joystick displacement for the specified joystick.
+     */
+    const Vector2& getJoystickValue(unsigned int joystickId) const;
+
+    /** 
+     * Listener for Control events on the gamepads Joystick or Buttons.
+     */
+    void controlEvent(Control* control, Control::Listener::EventType evt);
+
+    /**
+     * Returns whether the gamepad is represented with a UI form or not.
+     */
+    bool isVirtual() const;
+
+private:
+
+    /**
+     * Constructor.
+     */
+    Gamepad();
+    
+    /**
+     * Constructor.
+     * Create a gamepad from the specified formPath.
+     *
+     * @param formPath
+     */ 
+    Gamepad(const char* formPath);
+
+    /**
+     * Copy constructor.
+     */
+    Gamepad(const Gamepad& copy);
+
+    /** 
+     * Destructor.
+     */
+    virtual ~Gamepad();
+    
+    /** 
+     * Gets the Joystick and Button Control object's from the specified form.
+     */
+    void getGamepadControls(Form* form);
+
+    /**
+     * Updates the gamepad.
+     */
+    void update();
+
+    /**
+     * Renders the gamepad if it is based on a form and if the form is enabled.
+     */
+    void render();
+
+    int _playerIndex;
+    Vector2** _joystickValues;
+    unsigned int _joystickCount;
+    ButtonState** _buttonStates;
+    unsigned int _buttonCount;
+
+    Form* _gamepadForm;
+};
+
+}
+
+#endif

+ 65 - 47
gameplay/src/Joystick.cpp

@@ -1,12 +1,10 @@
 #include "Base.h"
 #include "Joystick.h"
 
-#define INVALID_CONTACT_INDEX ((unsigned int)-1)
-
 namespace gameplay
 {
 
-Joystick::Joystick() : _contactIndex(INVALID_CONTACT_INDEX), _absolute(true)
+Joystick::Joystick() : _absolute(true), _gamepadJoystickIndex(NULL)
 {
 }
 
@@ -16,6 +14,8 @@ Joystick::Joystick(const Joystick& copy)
 
 Joystick::~Joystick()
 {
+    if (_gamepadJoystickIndex)
+        SAFE_DELETE(_gamepadJoystickIndex);
 }
 
 Joystick* Joystick::create(const char* id, Theme::Style* style)
@@ -52,6 +52,11 @@ void Joystick::initialize(Theme::Style* style, Properties* properties)
     }
     _radius = properties->getFloat("radius");
 
+    if (properties->exists("absolute"))
+    {
+        setAbsolute(properties->getBool("absolute"));
+    }
+
     Vector4 v;
     if (properties->getVector4("region", &v))
     {
@@ -96,10 +101,12 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 _region.y = y + _bounds.y - _region.height * 0.5f;
             }
 
-            if ((dx >= -_radius && dx <= _radius) && (dy >= -_radius && dy <= _radius) && 
-                _contactIndex == INVALID_CONTACT_INDEX)
+            if ((dx >= -_radius && dx <= _radius) && (dy >= -_radius && dy <= _radius))
             {
-                _contactIndex = contactIndex;
+                _contactIndex = (int) contactIndex;
+
+                notifyListeners(Listener::PRESS);
+
                 _displacement.set(0.0f, 0.0f);
                 
                 Vector2 value(0.0f, 0.0f);
@@ -111,67 +118,71 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 }
 
                 _state = ACTIVE;
+
+                return _consumeInputEvents;
+            }
+            else
+            {
+                _state = NORMAL;
             }
+            break;
         }
         case Touch::TOUCH_MOVE:
         {
-            if (_contactIndex == contactIndex)
+            float dx = x - ((!_absolute) ? _region.x - _bounds.x : 0.0f) - _region.width * 0.5f;
+            float dy = -(y - ((!_absolute) ? _region.y - _bounds.y : 0.0f) - _region.height * 0.5f);
+            if (((dx * dx) + (dy * dy)) <= (_radius * _radius))
             {
-                float dx = x - ((!_absolute) ? _region.x - _bounds.x : 0.0f) - _region.width * 0.5f;
-                float dy = -(y - ((!_absolute) ? _region.y - _bounds.y : 0.0f) - _region.height * 0.5f);
-                if (((dx * dx) + (dy * dy)) <= (_radius * _radius))
-                {
-                    GP_ASSERT(_radius);
-                    Vector2 value(dx, dy);
-                    value.scale(1.0f / _radius);
-                    if (_value != value)
-                    {
-                        _value.set(value);
-                        _dirty = true;
-                        notifyListeners(Control::Listener::VALUE_CHANGED);
-                    }
-                }
-                else
+                GP_ASSERT(_radius);
+                Vector2 value(dx, dy);
+                value.scale(1.0f / _radius);
+                if (_value != value)
                 {
-                    Vector2 value(dx, dy);
-                    value.normalize();
-                    value.scale(_radius);
-                    value.normalize();
-                    if (_value != value)
-                    {
-                        _value.set(value);
-                        _dirty = true;
-                        notifyListeners(Control::Listener::VALUE_CHANGED);
-                    }
+                    _value.set(value);
+                    _dirty = true;
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
-
-                _displacement.set(dx, dy);
             }
-        }
-        break;
-        case Touch::TOUCH_RELEASE:
-        {
-            if (_contactIndex == contactIndex)
+            else
             {
-                // Reset displacement and direction vectors.
-                _contactIndex = INVALID_CONTACT_INDEX;
-                _displacement.set(0.0f, 0.0f);
-                
-                Vector2 value(0.0f, 0.0f);
+                Vector2 value(dx, dy);
+                value.normalize();
+                value.scale(_radius);
+                value.normalize();
                 if (_value != value)
                 {
                     _value.set(value);
                     _dirty = true;
                     notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
+            }
 
-                _state = NORMAL;
+            _displacement.set(dx, dy);
+
+            return _consumeInputEvents;
+        }
+        case Touch::TOUCH_RELEASE:
+        {
+            _contactIndex = INVALID_CONTACT_INDEX;
+
+            // Reset displacement and direction vectors.
+            _displacement.set(0.0f, 0.0f);
+
+            Vector2 value(0.0f, 0.0f);
+            if (_value != value)
+            {
+                _value.set(value);
+                _dirty = true;
+                notifyListeners(Control::Listener::VALUE_CHANGED);
             }
+
+            _state = NORMAL;
+
+            return _consumeInputEvents;
         }
-        break;
     }
 
-    return Control::touchEvent(touchEvent, x, y, contactIndex);
+    return false;
 }
 
 void Joystick::update(const Control* container, const Vector2& offset)
@@ -238,4 +249,11 @@ void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     spriteBatch->end();
 }
 
+const char* Joystick::getType() const
+{
+    return "joystick";
+}
+
+
+
 }

+ 7 - 1
gameplay/src/Joystick.h

@@ -12,6 +12,7 @@ namespace gameplay
 class Joystick : public Control
 {
     friend class Container;
+    friend class Gamepad;
 
 public:
 
@@ -79,6 +80,11 @@ public:
      */
     inline bool isAbsolute() const;
 
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
 protected:
     
     /**
@@ -145,11 +151,11 @@ private:
     Joystick(const Joystick& copy);
 
     float _radius;
-    unsigned int _contactIndex;
     bool _absolute;
     Vector2 _displacement;
     Vector2 _value;
     Rectangle _region;
+    int* _gamepadJoystickIndex;
 };
 
 }

+ 6 - 1
gameplay/src/Label.cpp

@@ -112,4 +112,9 @@ void Label::drawText(const Rectangle& clip)
     _dirty = false;
 }
 
-}
+const char* Label::getType() const
+{
+    return "label";
+}
+
+}

+ 6 - 1
gameplay/src/Label.h

@@ -57,6 +57,11 @@ public:
      */
     const char* getText();
 
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
     /**
      * Add a listener to be notified of specific events affecting
      * this control.  Event types can be OR'ed together.
@@ -142,4 +147,4 @@ private:
 
 }
 
-#endif
+#endif

+ 6 - 1
gameplay/src/RadioButton.cpp

@@ -198,4 +198,9 @@ void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     spriteBatch->draw(pos.x, pos.y, size.x, size.y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _viewportClipBounds);
 }
 
-}
+const char* RadioButton::getType() const
+{
+    return "radioButton";
+}
+
+}

+ 6 - 1
gameplay/src/RadioButton.h

@@ -71,6 +71,11 @@ public:
      */
     const Vector2& getImageSize() const;
 
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
     /**
      * Add a listener to be notified of specific events affecting
      * this control.  Event types can be OR'ed together.
@@ -170,4 +175,4 @@ private:
 
 }
 
-#endif
+#endif

+ 5 - 0
gameplay/src/Slider.cpp

@@ -275,4 +275,9 @@ void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     spriteBatch->draw(pos.x, pos.y, markerRegion.width, markerRegion.height, marker.u1, marker.v1, marker.u2, marker.v2, markerColor, _viewportClipBounds);
 }
 
+const char* Slider::getType() const
+{
+    return "slider";
+}
+
 }

+ 5 - 0
gameplay/src/Slider.h

@@ -103,6 +103,11 @@ public:
      */
     float getValue();
 
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
     /**
      * Add a listener to be notified of specific events affecting
      * this control.  Event types can be OR'ed together.

+ 11 - 2
gameplay/src/TextBox.cpp

@@ -64,6 +64,8 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                  y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
+            _contactIndex = (int) contactIndex;
+
             if (_state == NORMAL)
                 Game::getInstance()->displayKeyboard(true);
             else
@@ -74,6 +76,7 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         }
         else
         {
+            _contactIndex = INVALID_CONTACT_INDEX;
             _state = NORMAL;
             Game::getInstance()->displayKeyboard(false);
             _dirty = true;
@@ -101,7 +104,7 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             _state = NORMAL;
             Game::getInstance()->displayKeyboard(false);
         }
-
+        _contactIndex = INVALID_CONTACT_INDEX;
         _dirty = true;
         break;
     }
@@ -319,6 +322,7 @@ bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                 }
 
                 notifyListeners(Listener::TEXT_CHANGED);
+                break;
             }
         }
     }
@@ -378,4 +382,9 @@ void TextBox::setCaretLocation(int x, int y)
     }
 }
 
-}
+const char* TextBox::getType() const
+{
+    return "textBox";
+}
+
+}

+ 6 - 1
gameplay/src/TextBox.h

@@ -68,6 +68,11 @@ public:
      */
     int getLastKeypress();
 
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
 protected:
 
     /**
@@ -175,4 +180,4 @@ private:
 
 }
 
-#endif
+#endif

+ 1 - 0
gameplay/src/gameplay.h

@@ -7,6 +7,7 @@
 #include "Mouse.h"
 #include "FileSystem.h"
 #include "Bundle.h"
+#include "Gamepad.h"
 
 // Math
 #include "Rectangle.h"