00_base.py

import pyxel

# 1. Define constants for clarity
WIDTH = 160
HEIGHT = 120
FLOOR_Y = 100

class SimplePlatformer:
    def __init__(self):
        # Initialize the window
        pyxel.init(WIDTH, HEIGHT, title="Lesson 1: Basic Movement")
        
        # Player starting position
        self.x = WIDTH // 2
        self.y = FLOOR_Y
        
        # Start the game loop
        pyxel.run(self.update, self.draw)

    def update(self):
        # 2. Horizontal Movement logic
        if pyxel.btn(pyxel.KEY_LEFT):
            self.x -= 2
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.x += 2

        # 3. The "Infinite Wrap"
        # Keeps the player within the 0-160 range automatically
        self.x %= WIDTH

    def draw(self):
        # Clear the screen (0 = black)
        pyxel.cls(0)
        
        # 4. Draw the environment
        # rect(x, y, width, height, color)
        # 11 = Greenish-gray for the floor
        pyxel.rect(0, 108, WIDTH, 12, 11) 
        
        # 5. Draw the Player
        # Using a simple rectangle (color 3 = Green) for now
        pyxel.rect(self.x, self.y, 8, 8, 3)

# Run the game
SimplePlatformer()

01_base_and_jump.py

import pyxel

WIDTH = 160
HEIGHT = 120
FLOOR_Y = 100

class SimplePlatformer:
    def __init__(self):
        pyxel.init(WIDTH, HEIGHT, title="Lesson 2: Gravity & Jumping")
        
        self.x = WIDTH // 2
        self.y = FLOOR_Y
        
        # 1. New Physics Variables
        self.dy = 0           # Vertical velocity (delta Y)
        self.is_jumping = False
        
        pyxel.run(self.update, self.draw)

    def update(self):
        # Horizontal Movement (from Lesson 1)
        if pyxel.btn(pyxel.KEY_LEFT):
            self.x -= 2
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.x += 2
        self.x %= WIDTH

        # 2. The Jump Trigger
        # btnp means "button pressed" (only triggers once per tap)
        if pyxel.btnp(pyxel.KEY_SPACE) and not self.is_jumping:
            self.dy = -5      # Negative moves UP in computer graphics
            self.is_jumping = True

        # 3. Apply Gravity
        self.dy += 0.3        # Pull the player down slightly every frame
        self.y += self.dy     # Update the actual position based on velocity

        # 4. Floor Collision
        if self.y > FLOOR_Y:
            self.y = FLOOR_Y  # Snap to floor
            self.dy = 0       # Stop falling
            self.is_jumping = False

    def draw(self):
        pyxel.cls(0)
        pyxel.rect(0, 108, WIDTH, 12, 11) 
        pyxel.rect(self.x, self.y, 8, 8, 3)

SimplePlatformer()

02_smiley.py

import pyxel

WIDTH = 160
HEIGHT = 120
FLOOR_Y = 100

class SimplePlatformer:
    def __init__(self):
        pyxel.init(WIDTH, HEIGHT, title="Lesson 3: Bitmaps")
        
        # 1. Load your resource file
        # This brings in your images, tilemaps, and sounds
        pyxel.load("my_resource.pyxres")
        
        self.x = WIDTH // 2
        self.y = FLOOR_Y
        self.dy = 0
        self.is_jumping = False
        
        pyxel.run(self.update, self.draw)

    def update(self):
        # Horizontal Movement (unchanged)
        if pyxel.btn(pyxel.KEY_LEFT):
            self.x -= 2
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.x += 2
        self.x %= WIDTH

        # Gravity & Jumping (unchanged)
        if pyxel.btnp(pyxel.KEY_SPACE) and not self.is_jumping:
            self.dy = -5
            self.is_jumping = True

        self.dy += 0.3
        self.y += self.dy

        if self.y > FLOOR_Y:
            self.y = FLOOR_Y
            self.dy = 0
            self.is_jumping = False

    def draw(self):
        pyxel.cls(0)
        
        # Draw the Floor
        pyxel.rect(0, 108, WIDTH, 12, 11) 
        
        # 2. Draw the Player using a Bitmap
        # pyxel.blt(x, y, img_bank, u, v, width, height, colkey)
        # u,v = 0,0 is the top-left of your sprite sheet
        # colkey = 2 means color 2 (usually dark blue) is transparent
        pyxel.blt(self.x, self.y, 0, 0, 0, 8, 8, 2)

SimplePlatformer()

03_smiley_facing_movement.py

import pyxel

WIDTH = 160
HEIGHT = 120
FLOOR_Y = 100

class SimplePlatformer:
    def __init__(self):
        pyxel.init(WIDTH, HEIGHT, title="Lesson 4: Mirroring")
        pyxel.load("my_resource.pyxres")
        
        # 1. New Variable: facing_sign
        # 1 = Right (Normal), -1 = Left (Mirrored)
        self.facing_sign = 1
        
        self.x = WIDTH // 2
        self.y = FLOOR_Y
        self.dy = 0
        self.is_jumping = False
        
        pyxel.run(self.update, self.draw)

    def update(self):
        # 2. Update facing based on input
        if pyxel.btn(pyxel.KEY_LEFT):
            self.x -= 2
            self.facing_sign = -1  # Flip to left
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.x += 2
            self.facing_sign = 1   # Flip to right
            
        self.x %= WIDTH

        # Gravity & Jumping
        if pyxel.btnp(pyxel.KEY_SPACE) and not self.is_jumping:
            self.dy = -5
            self.is_jumping = True

        self.dy += 0.3
        self.y += self.dy

        if self.y > FLOOR_Y:
            self.y = FLOOR_Y
            self.dy = 0
            self.is_jumping = False

    def draw(self):
        pyxel.cls(0)
        pyxel.rect(0, 108, WIDTH, 12, 11) 
        
        # 3. Use facing_sign to mirror the sprite
        # Multiplying the width (8) by self.facing_sign 
        # results in 8 (normal) or -8 (mirrored)
        pyxel.blt(self.x, self.y, 0, 0, 0, self.facing_sign * 8, 8, 2)

SimplePlatformer()

04_jumping_animation.py

import pyxel

WIDTH = 160
HEIGHT = 120
FLOOR_Y = 100

class SimplePlatformer:
    def __init__(self):
        pyxel.init(WIDTH, HEIGHT, title="Lesson 5: Animation States")
        pyxel.load("my_resource.pyxres")
        
        self.facing_sign = 1
        self.x = WIDTH // 2
        self.y = FLOOR_Y
        self.dy = 0
        self.is_jumping = False
        
        pyxel.run(self.update, self.draw)

    def update(self):
        # 1. Movement and Facing
        if pyxel.btn(pyxel.KEY_LEFT):
            self.x -= 2
            self.facing_sign = -1
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.x += 2
            self.facing_sign = 1
        self.x %= WIDTH

        # 2. Gravity and Jump Logic
        if pyxel.btnp(pyxel.KEY_SPACE) and not self.is_jumping:
            self.dy = -5
            self.is_jumping = True

        self.dy += 0.3
        self.y += self.dy

        # 3. Ground Collision
        if self.y > FLOOR_Y:
            self.y = FLOOR_Y
            self.dy = 0
            self.is_jumping = False

    def draw(self):
        pyxel.cls(0)
        pyxel.rect(0, 108, WIDTH, 12, 11) 

        # 4. Logic to choose the correct Sprite (U, V)
        if self.is_jumping:
            if self.dy < 0: 
                # Jumping up (Ascending)
                u, v = 0, 8
            else:
                # Falling down (Descending)
                u, v = 8, 0
        else:
            # On the ground (Idle)
            u, v = 0, 0 

        # 5. Render the chosen sprite
        pyxel.blt(self.x, self.y, 0, u, v, self.facing_sign * 8, 8, 2)

SimplePlatformer()

05_generic_platforms.py

import pyxel

WIDTH = 160
HEIGHT = 120

class SimplePlatformer:
    def __init__(self):
        pyxel.init(WIDTH, HEIGHT, title="Lesson 6: Multiple Platforms")
        pyxel.load("my_resource.pyxres")
        
        # 1. Define our world platforms [x, y, w, h]
        self.platforms = [
            [0, 110, 160, 10],   # Main floor
            [40, 80, 40, 4],     # Low ledge
            [100, 60, 40, 4],    # High ledge
        ]
        
        self.x = 80
        self.y = 0               # Start in the air
        self.dy = 0
        self.facing_sign = 1
        self.is_jumping = True

        pyxel.run(self.update, self.draw)

    def update(self):
        # Movement
        if pyxel.btn(pyxel.KEY_LEFT):
            self.x -= 2
            self.facing_sign = -1
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.x += 2
            self.facing_sign = 1
        self.x %= WIDTH

        # Physics
        self.dy += 0.3
        new_y = self.y + self.dy
        on_platform = False
        
        for plat in self.platforms:
            px, py, pw, _ = plat
            # Check if player's X is within platform width
            if self.x + 8 > px and self.x < px + pw:
                # Check if player's feet are hitting the top of the platform
                # (and only if we are falling down)
                if self.y + 8 <= py and new_y + 8 >= py:
                    self.y = py - 8 # Snap to top
                    self.dy = 0
                    self.is_jumping = False
                    on_platform = True
                    break
        
        if not on_platform:
            self.y = new_y
            self.is_jumping = True

        # Jump Trigger
        if pyxel.btnp(pyxel.KEY_SPACE) and not self.is_jumping:
            self.dy = -5
            self.is_jumping = True

    def draw(self):
        pyxel.cls(0)
        for plat in self.platforms:
            pyxel.rect(*plat, 11) 

        # Choose sprite based on state
        if self.is_jumping:
            u, v = (0, 8) if self.dy < 0 else (8, 0)
        else:
            u, v = 0, 0
            
        pyxel.blt(self.x, self.y, 0, u, v, self.facing_sign * 8, 8, 2)

SimplePlatformer()

06_some_sounds.py

import pyxel

WIDTH, HEIGHT = 160, 120
class SimplePlatformer:
    def __init__(self):
        pyxel.init(WIDTH, HEIGHT, title="Lesson 7: Sound Effects")
        pyxel.load("my_resource.pyxres")
        
        # 1. Define Jump Sound (Sound 0)
        pyxel.sounds[0].set("c3c4", "S", "7", "N", 5)
        
        # 2. Define Landing Sound (Sound 1)
        pyxel.sounds[1].set("g1", "N", "5", "F", 10)

        self.platforms = [[0, 110, 160, 10], [40, 80, 40, 4], [100, 60, 40, 4]]
        self.x, self.y, self.dy = 80, 0, 0
        self.facing_sign = 1
        self.is_jumping = True

        pyxel.run(self.update, self.draw)

    def update(self):
        # Movement
        if pyxel.btn(pyxel.KEY_LEFT):
            self.x -= 2
            self.facing_sign = -1
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.x += 2
            self.facing_sign = 1
        self.x %= WIDTH

        # Physics
        self.dy += 0.3
        new_y = self.y + self.dy
        on_platform = False
        
        for plat in self.platforms:
            px, py, pw, _ = plat
            if self.x + 8 > px and self.x < px + pw:
                if self.y + 8 <= py and new_y + 8 >= py:
                    # Trigger Landing Sound
                    # We only play it if we were actually falling (is_jumping was True)
                    if self.is_jumping:
                        pyxel.play(0, 1) # (channel, sound_no)
                    
                    self.y = py - 8
                    self.dy = 0
                    self.is_jumping = False
                    on_platform = True
                    break
        
        if not on_platform:
            self.y = new_y
            self.is_jumping = True

        # Jump Trigger
        if pyxel.btnp(pyxel.KEY_SPACE) and not self.is_jumping:
            self.dy = -5
            self.is_jumping = True
            # 4. Trigger Jump Sound
            pyxel.play(0, 0)

    def draw(self):
        pyxel.cls(0)
        for plat in self.platforms:
            pyxel.rect(*plat, 11) 
        
        if self.is_jumping:
            u, v = (0, 8) if self.dy < 0 else (8, 0)
        else:
            u, v = 0, 0
            
        pyxel.blt(self.x, self.y, 0, u, v, self.facing_sign * 8, 8, 2)

SimplePlatformer()