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

2024-04-07

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:

当这种情况发生时,它仅发生在 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()


定义地图的角点:

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

您可以使用 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

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

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 - 等距 https://github.com/Rabbid76/PyGameExamplesAndAnswers/blob/master/documentation/pygame/pygame_isometric.md


最小的例子:

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()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Pygame 中获取等距平铺鼠标选择 的相关文章

随机推荐

  • 根据选择的选项更新输入值

    我正在尝试找出更新某些内容的最佳方法input值取决于从中选择的选项select 这是我想要实现的目标 我有一个显示域名详细信息的页面 我有一个表格input and select这允许更改价格 这input包含当前域名价格并允许用户输入新
  • Python 中的就地快速排序

    我必须用我选择的语言来实现作业的快速排序算法 所以我选择了 Python 在讲座中 我们被告知 QuickSort 内存效率高 因为它就地工作 即 它没有用于递归的输入数组部分的额外副本 考虑到这一点 我尝试在 Python 中实现 Qui
  • 使用 jquery 获取元素的可见高度而不是其实际高度

    这篇文章是相关的this one https stackoverflow com questions 14125303 change target of a link based on what is currently displayed
  • 无法在新 Mac 上打开 Xcode 项目

    我想我在这里犯了一个错误 几个月前我设计了一个应用程序 甚至在应用程序商店中找到了它 从那时起我就买了一台新的Mac 我将 Xcode 项目文件复制到我的新 Mac 上 我几个月来第一次回来看它 但什么也没有打开 我有时会收到无法解析的警告
  • 使用 JavaScript 更改 IE 中的 类型

    下面的代码适用于除 IE 之外的所有网络浏览器
  • 解决 Kotlin MPP 中的第三方 cocoapod 依赖关系

    我正在尝试设置一个用 Kotlin Multiplatform 编写的跟踪库来支持我们所有的移动客户端 Android 测试进展顺利 通过 gradle 集成 Snowplow 我还设法通过 cocoapods 将 Snowplow 集成到
  • java 负向前瞻

    我需要一个表达式来捕获这样的字符串 A 不是至少 5 且最多 6 位数字的字符串 B 换句话说 捕获不是以下内容的任何内容 A 0 9 0 9 0 9 0 9 0 9 B A 0 9 0 9 0 9 0 9 0 9 0 9 B 我尝试过消极
  • 如何检查libc++是否安装?

    我正在从源头构建一些东西 我的系统的 gcc 和 stdlibc 太旧了 但是我可以使用 clang 构建 默认情况下 clang 使用 stdlibc 但可以选择安装 libc 以供 clang 使用 检查 libc 是否与 clang
  • 调用未定义的函数 apache_request_headers()

    我刚刚将脚本切换到不同的服务器 在以前的服务器上 这工作得很好 现在我已经将它们切换到不同的服务器 我无法理解这个问题 我不确定这会有帮助 但这是相关的代码 headers apache request headers PHP版本是 PHP
  • 如何将SQL用户自定义函数添加到实体框架中?

    我可以像在 dbml 中那样向 edmx 文件添加 SQL 函数吗 如果可以的话 我该怎么做 如果我不能 有什么解决方法吗 我尝试谷歌 但找不到任何关于如何做到这一点的具体答案 根据给定的答案 我创建了一个存储过程并尝试添加 导入函数 但它
  • java 7 接口中默认方法的替代方案(仅在 Java 8 中)

    我想在我的一个接口类中使用默认方法 然后我意识到这仅在 Java 8 中可用 而我们正在使用 Java 7 在 Java 7 中实现类似的最佳方法是什么 将方法签名放在接口中 就像 Java 8 中一样 将您的方法默认实现放在实现该接口的抽
  • 无法通过代理建立隧道。代理通过 https 返回“HTTP/1.1 407”

    我尝试通过需要身份验证的 https 连接到服务器 此外 我中间有一个也需要身份验证的 http 代理 我使用 ProxyAuthSecurityHandler 向代理进行身份验证 使用 BasicAuthSecurityHandler 向
  • Rails 资源未更新

    我有一个 Rails 3 1 应用程序 由于某种原因 当我更改 CSS 时 更改不会显示 我做到了bundle exec rake assets precompile它曾经有帮助 但现在无论如何我都坚持使用旧的CSS 由于您的资产现已预编译
  • 如何使用 Window() 计算 PySpark 中的滚动中位数?

    如何计算前 3 个值的窗口大小的美元滚动中位数 输入数据 dollars timestampGMT 25 2017 03 18 11 27 18 17 2017 03 18 11 27 19 13 2017 03 18 11 27 20 2
  • “rails server”命令在终端中不起作用

    我对 Rails 还很陌生 正在尝试设置一个非常简单的测试站点 我使用以下命令创建了新的 Rails 应用程序目录rails new命令 但无法设置服务器以在浏览器中查看页面 以下是我尝试时收到的消息rails server MacBook
  • PyCryptodome 错误:MAC 检查失败

    我正在 Python 3 中使用 Pycryptodome 开发一个加密程序 我试图加密一个 字节 字符串 然后解密它并验证 MAC 标签 当我验证它时 会抛出错误 这是代码 from Crypto Cipher import AES fr
  • boost :仅迭代 ptree 的元素

    这应该很简单 我只是在学习提升 所以我错过了一些东西 我已经使用 json read 读取了一些简单的 JSON 现在有了一个 ptree 网络上的所有示例都显示使用 ptree get entry name 来获取条目 我想做的就是 pt
  • 在 AngularJS 中加载图像目录

    我正在尝试在我的 Angular 应用程序中实现一个图片库 我已经在这篇文章中管理了图片的上传 文件上传 如果目录不存在则创建 https stackoverflow com questions 30150646 file upload c
  • Rcpp 和 boost:它应该可以工作,但不能

    我对在以下位置找到的有趣帖子有疑问 具有四精度计算的 Rcpp https stackoverflow com questions 42262963 rcpp with quad precision computation I use Rc
  • 在 Pygame 中获取等距平铺鼠标选择

    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