我没能正确地计算出这个数学,而且用语言解释起来有点困难。我成功地创建了一个等距网格,您可以用鼠标完美地选择图 block ,并且我成功地使用 was 实现相机移动d 键,仍然可以正确选择图 block ,但是有一个小错误,我无法弄清楚它来自哪里。 这就是发生的情况,但只是有时,取决于相机偏移的位置:
当这种情况发生时,它仅发生在 x 轴上,而不是在每个图 block 中。 我几乎要放弃这个了,因为我找不到这个错误,想在这里发帖看看是否有人遇到类似的问题。
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 的角点:
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)
]
利用此信息,您可以计算 map 的 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 轴来计算 map 中的点作为行和列的函数:
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
如果你想根据鼠标光标获取行和列,则需要执行相反的操作。您需要计算inverse 2x2 matrix从 x 轴和 y 轴。使用逆矩阵,您可以计算作为 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])
另请参阅PyGameExamplesAndAnswers - Isometric
最小示例:
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()
关于python - 在 Pygame 中获取等距平铺鼠标选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71336864/