Game controller for Android PC
Hello! In this article I want to talk about how you can make a game controller (in common people - a joystick) from your Adndroid smartphone for a regular PC, namely a steering wheel .
The behavior of the steering wheel will be emulated using an accelerometer. For this, a continuous scanning of spatial coordinates is carried out and empirically selected boundaries for each direction of movement. Based on this data, game keys combinations are generated in real time. For example: W - forward, WA - forward and left, etc.
To deliver this data to a PC, a server must be running that accepts incoming commands and emulates the keystrokes. The server can be made single-threaded so that only one smartphone is connected. The connection will be via Wi-Fi.
And now the most interesting ...
The server is implemented in C ++ under Windows. Its main task is to continuously receive incoming messages and press keys. Below is the basic and simple code for this task:
Keystrokes:
The client's task is to connect to the server and send key combinations for pressing. For this, an accelerometer is used. Our task is to obtain the spatial coordinates of the phone. This is done like this:
An important point is that you need to read data from the accelerometer at a certain interval, otherwise your program will instantly be suspended from continuous requests to the sensor. Also, when folding, you need to untie the listener from the accelerometer so that system resources and batteries are not wasted. To do this, the onResume and onPause methods do the following:
The code for generating the keys is very simple. All boundaries were determined experimentally.
I experienced it all on Need For Speed Most Wanted. It feels, of course, not like a real steering wheel, but you can play. Unfortunately, the video could not be shot - there is only one camera in the house, and then on the tested phone. I’ll definitely post it in the near future. Here's how it looks on a PC and on a smartphone:


So far, the main drawback in the server is the global keystroke, independent of the application. In the future there will be something to do. Another problem that I encountered is the constant disconnection. I did not find a better solution than constant reconnection when the connection is disconnected.
Working with the accelerometer:
github.com/eburke/android_game_examples/blob/9d65f96aff5d60a2e765d8db894b7eb3fd02c315/GameExamples/src/com/stuffthathappens/games/Accel.java
emulation keystrokes:
www.codeproject.com/kb/system/keyboard.aspx
And most importantly - sources:
dl.dropbox.com/u/5636452/game_controller.zip
Task description
The behavior of the steering wheel will be emulated using an accelerometer. For this, a continuous scanning of spatial coordinates is carried out and empirically selected boundaries for each direction of movement. Based on this data, game keys combinations are generated in real time. For example: W - forward, WA - forward and left, etc.
To deliver this data to a PC, a server must be running that accepts incoming commands and emulates the keystrokes. The server can be made single-threaded so that only one smartphone is connected. The connection will be via Wi-Fi.
And now the most interesting ...
Server
The server is implemented in C ++ under Windows. Its main task is to continuously receive incoming messages and press keys. Below is the basic and simple code for this task:
Copy Source | Copy HTML- while (true) {
- std::cout << "Wait for connection...\n";
-
- try {
- socket = server.Accept();
- } catch (const char *error) {
- std::cout << error << std::endl;
- exit( 0);
- }
-
- bool keepAlive = true;
- int timeout = 10000;
-
- setsockopt(server.getSocket(), SOL_SOCKET, SO_KEEPALIVE, (char*)&keepAlive, sizeof(bool));
- setsockopt(server.getSocket(), SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(int));
-
- std::cout << "Connected!\n";
-
- while (true) {
- std::string msg = socket->ReceiveLine();
-
- if (msg.empty())
- break;
-
- processKeys(msg.c_str());
- }
-
- std::cout << "Disconnected.\n\n\n";
- }
Keystrokes:
Copy Source | Copy HTML- void pressKeys(char key1, char key2) {
- // отжать предыдущие клавиши
-
- for (std::map
::iterator it = scanCodes.begin() ; it != scanCodes.end(); it++ ) { - char curKey = it->first;
-
- if (curKey != key1 && curKey != key2)
- upKey(curKey);
- }
-
- downKey(key1);
- downKey(key2);
- }
-
- void downKey(char key) {
- keybd_event(VkKeyScan(key), scanCodes[key], 0, 0);
- }
-
- void upKey(char key) {
- keybd_event(VkKeyScan(key), scanCodes[key], KEYEVENTF_KEYUP, 0);
- }
Client
The client's task is to connect to the server and send key combinations for pressing. For this, an accelerometer is used. Our task is to obtain the spatial coordinates of the phone. This is done like this:
Copy Source | Copy HTML- public class MainActivity extends Activity implements SensorEventListener {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // ...
-
- sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
- accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-
- //...
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
- long curTime = System.currentTimeMillis();
-
- // считывание данных раз в 100 мс, иначе телефон загнется от сборщика мусора
- if (lastUpdate == -1 || (curTime - lastUpdate) > 100) {
- lastUpdate = curTime;
-
- x = event.values[DATA_X];
- y = event.values[DATA_Y];
- z = event.values[DATA_Z];
-
- xLabel.setText(String.format("X: %+2.5f", x));
- yLabel.setText(String.format("Y: %+2.5f", y));
- zLabel.setText(String.format("Z: %+2.5f", z));
-
- try {
- sendKeys(); // анализ координат для отправки клавиш на сервер
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
-
- }
-
- }
- }
An important point is that you need to read data from the accelerometer at a certain interval, otherwise your program will instantly be suspended from continuous requests to the sensor. Also, when folding, you need to untie the listener from the accelerometer so that system resources and batteries are not wasted. To do this, the onResume and onPause methods do the following:
Copy Source | Copy HTML- @Override
- protected void onResume() {
- super.onResume();
-
- sensorManager.registerListener(this, accelerometer, SENSOR_DELAY_NORMAL);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- sensorManager.unregisterListener(this);
- }
The code for generating the keys is very simple. All boundaries were determined experimentally.
Copy Source | Copy HTML- private String getKeys() {
- String keys = "";
-
- if (z > 7.5)
- keys += "W";
- else
- keys += "S";
-
- if (y < -3)
- keys += "A";
- else if (y > 3)
- keys += "D";
-
- return keys;
- }
How does it all work
I experienced it all on Need For Speed Most Wanted. It feels, of course, not like a real steering wheel, but you can play. Unfortunately, the video could not be shot - there is only one camera in the house, and then on the tested phone. I’ll definitely post it in the near future. Here's how it looks on a PC and on a smartphone:


Conclusion
So far, the main drawback in the server is the global keystroke, independent of the application. In the future there will be something to do. Another problem that I encountered is the constant disconnection. I did not find a better solution than constant reconnection when the connection is disconnected.
What to read
Working with the accelerometer:
github.com/eburke/android_game_examples/blob/9d65f96aff5d60a2e765d8db894b7eb3fd02c315/GameExamples/src/com/stuffthathappens/games/Accel.java
emulation keystrokes:
www.codeproject.com/kb/system/keyboard.aspx
And most importantly - sources:
dl.dropbox.com/u/5636452/game_controller.zip