Xbox gamepad support for GoPiGo with RPC

The first thing I wanted to do after finishing building my GoPiGo robot was to try and set it up with a gamepad so that I could manually drive it around. It seemed that the work had already been done for me here, as one of the example python scripts included with the software implements the ability to connect a PS3 controller and send commands with bluetooth. However, having discovered that it seems the current version (at time of writing) of the GoPiGo software can’t make use of the built in bluetooth I decided to try and make it work anyway.

Since what I really wanted to do was make my Xbox One gamepad work with the robot rather than a PS3 controller, I decided on having the controller connected to my PC rather than the Pi directly. Then, the plan was to send commands to the Pi over WiFi rather than directly through bluetooth. On the Pi, a python server would run that would listen for commands over the network and move the robot accordingly. On the PC, a python client would interface with the Xbox controller and send on input information to the server.

Reading gamepad input

Reading input from the gamepad actually turned out to be very simple using a library called PyGame. Whilst this does a whole lot more than just reading gamepad input, it abstracts away the details nicely and works out of the box for a variety of standard gamepads, including my Xbox One gamepad.

The first thing I did was try and use the gamepad input to simply move a sprite around the PyGame window. Initially, the sprite moved far too quickly, but I soon able to identify that the frame rate was the problem. I wasn’t capping the frame rate in any way, and without the speed of the sprite being frame-rate-independent the sprite was lost off the edge of the screen almost immediately after moving. As I wasn’t planning on using the gamepad input for anything within the PyGame window for this project, I fixed the problem simply by capping the frame rate as 60fps.

clock.tick(60)

PyGame triggers JOYAXISMOTION events when the analog sticks on the gamepad are moved, which contain data about where they actually moved. Now I just needed to send this data to the robot itself and interpret it as robot movements.

# Get joypad input
 if event.type == JOYAXISMOTION:
   if event.axis == 0:
     speed[0] = int(event.value * 10)
   elif event.axis == 1:
     speed[1] = int(event.value * 10)

Sending commands over the network

Although there are plenty of options for transferring the input data from the PC to the Pi, I decided on using RPC as it seemed the simplest option via the XMLRPC python library.

All I had to do was make a new proxy object on the PC client to represent the Pi server and call the exposed “move_robot(turn, forward)” function on it to transfer the input data to the Pi. The JOYAXISMOTION event triggers twice for vertical and horizontal movements of an analog stick respectively, so the values of the two parameters were simply the values returned by these two events. Horizontal movements would turn the robot left and right, and vertical movements would accelerate or decelerate the robot.

# Initialise RPC
server = xmlrpc.client.ServerProxy("http://192.168.1.103:8000")

...

server.move_robot(speed[0], speed[1])

The Pi client simply had to define the move_robot function,  publish it as an RPC function and then listen for calls to the function from the client.

server.register_function(move_robot, "move_robot")
server.serve_forever()

Moving the robot

With the axis values successfully being transferred across the network, I needed to interpret these into commands for the robot. The GoPiGo python API provides a lot of functions for manipulating the wheels in different ways, but since I had high fidelity analog data about how fast the robot should turn or move forward, all I really wanted to do was control the speed of each wheel individually. However, the functions that do that don’t accept negative values, so you can’t move the robot backwards with just those. I had to tell the robot which general direction to move in to begin with based on the vertical axis value.

# throttle
 if forward < 0:
   fwd()
 elif forward > 0:
   bwd()
 else:
   stop()

With that out of the way, I set the base line speed to the absolute value of the vertical stick and figure out how much to turn based on the absolute value of the horizontal stick.

set_speed(abs(forward)*speed_multiplier)
# turning
  if turn < 0:
    # turn left
    set_right_speed(abs(forward) + abs(turn) * turn_multiplier)
    set_left_speed(abs(forward) - abs(turn) * turn_multiplier)
  elif turn > 0:
    # turn right
    set_right_speed(abs(forward) - abs(turn) * turn_multiplier)
    set_left_speed(abs(forward) + abs(turn) * turn_multiplier)

And that was it! I was able to drive the robot around using the analog stick on the Xbox One gamepad, albeit through the medium of my PC.

Making a simple platformer

Leap

A little while ago, I decided to have a go at making this Amiga game from my childhood:

This project actually ended up being much more difficult than I anticipated, which I guess is why it makes a lot of sense to start out with these smaller scope projects. Whilst the majority of it didn’t cause too many issues, there were one or two key problems which I spent a lot of time on.

Sprites and animation

The first thing I did was attempt to make some sprites. I’m actually pretty happy with the little guy in a suit, even if his Microsoft Paint heritage wasn’t particularly subtle.

LeapPlayerDeadLeft.png

I also made a bunch of animation frames for him to run around and set about making him animate in Unity. The animation tools allow you to set up a state machine that decides what animation should be playing at any one moment. The transitions can be triggered by setting the parameters from the C# scripts assigned to the player object.

2017-01-09 (3).png

This diagram used to be a lot more complicated, as I had separate sprites/animations for moving left and moving right. It turns out that you can flip the sprite easily by simply negating the scale of the object’s transform. This trick made the state machine much simpler and much less confusing. This lesson alone was probably worth the time I put in so far, since moving a 2D character left and right is probably something I’m going to want to do again at some point.

Collisions

The next most interesting challenge came at the point when I realised that I wanted my character to have two different collision behaviours with the same object, namely a platform. He needed to be able to stand on top of the platforms, but if he hit a platform with anything other than his feet, he’d need to fall over and “sleep” for a few seconds before the player regained control. I chose to solve this problem by creating separate sub-objects for the player’s feet and for their body, each with their own colliders and own scripts for dictating what should happen in the event of a collision.

After having this working, it was clear there was something else that was wrong. My character, after jumping, could happy jump again and again in mid air without any ground beneath his feet. Similar to the sprite-flipping moment earlier, I felt the sense again that I was stumbling across a problem that had tripped up many a budding platformer developer in the past. The solution was to keep track of when my character was on the ground using a flag – set the flag to True when the character’s feet collide with a platform, and the flag to False if he is commanded to jump. That way, I could ignore jump commands if the flag was set to False.

At this point, I had my character running and jumping around, able to land on platforms and also knocked out by them. The next problem I encountered was easily the toughest to solve, and I again felt like I was treading in the footsteps of many developers before me. I needed holes in my platforms, and I needed the holes to move along the platforms.

2017-01-09 (4).png

Holes

It turns out it’s not particularly easy to make a hole in a rectangular collider. You have to use workarounds in one way or another to make it work. If it were the platforms that were moving, it would be easier because the holes would simply be gaps between the platforms themselves, which would have separate colliders. In this case though, the holes move along the platforms – you can see this in the video above.

The solution I used in the end I think really highlights how a lot of what you see in a game is smoke and mirrors. Whether the illusion is down to constraints of the engine or constraints on computing power, there is always a kind of magician’s act going on. When bizarre glitches occur, quite often I bet what is actually happening is that the illusion has broken down momentarily and we are seeing the game world for what it really is.

Anyway, in my case, the holes had to be separate objects that could move independently along platforms. Therefore, Unity objects were instantiated for each hole, and their colour was set to match the background colour so they would appear invisible.

2017-01-09 (7).png

Next, in order for the character to actually pass through them, I would need to detect when the player was in a hole, which meant assigning a trigger collider (a collider that detect collisions but does not apply any forces as a result). A script was set up on these hole objects so that, if the player jumped into one, the collider for the entire parent platform would be switched off. I actually made the colliders for the holes quite tall so that whenever the player was standing above or below a hole the collider for the respective platform would be switched off.

leapcollider.gif

The issue with this approach was that it introduced a whole bunch of bugs into the game due to the number of edge cases, such as jumping through a hole and immediately falling back through it or falling through a hole without jumping first. Because I was switching the collider for the platform on and off manually, certain OnCollisionEnter2D function calls were not executing which I was relying on for checking if the player was on the ground or not. Without that function call, the game still thought the player was on the ground and so could jump in mid air again.

Eventually however I was able to overcome those bugs by essentially keeping an eye out for those edge cases and making sure that the correct code was executed at those times. Perhaps there is a more elegant solution for dealing with moving holes which reduces the number of edge cases, but in the end this solution worked well enough for me.

End

The final steps of the project were to add enemies and make the game work on my Android phone, neither of which kept me back too long.

Overall, I feel like the project was a good learning experience, especially in terms of learning a bit more about the common problems faced when building a simple platformer. Next time I think I’ll try something outside of Unity.

leap.gif