BGE: Adding a Python interface for handling joysticks without needing logic bricks. These new SCA_PythonJoystick objects can be accessed using bge.logic.joysticks, which is a list of joysticks. The length of the list is the number of maximum supported joysticks, and indexes that do not have a joystick available are set to None. This means joysticks can be checked for using something like:

if bge.logic.joysticks[0]:
    activate_player_one()

if bge.logic.joysticks[1]:
    activate_player_two()

etc..

The interface exposed by SCA_PythonJoystick is very similar to the joystick logic brick except for one key difference: axis values are normalized to a -1.0 to 1.0 range instead of -32767 to 32767, which is what the logic brick exposed.
This commit is contained in:
Mitchell Stokes
2012-12-21 02:28:59 +00:00
parent 26752e8b3a
commit f2f2b6153a
10 changed files with 370 additions and 5 deletions

View File

@@ -125,6 +125,10 @@ Variables
The current mouse wrapped in an :class:`~bge.types.SCA_PythonMouse` object.
.. data:: joysticks
A list of attached joysticks. The list size it he maximum number of supported joysticks. If no joystick is available for a given slot, the slot is set to None.
*****************
General functions
*****************

View File

@@ -141,6 +141,74 @@ Types
:type: boolean
.. class:: SCA_PythonJoystick(PyObjectPlus)
A Python interface to a joystick.
.. attribute:: name
The name assigned to the joystick by the operating system. (read-only)
:type: string
.. attribute:: activeButtons
A list of active button values. (read-only)
:type: list
.. attribute:: axisValues
The state of the joysticks axis as a list of values :data:`numAxis` long. (read-only).
:type: list of ints.
Each specifying the value of an axis between -1.0 and 1.0 depending on how far the axis is pushed, 0 for nothing.
The first 2 values are used by most joysticks and gamepads for directional control. 3rd and 4th values are only on some joysticks and can be used for arbitary controls.
* left:[-1.0, 0.0, ...]
* right:[1.0, 0.0, ...]
* up:[0.0, -1.0, ...]
* down:[0.0, 1.0, ...]
.. attribute:: hatValues
The state of the joysticks hats as a list of values :data:`numHats` long. (read-only).
:type: list of ints
Each specifying the direction of the hat from 1 to 12, 0 when inactive.
Hat directions are as follows...
* 0:None
* 1:Up
* 2:Right
* 4:Down
* 8:Left
* 3:Up - Right
* 6:Down - Right
* 12:Down - Left
* 9:Up - Left
.. attribute:: numAxis
The number of axes for the joystick at this index. (read-only).
:type: integer
.. attribute:: numButtons
The number of buttons for the joystick at this index. (read-only).
:type: integer
.. attribute:: numHats
The number of hats for the joystick at this index. (read-only).
:type: integer
.. class:: SCA_IObject(CValue)
This class has no python functions
@@ -3977,7 +4045,7 @@ Types
:type: list of ints.
Each spesifying the value of an axis between -32767 and 32767 depending on how far the axis is pushed, 0 for nothing.
Each specifying the value of an axis between -32767 and 32767 depending on how far the axis is pushed, 0 for nothing.
The first 2 values are used by most joysticks and gamepads for directional control. 3rd and 4th values are only on some joysticks and can be used for arbitary controls.
* left:[-32767, 0, ...]
@@ -4001,7 +4069,7 @@ Types
:type: list of ints
Each spesifying the direction of the hat from 1 to 12, 0 when inactive.
Each specifying the direction of the hat from 1 to 12, 0 when inactive.
Hat directions are as follows...

View File

@@ -71,6 +71,7 @@ set(SRC
SCA_PropertyEventManager.cpp
SCA_PropertySensor.cpp
SCA_PythonController.cpp
SCA_PythonJoystick.cpp
SCA_PythonKeyboard.cpp
SCA_PythonMouse.cpp
SCA_RandomActuator.cpp
@@ -114,6 +115,7 @@ set(SRC
SCA_PropertyEventManager.h
SCA_PropertySensor.h
SCA_PythonController.h
SCA_PythonJoystick.h
SCA_PythonKeyboard.h
SCA_PythonMouse.h
SCA_RandomActuator.h

View File

@@ -321,3 +321,12 @@ int SCA_Joystick::pAxisTest(int axisnum)
return 0;
#endif /* WITH_SDL */
}
const char *SCA_Joystick::GetName()
{
#ifdef WITH_SDL
return SDL_JoystickName(m_joyindex);
#else /* WITH_SDL */
return "";
#endif /* WITH_SDL */
}

View File

@@ -192,6 +192,11 @@ public:
* Test if the joystick is connected
*/
int Connected(void);
/**
* Name of the joytsick
*/
const char *GetName();
};
#endif

View File

@@ -60,14 +60,16 @@ SCA_JoystickManager::~SCA_JoystickManager()
void SCA_JoystickManager::NextFrame(double curtime,double deltatime)
{
// We should always handle events in case we want to grab them with Python
#ifdef WITH_SDL
SCA_Joystick::HandleEvents(); /* Handle all SDL Joystick events */
#endif
if (m_sensors.Empty()) {
return;
}
else {
;
#ifdef WITH_SDL
SCA_Joystick::HandleEvents(); /* Handle all SDL Joystick events */
#endif
SG_DList::iterator<SCA_JoystickSensor> it(m_sensors);
for (it.begin();!it.end();++it)
{

View File

@@ -0,0 +1,184 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Mitchell Stokes.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file gameengine/GameLogic/SCA_PythonJoystick.cpp
* \ingroup gamelogic
*/
#include "SCA_PythonJoystick.h"
#include "./Joystick/SCA_Joystick.h"
#include "SCA_IInputDevice.h"
//#include "GHOST_C-api.h"
/* ------------------------------------------------------------------------- */
/* Native functions */
/* ------------------------------------------------------------------------- */
SCA_PythonJoystick::SCA_PythonJoystick(SCA_Joystick* joystick)
: PyObjectPlus(),
m_joystick(joystick)
{
#ifdef WITH_PYTHON
m_event_dict = PyDict_New();
#endif
}
SCA_PythonJoystick::~SCA_PythonJoystick()
{
#ifdef WITH_PYTHON
PyDict_Clear(m_event_dict);
Py_DECREF(m_event_dict);
#endif
}
#ifdef WITH_PYTHON
/* ------------------------------------------------------------------------- */
/* Python functions */
/* ------------------------------------------------------------------------- */
PyObject* SCA_PythonJoystick::py_repr(void)
{
return PyUnicode_FromString(m_joystick->GetName());
}
/* Integration hooks ------------------------------------------------------- */
PyTypeObject SCA_PythonJoystick::Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"SCA_PythonJoystick",
sizeof(PyObjectPlus_Proxy),
0,
py_base_dealloc,
0,
0,
0,
0,
py_base_repr,
0,0,0,0,0,0,0,0,0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
0,0,0,0,0,0,0,
Methods,
0,
0,
&PyObjectPlus::Type,
0,0,0,0,0,0,
py_base_new
};
PyMethodDef SCA_PythonJoystick::Methods[] = {
{NULL,NULL} //Sentinel
};
PyAttributeDef SCA_PythonJoystick::Attributes[] = {
KX_PYATTRIBUTE_RO_FUNCTION("numButtons", SCA_PythonJoystick, pyattr_get_num_x),
KX_PYATTRIBUTE_RO_FUNCTION("numHats", SCA_PythonJoystick, pyattr_get_num_x),
KX_PYATTRIBUTE_RO_FUNCTION("numAxis", SCA_PythonJoystick, pyattr_get_num_x),
KX_PYATTRIBUTE_RO_FUNCTION("activeButtons", SCA_PythonJoystick, pyattr_get_active_buttons),
KX_PYATTRIBUTE_RO_FUNCTION("hatValues", SCA_PythonJoystick, pyattr_get_hat_values),
KX_PYATTRIBUTE_RO_FUNCTION("axisValues", SCA_PythonJoystick, pyattr_get_axis_values),
KX_PYATTRIBUTE_RO_FUNCTION("name", SCA_PythonJoystick, pyattr_get_name),
{ NULL } //Sentinel
};
// Use one function for numAxis, numButtons, and numHats
PyObject* SCA_PythonJoystick::pyattr_get_num_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_PythonJoystick* self = static_cast<SCA_PythonJoystick*>(self_v);
if (strcmp(attrdef->m_name, "numButtons") == 0)
return PyLong_FromLong(self->m_joystick->GetNumberOfButtons());
else if (strcmp(attrdef->m_name, "numAxis") == 0)
return PyLong_FromLong(self->m_joystick->GetNumberOfAxes());
else if (strcmp(attrdef->m_name, "numHats") == 0)
return PyLong_FromLong(self->m_joystick->GetNumberOfHats());
// If we got here, we have a problem...
PyErr_SetString(PyExc_AttributeError, "invalid attribute");
return NULL;
}
PyObject* SCA_PythonJoystick::pyattr_get_active_buttons(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_PythonJoystick* self = static_cast<SCA_PythonJoystick*>(self_v);
int button_index = self->m_joystick->GetNumberOfButtons();
PyObject *list = PyList_New(0);
PyObject *value;
for (int i=0; i < self->m_joystick->GetNumberOfButtons(); i++) {
if (self->m_joystick->aButtonPressIsPositive(i)) {
value = PyLong_FromSsize_t(i);
PyList_Append(list, value);
Py_DECREF(value);
}
}
return list;
}
PyObject* SCA_PythonJoystick::pyattr_get_hat_values(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_PythonJoystick* self = static_cast<SCA_PythonJoystick*>(self_v);
int hat_index = self->m_joystick->GetNumberOfHats();
PyObject *list = PyList_New(hat_index);
while (hat_index--) {
PyList_SET_ITEM(list, hat_index, PyLong_FromLong(self->m_joystick->GetHat(hat_index)));
}
return list;
}
PyObject* SCA_PythonJoystick::pyattr_get_axis_values(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_PythonJoystick* self = static_cast<SCA_PythonJoystick*>(self_v);
int axis_index = self->m_joystick->GetNumberOfAxes();
PyObject *list = PyList_New(axis_index);
int position;
while (axis_index--) {
position = self->m_joystick->GetAxisPosition(axis_index);
// We get back a range from -32768 to 32767, so we use an if here to
// get a perfect -1.0 to 1.0 mapping. Some oddball system might have an
// actual min of -32767 for shorts, so we use SHRT_MIN/MAX to be safe.
if (position < 0)
PyList_SET_ITEM(list, axis_index, PyFloat_FromDouble(position/((double)-SHRT_MIN)));
else
PyList_SET_ITEM(list, axis_index, PyFloat_FromDouble(position/(double)SHRT_MAX));
}
return list;
}
PyObject* SCA_PythonJoystick::pyattr_get_name(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_PythonJoystick* self = static_cast<SCA_PythonJoystick*>(self_v);
return PyUnicode_FromString(self->m_joystick->GetName());
}
#endif

View File

@@ -0,0 +1,56 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Mitchell Stokes.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file SCA_PythonJoystick.h
* \ingroup gamelogic
*/
#ifndef __SCA_PYTHONJOYSTICK_H__
#define __SCA_PYTHONJOYSTICK_H__
#include "PyObjectPlus.h"
class SCA_PythonJoystick : public PyObjectPlus
{
Py_Header
private:
class SCA_Joystick *m_joystick;
#ifdef WITH_PYTHON
PyObject* m_event_dict;
#endif
public:
SCA_PythonJoystick(class SCA_Joystick* joystick);
virtual ~SCA_PythonJoystick();
#ifdef WITH_PYTHON
virtual PyObject* py_repr(void);
static PyObject* pyattr_get_num_x(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef);
static PyObject* pyattr_get_active_buttons(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef);
static PyObject* pyattr_get_hat_values(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef);
static PyObject* pyattr_get_axis_values(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef);
static PyObject* pyattr_get_name(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef);
#endif
};
#endif //__SCA_PYTHONJOYSTICK_H__

View File

@@ -92,6 +92,8 @@ extern "C" {
#include "SCA_PropertySensor.h"
#include "SCA_RandomActuator.h"
#include "SCA_KeyboardSensor.h" /* IsPrintable, ToCharacter */
#include "SCA_JoystickManager.h" /* JOYINDEX_MAX */
#include "SCA_PythonJoystick.h"
#include "SCA_PythonKeyboard.h"
#include "SCA_PythonMouse.h"
#include "KX_ConstraintActuator.h"
@@ -151,6 +153,7 @@ static char gp_GamePythonPathOrig[FILE_MAX] = ""; // not super happy about this,
static SCA_PythonKeyboard* gp_PythonKeyboard = NULL;
static SCA_PythonMouse* gp_PythonMouse = NULL;
static SCA_PythonJoystick* gp_PythonJoysticks[JOYINDEX_MAX] = {NULL};
#endif // WITH_PYTHON
static KX_Scene* gp_KetsjiScene = NULL;
@@ -1420,6 +1423,22 @@ PyObject *initGameLogic(KX_KetsjiEngine *engine, KX_Scene* scene) // quick hack
gp_PythonMouse = new SCA_PythonMouse(gp_KetsjiEngine->GetMouseDevice(), gp_Canvas);
PyDict_SetItemString(d, "mouse", gp_PythonMouse->NewProxy(true));
PyObject* joylist = PyList_New(JOYINDEX_MAX);
SCA_JoystickManager* joyevent = (SCA_JoystickManager*)gp_KetsjiScene->GetLogicManager()->FindEventManager(SCA_EventManager::JOY_EVENTMGR);
for (int i=0; i<JOYINDEX_MAX; ++i) {
SCA_Joystick* joy = joyevent->GetJoystickDevice(i);
if (joy && joy->Connected()) {
gp_PythonJoysticks[i] = new SCA_PythonJoystick(joy);
PyObject* tmp = gp_PythonJoysticks[i]->NewProxy(true);
Py_INCREF(tmp);
PyList_SET_ITEM(joylist, i, tmp);
} else {
Py_INCREF(Py_None);
PyList_SET_ITEM(joylist, i, Py_None);
}
}
PyDict_SetItemString(d, "joysticks", joylist);
ErrorObject = PyUnicode_FromString("GameLogic.error");
PyDict_SetItemString(d, "error", ErrorObject);
Py_DECREF(ErrorObject);
@@ -1937,6 +1956,13 @@ void exitGamePlayerPythonScripting()
delete gp_PythonMouse;
gp_PythonMouse = NULL;
for (int i=0; i<JOYINDEX_MAX; ++i) {
if (gp_PythonJoysticks[i]) {
delete gp_PythonJoysticks[i];
gp_PythonJoysticks[i] = NULL;
}
}
/* since python restarts we cant let the python backup of the sys.path hang around in a global pointer */
restorePySysObjects(); /* get back the original sys.path and clear the backup */
@@ -1985,6 +2011,13 @@ void exitGamePythonScripting()
delete gp_PythonMouse;
gp_PythonMouse = NULL;
for (int i=0; i<JOYINDEX_MAX; ++i) {
if (gp_PythonJoysticks[i]) {
delete gp_PythonJoysticks[i];
gp_PythonJoysticks[i] = NULL;
}
}
restorePySysObjects(); /* get back the original sys.path and clear the backup */
bpy_import_main_set(NULL);
PyObjectPlus::ClearDeprecationWarning();

View File

@@ -81,6 +81,7 @@
#include "SCA_RandomSensor.h"
#include "SCA_XNORController.h"
#include "SCA_XORController.h"
#include "SCA_PythonJoystick.h"
#include "SCA_PythonKeyboard.h"
#include "SCA_PythonMouse.h"
#include "KX_IpoActuator.h"
@@ -250,6 +251,7 @@ void initPyTypes(void)
PyType_Ready_Attr(dict, SCA_XNORController, init_getset);
PyType_Ready_Attr(dict, SCA_XORController, init_getset);
PyType_Ready_Attr(dict, SCA_IController, init_getset);
PyType_Ready_Attr(dict, SCA_PythonJoystick, init_getset);
PyType_Ready_Attr(dict, SCA_PythonKeyboard, init_getset);
PyType_Ready_Attr(dict, SCA_PythonMouse, init_getset);
}