在 Pygame 中获取等距平铺鼠标选择

发布于 2025-01-11 09:48:19 字数 8767 浏览 0 评论 0原文

我没能正确地计算出这个数学,而且用语言解释起来有点困难。我成功地创建了一个等距网格,您可以用鼠标完美地选择图块,并且我成功地使用 was 实现相机移动d 键,仍然可以正确选择图块,但是有一个小错误,我无法弄清楚它来自哪里。 这就是发生的情况,但只是有时,取决于相机偏移的位置:

当发生这种情况时,它仅在 x 轴上,而不是在每个图块中。 我几乎要放弃这个了,因为我找不到这个错误,想在这里发帖看看是否有人遇到类似的问题。

import time
import pygame
import sys
import math
from os import path
from settings import *
from sprites import *

# ------------------------- SETTINGS ---------------------------- #
# COLORS (r, g, b)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
DARKGREY = (40, 40, 40)
LIGHTGREY = (100, 100, 100)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)

# game settings
WIDTH = 1024
HEIGHT = 768
FPS = 60
title = "Isometric-Based game"
BGCOLOUR = DARKGREY

TILE_X = 80
TILE_Y = 40

WORLD_X, WORLD_Y = 14, 10
ORIGIN_X, ORIGIN_Y = 5, 1

# Debug
pygame.init()
font = pygame.font.Font(None, 25)

CAMERA_SPEED = 300


def get_info(info_list):
    display_surface = pygame.display.get_surface()
    for i, key in enumerate(info_list):
        text = font.render(str(key) + " : " + str(info_list[key]), True, (255, 255, 255), (0, 0, 0))
        text_rect = text.get_rect()
        text_rect.y = 20 * i
        display_surface.blit(text, text_rect)


# ------------------------- SPRITES ---------------------------- #

class Camera:
    def __init__(self, game, x, y):
        self.game = game
        self.x, self.y = self.game.to_screen(x, y)
        self.vx, self.vy = 0, 0

    def update(self):
        self.get_keys()
        self.x += self.vx * self.game.dt
        self.y += self.vy * self.game.dt

    def get_keys(self):
        self.vx, self.vy = 0, 0
        keys = pygame.key.get_pressed()
        if keys[pygame.K_w]:
            self.vy = -CAMERA_SPEED
        if keys[pygame.K_s]:
            self.vy = CAMERA_SPEED
        if keys[pygame.K_a]:
            self.vx = -CAMERA_SPEED
        if keys[pygame.K_d]:
            self.vx = CAMERA_SPEED
        if self.vx != 0 and self.vy != 0:
            self.vx *= 1.0
            self.vy *= 0.50


class MouseSelection:
    def __init__(self, game, image):
        self.game = game
        self.image = image

    def update(self):
        # get mouse x and y
        self.mouse_x, self.mouse_y = pygame.mouse.get_pos()

        # get the mouse offset position inside the tile
        self.offset_x, self.offset_y = self.mouse_x % TILE_X, self.mouse_y % TILE_Y
        self.offset_x += self.game.scroll_x % TILE_X  # Add camera scroll to offset
        self.offset_y += self.game.scroll_y % TILE_Y

        # get the cell number
        self.cell_x, self.cell_y = (self.mouse_x // TILE_X), (self.mouse_y // TILE_Y)
        self.cell_x += int((self.game.scroll_x // TILE_X))  # Add camera scroll to cell
        self.cell_y += int((self.game.scroll_y // TILE_Y))

        # get the selected cell in iso grid
        self.selected_x = (self.cell_y - ORIGIN_Y) + (self.cell_x - ORIGIN_X)
        self.selected_y = (self.cell_y - ORIGIN_Y) - (self.cell_x - ORIGIN_X)

        # height and width of a quarter of a tile, select the corner of the tile to nodge to a direction
        h, w = TILE_Y / 2, TILE_X / 2
        if self.offset_y < (h / w) * (w - self.offset_x):
            self.selected_x -= 1
        if self.offset_y > (h / w) * self.offset_x + h:
            self.selected_y += 1
        if self.offset_y < (h / w) * self.offset_x - h:
            self.selected_y -= 1
        if self.offset_y > (h / w) * (2 * w - self.offset_x) + h:
            self.selected_x += 1

        # translate the selected cell to world coordinate
        self.selectedWorld_x, self.selectedWorld_y = self.game.to_screen(self.selected_x, self.selected_y)

    def draw(self):
        # Draw the selected tile with the camera scroll offset
        self.game.screen.blit(self.image, (self.selectedWorld_x - self.game.scroll_x,
                                           self.selectedWorld_y - self.game.scroll_y))


class SpriteSheet:
    def __init__(self, image):
        self.image = image
        self.frames = []

    def get_image(self):
        for row in range(2):
            for col in range(4):
                if row == 0:
                    image = pygame.Surface((TILE_Y, TILE_Y / 2)).convert_alpha()
                    image.blit(self.image, (0, 0), (col * TILE_X / 2, row * TILE_Y / 2, TILE_X, TILE_Y))
                    image = pygame.transform.scale(image, (TILE_X, TILE_Y))
                else:
                    image = pygame.Surface((TILE_Y, TILE_Y)).convert_alpha()
                    image.blit(self.image, (0, 0), (col * TILE_X / 2, row * TILE_Y / 2, TILE_X, TILE_Y * 2))
                    image = pygame.transform.scale(image, (TILE_X, TILE_Y * 2))
                image.set_colorkey(WHITE)
                self.frames.append(image)
        return self.frames


# ------------------------- GAME LOOP ---------------------------- #
class Game:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        pygame.display.set_caption(title)
        self.clock = pygame.time.Clock()
        pygame.key.set_repeat(400, 100)
        self.debug = {}
        self.sprite_sheet_image = pygame.image.load("isometric_whitebg - Copy.png")
        self.index = 1
        self.scroll_x, self.scroll_y = 0, 0

    def new(self):
        # initialize all variables and do all the setup for a new game
        self.sprite_sheet = SpriteSheet(self.sprite_sheet_image)
        self.tile_selected = self.sprite_sheet.get_image()[0]
        self.tiles = self.sprite_sheet.get_image()
        self.mouse_selection = MouseSelection(self, self.tile_selected)
        self.camera = Camera(self, 1, 1)

    def run(self):
        # game loop - set self.playing = False to end the game
        self.playing = True
        while self.playing:
            self.dt = self.clock.tick(FPS) / 1000
            self.events()
            self.update()
            self.draw()

    def quit(self):
        pygame.quit()
        sys.exit()

    def update(self):
        # update portion of the game loop
        self.camera.update()
        self.mouse_selection.update()
        self.mx, self.my = pygame.mouse.get_pos()

        # -------------------------------------------------- CAMERA SCROLLING ----------------------------------------#
        if self.camera.x - self.scroll_x != WIDTH / 2:
            self.scroll_x += (self.camera.x - (self.scroll_x + WIDTH / 2)) / 10
        if self.camera.y - self.scroll_y != HEIGHT / 2:
            self.scroll_y += (self.camera.y - (self.scroll_y + HEIGHT / 2)) / 10
        # -------------------------------------------------- CAMERA SCROLLING ----------------------------------------#

        self.debug_info()

    def to_screen(self, x, y):
        screen_x = (ORIGIN_X * TILE_X) + (x - y) * (TILE_X / 2)
        screen_y = (ORIGIN_Y * TILE_Y) + (x + y) * (TILE_Y / 2)
        return screen_x, screen_y

    def draw_world(self):
        for y in range(WORLD_Y):
            for x in range(WORLD_X):
                vWorld_x, vWorld_y = self.to_screen(x, y)
                # Invisible tile
                if self.index == 0:
                    self.screen.blit(self.tiles[1], (vWorld_x, vWorld_y))
                # Grass
                elif self.index == 1:
                    self.screen.blit(self.tiles[2], (vWorld_x - self.scroll_x, vWorld_y - self.scroll_y))

    def draw(self):
        self.screen.fill(BGCOLOUR)
        self.draw_world()
        self.mouse_selection.draw()

        get_info(self.debug)
        pygame.display.flip()

    def debug_info(self):
        self.debug["FPS"] = int(self.clock.get_fps())
        self.debug["Cell"] = self.mouse_selection.cell_x, self.mouse_selection.cell_y
        self.debug["Selected"] = int(self.mouse_selection.selected_x), int(self.mouse_selection.selected_y)
        self.debug["Scroll"] = int(self.scroll_x), int(self.scroll_y)
        self.debug["Mouse"] = int(self.mx), int(self.my)
        self.debug["Mouse_offset"] = int(self.mouse_selection.offset_x), int(self.mouse_selection.offset_y)

    def events(self):
        # catch all events here
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    self.quit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    pass


game = Game()
while True:
    game.new()
    game.run()

I'm not managing to get this math correct, and it's a little bit difficult to explain in words. I have managed to create a isometric grid, which you can select the tiles with the mouse perfectly, and I have managed to implement a camera movement using wasd keys and still get the tiles correctly selected, but there is a slightly bug which I can not figure out where is coming from.
This is what happens, but only sometimes, depend where the camera offset is:

when this happens, it is only on the x axis, and not in every tile.
I'm almost giving up on this cause I can't find the bug, thought of posting here to see if anyone had similar problem.

import time
import pygame
import sys
import math
from os import path
from settings import *
from sprites import *

# ------------------------- SETTINGS ---------------------------- #
# COLORS (r, g, b)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
DARKGREY = (40, 40, 40)
LIGHTGREY = (100, 100, 100)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)

# game settings
WIDTH = 1024
HEIGHT = 768
FPS = 60
title = "Isometric-Based game"
BGCOLOUR = DARKGREY

TILE_X = 80
TILE_Y = 40

WORLD_X, WORLD_Y = 14, 10
ORIGIN_X, ORIGIN_Y = 5, 1

# Debug
pygame.init()
font = pygame.font.Font(None, 25)

CAMERA_SPEED = 300


def get_info(info_list):
    display_surface = pygame.display.get_surface()
    for i, key in enumerate(info_list):
        text = font.render(str(key) + " : " + str(info_list[key]), True, (255, 255, 255), (0, 0, 0))
        text_rect = text.get_rect()
        text_rect.y = 20 * i
        display_surface.blit(text, text_rect)


# ------------------------- SPRITES ---------------------------- #

class Camera:
    def __init__(self, game, x, y):
        self.game = game
        self.x, self.y = self.game.to_screen(x, y)
        self.vx, self.vy = 0, 0

    def update(self):
        self.get_keys()
        self.x += self.vx * self.game.dt
        self.y += self.vy * self.game.dt

    def get_keys(self):
        self.vx, self.vy = 0, 0
        keys = pygame.key.get_pressed()
        if keys[pygame.K_w]:
            self.vy = -CAMERA_SPEED
        if keys[pygame.K_s]:
            self.vy = CAMERA_SPEED
        if keys[pygame.K_a]:
            self.vx = -CAMERA_SPEED
        if keys[pygame.K_d]:
            self.vx = CAMERA_SPEED
        if self.vx != 0 and self.vy != 0:
            self.vx *= 1.0
            self.vy *= 0.50


class MouseSelection:
    def __init__(self, game, image):
        self.game = game
        self.image = image

    def update(self):
        # get mouse x and y
        self.mouse_x, self.mouse_y = pygame.mouse.get_pos()

        # get the mouse offset position inside the tile
        self.offset_x, self.offset_y = self.mouse_x % TILE_X, self.mouse_y % TILE_Y
        self.offset_x += self.game.scroll_x % TILE_X  # Add camera scroll to offset
        self.offset_y += self.game.scroll_y % TILE_Y

        # get the cell number
        self.cell_x, self.cell_y = (self.mouse_x // TILE_X), (self.mouse_y // TILE_Y)
        self.cell_x += int((self.game.scroll_x // TILE_X))  # Add camera scroll to cell
        self.cell_y += int((self.game.scroll_y // TILE_Y))

        # get the selected cell in iso grid
        self.selected_x = (self.cell_y - ORIGIN_Y) + (self.cell_x - ORIGIN_X)
        self.selected_y = (self.cell_y - ORIGIN_Y) - (self.cell_x - ORIGIN_X)

        # height and width of a quarter of a tile, select the corner of the tile to nodge to a direction
        h, w = TILE_Y / 2, TILE_X / 2
        if self.offset_y < (h / w) * (w - self.offset_x):
            self.selected_x -= 1
        if self.offset_y > (h / w) * self.offset_x + h:
            self.selected_y += 1
        if self.offset_y < (h / w) * self.offset_x - h:
            self.selected_y -= 1
        if self.offset_y > (h / w) * (2 * w - self.offset_x) + h:
            self.selected_x += 1

        # translate the selected cell to world coordinate
        self.selectedWorld_x, self.selectedWorld_y = self.game.to_screen(self.selected_x, self.selected_y)

    def draw(self):
        # Draw the selected tile with the camera scroll offset
        self.game.screen.blit(self.image, (self.selectedWorld_x - self.game.scroll_x,
                                           self.selectedWorld_y - self.game.scroll_y))


class SpriteSheet:
    def __init__(self, image):
        self.image = image
        self.frames = []

    def get_image(self):
        for row in range(2):
            for col in range(4):
                if row == 0:
                    image = pygame.Surface((TILE_Y, TILE_Y / 2)).convert_alpha()
                    image.blit(self.image, (0, 0), (col * TILE_X / 2, row * TILE_Y / 2, TILE_X, TILE_Y))
                    image = pygame.transform.scale(image, (TILE_X, TILE_Y))
                else:
                    image = pygame.Surface((TILE_Y, TILE_Y)).convert_alpha()
                    image.blit(self.image, (0, 0), (col * TILE_X / 2, row * TILE_Y / 2, TILE_X, TILE_Y * 2))
                    image = pygame.transform.scale(image, (TILE_X, TILE_Y * 2))
                image.set_colorkey(WHITE)
                self.frames.append(image)
        return self.frames


# ------------------------- GAME LOOP ---------------------------- #
class Game:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        pygame.display.set_caption(title)
        self.clock = pygame.time.Clock()
        pygame.key.set_repeat(400, 100)
        self.debug = {}
        self.sprite_sheet_image = pygame.image.load("isometric_whitebg - Copy.png")
        self.index = 1
        self.scroll_x, self.scroll_y = 0, 0

    def new(self):
        # initialize all variables and do all the setup for a new game
        self.sprite_sheet = SpriteSheet(self.sprite_sheet_image)
        self.tile_selected = self.sprite_sheet.get_image()[0]
        self.tiles = self.sprite_sheet.get_image()
        self.mouse_selection = MouseSelection(self, self.tile_selected)
        self.camera = Camera(self, 1, 1)

    def run(self):
        # game loop - set self.playing = False to end the game
        self.playing = True
        while self.playing:
            self.dt = self.clock.tick(FPS) / 1000
            self.events()
            self.update()
            self.draw()

    def quit(self):
        pygame.quit()
        sys.exit()

    def update(self):
        # update portion of the game loop
        self.camera.update()
        self.mouse_selection.update()
        self.mx, self.my = pygame.mouse.get_pos()

        # -------------------------------------------------- CAMERA SCROLLING ----------------------------------------#
        if self.camera.x - self.scroll_x != WIDTH / 2:
            self.scroll_x += (self.camera.x - (self.scroll_x + WIDTH / 2)) / 10
        if self.camera.y - self.scroll_y != HEIGHT / 2:
            self.scroll_y += (self.camera.y - (self.scroll_y + HEIGHT / 2)) / 10
        # -------------------------------------------------- CAMERA SCROLLING ----------------------------------------#

        self.debug_info()

    def to_screen(self, x, y):
        screen_x = (ORIGIN_X * TILE_X) + (x - y) * (TILE_X / 2)
        screen_y = (ORIGIN_Y * TILE_Y) + (x + y) * (TILE_Y / 2)
        return screen_x, screen_y

    def draw_world(self):
        for y in range(WORLD_Y):
            for x in range(WORLD_X):
                vWorld_x, vWorld_y = self.to_screen(x, y)
                # Invisible tile
                if self.index == 0:
                    self.screen.blit(self.tiles[1], (vWorld_x, vWorld_y))
                # Grass
                elif self.index == 1:
                    self.screen.blit(self.tiles[2], (vWorld_x - self.scroll_x, vWorld_y - self.scroll_y))

    def draw(self):
        self.screen.fill(BGCOLOUR)
        self.draw_world()
        self.mouse_selection.draw()

        get_info(self.debug)
        pygame.display.flip()

    def debug_info(self):
        self.debug["FPS"] = int(self.clock.get_fps())
        self.debug["Cell"] = self.mouse_selection.cell_x, self.mouse_selection.cell_y
        self.debug["Selected"] = int(self.mouse_selection.selected_x), int(self.mouse_selection.selected_y)
        self.debug["Scroll"] = int(self.scroll_x), int(self.scroll_y)
        self.debug["Mouse"] = int(self.mx), int(self.my)
        self.debug["Mouse_offset"] = int(self.mouse_selection.offset_x), int(self.mouse_selection.offset_y)

    def events(self):
        # catch all events here
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    self.quit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    pass


game = Game()
while True:
    game.new()
    game.run()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

£冰雨忧蓝° 2025-01-18 09:48:19

定义地图的角点:

map_outline = [
    pygame.math.Vector2(left_x, left_y), 
    pygame.math.Vector2(top_x, top_y),
    pygame.math.Vector2(right_x, right_y,
    pygame.math.Vector2(bottom_x, bottom_y)
]

利用此信息,您可以计算地图的 x 轴和 y 轴:

origin = map_outline[0]
x_axis = (map_outline[1] - map_outline[0]) / columns
y_axis = (map_outline[3] - map_outline[0]) / rows

< img src="https://i.sstatic.net/76T75.png" alt="">

您可以使用 x 轴和 y 轴来计算地图中的点作为以下函数行和列:

def transform(p, mat2x2):
    x = p[0] * mat2x2[0][0] + p[1] * mat2x2[1][0]
    y = p[0] * mat2x2[0][1] + p[1] * mat2x2[1][1]
    return pygame.math.Vector2(x, y)

p_position = transform((column + 0.5, row + 0.5), (x_axis, y_axis)) + origin

如果你想根据鼠标光标获取行和列,则需要执行相反的操作。您需要从 x 轴和 y 轴计算2x2 逆矩阵。使用逆矩阵,您可以将行和列计算为地图上点的函数:

def inverseMat2x2(m):
    a, b, c, d = m[0].x, m[0].y, m[1].x, m[1].y
    det = 1 / (a*d - b*c)
    return [(d*det, -b*det), (-c*det, a*det)]

m_pos = pygame.mouse.get_pos()
m_grid_pos = transform(pygame.math.Vector2(m_pos) - origin, point_to_grid)
m_col, m_row = int(m_grid_pos[0]), int(m_grid_pos[1])

另请参阅 PyGameExamplesAndAnswers - 等距


最小示例:

replit.com/@Rabbid76/Pygame-IsometircMap

import pygame

pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()

colors = {'g': (40, 128, 40), 'd': (90, 60, 40)}
tilemap = [
    'gdddg',
    'dgddd',
    'ggddg',
    'ggddg',
    'ddddg',
    'dgggd'
]
columns, rows = len(tilemap[0]), len(tilemap)

isometric_tiles = {}
for key, color in colors.items():
    tile_surf = pygame.Surface((50, 50), pygame.SRCALPHA)
    tile_surf.fill(color)
    tile_surf = pygame.transform.rotate(tile_surf, 45)
    isometric_size = tile_surf.get_width()
    tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
    isometric_tiles[key] = tile_surf
tile_size = (isometric_size, isometric_size//2)

def tileRect(column, row, tile_size):
    x = (column + row) * tile_size[0] // 2
    y = ((columns - column - 1) + row) * tile_size[1] // 2 
    return pygame.Rect(x, y, *tile_size)

game_map = pygame.Surface(((columns+rows) * isometric_size // 2, (columns+rows) * isometric_size // 4), pygame.SRCALPHA)
for column in range(columns):
    for row in range(rows):
        tile_surf = isometric_tiles[tilemap[row][column]]
        tile_rect = tileRect(column, row, tile_size)
        game_map.blit(tile_surf, tile_rect)

map_rect = game_map.get_rect(center = window.get_rect().center)
map_outline = [
    pygame.math.Vector2(0, columns * isometric_size / 4), 
    pygame.math.Vector2(columns * isometric_size / 2, 0),
    pygame.math.Vector2((columns+rows) * isometric_size // 2, rows * isometric_size / 4),
    pygame.math.Vector2(rows * isometric_size / 2, (columns+rows) * isometric_size // 4)
]
for pt in map_outline:
   pt += map_rect.topleft 

origin = map_outline[0]
x_axis = (map_outline[1] - map_outline[0]) / columns
y_axis = (map_outline[3] - map_outline[0]) / rows

def inverseMat2x2(m):
    a, b, c, d = m[0].x, m[0].y, m[1].x, m[1].y
    det = 1 / (a*d - b*c)
    return [(d*det, -b*det), (-c*det, a*det)]

point_to_grid = inverseMat2x2((x_axis, y_axis))

def transform(p, mat2x2):
    x = p[0] * mat2x2[0][0] + p[1] * mat2x2[1][0]
    y = p[0] * mat2x2[0][1] + p[1] * mat2x2[1][1]
    return pygame.math.Vector2(x, y)
    
font = pygame.font.SysFont(None, 30)
textO = font.render("O", True, (255, 255, 255))
textX = font.render("X", True, (255, 0, 0))
textY = font.render("Y", True, (0, 255, 0))

p_col, p_row = 2, 2

run = True 
while run:
    clock.tick(100)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_a and p_col > 0:
                p_col -= 1
            if event.key == pygame.K_d and p_col < columns-1:
                p_col += 1
            if event.key == pygame.K_w and p_row > 0:
                p_row -= 1
            if event.key == pygame.K_s and p_row < rows-1:
                p_row += 1

    p_position = transform((p_col + 0.5, p_row + 0.5), (x_axis, y_axis)) + origin
    m_pos = pygame.mouse.get_pos()
    m_grid_pos = transform(pygame.math.Vector2(m_pos) - origin, point_to_grid)
    m_col, m_row = int(m_grid_pos[0]), int(m_grid_pos[1])
        
    window.fill((0, 0, 0))
    window.blit(game_map, map_rect)
    pygame.draw.lines(window, (164, 164, 164), True, map_outline, 3)
    pygame.draw.line(window, (255, 0, 0), origin, origin+x_axis, 3)
    pygame.draw.line(window, (0, 255, 0), origin, origin+y_axis, 3)
    pygame.draw.circle(window, (255, 255, 255), origin, 5)
    pygame.draw.circle(window, (255, 0, 0), origin+x_axis, 5)
    pygame.draw.circle(window, (0, 255, 0), origin+y_axis, 5)
    window.blit(textO, textO.get_rect(topright = origin+(-5, 5)))   
    window.blit(textX, textX.get_rect(bottomright = origin+x_axis+(-5, -5)))
    window.blit(textY, textX.get_rect(topright = origin+y_axis+(-5, 5))) 
    pygame.draw.ellipse(window, (255, 255, 0), (p_position[0]-16, p_position[1]-8, 32, 16))
    if 0 <= m_grid_pos[0] < columns and 0 <= m_grid_pos[1] < rows:
        tile_rect = tileRect(m_col, m_row, tile_size).move(map_rect.topleft)
        pts = [tile_rect.midleft, tile_rect.midtop, tile_rect.midright, tile_rect.midbottom]
        pygame.draw.lines(window, (255, 255, 255), True, pts, 4)
    pygame.display.update()

pygame.quit()
exit()

Define the corner points of the map:

map_outline = [
    pygame.math.Vector2(left_x, left_y), 
    pygame.math.Vector2(top_x, top_y),
    pygame.math.Vector2(right_x, right_y,
    pygame.math.Vector2(bottom_x, bottom_y)
]

With this information you can calculate the x and y axis of the map:

origin = map_outline[0]
x_axis = (map_outline[1] - map_outline[0]) / columns
y_axis = (map_outline[3] - map_outline[0]) / rows

You can use the x-axis and the y-axis to calculate a point in the map as a function of the row and column:

def transform(p, mat2x2):
    x = p[0] * mat2x2[0][0] + p[1] * mat2x2[1][0]
    y = p[0] * mat2x2[0][1] + p[1] * mat2x2[1][1]
    return pygame.math.Vector2(x, y)

p_position = transform((column + 0.5, row + 0.5), (x_axis, y_axis)) + origin

If you want to get the row and column depending on the mouse cursor, you need to do the opposite. You need to calculate the inverse 2x2 matrix from the x and y axis. Using the inverse matrix, you can calculate the row and column as a function of a point on the map:

def inverseMat2x2(m):
    a, b, c, d = m[0].x, m[0].y, m[1].x, m[1].y
    det = 1 / (a*d - b*c)
    return [(d*det, -b*det), (-c*det, a*det)]

m_pos = pygame.mouse.get_pos()
m_grid_pos = transform(pygame.math.Vector2(m_pos) - origin, point_to_grid)
m_col, m_row = int(m_grid_pos[0]), int(m_grid_pos[1])

Also see PyGameExamplesAndAnswers - Isometric


Minimal example:

replit.com/@Rabbid76/Pygame-IsometircMap

import pygame

pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()

colors = {'g': (40, 128, 40), 'd': (90, 60, 40)}
tilemap = [
    'gdddg',
    'dgddd',
    'ggddg',
    'ggddg',
    'ddddg',
    'dgggd'
]
columns, rows = len(tilemap[0]), len(tilemap)

isometric_tiles = {}
for key, color in colors.items():
    tile_surf = pygame.Surface((50, 50), pygame.SRCALPHA)
    tile_surf.fill(color)
    tile_surf = pygame.transform.rotate(tile_surf, 45)
    isometric_size = tile_surf.get_width()
    tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
    isometric_tiles[key] = tile_surf
tile_size = (isometric_size, isometric_size//2)

def tileRect(column, row, tile_size):
    x = (column + row) * tile_size[0] // 2
    y = ((columns - column - 1) + row) * tile_size[1] // 2 
    return pygame.Rect(x, y, *tile_size)

game_map = pygame.Surface(((columns+rows) * isometric_size // 2, (columns+rows) * isometric_size // 4), pygame.SRCALPHA)
for column in range(columns):
    for row in range(rows):
        tile_surf = isometric_tiles[tilemap[row][column]]
        tile_rect = tileRect(column, row, tile_size)
        game_map.blit(tile_surf, tile_rect)

map_rect = game_map.get_rect(center = window.get_rect().center)
map_outline = [
    pygame.math.Vector2(0, columns * isometric_size / 4), 
    pygame.math.Vector2(columns * isometric_size / 2, 0),
    pygame.math.Vector2((columns+rows) * isometric_size // 2, rows * isometric_size / 4),
    pygame.math.Vector2(rows * isometric_size / 2, (columns+rows) * isometric_size // 4)
]
for pt in map_outline:
   pt += map_rect.topleft 

origin = map_outline[0]
x_axis = (map_outline[1] - map_outline[0]) / columns
y_axis = (map_outline[3] - map_outline[0]) / rows

def inverseMat2x2(m):
    a, b, c, d = m[0].x, m[0].y, m[1].x, m[1].y
    det = 1 / (a*d - b*c)
    return [(d*det, -b*det), (-c*det, a*det)]

point_to_grid = inverseMat2x2((x_axis, y_axis))

def transform(p, mat2x2):
    x = p[0] * mat2x2[0][0] + p[1] * mat2x2[1][0]
    y = p[0] * mat2x2[0][1] + p[1] * mat2x2[1][1]
    return pygame.math.Vector2(x, y)
    
font = pygame.font.SysFont(None, 30)
textO = font.render("O", True, (255, 255, 255))
textX = font.render("X", True, (255, 0, 0))
textY = font.render("Y", True, (0, 255, 0))

p_col, p_row = 2, 2

run = True 
while run:
    clock.tick(100)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_a and p_col > 0:
                p_col -= 1
            if event.key == pygame.K_d and p_col < columns-1:
                p_col += 1
            if event.key == pygame.K_w and p_row > 0:
                p_row -= 1
            if event.key == pygame.K_s and p_row < rows-1:
                p_row += 1

    p_position = transform((p_col + 0.5, p_row + 0.5), (x_axis, y_axis)) + origin
    m_pos = pygame.mouse.get_pos()
    m_grid_pos = transform(pygame.math.Vector2(m_pos) - origin, point_to_grid)
    m_col, m_row = int(m_grid_pos[0]), int(m_grid_pos[1])
        
    window.fill((0, 0, 0))
    window.blit(game_map, map_rect)
    pygame.draw.lines(window, (164, 164, 164), True, map_outline, 3)
    pygame.draw.line(window, (255, 0, 0), origin, origin+x_axis, 3)
    pygame.draw.line(window, (0, 255, 0), origin, origin+y_axis, 3)
    pygame.draw.circle(window, (255, 255, 255), origin, 5)
    pygame.draw.circle(window, (255, 0, 0), origin+x_axis, 5)
    pygame.draw.circle(window, (0, 255, 0), origin+y_axis, 5)
    window.blit(textO, textO.get_rect(topright = origin+(-5, 5)))   
    window.blit(textX, textX.get_rect(bottomright = origin+x_axis+(-5, -5)))
    window.blit(textY, textX.get_rect(topright = origin+y_axis+(-5, 5))) 
    pygame.draw.ellipse(window, (255, 255, 0), (p_position[0]-16, p_position[1]-8, 32, 16))
    if 0 <= m_grid_pos[0] < columns and 0 <= m_grid_pos[1] < rows:
        tile_rect = tileRect(m_col, m_row, tile_size).move(map_rect.topleft)
        pts = [tile_rect.midleft, tile_rect.midtop, tile_rect.midright, tile_rect.midbottom]
        pygame.draw.lines(window, (255, 255, 255), True, pts, 4)
    pygame.display.update()

pygame.quit()
exit()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文