Moving and Rotating Your Objects
Each of these is going to need a bit of code specific to it, so you’ll want to create objects more specific than
GameObject is still a good base class for the common code of all your sprites, but in this lesson, you’re going to add a child of
GameObject that is the ship itself. To fly your ship, you’ll need the game to respond to keyboard events.
00:43 Once your new ship class is in place, you’ll add some code for controlling its direction. The movement model for the ship is a single rocket booster and the ability to spin. There is no linear deceleration. If you want to slow down, turn the ship around 180 degrees and fire the rocket in the other direction.
01:03 This makes flying the ship a bit challenging, but that’s part of the fun of the game. One quick thing to remember from the coordinate system: It’s a bit counterintuitive, but up is a negative number.
As the ship moves and rotates, you’re going to need to do some vector math. To make the code clearer, I’ve created a constant here called
DIRECTION_UP that represents the up direction on the screen.
02:49 Our good little ship needs to turn. To do that, you’ll have to rotate the sprite and track which direction it is pointing in. Just like when moving the little red dot, the rotation movement also has a speed.
A bit of math is needed to rotate the sprite correctly, so I’ve grouped that together inside of this
.rotate() method. This first part figures out which direction to rotate. The default for the method is clockwise. To rotate clockwise, you’ll multiply the rotation by
1. To go counter-clockwise, you’ll multiply by
.rotate_ip() method on the
.direction vector does all the funky trig math you need to do to do the rotation. With the
.rotate() method all set to go, you now just need to update the
.draw() method to appropriately rotate the sprite based on the current direction vector. The Pygame
rotozoom() method does both a rotation and scaling of a sprite at the same time. It takes three parameters: the sprite image being rotated, the angle of rotation, and a scale factor.
The angle here is calculated using the
.angle_to() method on the
.direction vector. This figures out the difference in degrees between the current direction and straight up. As you’re only interested in rotating and not scaling, the final parameter to
rotozoom() is a scale factor of
The newly-rotated sprite isn’t in this situation anymore. This makes the bounding box of the blit incorrect. If you just use the coordinates of the rotated sprite, you might chop part of the image off. Fortunately, the
.get_size() method knows how to deal with this problem.
You’ll also recall that the
.position vector is based on the center of the game object. Previously, you just used the radius of the object to translate the ship’s local position into a blittable one. Now you do something similar, but use the recently calculated bounding box instead of the radius.
In the case of rotating the ship, you want to be able to hold the key and have rotation continue. In this case, instead of using the
KEYDOWN event, you use the
This returns a dict that has a key-value pair for every key Pygame supports. If the key is pressed at the time of the call to
.get_pressed(), then the value in the dictionary will be
True. Lines 30 through 31 are adding the ability to quit the game by pressing the Escape or q keys. Lines 32 and 33 call
.ship.rotate() in the clockwise direction when the right arrow is being pressed. And lines 34 and 35 rotate the ship in the other direction when left is pressed. With all that in place, your little ship can spin. Let’s check that out.
07:12 Rotating is cool and all, but I want to fly. This ship needs a rocket booster. To keep it simple, let’s not animate any flames or anything, but it can definitely do with some motion. As I mentioned before, this ship will have a single rocket engine out the back and therefore can only accelerate in the direction it is pointing. To slow your ship down, you have to rotate and accelerate in the opposite direction.
07:55 Just like with rotation, the acceleration math will be encapsulated in a method call. This math is simpler than rotation. All you have to do is multiply the direction in travel by the acceleration constant, making it go faster, and then add the new result to the velocity.
08:41 Uh oh, where did I go? Like our never-ending rock from lessons back, my ship is still trucking along, but trying to figure out the right rotation and acceleration to get it back on the screen could be a bit of a challenge without being able to see the ship.
If the ship is to stay in the boundaries of the screen, this is the place to make that happen. Line 20 is the old
.move() line. Line 21 is the new change to the method that ensures the object’s always on the screen.
GameObject is out of the surface’s bounds,
wrap_position() will return a new position that is still in the bounds. Note that the
.move() method now needs the surface being moved within for the call to work. The
game.py script will need updating, but I’ll get back to that in a minute. You’ve seen how
wrap_position() is used.
x value is modded against the width and the
y value modded against the height. If the
x of the object’s position exceeds the width, the mod operation will wrap it around. Same goes for the
y and the height.
Become a Member to join the conversation.