将滚动相机放在 pygame 中的迷你 window

Putting the scrolling camera in a mini-window in pygame

我正在尝试创建一个游戏,其中操作显示在主屏幕对象内的一个小方框内,释放周围的 space 用于文本和菜单等等。由于地图比分配的 window 大,我编写了一个基本的 "camera" 来跟随玩家。它大部分都有效,但我在 "trimming off" 这个 window.

之外的区域遇到了问题

这是相关的代码位(经过编辑以提供工作示例):

import pygame, os, sys
from pygame.locals import *
pygame.init()

RIGHT = 'RIGHT'
LEFT = 'LEFT'
UP = 'UP'
DOWN = 'DOWN'

class Camera():
    def __init__(self, screen, x_ratio = 1, y_ratio = 1, x_offset = 0, y_offset = 0):
        self.screen = screen.copy()
        self.rec = self.screen.get_rect()
        self.rec.width *= x_ratio
        self.rec.height *= y_ratio
        self.x_offset = x_offset
        self.y_offset = y_offset
    def get_pos(self):
        return (self.x_offset - self.rec.x, self.y_offset - self.rec.y)
    def get_window(self):
        w = pygame.Rect(self.rec)
        w.topleft = (0 - self.rec.x, 0 - self.rec.y)
        return w
    def move(self, x, y):
        """Move camera into new position"""
        self.rec.x = x
        self.rec.y = y
    def track(self, obj):
        while obj.rec.left < self.rec.left:
            self.rec.x -= 1
        while obj.rec.right > self.rec.right:
            self.rec.x += 1
        while obj.rec.top < self.rec.top:
            self.rec.y -= 1
        while obj.rec.bottom > self.rec.bottom:
            self.rec.y += 1

class Map:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.rec = pygame.Rect(0,0,self.width,self.height)

    def draw(self, screen):
        pygame.draw.rect(screen, (200,200,200), self.rec)

class Obj:
    def __init__(self, char, x = 0, y = 0, width = 0, height = 0):
        self.width = width
        self.height = height
        self.rec = pygame.Rect(x, y, width, height)
        self.cur_map = None
        self.timers = {}

        #Dummying in chars for sprites
        self.char = char

        self.x_dir = 1
        self.y_dir = 1
        self.speed = 1
        self.moving = False        

    def move(self):
        if self.x_dir != 0 or self.y_dir != 0:
            new_x = self.rec.x + (self.x_dir*self.speed)
            new_y = self.rec.y + (self.y_dir*self.speed)
            new_rec = pygame.Rect(new_x, new_y, self.width, self.height)

            #Keep movement within bounds of map
            while new_rec.left < self.cur_map.rec.left:
                new_rec.x += 1
            while new_rec.right > self.cur_map.rec.right:
                new_rec.x -= 1
            while new_rec.top < self.cur_map.rec.top:
                new_rec.y += 1
            while new_rec.bottom > self.cur_map.rec.bottom:
                new_rec.y -= 1

            self.rec = new_rec

    def set_dir(self, d):
        self.x_dir = 0
        self.y_dir = 0
        if d == LEFT:
            self.x_dir = -1
        elif d == RIGHT:
            self.x_dir = 1
        elif d == UP:
            self.y_dir = -1
        elif d == DOWN:
            self.y_dir = 1

    def set_moving(self, val = True):
        self.moving = val

class Game:
    def __init__(self):
        self.screen_size = (800, 600)
        self.screen = pygame.display.set_mode(self.screen_size)
        self.map_screen = self.screen.copy()
        self.title = 'RPG'
        pygame.display.set_caption(self.title)

        self.camera = Camera(self.screen, 0.75, 0.75)#, 10, 75)

        self.fps = 80
        self.clock = pygame.time.Clock()
        self.debug = False
        self.bg_color = (255,255,255)
        self.text_size = 18
        self.text_font = 'Arial'
        self.text_style = pygame.font.SysFont(self.text_font, self.text_size)
        self.key_binds = {LEFT : [K_LEFT, K_a], RIGHT : [K_RIGHT, K_d], UP : [K_UP, K_w], DOWN : [K_DOWN, K_s],
                          'interact' : [K_RETURN, K_z], 'inventory' : [K_i,  K_SPACE], 'quit' : [K_ESCAPE]}

        self.player = Obj('p', 0, 0, 10, self.text_size)     

    def draw(self, obj):
        char = obj.char       
        self.draw_text(char, obj.rec.x, obj.rec.y, screen = self.map_screen)

    def draw_text(self, text, x, y, color = (0,0,0), screen = None):
        textobj = self.text_style.render(text, 1, color)
        textrect = textobj.get_rect()
        textrect.x = x
        textrect.y = y
        if screen == None:
            """Use default screen"""
            self.screen.blit(textobj, textrect)
        else:
            screen.blit(textobj, textrect)

    def play(self):
        done = False
        cur_map = Map(800, 800)
        self.map_screen = pygame.Surface((cur_map.width, cur_map.height))
        self.map_screen.fill(self.bg_color)

        bg = pygame.Surface((cur_map.width, cur_map.height))
        cur_map.draw(bg)

        self.player.cur_map = cur_map

        while not done:

            for event in pygame.event.get():
                if event.type == QUIT:
                    pygame.quit()
                    sys.exit()
                if event.type == KEYDOWN:
                    if event.key in self.key_binds[LEFT]:
                        self.player.set_dir(LEFT)
                        self.player.set_moving()
                    elif event.key in self.key_binds[RIGHT]:
                        self.player.set_dir(RIGHT)
                        self.player.set_moving()
                    elif event.key in self.key_binds[UP]:
                        self.player.set_dir(UP)
                        self.player.set_moving()
                    elif event.key in self.key_binds[DOWN]:
                        self.player.set_dir(DOWN)
                        self.player.set_moving()

                elif event.type == KEYUP:
                    self.player.set_moving(False)


            if self.player.moving:
                self.player.move()

            self.camera.track(self.player)
            self.clock.tick()

            self.screen.fill(self.bg_color)
            self.map_screen.blit(bg, (0,0))            
            self.draw(self.player)

            pygame.draw.rect(self.map_screen, (0,0,0), self.camera.rec, 1)
            #self.screen.blit(self.map_screen, (0,0), [0 - self.camera.rec.x, 0 - self.camera.rec.y, self.camera.rec.width, self.camera.rec.height])
            self.screen.blit(self.map_screen, self.camera.get_pos(), self.camera.get_window())
            pygame.display.flip()

game = Game()
game.play()

将玩家移过摄像机 window 的边界会导致 window 完全卷起并消失。我尝试按照之前的建议调整 blitting 坐标,但它似乎只改变了 window 卷起的方向。

根据您更新的代码,self.screen.blit(...) 的 blitting 坐标仍在变化:self.camera.get_window() 更改值,因为 rec.xrec.y 是指玩家在其中的位置的值地图。因此你应该定义一个恒定的小地图坐标,这应该与相机偏移相同。

self.screen.blit(self.map_screen, (self.camera.x_offset,self.camera.y_offset), (*self.camera.get_pos(), self.camera.rec.width, self.camera.rec.height))

Camera().get_pos() 更改为:

def get_pos(self):
    return (self.rec.x, self.rec.y)

我相信我只是更改了 self.screen.blit(...) 并停止使用或重写了您的相机功能,因为您对所有 rec 变量感到困惑。

为了说明它的工作原理,将 Map().draw(screen) 修改为:

def draw(self, screen):
    pygame.draw.rect(screen, (200,200,200), self.rec)
    pygame.draw.circle(screen, (255, 255, 255), (50, 50), 20, 2)

还有一个提示,不要在每个循环中绘制整个地图,只绘制可见的部分。