Advertisement
Windspar

Example Scrolling Tile Map And Entities

Nov 9th, 2022
932
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.86 KB | Gaming | 0 0
  1. import pygame
  2. import random
  3. from pygame.sprite import Group, Sprite
  4.  
  5. class State:
  6.     def __init__(self, engine):
  7.         self.engine = engine
  8.  
  9.     def on_draw(self, surface): pass
  10.     def on_event(self, event): pass
  11.     def on_update(self, delta, ticks): pass
  12.  
  13. class DisplayStateMachine:
  14.     def __init__(self, caption, width, height, fps=60, flags=0):
  15.         pygame.display.set_caption(caption)
  16.         self.surface = pygame.display.set_mode((width, height), flags)
  17.         self.rect = self.surface.get_rect()
  18.         self.clock = pygame.time.Clock()
  19.         self.running = False
  20.         self.delta = 0
  21.         self.fps = fps
  22.  
  23.         self._state = State(self)
  24.         self.next_state = None
  25.  
  26.     def loop(self):
  27.         self.running = True
  28.  
  29.         while self.running:
  30.             if self.next_state is not None:
  31.                 self._state = self.next_state
  32.                 self.next_state = None
  33.  
  34.             for event in pygame.event.get():
  35.                 if event.type == pygame.QUIT:
  36.                     self.running = False
  37.                 else:
  38.                     self._state.on_event(event)
  39.  
  40.             ticks = pygame.time.get_ticks()
  41.             self._state.on_draw(self.surface)
  42.             self._state.on_update(self.delta, ticks)
  43.             pygame.display.flip()
  44.             self.delta = self.clock.tick(self.fps) * 0.001
  45.  
  46. class TickTimer:
  47.     def __init__(self, tick, interval):
  48.         self.tick = tick + interval
  49.         self.interval = interval
  50.  
  51.     def elapsed(self, ticks):
  52.         if ticks > self.tick:
  53.             self.tick = ticks + self.interval
  54.             return True
  55.  
  56.         return False
  57.  
  58. class RectPosition:
  59.     def __init__(self, position, anchor="topleft"):
  60.         self.position = position
  61.         self.anchor = anchor
  62.  
  63.     def set(self, rect):
  64.         setattr(rect, self.anchor, self.position)
  65.  
  66. class PenText(Sprite):
  67.     def __init__(self, pen, text, rect_pos):
  68.         super().__init__()
  69.         self.rect_pos = rect_pos
  70.         self.pen = pen
  71.         self.render(text)
  72.  
  73.     def render(self, text):
  74.         self.image = self.pen.render(text)
  75.         self.rect = self.image.get_rect()
  76.         self.rect_pos.set(self.rect)
  77.  
  78. class Pen:
  79.     def __init__(self, font, color):
  80.         self.font = font
  81.         self.color = color
  82.  
  83.     def render(self, text):
  84.         return self.font.render(text, 1, self.color)
  85.  
  86.     def write(self, text, rposition):
  87.         return PenText(self, text, rposition)
  88.  
  89. class Point:
  90.     @classmethod
  91.     def from_tuple(cls, tup):
  92.         return cls(tup[0], tup[1])
  93.  
  94.     def __init__(self, x, y):
  95.         self.x = x
  96.         self.y = y
  97.  
  98. class Camera:
  99.     def __init__(self, speed, rect, area):
  100.         self.speed = speed
  101.         self.rect = rect
  102.         self.area = area
  103.         self.position = pygame.Vector2(self.rect.topleft)
  104.  
  105.     def clamp(self):
  106.         clamp = self.rect.clamp(self.area)
  107.         if clamp.x != self.rect.x:
  108.             self.rect.x = clamp.x
  109.             self.position.x = clamp.x
  110.  
  111.         if clamp.y != self.rect.y:
  112.             self.rect.y = clamp.y
  113.             self.position.y = clamp.y
  114.  
  115.     def get_position(self):
  116.         return self.rect.topleft
  117.  
  118.     def move(self, vector, delta):
  119.         self.position += vector * self.speed * delta
  120.         self.rect.topleft = self.position
  121.         self.clamp()
  122.  
  123. class EdgeScroll:
  124.     def __init__(self, camera, distance, speed):
  125.         self.speed = 1 / speed
  126.         self.camera = camera
  127.         self.distance = distance
  128.  
  129.     def scroll(self, delta):
  130.         mpos = Point.from_tuple(pygame.mouse.get_pos())
  131.         width, height = self.camera.rect.size
  132.         direction = pygame.Vector2()
  133.  
  134.         if mpos.x < self.distance:
  135.             direction.x = mpos.x - self.distance
  136.         elif mpos.x > width - self.distance:
  137.             direction.x = mpos.x - (width - self.distance)
  138.  
  139.         if mpos.y < self.distance:
  140.             direction.y = mpos.y - self.distance
  141.         elif mpos.y > height - self.distance:
  142.             direction.y = mpos.y - (height - self.distance)
  143.  
  144.         if direction != pygame.Vector2():
  145.             direction *= self.speed
  146.             dir = abs(direction.x), abs(direction.y)
  147.             direction = direction.normalize().elementwise() * dir
  148.             self.camera.move(direction, delta)
  149.  
  150. class TileEngine:
  151.     def __init__(self, map_size, tile_size):
  152.         self.msize = Point.from_tuple(map_size)
  153.         self.tsize = Point.from_tuple(tile_size)
  154.  
  155.         row, col = map_size[0], map_size[1]
  156.         self.tiles = [[None for w in range(row)] for h in range(col)]
  157.  
  158.     def draw(self, surface, position):
  159.         rect = surface.get_rect()
  160.         width = rect.width // self.tsize.x
  161.         height = rect.height // self.tsize.y
  162.         tpos = Point.from_tuple(position)
  163.         tpos.x //= self.tsize.x
  164.         tpos.y //= self.tsize.y
  165.         offset = Point(position[0] - tpos.x * self.tsize.x,
  166.                        position[1] - tpos.y * self.tsize.y)
  167.  
  168.         for y in range(tpos.y, max(tpos.y + height, self.msize.y)):
  169.             for x in range(tpos.x, max(tpos.x + width, self.msize.x)):
  170.                 if self.tiles[y][x] is not None:
  171.                     pos = ((x - tpos.x) * self.tsize.x - offset.x,
  172.                            (y - tpos.y) * self.tsize.y - offset.y)
  173.  
  174.                     surface.blit(self.tiles[y][x], pos)
  175.  
  176.     def set_at(self, image, position):
  177.         x, y = position
  178.         self.tiles[y][x] = image
  179.  
  180.     def step(self):
  181.         for y in range(self.msize.y):
  182.             for x in range(self.msize.x):
  183.                 yield x, y
  184.  
  185. class TileImages:
  186.     def __init__(self):
  187.         self.tile_size = 50, 50
  188.         self.tiles = [
  189.             self.create_tile("firebrick", 30),
  190.             self.create_tile("blue", 20),
  191.             self.create_tile("lawngreen", 40),
  192.             self.create_tile("purple", 10)
  193.         ]
  194.  
  195.     def create_tile(self, color, dots):
  196.         surface = pygame.Surface(self.tile_size)
  197.         surface.fill(color)
  198.         dotcolor = pygame.Color(color).lerp("black", 0.4)
  199.         for r in range(dots):
  200.             w = random.randint(0, self.tile_size[0])
  201.             h = random.randint(0, self.tile_size[1])
  202.             surface.set_at((w, h), dotcolor)
  203.  
  204.         return surface
  205.  
  206. class Images:
  207.     def __init__(self):
  208.         self.triangle = self.create_triangle("orange", (30, 20))
  209.  
  210.     def create_triangle(self, color, size):
  211.         transparent = (0, 0, 0, 0)
  212.         surface = pygame.Surface(size, pygame.SRCALPHA)
  213.         surface.fill(transparent)
  214.         rect = surface.get_rect()
  215.         points = rect.topleft, rect.midright, rect.bottomleft
  216.         pygame.draw.polygon(surface, color, points)
  217.         return surface
  218.  
  219. class Entity(Sprite):
  220.     def __init__(self, image, position, anchor):
  221.         super().__init__()
  222.         self.image = image
  223.         self.rect = image.get_rect()
  224.         setattr(self.rect, anchor, position)
  225.         self.center = pygame.Vector2(self.rect.center)
  226.  
  227.     def move(self, vector):
  228.         self.center += vector
  229.         self.rect.center = self.center
  230.  
  231. # This need more work. You get the idea
  232. class Wander:
  233.     def __init__(self, sprite, area, speed):
  234.         self.oimage = sprite.image
  235.         self.sprite = sprite
  236.         self.speed = speed
  237.         self.area = area
  238.         self.area.center = sprite.rect.center
  239.         self.vector = pygame.Vector2()
  240.         self.angle = random.randint(0, 360)
  241.         self.vector.from_polar((1, self.angle))
  242.         self.wander_count = 0
  243.         self.random_wander()
  244.         self.timer = TickTimer(pygame.time.get_ticks(), 1000)
  245.         self.update_rotation()
  246.  
  247.     def random_wander(self):
  248.         self.wander = random.randint(30, 100)
  249.  
  250.     def change_movement(self):
  251.         if self.wander_count >= self.wander:
  252.             self.random_wander()
  253.             self.wander_count = 0
  254.             if self.angle <= 90 or self.angle >= 270:
  255.                 left = (self.angle + 90) % 360
  256.                 right = (self.angle - 90) % 360
  257.                 self.angle = random.randint(left, right)
  258.             else:
  259.                 left = (self.angle - 90) % 360
  260.                 right = (self.angle + 90) % 360
  261.                 choices = random.randint(0, left), random.randint(right, 360)
  262.                 self.angle = random.choice(choices)
  263.  
  264.             self.vector.from_polar((1, self.angle))
  265.             self.update_rotation()
  266.  
  267.     def update(self, delta, ticks):
  268.         vector = self.vector * self.speed * delta
  269.         rect = self.sprite.move(vector)
  270.         if ticks > self.timer.elapsed(ticks):
  271.             self.wander_count += 1
  272.             self.change_movement()
  273.  
  274.     def update_rotation(self):
  275.         self.sprite.image = pygame.transform.rotate(self.oimage, -self.angle)
  276.  
  277. class GameWorld(State):
  278.     def __init__(self, engine):
  279.         super().__init__(engine)
  280.         self.t_images = TileImages()
  281.         tpoint = Point.from_tuple(self.t_images.tile_size)
  282.         wpoint = Point(40, 40)
  283.         area = pygame.Rect(0, 0, tpoint.x * wpoint.x, tpoint.y * wpoint.y)
  284.         self.camera = Camera(40, engine.rect.copy(), area)
  285.         self.edge = EdgeScroll(self.camera, 40, 8)
  286.         self.tile_engine = TileEngine((wpoint.x, wpoint.y), self.t_images.tile_size)
  287.  
  288.         for pos in self.tile_engine.step():
  289.             self.tile_engine.set_at(random.choice(self.t_images.tiles), pos)
  290.  
  291.         pen = Pen(pygame.font.Font(None, 36), "snow")
  292.         self.text = pen.write("Pos :", RectPosition((10, 10), "topleft"))
  293.         self.text_timer = TickTimer(pygame.time.get_ticks(), 100)
  294.         self.text_group = Group(self.text)
  295.  
  296.         self.images = Images()
  297.         self.wanders = []
  298.         self.sprites = Group()
  299.         items = (
  300.             ((320, 320), pygame.Rect(0, 0, 100, 200)),
  301.             ((1200, 500), pygame.Rect(0, 0, 300, 200)),
  302.         )
  303.  
  304.         for pos, area in items:
  305.             entity = Entity(self.images.triangle, pos, "center")
  306.             self.sprites.add(entity)
  307.             self.wanders.append(Wander(entity, area, 30))
  308.  
  309.     def on_draw(self, surface):
  310.         surface.fill('black')
  311.         self.tile_engine.draw(surface, self.camera.get_position())
  312.         self.text_group.draw(surface)
  313.         for sprite in self.sprites:
  314.             if sprite.rect.colliderect(self.camera.rect):
  315.                 x = sprite.rect.x - self.camera.rect.x
  316.                 y = sprite.rect.y - self.camera.rect.y
  317.                 surface.blit(sprite.image, (x, y))
  318.  
  319.     def on_update(self, delta, ticks):
  320.         self.edge.scroll(delta)
  321.         if self.text_timer.elapsed(ticks):
  322.             self.text.render("Pos : " + str(self.camera.get_position()))
  323.  
  324.         for wander in self.wanders:
  325.             wander.update(delta, ticks)
  326.  
  327. if __name__ == "__main__":
  328.     pygame.init()
  329.     engine = DisplayStateMachine("Scrolling", 800, 600)
  330.     game = GameWorld(engine)
  331.     engine.next_state = game
  332.     engine.loop()
  333.     pygame.quit()
  334.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement