DEV Community

Kajiru
Kajiru

Posted on

Getting Started with 2D Games Using Pyxel (Part 7): Control the Character

Control the Character

In this chapter, we detect the timing when the Space key is pressed

and implement logic to flip the player’s movement direction left and right.

This gives a stronger sense that you are directly controlling the player.

The complete code is shown at the end of this chapter.

1. Add a Method to Flip Left and Right

First, set the player’s initial movement direction.

In the constructor of the Game class,

make the player move either to the right (0 degrees) or to the left (180 degrees).

# main.py
# inside Game.__init__()

# Randomly choose left or right
deg = 0 if random.random() < 0.5 else 180
self.ship.move(SHIP_SPD, deg)
Enter fullscreen mode Exit fullscreen mode

Next, add a flip_x() method to switch the movement direction (left/right).

This method multiplies the x-axis velocity vx by -1.

# sprite.py
# added to BaseSprite class

def flip_x(self):
    """Flip movement in the x direction"""
    self.vx *= -1
Enter fullscreen mode Exit fullscreen mode

By reversing the sign of the x-axis velocity,

you can switch directions like this:

  • Right → Left
  • Left → Right

2. Handle Keyboard Input

Next, add a method to handle keyboard input.

Add a new method called control_ship() to the Game class.

Here, we use pyxel.btnp() to detect the exact moment a key is pressed.

By passing pyxel.KEY_SPACE as an argument,

we detect when the Space key is pressed.

# main.py
# added to Game class

def control_ship(self):
    """Action"""
    if pyxel.btnp(pyxel.KEY_SPACE):
        self.ship.flip_x()  # Reverse movement
Enter fullscreen mode Exit fullscreen mode

Each time you press the Space key,

the player’s movement direction switches between left and right.

3. Screen Wrap (Overlap) Processing

Finally, add logic for when the character goes off the screen.

We compare the character’s position with the screen boundaries

and add an overlap_spr() method to detect when it leaves the screen.

If the character goes off-screen,

it reappears on the opposite side (left ↔ right, top ↔ bottom).

By using spr as an argument,

this method can be reused not only for the player

but also for future objects such as asteroids.

# main.py
# added to Game class

def overlap_spr(self, spr):
    """Move to the opposite side when leaving the screen"""
    if spr.x < -spr.w:
        spr.x = W
        return
    if W < spr.x:
        spr.x = -spr.w
        return
    if spr.y < -spr.h:
        spr.y = H
        return
    if H < spr.y:
        spr.y = -spr.h
        return
Enter fullscreen mode Exit fullscreen mode

Complete Code

Below is the complete code implementing all the features covered so far.

# sprite.py
import pyxel
import math
import random

class BaseSprite:

    def __init__(self, x, y, w=8, h=8):
        """Constructor"""
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.vx = 0
        self.vy = 0

    def update(self):
        """Update"""
        self.x += self.vx
        self.y += self.vy

    def draw(self):
        """Draw (implemented in subclasses)"""
        pass

    def move(self, spd, deg):
        """Move"""
        rad = deg * math.pi / 180
        self.vx = spd * math.cos(rad)  # x-axis velocity
        self.vy = spd * math.sin(rad)  # y-axis velocity

    def flip_x(self):
        """Flip movement in the x direction"""
        self.vx *= -1

class ShipSprite(BaseSprite):

    def __init__(self, x, y):
        """Constructor"""
        super().__init__(x, y)

    def draw(self):
        """Draw"""
        pyxel.blt(
            self.x, self.y, 0,
            0, 0,
            self.w, self.h, 0  # Ship
        )
Enter fullscreen mode Exit fullscreen mode
# main.py
import pyxel
import math
import random
import sprite

W, H = 160, 120
SHIP_SPD = 1.4  # Player speed

# Game
class Game:
    def __init__(self):
        """Constructor"""

        # Initialize score
        self.score = 0

        # Initialize player
        self.ship = sprite.ShipSprite(W / 2, H - 40)

        # Randomly choose left or right
        deg = 0 if random.random() < 0.5 else 180
        self.ship.move(SHIP_SPD, deg)

        # Start Pyxel
        pyxel.init(W, H, title="Hello, Pyxel!!")
        pyxel.load("shooter.pyxres")
        pyxel.run(self.update, self.draw)

    def update(self):
        """Update"""

        # Update player
        self.ship.update()
        # Control player
        self.control_ship()
        # Screen wrap check
        self.overlap_spr(self.ship)

    def draw(self):
        """Draw"""
        pyxel.cls(0)

        # Draw score
        pyxel.text(
            10, 10,
            "SCORE:{:04}".format(self.score), 12
        )

        # Draw player
        self.ship.draw()

    def control_ship(self):
        """Action"""
        if pyxel.btnp(pyxel.KEY_SPACE):
            self.ship.flip_x()  # Reverse movement

    def overlap_spr(self, spr):
        """Move to the opposite side when leaving the screen"""
        if spr.x < -spr.w:
            spr.x = W
            return
        if W < spr.x:
            spr.x = -spr.w
            return
        if spr.y < -spr.h:
            spr.y = H
            return
        if H < spr.y:
            spr.y = -spr.h
            return

def main():
    """Main"""
    Game()

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

The result looks like this when you run the game.

Next Time...

Thank you for reading!

In the next chapter, we’ll cover “Let’s Spawn Asteroids.”

Stay tuned!

Top comments (0)