platformer.py

Created by azerlap652

Created on December 31, 2024

10.8 KB

Le niveau 4 est super dur mais le niveau 5 l’est 5x plus >:D

Flèches: se déplacer OK / flèche haut: sauter ALPHA: changer le thème EXE: recommencer le niveau

😁


## ENGINE
from math import *
from random import *
from kandinsky import *
from ion import *
from time import sleep, monotonic

W,H = 320,222
SIZE, WIN = (W,H), (0,0,W,H)
FONTW, FONTH = 10,18

# Colors
BLACK = (0, 0, 0)
DARK_GRAY = (95, 87, 79)
LIGHT_GRAY = (194, 195, 199)
WHITE = (255, 241, 232)
# 4-7
DARK_BLUE = (29, 43, 83)
BLUE = (41, 173, 255)
DARK_GREEN = (0, 135, 81)
GREEN = (0, 228, 54)
# 8-B
YELLOW = (255, 236, 39)
ORANGE = (255, 163, 0)
RED = (255, 0, 77)
BROWN = (171, 82, 54)
# C-F
PEACH = (255, 204, 170)
PINK = (255, 119, 168)
PURPLE = (126, 37, 83)
LAVENDER = (131, 118, 156)

COLORS = [BLACK, DARK_GRAY, LIGHT_GRAY, WHITE, DARK_BLUE, BLUE, DARK_GREEN, GREEN, YELLOW, ORANGE, RED, BROWN, PEACH, PINK, PURPLE, LAVENDER]

# Utils
def clamp(m,x,M): return max(m,min(x,M))
def proba(n): return randint(1,n)==1
def ints(list): return tuple(int(x) for x in list)
def center_pos(rect): return rect[0]+rect[2]//2, rect[1]+rect[3]//2

# - collision
def collide_x(r1,r2): return r1[0] < r2[0]+r2[2] and r2[0] < r1[0]+r1[2]
def collide_y(r1,r2): return r1[1] < r2[1]+r2[3] and r2[1] < r1[1]+r1[3]
def collide  (r1,r2): return collide_x(r1,r2) and collide_y(r1,r2)

# - rect
def bottom(rect): return rect[1]+rect[3]
def right(rect):  return rect[0]+rect[2]

def add(r1,r2):
    res = []
    for i in range(min(4,max(len(r1),len(r2)))):
        x = r1[i] if i < len(r1) else 0
        y = r2[i] if i < len(r2) else 0
        res.append(x+y)
    return res

# - drawing
def draw_rect(rect, color):
    fill_rect(int(rect[0]),int(rect[1]),int(rect[2]),int(rect[3]),color)

def draw_rect_mult(x,y,w,h, color):
    fill_rect(int(x),int(y),int(w),int(h),color)

def fill(color=WHITE):
    draw_rect(WIN, color)

def draw_rects(img,color=BLACK, X=0,Y=0):
    """( (x,y,w,h[,color]), ... )"""
    for r in img:
        draw_rect_mult(X+r[0],Y+r[1], r[2], r[3], (r[4] if len(r) > 4 else color))
    
def draw_text(text, x, y, side="topleft", color=BLACK, background=WHITE):
    if "right" in side: x -= len(text)*FONTW
    elif not "left" in side: x -= len(text)*FONTW/2
    if "bottom" in side: y -= FONTH
    elif not "top" in side: y -= FONTH/2
    draw_string(text, int(round(x)),int(round(y)), color, background)

# - time (time: ne pas utiliser en dehors d'engine)
_time,timers = 0,{}
def every(dt):
    if dt not in timers: timers[dt] = _time
    return (_time - timers[dt]) >= dt
    
def timer(t, dura):
    return (monotonic() - t) >= dura

# - update
just_pressed,_pressed = {},{}

def update(keys=[KEY_OK]):
    """ keys: touches inscrites dans just_pressed. ATTENTION: ne JAMAIS utiliser just_pressed dans un "every" """
    global _time
    for dt in timers:
        if _time-timers[dt] >= dt:
            timers[dt] = _time
    _time = monotonic()

    for key in keys:
        kd = keydown(key)
        just_pressed[key] = kd and not _pressed.get(key)
        _pressed[key] = kd
    return True


## JEU
MAPW, MAPH = 320,200
ECART = H-MAPH
in_bounds = lambda r: 0 <= r[0] and right(r) < MAPW and 0 <= r[1] and bottom(r) < MAPH

TS = 20
MOVE_COOLDOWN = 0.05
SPEED = 10
COYOTE = .2

GRAVITY = 15
JUMP = 20
GRAVITY_ACCEL = 2

# Couleurs
def apply_theme(theme):
    global BG, PLR_COLOR, BLOCKS, HEADER_COLOR, END_COLOR
    BG, PLR_COLOR, BLOCKS[0], HEADER_COLOR, BLOCKS[1], END_COLOR = themes[theme]

themes = [
    # Background, Player, Blocks, Header, Coin, End
    (BLUE, BLACK, DARK_GREEN, DARK_BLUE, YELLOW, BROWN),
    (WHITE, DARK_GRAY, LIGHT_GRAY, DARK_GRAY, ORANGE, GREEN),
    (PINK, PURPLE, LAVENDER, PURPLE, WHITE, BROWN),
    (DARK_GRAY, WHITE, BLACK, BLACK, YELLOW, GREEN),
]
BG = BLUE
PLR_COLOR = DARK_BLUE
BLOCKS = [DARK_GREEN, YELLOW]
END_COLOR = GREEN
HEADER_COLOR = DARK_BLUE
apply_theme(0)

# Créer monde
levels = [
[(2, 9, 0), (6, 7, 0), (10, 5, 0), (14, 8, 0), (14, 7, 0), (14, 6, 0), (14, 5, 0), (14, 4, 0), (14, 3, 0), (14, 2, 0), (14, 1, 'end'), (1, 9, 0), (1, 8, 'player'), (0, 9, 0), (15, 2, 0), (15, 3, 0), (15, 4, 0), (15, 5, 0), (15, 6, 0), (15, 7, 0), (15, 8, 0), (15, 9, 0), (14, 9, 0)],
[(3, 2, 0), (3, 3, 0), (3, 4, 0), (3, 5, 0), (3, 8, 0), (3, 9, 0), (3, 7, 0), (7, 0, 0), (7, 1, 0), (7, 2, 0), (7, 7, 0), (11, 3, 0), (11, 4, 0), (11, 5, 0), (11, 6, 0), (15, 0, 'end'), (15, 7, 0), (0, 9, 0), (2, 6, 0), (0, 8, 'player'), (11, 9, 0), (11, 2, 1), (12, 4, 0), (3, 6, 0), (2, 7, 0), (2, 8, 0), (2, 9, 0), (2, 10, 0), (2, 11, 0), (8, 0, 0), (8, 1, 0), (8, 2, 0), (8, 11, 0), (15, 8, 0), (15, 9, 0), (15, 10, 0), (12, 5, 0), (12, 2, 0), (11, 1, 0), (12, 1, 0), (11, 0, 0), (6, 0, 0), (0, 0, 0), (0, 1, 0), (1, 0, 0), (7, 3, 0), (7, 4, 0), (0, 2, 0)],
[(1, 9, 0), (6, 9, 0), (11, 9, 0), (15, 6, 0), (12, 5, 0), (11, 4, 0), (4, 0, 'end'), (1, 7, 'player'), (1, 3, 0), (0, 3, 0), (1, 2, 1), (4, 3, 0), (0, 4, 0), (0, 5, 0), (0, 6, 0), (0, 7, 0), (0, 8, 0), (0, 9, 0), (1, 4, 0), (1, 5, 0), (11, 5, 0), (15, 7, 0), (15, 8, 0), (15, 9, 0), (14, 9, 0), (13, 9, 0), (12, 9, 0), (3, 2, 0), (3, 3, 0), (2, 3, 0), (2, 4, 0), (3, 4, 0)],
[(0, 1, 0), (0, 0, 'player'), (10, 6, 0), (10, 2, 0), (7, 9, 0), (7, 6, 0), (7, 5, 0), (7, 3, 0), (7, 0, 0), (3, 0, 0), (3, 1, 0), (3, 2, 0), (3, 3, 0), (3, 4, 0), (3, 5, 0), (3, 9, 0), (3, 7, 0), (3, 6, 0), (10, 5, 0), (10, 4, 0), (8, 2, 0), (9, 1, 0), (9, 7, 0), (8, 7, 0), (10, 7, 1), (7, 7, 0), (15, 0, 'end'), (12, 5, 0), (12, 4, 0), (12, 3, 0), (12, 2, 0), (12, 1, 0), (12, 0, 0), (14, 0, 0), (14, 1, 0), (14, 2, 0), (14, 9, 0), (14, 5, 0)],
[(15, 0, 'end'), (0, 8, 'player'), (0, 9, 0), (1, 9, 0), (0, 6, 0), (1, 6, 0), (2, 6, 0), (4, 7, 0), (5, 9, 0), (6, 7, 0), (4, 5, 0), (6, 4, 0), (7, 5, 0), (4, 2, 0), (3, 3, 0), (10, 6, 0), (11, 7, 0), (1, 4, 0), (5, 6, 0), (9, 5, 0), (8, 7, 0), (7, 8, 0), (7, 7, 0), (4, 3, 0), (4, 6, 0), (9, 9, 0), (13, 9, 0), (13, 8, 0), (14, 6, 0), (15, 8, 0), (15, 4, 0), (13, 5, 0), (13, 3, 0), (15, 2, 0), (12, 3, 0), (9, 1, 0), (8, 4, 0), (6, 2, 0), (7, 0, 0), (5, 0, 0), (8, 2, 0), (12, 6, 0), (13, 6, 0), (6, 3, 0), (10, 2, 0), (12, 4, 0), (10, 3, 0), (11, 9, 0), (12, 1, 0), (12, 2, 0), (1, 7, 0), (3, 9, 0), (0, 0, 1), (0, 1, 0), (1, 2, 0), (2, 0, 0), (3, 1, 0), (3, 0, 0), (10, 1, 0), (9, 0, 0)],
[(11, 3, 0), (4, 7, 0), (5, 8, 0), (6, 8, 0), (7, 8, 0), (8, 8, 0), (9, 8, 0), (10, 8, 0), (11, 7, 0), (11, 2, 0), (12, 2, 0), (3, 1, 'player'), (11, 4, 0), (12, 4, 0), (13, 4, 0), (13, 3, 0), (13, 2, 0), (12, 3, 'end'), (3, 3, 0), (3, 4, 0), (4, 4, 0), (4, 3, 0)],
]

def create_world(level):
    world = [(0,-MAPH,MAPW,MAPH,0),(-MAPW,0,MAPW,MAPH,0),(MAPW,0,MAPW,MAPH,0)]
    blocks = []
    for x,y,type in levels[level-1]:
        if type == "player":
            plr = (x*TS+TS//4,y*TS+TS//4)
        elif type == "end":
            end = (x*TS+TS//4,y*TS+TS//4)
        elif type == 1:
            blocks.append((x*TS+TS//4,y*TS+TS//4,TS//2,TS//2,1))
        else:
            blocks.append((x*TS,y*TS,TS,TS,type))
    world.extend(blocks)

    plr_rect = [plr[0], plr[1], TS//2,TS//2]
    end_rect = [end[0], end[1], TS//2,TS//2]
    return world, plr_rect, end_rect

# Dessiner monde
def draw_block(rect, color, offset):
    fill_rect(rect[0]-offset[0], ECART+rect[1]-offset[1], rect[2], rect[3], color)

def draw_world(world, plr_rect, offset, end_rect, level, coins):
    fill(BG)
    draw_top(level, coins)
    for block in world[3:]:
        draw_block(block, BLOCKS[block[4]], offset)
    draw_block(plr_rect, PLR_COLOR, offset)
    draw_block(end_rect, END_COLOR, offset)

def draw_top(level, coins):
    fill_rect(0,0,W,ECART,HEADER_COLOR)
    draw_text("LEVEL "+str(level)+"/"+str(len(levels)), 20, ECART//2, "midleft", WHITE, HEADER_COLOR)
    draw_text("coins "+str(coins), W-20, ECART//2, "midright", WHITE, HEADER_COLOR)

def plr_collide(world, plr_rect):
    for rect in world:
        if collide(rect, plr_rect):
            return True
        
def game():
    # Setup
    level, coins = 1, 0
    theme = 0
    world, plr_rect, end_rect = create_world(level)
    plr_spawn = plr_rect[:]

    move_timer = 0
    offset = [0,0]
    vy = GRAVITY
    grounded = -float('inf')


    # Dessiner au début
    draw_world(world, plr_rect, offset, end_rect, level, coins)

    # Update
    while update(keys=[KEY_ALPHA]):

        # Mouvement
        dx, dy = 0, 0

        if (keydown(KEY_UP) or keydown(KEY_OK))and not timer(grounded, COYOTE):
            vy = -JUMP
            grounded = -float('inf')

        if timer(move_timer, MOVE_COOLDOWN):
            vy = min(GRAVITY, vy+GRAVITY_ACCEL)
            dy += vy
            dx -= (keydown(KEY_LEFT) and plr_rect[0] > 0) * SPEED
            dx += (keydown(KEY_RIGHT) and right(plr_rect) < MAPW) * SPEED
        has_moved = dx != 0 or dy != 0

        # - collision
        if has_moved and not plr_collide(world, plr_rect):
            r_dx = add(plr_rect,(dx,0))
            for block in world[:]:
                if collide(block, r_dx):
                    if block[4] == 1:
                        draw_block(block, BG, offset)
                        world.remove(block)
                        coins += 1
                        draw_top(level, coins)
                    elif dx > 0:
                        dx = block[0] - right(plr_rect)
                    elif dx < 0:
                        dx = right(block) - plr_rect[0]
                    r_dx[0] = plr_rect[0] + dx
            
            r_dy = add(plr_rect,(dx,dy))
            for block in world[:]:
                if collide(block, r_dy):
                    if block[4] == 1:
                        draw_block(block, BG, offset)
                        world.remove(block)
                        coins += 1
                        draw_top(level, coins)
                    elif dy > 0:
                        dy = block[1] - bottom(plr_rect)
                        grounded = monotonic()
                    elif dy < 0:
                        dy = bottom(block) - plr_rect[1]
                        vy = max(vy, 0)

            has_moved = dx != 0 or dy != 0

        # - move
        old_rect = plr_rect[:]
        if has_moved:
            plr_rect[0] += dx; plr_rect[1] += dy
            move_timer = monotonic()

        # End
        if collide(end_rect, plr_rect):
            level = min(len(levels), level+1)
            world, plr_rect, end_rect = create_world(level)
            plr_spawn = plr_rect[:]
            draw_world(world, plr_rect, offset, end_rect, level, coins)
            continue

        # Reset
        if keydown(KEY_EXE) or plr_rect[1] >= H:
            draw_block(old_rect, BG, offset)
            plr_rect = plr_spawn[:]

        # Draw
        if has_moved:
            draw_block(old_rect, BG, offset)
            draw_block(plr_rect, PLR_COLOR, offset)
            
        # Theme
        if just_pressed[KEY_ALPHA]:
            theme = (theme+1)%len(themes)
            apply_theme(theme)
            draw_world(world, plr_rect, offset, end_rect, level, coins)

        
    # Game over
    draw_text("ok to retry", W/2,H*.7,"center",BLACK,BG)
    draw_text("esc to quit", W/2,H*.8,"center",BLACK,BG)
    while not (update() and just_pressed.get(KEY_OK)): pass
    game()

# Start
game()

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.