Python Forum
[PyGame] How to make collisions in isometrick game? - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: Game Development (https://python-forum.io/forum-11.html)
+--- Thread: [PyGame] How to make collisions in isometrick game? (/thread-34104.html)



How to make collisions in isometrick game? - Grigory - Jun-26-2021

Win 10
Python 3.9.5

I need to make collisions for my isometric game and for player
Can someone help me?
Sorry for bad english, i from Ukraine.

here is code:
import pygame
import sys
import time
import random

WIDTH = 700
HEIGHT = 700
from pygame.constants import QUIT



clock = pygame.time.Clock()


pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT), 0, 32)
pygame.display.set_caption("game base")
display = pygame.Surface((300, 300))

ground_img = pygame.image.load('ground3.png')
ground_img.set_colorkey((0, 0, 0))
stone_img = pygame.image.load("stone2.png").convert()
stone_img.set_colorkey((0, 0, 0))


ground_dict = {}  # словарь {ряд-номер клетки-номер слоя: название картинки}

# читаем данные карты из файла и заполняем ими словарь
with open('map.txt') as f:
    map_data = [[int(c) for c in row.strip()] for row in f]
    for y, row in enumerate(map_data):
        for x, tile in enumerate(row):              
            if tile:
                ground_dict[f'{y}-{x}-1'] = 'stone'
                # для второго слоя
                if random.randint(0, 1):
                    ground_dict[f'{y}-{x}-2'] = 'ground'                       

while True:
    display.fill((0, 0, 0))
    clock.tick(60)
    # проходим в цикле по словарю
    for key, value in ground_dict.items():
        # получаем ряд, номер клетки и номер слоя
        y, x, n = list(map(int, key.split('-')))
        # определяем нужную картинку для клетки
        z = stone_img if value == 'stone' else ground_img 
        # отрисовываем в зависимости от номера слоя
        if n == 1:
            display.blit(z, (150 + x * 10 - y * 10, 100 + x * 5 + y * 5))
        elif n == 2:
            display.blit(z, (150 + x * 10 - y * 10, 100 + x * 5 + y * 5 - 14))   
    
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    
    screen.blit(pygame.transform.scale(display, screen.get_size()), (55, 55))
    pygame.display.update()
    time.sleep(1)



RE: How to make collisions in isometrick game? - Windspar - Jul-01-2021

I couldn't get internet formula to work. So I made my own. Still working on it.
Something I been playing around with. No real purpose. Just testing.
import pygame
import itertools
from types import SimpleNamespace

color = SimpleNamespace(**dict([(k, pygame.Color(v)) for k, v in pygame.color.THECOLORS.items()]))

def create_box_image(color, size):
    size = size, size
    surface = pygame.Surface(size, pygame.SRCALPHA)
    surface.fill((0, 0, 0, 0))
    pygame.draw.rect(surface, color, (0, 0, *size), 1)
    return surface

def shift_image(image):
    w, h = image.get_size()
    size = w + h, h
    surface = pygame.Surface(size, pygame.SRCALPHA)
    for x in range(w):
        for y in range(h):
            color = image.get_at((x, y))
            surface.set_at((x + y, y), color)

    return surface

class TileMap:
    def __init__(self, tilemap, tilekey):
        self.tilemap = tilemap
        self.tilekey = tilekey
        self.hover = None
        self.hover2 = None

    def draw(self, camera, surface):
        for y, row in enumerate(self.tilemap):
            for x, key in enumerate(row):
                if key != 0:
                    image = self.tilekey[key]

                    if self.hover == self.hover2 and self.hover != None:
                        if self.hover[0] == x and self.hover[1] == y:
                            image = self.tilekey["hover3"]
                    else:
                        if self.hover:
                            if self.hover[0] == x and self.hover[1] == y:
                                image = self.tilekey["hover"]

                        if self.hover2:
                            if self.hover2[0] == x and self.hover2[1] == y:
                                image = self.tilekey["hover2"]

                    surface.blit(image, camera.map_to_screen((x, y)))

class TileCamera:
    def __init__(self, worldsize, position, view, tilesize, map):
        self.map = map
        self.view = view
        self.position = position
        self.tile = pygame.Rect(0, 0, tilesize, tilesize)
        self.surface = pygame.Surface(worldsize, pygame.SRCALPHA)

    def draw(self, surface):
        self.surface.fill(color.gray40)
        self.map.draw(self, self.surface)

        surface.blit(self.surface, self.position)

    def map_to_screen(self, position):
        return self.view[0] + position[0] * self.tile.w, self.view[1] + position[1] * self.tile.h

    def iso_hover(self, position):
        self.map.hover = position[0] // self.tile.w, position[1] // self.tile.h

class IsoCamera:
    def __init__(self, worldsize, position, view, tilesize, map):
        self.map = map
        self.view = view
        self.position = position
        self.tile = pygame.Rect(0, 0, tilesize * 2, tilesize)
        self.surface = pygame.Surface(worldsize, pygame.SRCALPHA)

    def draw(self, surface):
        self.surface.fill(color.gray30)
        self.map.draw(self, self.surface)

        surface.blit(self.surface, self.position)

    def get_position(self, mpos):
        return mpos[0] - self.view[0] - self.position[0], mpos[1] - self.view[1] - self.position[1]

    # Formula found on the net
    def real_screen_to_map(self, position):
        x = (position[0] - self.view[0] - self.position[0]) / self.tile.width
        y = (position[1] - self.view[1] - self.position[1]) / self.tile.height
        return int(x + y), int(y - x)

    # Formula I came up with
    def screen_to_map(self, position):
        # Remove all offset
        posx = position[0] - self.position[0] - self.view[0]
        posy = position[1] - self.position[1] - self.view[1]
        # Change to map position
        y = (posy - posx * 0.5 + self.tile.centery)
        x = (posx + y - self.tile.height) // self.tile.height
        y /= self.tile.height
        if y < 0:
            y -= 1
        return int(x), int(y)

    # Formula found on the net
    def map_to_screen(self, position):
        x, y = position
        return (self.view[0] + (x - y) * self.tile.centerx,
                self.view[1] + (x + y) * self.tile.centery)

class TileLabels:
    def __init__(self):
        pen = Pen(pygame.font.Font(None, 24), color.white)
        iso_ypos = itertools.count(10, 30)
        ypos = itertools.count(10, 30)
        self.labels = {
            "Mouse" : Label(pen, "Mouse Pos: ({}, {})", (0, 0), (10, next(iso_ypos)), "topleft"),
            "ISO Position" : Label(pen, "ISO Position: ({}, {})", (0, 0), (10, next(iso_ypos)), "topleft"),

            "Position" : Label(pen, "Position: ({}, {})", (0, 0), (410, next(ypos)), "topleft"),
            "Position2" : Label(pen, "Position: ({}, {})", (0, 0), (410, next(ypos)), "topleft"),
            }

    def draw(self, surface):
        for value in self.labels.values():
            value.draw(surface)

    # Formula I came up with
    def update(self, mpos, iso_cam):
        self.labels["Mouse"].render(*mpos)
        ix, iy = iso_cam.get_position(mpos)
        self.labels["ISO Position"].render(ix, iy)
        y = iy - ix * 0.5 + iso_cam.tile.centery
        x = ix + y - iso_cam.tile.h
        self.labels["Position"].render(x, y)
        self.labels["Position2"].render(*iso_cam.screen_to_map(mpos))
        return x, y

class Pen:
    def __init__(self, font, color):
        self.font = font
        self.color = color

    def render(self, string):
        return self.font.render(string, 1, self.color)

class Label:
    def __init__(self, pen, fstring, data, position, anchor):
        self.pen = pen
        self.fstring = fstring
        self.position = position
        self.anchor = anchor
        self.render(*data)

    def draw(self, surface):
        surface.blit(self.image, self.rect)

    def render(self, *data):
        text = self.fstring.format(*data)
        self.image = self.pen.render(text)
        self.rect = self.image.get_rect(**{self.anchor: self.position})

class Main:
    def __init__(self, caption, width, height, flags=0):
        pygame.display.set_caption(caption)
        self.surface = pygame.display.set_mode((width, height), flags)
        self.rect = self.surface.get_rect()
        self.clock = pygame.time.Clock()
        self.running = False
        self.delta = 0
        self.fps = 60

        self.tile_labels = TileLabels()

        tilesize = 32
        worldsize = 400, 400
        view = 150, 100
        self.maptile = [[1, 1, 1, 1, 1],
                        [1, 1, 1, 1, 1],
                        [1, 1, 1, 1, 1],
                        [1, 1, 1, 1, 1]]

        self.tilekey = {
            "hover" : create_box_image(color.lawngreen, tilesize),
            "hover2" : create_box_image(color.firebrick, tilesize),
            "hover3" : create_box_image(color.dodgerblue, tilesize),
            1 : create_box_image(color.snow, tilesize)
        }

        rotate = pygame.transform.rotate
        scale = pygame.transform.smoothscale
        self.isokey = {
            "hover" : scale(rotate(self.tilekey["hover"], 45), (64, 32)),
            "hover2" : scale(rotate(self.tilekey["hover2"], 45), (64, 32)),
            "hover3" : scale(rotate(self.tilekey["hover3"], 45), (64, 32)),
            1 : scale(pygame.transform.rotate(self.tilekey[1], 45), (64, 32))
        }

        self.tile_map = TileMap(self.maptile, self.tilekey)
        self.iso_map = TileMap(self.maptile, self.isokey)
        self.tile_camera = TileCamera(worldsize, (400, 100), view, tilesize, self.tile_map)
        self.iso_camera = IsoCamera(worldsize, (0, 100), view, tilesize, self.iso_map)

        self.inverse_mouse = None

    def draw(self):
        self.iso_camera.draw(self.surface)
        self.tile_camera.draw(self.surface)
        self.tile_labels.draw(self.surface)

    def mainloop(self):
        self.running = True
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.MOUSEMOTION:
                    self.iso_camera.map.hover2 = self.iso_camera.screen_to_map(event.pos)
                    pos = self.tile_labels.update(event.pos, self.iso_camera)
                    self.tile_camera.iso_hover(pos)
                    self.iso_camera.map.hover = self.tile_camera.map.hover
                elif event.type == pygame.QUIT:
                    self.running = False

            self.surface.fill(color.black)
            self.draw()
            pygame.display.flip()
            self.delta = self.clock.tick(self.fps) * 0.001

def main():
    pygame.init()
    app = Main("IsoMetric", 800, 500)
    app.mainloop()
    pygame.quit()

main()