Python Forum
D-Pad/No dependency platformer + tilemap
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
D-Pad/No dependency platformer + tilemap
#1
Contoler examples are hard to find, so I'm sharing this.

Plug in your controller and run the file. "Start" resets and "B" jumps.

It's meant to be an example/template for beginners to customize and take off from (ie. add sound, sprite images/animation, new levels, etc.)




import pygame as pg
import sys
from os import path
vec = pg.math.Vector2
import random

map1 = ("11111111111111111111111111111111111111111111111111111111111111111111111111111111",
        "1..............................................................................1",
        "1..............................................................................1",
        "1.............k..k...k...............................................k.........1",
        "1........................................111...................................1",
        "1...b................b...........................111...................b.......1",
        "1..............................................................................1",
        "1...........................................b...................m..............1",
        "1.........1111111111111111111111m..........111.111.111.......1LLLLLLLLLLLLLLLLL1",
        "1......1111............................................1111111111111111111111111",
        "1....................................b.........................................1",
        "1....................................k....k....................................1",
        "1...111................b...................................b...................1",
        "1..............................k..m.......................................111111",
        "1..............................1...............................................1",
        "1...P.....1111............................1....................................1",
        "1...........................1.......................1....1111...............k..1",
        "1....................m.........................................m.........1111111",
        "111111111111111111111LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL11111",
        "11111111111111111111111111111111111111111111111111111111111111111111111111111111",
        "11111111111111111111111111111111111111111111111111111111111111111111111111111111")

class Block(pg.sprite.Sprite):

    def __init__(self, game, col, row):
        self.groups = game.all_sprites, game.blocks
        pg.sprite.Sprite.__init__(self, self.groups)
        self.layer = 5
        self.game = game
        self.visability = 1
        self.image = pg.Surface((game.tile_size, game.tile_size))
        self.image.fill((120, 120, 100))
        self.rect = self.image.get_rect()
        self.rect.x = col * game.tile_size
        self.rect.y = row * game.tile_size
        self.pos = vec(self.rect.center)
        self.state = "normal"


    def update(self):
        if self.visability == 1:
            self.rect.center = self.pos

        else:
            self.rect.center = self.pos + (-10000, -10000)
class Moving_platform(pg.sprite.Sprite):

    def __init__(self, game, col, row, w, h, movement):
        self.groups = game.all_sprites, game.blocks
        pg.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self.image = pg.Surface((w, h))
        self.image.fill((200, 120, 200))
        self.rect = self.image.get_rect()
        self.rect.x = col * game.tile_size
        self.rect.y = row * game.tile_size
        self.vel = vec(0, 0)
        self.frame = 0
        self.movement = vec(movement)

    def update(self):
        self.frame += 1
        if self.frame < 100:
            self.vel = self.movement
        if self.frame > 100:
            self.vel = self.movement * -1
        if self.frame == 200:
            self.frame = 0
        self.rect.center += self.vel
        self.rect.y -= 2
        ride = self.rect.colliderect(self.game.player)
        self.rect.y += 2
        if ride:
             self.game.player.rect.center += vec(self.vel)
        else:
            self.game.player.platform_movement = vec(0, 0)
class Lava(pg.sprite.Sprite):
    def __init__(self, game, col, row):
        self.groups = game.all_sprites, game.lava
        pg.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self.image = pg.Surface((game.tile_size, game.tile_size - 4))
        self.image.fill((255, 120, 0))
        self.rect = self.image.get_rect()
        self.rect.x = col * game.tile_size
        self.rect.y = row * game.tile_size + 4

    def update(self):
        hit = self.rect.colliderect(self.game.player)
        if hit:
            self.frame = 0
            self.game.player.die()

class Player(pg.sprite.Sprite):
    def __init__(self, game, col, row):
        self.groups = game.all_sprites
        pg.sprite.Sprite.__init__(self, self.groups)
        self._layer = 5
        self.game = game
        self.width = 18
        self.height = 40
        self.original_image = pg.Surface((self.width, self.height))
        self.image = self.original_image
        self.image.fill((250, 200, 250))
        pg.draw.rect(self.original_image, (0, 0, 0), (self.width - 8, 10, 4, 4))
        pg.draw.rect(self.original_image, (0, 0, 0), (self.width - 6, 21, 6, 3))
        self.rect = self.image.get_rect()
        self.acc = vec(0, 0)
        self.vel = vec(0, 0)
        self.last_update = pg.time.get_ticks()
        self.rect.center = (col * self.game.tile_size, row * self.game.tile_size)
        self.frame = 0
        self.frame_rate = 60
        self.friction = -.12
        self.move_right = False
        self.move_left = False
        self.jump = False
        self.jumping = False
        self.jump_strength = 15
        self.gravity = .7
        self.friction = -.7
        self.terminal_vel = 10
        self.state = "normal"
        self.jump_safety = 0
        self.platform_movement = vec(0, 0)
        self.current_image = self.original_image
        self.pushing = False
        self.hit_rect = self.rect

    def update(self):
        if self.state == "dying":
            self.die()
        if self.state == "normal":
            self.acc = vec(0, self.gravity)
            self.hit_rect.center += self.platform_movement
            if self.move_right == True:
                self.acc.x = 4
            if self.move_left == True:
                self.acc.x = -4

            self.acc.x += self.vel.x * self.friction

            if self.vel.y > self.terminal_vel:
                self.vel.y = self.terminal_vel

            self.vel.x += self.acc.x

            self.hit_rect.x += self.vel.x
            if self.vel.x > -.1 and self.vel.x < .1:
                self.vel.x = 0
            for block in self.game.blocks:

                hit = self.hit_rect.colliderect(block)

                if hit:

                    if isinstance(block, Block):
                        if self.vel.x > 0:
                            self.hit_rect.right = block.rect.left
                        if self.vel.x < 0:
                            self.hit_rect.left = block.rect.right
                        self.vel.x = 0
                        self.acc.x = 0
                        self.hit_rect.x -= self.vel.x


            else:
                if self.vel.x < 0:
                    self.image = pg.transform.flip(self.original_image, True, False)
                if self.vel.x > 0:
                    self.image = self.original_image
            self.vel.y += self.acc.y
            self.hit_rect.y += self.vel.y

            for block in self.game.blocks:
                hit = self.hit_rect.colliderect(block)
                if hit:
                    if self.vel.y > self.gravity:
                        self.done_squishing = False
                    if self.vel.y > 0:
                        self.hit_rect.bottom = block.rect.top
                        self.vel.y = 0
                        self.jump_safety = pg.time.get_ticks()
                        self.jumping = False
                    if self.vel.y < 0:
                        self.hit_rect.top = block.rect.bottom
                        self.vel.y = 0

        if self.jump == True and self.jumping == False:
            self.jump_function()
        self.rect.midbottom = self.hit_rect.midbottom

    def die(self):
        if self.frame == 0:
            self.state = "dying"
            for i in range(13):
                Sparkle(self)
            self.image.fill((0, 0, 0))
        self.frame += 1
        if self.frame == 120:
            self.game.new()

    def jump_function(self):
        now = pg.time.get_ticks()
        if now - self.jump_safety < 250:
            self.jumping = True
            self.jump = False
            self.vel.y -= self.jump_strength

class Pickup(pg.sprite.Sprite):

    def __init__(self, game, col, row):
        self.groups = game.all_sprites, game.items
        pg.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self.image = pg.Surface((16, 16))
        self.rect = self.image.get_rect()
        self.rect.centerx = col * self.game.tile_size + self.game.tile_size/2
        self.rect.centery = row * self.game.tile_size + self.game.tile_size/2
        pg.draw.circle(self.image, (3, 255, 255), (8, 8), 8)
        self.game.coin_count += 1
        self.state = 1

    def update(self):
        self.pos = vec(self.rect.center)
        hit = self.rect.colliderect(self.game.player)
        if hit:

            self.game.coin_count -= 1
            for i in range(5):
                Sparkle(self)
            self.kill()

class Sparkle(pg.sprite.Sprite):

    def __init__(self, start):
        self.start = start
        self.image = pg.Surface((16, 16))
        self.rect = self.image.get_rect()
        self.radius = 7
        self.rect.center = start.rect.center
        speeds = [-3, -2, -1, 1, 2, 3]
        self.dir = vec(random.choice(speeds), random.choice(speeds))
        self.frame_rate = 60
        self.start.game.effects.append(self)
        self.last_update = 0
        self.spawntime = pg.time.get_ticks()
        self.life_span = 500
    def update(self):
        now = pg.time.get_ticks()
        if now - self.spawntime > self.life_span:
            self.start.game.effects.remove(self)
        if now - self.last_update > self.frame_rate:
            self.last_update = now
            if self.radius > 1:
                self.radius -= 1
        self.rect.center += self.dir + (0, 2)

class Bouncer(pg.sprite.Sprite):

    def __init__(self, game, col, row):
        self.groups = game.all_sprites, game.items
        pg.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self.image = pg.Surface((20, 20))
        self.image.fill((255, 155, 0))
        self.rect = self.image.get_rect()
        self.radius = 7
        self.rect.centerx = col * game.tile_size
        self.rect.centery = row * game.tile_size
        speeds = [-3, -2, -1, 1, 2, 3]
        self.dir = vec(random.choice(speeds), random.choice(speeds))
        self.frame_rate = 60
        self.last_update = 0


    def update(self):

        self.rect.centerx += self.dir.x

        hits = pg.sprite.spritecollide(self, self.game.blocks, False)
        if len(hits) > 0:
            self.dir.x *= -1
            self.rect.x += self.dir.x


        self.rect.y += self.dir.y
        hits = pg.sprite.spritecollide(self, self.game.blocks, False)

        if len(hits) > 0:
            self.dir.y *= -1
            self.rect.y += self.dir.y
        hit = self.rect.colliderect(self.game.player)
        if hit:
            self.game.player.die()

class TileMap:
    def __init__(self, game):
        self.game = game
        self.map_data = map1
        self.game.map_width = len(self.map_data[0]) * self.game.tile_size

    def make_map(self):
        map_surface = pg.Surface((600, 500))
        self.render(map_surface)
        return map_surface

    def render(self, surface):
        for row, tiles in enumerate(self.map_data):
            for col, tile in enumerate(tiles):
                        if tile == '1':
                            Block(self.game, col, row)

                        if tile == "k":
                            Pickup(self.game, col, row)

                        if tile == "L":
                            Lava(self.game, col, row)

                        if tile == "P":
                            self.game.player = Player(self.game, col, row)

                        if tile == "b":
                            Bouncer(self.game, col, row)

                        if tile == "m":
                            Moving_platform(self.game, col, row, self.game.tile_size * 3, self.game.tile_size/2, (2, 0))

class Camera:
    def __init__(self, width, height):
        self.camera = pg.Rect(0, 0, width, height)
        self.width = width
        self.height = height

    def apply(self, entity):
        return entity.rect.move(self.camera.topleft)

    def update(self, target):
        x = -target.rect.centerx + int(300)
        y = -target.rect.bottom + int(250)
        # limit scrolling to map size
        x = min(0, x)  # left
        y = min(0, y)  # top
        x = max(-(self.width - 600), x)  # right
        y = max(-(self.height - 100), y)  # bottom
        self.camera = pg.Rect(x, y, self.width, self.height)

class States(object):
    share = 'share'
    def __init__(self):
        self.done = False
        self.next = None
        self.quit = False
        self.previous = None

class Game(States):
    def __init__(self):
        States.__init__(self)
        self.next = 'start screen'
        self.width = 800
        self.height = 600
        self.screen = pg.display.set_mode((self.width, self.height))
        self.level = 0
        self.load_data()
        self.new()

    def cleanup(self):
        pass
    def startup(self):
        self.new()

    def load_data(self):
        self.tile_size = 32
        self.game_folder = path.dirname(__file__)
        self.main_font = pg.font.SysFont("comicsansms", 45)
        self.large_font = pg.font.SysFont("comicsansms", 76)
        self.coin_count = 0

    def new(self):
        if self.coin_count == 0:
            self.level += 1
            if self.level > 1:
                self.level = 1
        else:
            self.coin_count = 0
        self.all_sprites = pg.sprite.LayeredUpdates()
        self.blocks = pg.sprite.LayeredUpdates()
        self.lava = pg.sprite.Group()
        self.items = pg.sprite.LayeredUpdates()
        self.effects = []
        self.camera = Camera(2500, 800)
        self.tilemap = TileMap(self)
        self.tilemap.render(self.screen)
        self.my_joystick = pg.joystick.Joystick(0)
        self.my_joystick.init()
        self.start_time = pg.time.get_ticks()

    def get_event(self, event, dt):

        if self.my_joystick.get_button(0) == True:
            pass
        if self.my_joystick.get_button(0) == False:
            pass
        if self.my_joystick.get_button(7) == True:
            self.coin_count = 0
            self.new()
        if self.my_joystick.get_button(0) == True and self.player.jumping == False:
            self.player.jump = True
        if self.my_joystick.get_button(0) == False and self.player.jumping == True:
            if self.player.vel.y < 0:
                self.player.vel.y *= .5
        if self.my_joystick.get_button(2) == True:
            if self.play_music == 1:
                pg.mixer.music.play(loops=-1)
            else:
                pg.mixer.music.stop()
            self.play_music *= -1

        horiz_axis_pos = self.my_joystick.get_axis(0)
        vert_axis_pos = self.my_joystick.get_axis(1)

        if round(horiz_axis_pos) == 1:
            self.player.move_right = True
            self.player.move_left = False
        if round(horiz_axis_pos) == -1:
            self.player.move_left = True
            self.player.move_right = False
        if round(horiz_axis_pos) == 0:
            self.player.move_right = False
            self.player.move_left = False

    def update(self, screen, dt):
        self.all_sprites.update()
        self.camera.update(self.player)
        self.draw(screen)

    def draw(self, screen):
        screen.fill((2, 20, 1))
        for sprite in self.all_sprites:
            screen.blit(sprite.image, self.camera.apply(sprite))
        textsurface = self.main_font.render(f'{self.coin_count} LEFT', True, (250, 250, 255))
        screen.blit(textsurface, (20, 20))
        if self.coin_count == 0:
            textsurface = self.large_font.render('yOu WiN!!', True, (250, 250, 255))
            screen.blit(textsurface, (150, 200))
            textsurface = self.main_font.render('press  START', True, (250, 250, 255))
            screen.blit(textsurface, (275, 300))

        for effect in self.effects:
            effect.update()
            location = self.camera.apply(effect)
            pg.draw.circle(self.screen, (255, 255, 255), (location.x, location.y), effect.radius)
        pg.draw.rect(screen, ((255, 255, 255)), (0, 0, self.width, self.height), 2)

class Control:
    def __init__(self):
        self.done = False
        self.fps = 60
        self.clock = pg.time.Clock()
        pg.mixer.pre_init(44100, -16, 1, 2048)
        pg.init()

    def setup_states(self, state_dict, start_state):
        self.state_dict = state_dict
        self.state_name = start_state
        self.state = self.state_dict[self.state_name]
    def flip_state(self):
        self.state.done = False
        previous,self.state_name = self.state_name, self.state.next
        self.state.cleanup()
        self.state = self.state_dict[self.state_name]
        self.state.startup()
        self.state.previous = previous
    def update(self, dt):
        if self.state.quit:
            self.done = True
        elif self.state.done:
            self.flip_state()
        self.state.update(self.state_dict["game"].screen, dt)
    def event_loop(self, dt):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True
            self.state.get_event(event, dt)
    def main_game_loop(self):
        while not self.done:
            delta_time = self.clock.tick(self.fps)/1000.0
            self.event_loop(delta_time)
            self.update(delta_time)
            pg.display.update()

app = Control()
state_dict = {'game': Game()}
app.setup_states(state_dict, 'game')
app.main_game_loop()
pg.quit()
sys.exit()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  super lightweight (only 35 lines) dependency injection (ioc) support for Python kenttong 5 2,404 Jan-14-2023, 03:19 PM
Last Post: Gribouillis

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020