Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Raspberry pi pico 2w
90 min
Share

بناء لعبة العدّاء اللانهائي باستخدام لوحة راسبيرى باى بيكو

في هذا الدرس سنتعلم كيفية بناء لعبة العداء اللا نهائى باستخدام لوحة راسبيرى باى بيكو 2W، فى هذه اللعبة يقابل اللاعب عوائق قادمة فى طريقة وعليه القفز فوق الحواجز القادمة فى الوقت المثالى عن طريق الضغط على مفتاح ضغاط متصل باللوحة.

Project Video

Overview

Getting the Items

Raspberry Pi Pico 2 wireless
Get Item
2×16 LCD with I2C Module
Get Item
Push button Switch (5 Pack) - 12mm
Get Item
Full-size Breadboard
Get Item
Jumper Wires - Male to Male (40 Pack)
Get Item
Jumper Wires – Male to Female (40 Pack)
Get Item

Steps

Wiring it Up

قم بتوصيل الأسلاك بين لوحة راسبيرى باى بيكو 2W والشاشة الكريستالية والمفتاح الضغاط كما فى الصورة التالية:

التوصيلات من لوحة راسبيرى باى بيكو 2W :

• نقوم بتوصيل منفذ ال VBUS بلوحة راسبيرى باى بيكو2W   ← المنافذ الموجبة بلوحة التجارب

• منفذ ال GND بلوحة راسبيرى باى بيكو2W  ←المنافذ السالبة بلوحة التجارب

ثانيا : التوصيلات من المفتاح :

• الطرف الاول من المفتاح ← المنافذ السالبة بلوحة التجارب

• الطرف الثانى من المفتاح ← منفذ رقم 2 بلوحة راسبيرى باى بيكو 2W

ثالثا : التوصيلات من الشاشة الكريستالية:

• منفذ ال VCC  للشاشة الكريستالية ← المنافذ الموجبة بلوحة التجارب

• منفذ ال GND  للشاشة الكريستالية ← المنافذ السالبة بلوحة التجارب

• منفذ SDA للشاشة الكريستالية ← منفذ رقم 0 بلوحة راسبيرى باى بيكو 2W

• منفذ SCL للشاشة الكريستالية ← منفذ رقم 1 بلوحة راسبيرى باى بيكو 2W

Coding

وظيفة النص البرمجي التالي هي التحكم في قفز العداء عن طريق الضغط على المفتاح الضغاط المتصل بلوحة راسبيرى باي بيكو.

'''

Voltaat Learn (http://learn.voltaat.com)

Link to the full tutorial:

Tutorial: Building an endless runner game using a Raspberry Pi Pico board.

This code is for building an endless runner game using a Raspberry Pi Pico board

Note: You can use this sketch with any Raspberry Pi Pico.

'''

import machine

import random

import time

from machine import I2C, Pin

from lcd_api import LcdApi

from pico_i2c_lcd import I2cLcd

# Game settings

GAME_SPEED = 1

PIN_BUTTON = 2

# Sprite definitions

SPRITE_RUN1 = 1

SPRITE_RUN2 = 2

SPRITE_JUMP = 3

SPRITE_JUMP_UPPER = 4

SPRITE_JUMP_LOWER = 5

SPRITE_TERRAIN_EMPTY = ' '

SPRITE_TERRAIN_SOLID = 6

SPRITE_TERRAIN_SOLID_RIGHT = 7

SPRITE_TERRAIN_SOLID_LEFT = 8

HERO_HORIZONTAL_POSITION = 1

TERRAIN_WIDTH = 16

TERRAIN_EMPTY = 0

TERRAIN_LOWER_BLOCK = 1

TERRAIN_UPPER_BLOCK = 2

# Hero positions (simplified full-body jump)

HERO_POSITION_OFF = 0

HERO_POSITION_RUN_LOWER_1 = 1

HERO_POSITION_RUN_LOWER_2 = 2

HERO_POSITION_JUMP_LOWER = 3       # Full-body jump at bottom

HERO_POSITION_JUMP_MID = 4         # Full-body jump in middle

HERO_POSITION_JUMP_UPPER = 5       # Full-body jump at top

HERO_POSITION_RUN_UPPER_1 = 6      # Running at top

HERO_POSITION_RUN_UPPER_2 = 7      # Running at top

# I2C and LCD setup

I2C_ADDR = 0x27

I2C_NUM_ROWS = 2

I2C_NUM_COLS = 16

# Global variables

terrain_upper = [SPRITE_TERRAIN_EMPTY] * TERRAIN_WIDTH

terrain_lower = [SPRITE_TERRAIN_EMPTY] * TERRAIN_WIDTH

button_pushed = False

lcd = None

def init_lcd():

   global lcd

   try:

       addresses = [0x27, 0x3F, 0x20, 0x38]

       i2c = None

       lcd = None

       

       for addr in addresses:

           try:

               i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=100000)

               devices = i2c.scan()

               print("Detected I2C devices:", [hex(device) for device in devices])

               

               if addr in devices:

                   lcd = I2cLcd(i2c, addr, I2C_NUM_ROWS, I2C_NUM_COLS)

                   print(f"LCD found at address: {hex(addr)}")

                   break

           except Exception as e:

               print(f"Error at address {hex(addr)}: {e}")

               continue

       

       if lcd is None:

           print("LCD not found, using simulation mode.")

           lcd = DummyLcd()

           

       return lcd

       

   except Exception as e:

       print(f"LCD initialization error: {e}")

       return DummyLcd()

class DummyLcd:

   def __init__(self):

       self.rows = I2C_NUM_ROWS

       self.cols = I2C_NUM_COLS

       print("Dummy LCD initialized")

   

   def clear(self):

       print(" " * 40)

   

   def move_to(self, x, y):

       pass

   

   def putstr(self, text):

       print(text)

   

   def custom_char(self, location, charmap):

       print(f"Custom character created at location {location}")

   

   def backlight_on(self):

       pass

   

   def backlight_off(self):

       pass

def button_handler(pin):

   global button_pushed

   button_pushed = True

def initialize_graphics():

   global lcd

   

   # Custom character graphics (full-body hero jump)

   graphics = [

       # Run position 1 - running man

       [0x0E, 0x0E, 0x04, 0x0E, 0x15, 0x04, 0x0A, 0x11],

       # Run position 2 - running man

       [0x0E, 0x0E, 0x04, 0x0E, 0x15, 0x04, 0x0A, 0x0A],

       # Jump full-body - hero jumping (entire body)

       [0x0E, 0x0E, 0x15, 0x0E, 0x04, 0x04, 0x0A, 0x11],

       # Jump full mid - hero mid-air

       [0x0E, 0x0E, 0x15, 0x0E, 0x04, 0x04, 0x0A, 0x11],

       # Jump full upper - hero at top

       [0x0E, 0x0E, 0x15, 0x0E, 0x04, 0x04, 0x0A, 0x11],

       # Ground - solid block

       [0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F],

       # Ground right - solid block

       [0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F],

       # Ground left - solid block

       [0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F],

   ]

   

   try:

       for i in range(min(8, len(graphics))):

           lcd.custom_char(i + 1, graphics[i])

       print("Graphics initialized successfully")

   except Exception as e:

       print(f"Error creating custom characters: {e}")

   

   for i in range(TERRAIN_WIDTH):

       terrain_upper[i] = SPRITE_TERRAIN_EMPTY

       terrain_lower[i] = SPRITE_TERRAIN_EMPTY

def advance_terrain(terrain, new_terrain):

   for i in range(TERRAIN_WIDTH):

       current = terrain[i]

       next_val = new_terrain if i == TERRAIN_WIDTH - 1 else terrain[i + 1]

       

       if current == SPRITE_TERRAIN_EMPTY:

           terrain[i] = SPRITE_TERRAIN_SOLID if next_val == SPRITE_TERRAIN_SOLID else SPRITE_TERRAIN_EMPTY

       elif current == SPRITE_TERRAIN_SOLID:

           terrain[i] = SPRITE_TERRAIN_EMPTY if next_val == SPRITE_TERRAIN_EMPTY else SPRITE_TERRAIN_SOLID

       elif current in [SPRITE_TERRAIN_SOLID_RIGHT, SPRITE_TERRAIN_SOLID_LEFT]:

           terrain[i] = SPRITE_TERRAIN_SOLID

def draw_hero(position, terrain_upper, terrain_lower, score):

   collide = False

   upper_save = terrain_upper[HERO_HORIZONTAL_POSITION]

   lower_save = terrain_lower[HERO_HORIZONTAL_POSITION]

   

   upper = SPRITE_TERRAIN_EMPTY

   lower = SPRITE_TERRAIN_EMPTY

   

   # Hero states with full-body jump

   if position == HERO_POSITION_OFF:

       upper = lower = SPRITE_TERRAIN_EMPTY

   elif position == HERO_POSITION_RUN_LOWER_1:

       upper = SPRITE_TERRAIN_EMPTY

       lower = SPRITE_RUN1

   elif position == HERO_POSITION_RUN_LOWER_2:

       upper = SPRITE_TERRAIN_EMPTY

       lower = SPRITE_RUN2

   elif position == HERO_POSITION_JUMP_LOWER:

       upper = SPRITE_TERRAIN_EMPTY

       lower = SPRITE_JUMP

   elif position in [HERO_POSITION_JUMP_MID, HERO_POSITION_JUMP_UPPER]:

       upper = SPRITE_JUMP

       lower = SPRITE_TERRAIN_EMPTY

   elif position == HERO_POSITION_RUN_UPPER_1:

       upper = SPRITE_RUN1

       lower = SPRITE_TERRAIN_EMPTY

   elif position == HERO_POSITION_RUN_UPPER_2:

       upper = SPRITE_RUN2

       lower = SPRITE_TERRAIN_EMPTY

   

   if upper != ' ':

       terrain_upper[HERO_HORIZONTAL_POSITION] = upper

       collide = False if upper_save == SPRITE_TERRAIN_EMPTY else True

   

   if lower != ' ':

       terrain_lower[HERO_HORIZONTAL_POSITION] = lower

       collide = collide or (False if lower_save == SPRITE_TERRAIN_EMPTY else True)

   

   digits = 5 if score > 9999 else 4 if score > 999 else 3 if score > 99 else 2 if score > 9 else 1

   

   upper_line = "".join(chr(c) if isinstance(c, int) and 1 <= c <= 8 else str(c) for c in terrain_upper[:TERRAIN_WIDTH])

   lower_line = "".join(chr(c) if isinstance(c, int) and 1 <= c <= 8 else str(c) for c in terrain_lower[:TERRAIN_WIDTH])

   

   score_str = str(score)

   if len(upper_line) + len(score_str) <= I2C_NUM_COLS:

       upper_display = upper_line + " " * (I2C_NUM_COLS - len(upper_line) - len(score_str)) + score_str

   else:

       upper_display = upper_line[:I2C_NUM_COLS - len(score_str)] + score_str

   

   lcd.clear()

   lcd.move_to(0, 0)

   lcd.putstr(upper_display[:I2C_NUM_COLS])

   lcd.move_to(0, 1)

   lcd.putstr(lower_line[:I2C_NUM_COLS])

   

   terrain_upper[HERO_HORIZONTAL_POSITION] = upper_save

   terrain_lower[HERO_HORIZONTAL_POSITION] = lower_save

   

   return collide

class TerrainGenerator:

   def __init__(self):

       self.last_block_type = TERRAIN_EMPTY

       self.blocks_since_last_gap = 0

       self.min_gap_interval = 4

       self.max_blocks_before_gap = 3

       self.difficulty_level = 0

       self.distance_counter = 0

       self.last_block_end = -10

       

   def update_difficulty(self, distance):

       self.distance_counter = distance

       self.difficulty_level = min(3, distance // 50)

       

   def generate_terrain_block(self):

       blocks_since_last = self.blocks_since_last_gap

       if blocks_since_last < self.min_gap_interval:

           gap_needed = self.min_gap_interval - blocks_since_last

           self.blocks_since_last_gap += gap_needed

           self.last_block_type = TERRAIN_EMPTY

           return TERRAIN_EMPTY, gap_needed

       

       base_density = 0.8

       density_bonus = self.difficulty_level * 0.05

       block_density = min(0.9, base_density + density_bonus)

       

       if self.blocks_since_last_gap >= self.min_gap_interval + self.max_blocks_before_gap:

           gap_length = random.randint(4, 6)

           self.blocks_since_last_gap = 0

           self.last_block_type = TERRAIN_EMPTY

           return TERRAIN_EMPTY, gap_length

       

       if random.random() < block_density:

           self.blocks_since_last_gap = 1

           

           if self.last_block_type in [TERRAIN_UPPER_BLOCK, TERRAIN_LOWER_BLOCK]:

               if random.random() < 0.7:

                   block_type = self.last_block_type

               else:

                   block_type = random.choice([TERRAIN_LOWER_BLOCK, TERRAIN_UPPER_BLOCK])

           else:

               block_type = random.choice([TERRAIN_LOWER_BLOCK, TERRAIN_UPPER_BLOCK])

           

           self.last_block_type = block_type

           block_length = random.randint(2, 3)

           return block_type, block_length

       else:

           gap_length = random.randint(1, 2)

           self.blocks_since_last_gap += gap_length

           self.last_block_type = TERRAIN_EMPTY

           return TERRAIN_EMPTY, gap_length

def main():

   global button_pushed, lcd

   

   print("Starting the game...")

   

   lcd = init_lcd()

   button = Pin(PIN_BUTTON, Pin.IN, Pin.PULL_UP)

   button.irq(trigger=Pin.IRQ_FALLING, handler=button_handler)

   initialize_graphics()

   

   hero_pos = HERO_POSITION_RUN_LOWER_1

   new_terrain_type = TERRAIN_EMPTY

   current_block_length = 4

   playing = False

   blink = False

   distance = 0

   jump_progress = 0

   is_jumping = False

   

   terrain_gen = TerrainGenerator()

   

   print("Game ready. Press the button to start...")

   

   while True:

       if not playing:

           if blink:

               lcd.clear()

               lcd.move_to(3, 0)

               lcd.putstr("Start Game")

               lcd.move_to(1, 1)

               lcd.putstr("VOLTAAT LEARN")

               time.sleep(0.5)

           else:

               lcd.clear()

               time.sleep(0.3)

           

           blink = not blink

           

           if button_pushed:

               print("Game started...")

               initialize_graphics()

               hero_pos = HERO_POSITION_RUN_LOWER_1

               playing = True

               button_pushed = False

               distance = 0

               jump_progress = 0

               is_jumping = False

               current_block_length = 4

               new_terrain_type = TERRAIN_EMPTY

               terrain_gen = TerrainGenerator()

               lcd.clear()

           continue

       

       terrain_gen.update_difficulty(distance)

       

       advance_terrain(terrain_lower, SPRITE_TERRAIN_SOLID if new_terrain_type == TERRAIN_LOWER_BLOCK else SPRITE_TERRAIN_EMPTY)

       advance_terrain(terrain_upper, SPRITE_TERRAIN_SOLID if new_terrain_type == TERRAIN_UPPER_BLOCK else SPRITE_TERRAIN_EMPTY)

       

       current_block_length -= 1

       if current_block_length <= 0:

           new_terrain_type, current_block_length = terrain_gen.generate_terrain_block()

           print(f"New block: {new_terrain_type}, length: {current_block_length}, distance: {distance}")

       

       # Jump handling - full-body jump

       if button_pushed and not is_jumping and hero_pos in [HERO_POSITION_RUN_LOWER_1, HERO_POSITION_RUN_LOWER_2, HERO_POSITION_RUN_UPPER_1, HERO_POSITION_RUN_UPPER_2]:

           is_jumping = True

           jump_progress = 0

           button_pushed = False

       

       if is_jumping:

           jump_progress += 1

           # Simple jump sequence

           if jump_progress == 1:

               hero_pos = HERO_POSITION_JUMP_LOWER

           elif jump_progress == 2:

               hero_pos = HERO_POSITION_JUMP_MID

           elif jump_progress == 3:

               hero_pos = HERO_POSITION_JUMP_UPPER

           elif jump_progress == 4:

               hero_pos = HERO_POSITION_JUMP_MID

           elif jump_progress == 5:

               hero_pos = HERO_POSITION_JUMP_LOWER

           else:

               is_jumping = False

               jump_progress = 0

               if terrain_lower[HERO_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY:

                   hero_pos = HERO_POSITION_RUN_UPPER_1

               else:

                   hero_pos = HERO_POSITION_RUN_LOWER_1

       

       # Running animation when not jumping

       if not is_jumping:

           if hero_pos in [HERO_POSITION_RUN_LOWER_1, HERO_POSITION_RUN_UPPER_1]:

               hero_pos += 1

           elif hero_pos in [HERO_POSITION_RUN_LOWER_2, HERO_POSITION_RUN_UPPER_2]:

               hero_pos -= 1

       

       if draw_hero(hero_pos, terrain_upper, terrain_lower, distance >> 2):

           print(f"Game over! Score: {distance >> 2}")

           playing = False

           for i in range(6):

               lcd.clear()

               lcd.move_to(4, 0)

               lcd.putstr("Game Over")

               lcd.move_to(3, 1)

               lcd.putstr(f"Score: {distance >> 2}")

               time.sleep(0.3)

               lcd.clear()

               time.sleep(0.2)

       else:

           distance += 1

       

       time.sleep(0.2 * (1 / GAME_SPEED))

if __name__ == "__main__":

   main()

Testing it Out

بعد رفع الكود البرمجى قم بالضغط على المفتاح الضغاط ليقفز اللاعب على العوائق التى امامه, ويجب عليك الضغط على المفتاح فى الوقت المثالى حتى لا يصطدم اللاعب بالعوائق.

Resources

No items found.