Kivy/Python游戏地图块无法正确绘制问题求助
问题分析与解决
核心问题出在Tile坐标计算逻辑错误:你在绘制Tile时,把视口位置(viewport_pos)加到了绝对坐标上,而正确的做法应该是用Tile的绝对坐标减去视口位置,将其转换为Widget可视区域内的相对坐标。
当玩家移动后,viewport_pos会变成负数(比如玩家向右移动时,player.x减小,viewport_pos.x = player.x - 视口宽/2会是负数),此时加上viewport_pos会让Tile的绘制位置变成负数,超出了GameScreen Widget的显示范围(Widget的坐标从(0,0)开始),所以新生成的Tile无法被看到。
修改步骤
1. 修正Tile绘制坐标
在GameScreen.draw_chunks方法中,将Rectangle的pos计算从加法改为减法:
原代码:
Rectangle(pos=( (x * CHUNK_SIZE + i) * TILE_SIZE + self.viewport_pos[0], (y * CHUNK_SIZE + j) * TILE_SIZE + self.viewport_pos[1]), size=(TILE_SIZE, TILE_SIZE))
修改为:
Rectangle(pos=( (x * CHUNK_SIZE + i) * TILE_SIZE - self.viewport_pos[0], (y * CHUNK_SIZE + j) * TILE_SIZE - self.viewport_pos[1]), size=(TILE_SIZE, TILE_SIZE))
2. 优化帧率(可选但建议)
将ProceduralGenerationGameApp.build中的Clock调度间隔从1秒改为60帧每秒,提升流畅度:
原代码:
Clock.schedule_interval(game_screen.update, 1)# 1.0 / 60.0)
修改为:
Clock.schedule_interval(game_screen.update, 1.0 / 60.0)
3. 可选:修正移动方向(根据预期调整)
当前Player的移动逻辑可能和直觉相反(比如按左键实际向右移动),如果需要调整方向,可以修改Player类的移动方法:
def move_left(self): self.x -= self.speed # 向左移动,x减小 def move_right(self): self.x += self.speed # 向右移动,x增大 def move_up(self): self.y += self.speed # 向上移动,y增大(Kivy y轴向上为正) def move_down(self): self.y -= self.speed # 向下移动,y减小
修改后的完整代码(关键部分已修正)
import kivy import random from kivy.app import App from kivy.clock import Clock from kivy.graphics import Color, Ellipse, Line, Rectangle from kivy.uix.widget import Widget from kivy.config import Config from kivy.core.window import Window import enum # Constants for the chunk size and map dimensions CHUNK_SIZE = 48 TILE_SIZE = 16 MAP_WIDTH = 256 MAP_HEIGHT = 256 #************************** #* Tile Int Enum Class * #************************** class TileEnum(enum.IntEnum): GRASS = 0 DIRT = 1 CONCRETE = 2 ASPHALT = 3 # Class to represent the game map class GameMap: def __init__(self, seed=None): self.seed = seed if seed is not None: random.seed(seed) self.chunks = {} self.first_chunk = False def generate_chunk(self, chunk_x, chunk_y): # Use the RNG to generate the terrain for this chunk terrain = [] for x in range(0, CHUNK_SIZE): column = [] for y in range(0, CHUNK_SIZE): column.append(random.randint(0, 3)) terrain.append(column) return terrain def get_chunk(self, chunk_x, chunk_y): # Check if the chunk has already been generated if (chunk_x, chunk_y) in self.chunks: print("found it",chunk_x, chunk_y) return self.chunks[(chunk_x, chunk_y)] else: # Generate the chunk and store it in the chunk cache chunk = self.generate_chunk(chunk_x, chunk_y) self.chunks[(chunk_x, chunk_y)] = chunk print("made it",chunk_x,chunk_y) return chunk # Class to represent the player class Player: def __init__(self, pos=(0, 0)): self.x, self.y = pos self.speed = TILE_SIZE/2 def move_left(self): self.x -= self.speed def move_right(self): self.x += self.speed def move_up(self): self.y += self.speed def move_down(self): self.y -= self.speed class GameScreen(Widget): def __init__(self, **kwargs): super().__init__(**kwargs) self.viewport_size = (TILE_SIZE*CHUNK_SIZE, TILE_SIZE*CHUNK_SIZE) self.viewport_pos = (0, 0) self.size = self.viewport_size self.map = GameMap(seed=123) self.player = Player((self.viewport_size[0]/2, self.viewport_size[1]/2)) self._keyboard = Window.request_keyboard(self._keyboard_closed, self) self._keyboard.bind(on_key_down=self._on_keyboard_down) def draw_chunks(self): # Determine the chunks that are currently in view viewport_left = int(self.viewport_pos[0] // (CHUNK_SIZE * TILE_SIZE)) viewport_top = int(self.viewport_pos[1] // (CHUNK_SIZE * TILE_SIZE)) viewport_right = int((self.viewport_pos[0] + self.viewport_size[0]) // (CHUNK_SIZE * TILE_SIZE)) viewport_bottom = int((self.viewport_pos[1] + self.viewport_size[1]) // (CHUNK_SIZE * TILE_SIZE)) print(viewport_left, viewport_top, viewport_right, viewport_bottom) # Iterate over the visible chunks and draw them for x in range(viewport_left, viewport_right + 1): for y in range(viewport_top, viewport_bottom + 1): chunk = self.map.get_chunk(x, y) #print(chunk) for i in range(len(chunk)): for j in range(len(chunk[i])): if chunk[i][j] == TileEnum.GRASS: # Draw a green square for grass with self.canvas: Color(0.25, 0.75, 0.25) elif chunk[i][j] == TileEnum.DIRT: # Draw a brown square for dirt with self.canvas: Color(0.75, 0.5, 0.25) elif chunk[i][j] == TileEnum.CONCRETE: # Draw a gray square for concrete with self.canvas: Color(0.5, 0.5, 0.75) elif chunk[i][j] == TileEnum.ASPHALT: # Draw a black square for asphalt with self.canvas: Color(0.25, 0.25, 0.5) with self.canvas: Rectangle(pos=( (x * CHUNK_SIZE + i) * TILE_SIZE - self.viewport_pos[0], (y * CHUNK_SIZE + j) * TILE_SIZE - self.viewport_pos[1]), size=(TILE_SIZE, TILE_SIZE)) def draw_player(self): # Draw a circle for the player with self.canvas: Color(0, 0.5, 0) Ellipse(pos=(self.viewport_size[0]/2 - (TILE_SIZE/2), self.viewport_size[0]/2 - (TILE_SIZE/2)), size=(TILE_SIZE, TILE_SIZE)) def update(self, dt): # Update the viewport position to keep the player centered self.viewport_pos = (self.player.x - self.viewport_size[0]/2, self.player.y - self.viewport_size[1]/2) print(self.viewport_pos) # Redraw the chunks and player self.canvas.clear() self.draw_chunks() self.draw_player() def _keyboard_closed(self): self._keyboard.unbind(on_key_down=self._on_keyboard_down) self._keyboard = None def _on_keyboard_down(self, keyboard, keycode, text, modifiers): #print(keycode) if keycode[1] == 'left': self.player.move_left() elif keycode[1] == 'right': self.player.move_right() elif keycode[1] == 'up': self.player.move_up() elif keycode[1] == 'down': self.player.move_down() # Main application class class ProceduralGenerationGameApp(App): def build(self): self.title = "Procedural Generation Game" Config.set("graphics", "width", "768") Config.set("graphics", "height", "768") Config.set("graphics", "resizable", False) Config.set("graphics", "borderless", False) Config.set("graphics", "fullscreen", False) Config.set("graphics", "window_state", "normal") Config.set("graphics", "show_cursor", True) Config.write() window_width = Config.getint("graphics", "width") window_height = Config.getint("graphics", "height") # Create the game screen and schedule the update function to be called every frame game_screen = GameScreen() Window.size = (window_width, window_height) Clock.schedule_interval(game_screen.update, 1.0 / 60.0) return game_screen if __name__ == "__main__": ProceduralGenerationGameApp().run()
内容的提问来源于stack exchange,提问作者HelpMeSomePlease
相关产品推荐
相关产品推荐

