4

因此,我在 pygame 中编写了一个小型平台游戏,您可以在其中放置块并在它们上面跳来跳去。但是,游戏仅限于窗口的边界(显然)。那么如何添加一种使用 A 和 D 键滚动“相机”的方法呢?

这是游戏的代码:

    import pygame,random
    from pygame.locals import *
    from collections import namedtuple

    pygame.init()
    clock=pygame.time.Clock()
    screen=pygame.display.set_mode((640,480))
    pygame.display.set_caption("PiBlocks | By Sam Tubb")
    max_gravity = 100
    blocksel="texture\\dirt.png"
    curs = pygame.image.load("texture\\cursor.png").convert()
    curs.set_colorkey((0,255,0))

    class Block(object):
            def __init__(self,x,y,sprite):
                    self.sprite = pygame.image.load(sprite).convert_alpha()
                    self.rect = self.sprite.get_rect(centery=y, centerx=x)

    class Player(object):
        sprite = pygame.image.load("texture\\playr.png").convert()
        sprite.set_colorkey((0,255,0))
        def __init__(self, x, y):
            self.rect = self.sprite.get_rect(centery=y, centerx=x)
            # indicates that we are standing on the ground
            # and thus are "allowed" to jump
            self.on_ground = True
            self.xvel = 0
            self.yvel = 0
            self.jump_speed = 7
            self.move_speed = 3

        def update(self, move, blocks):

            # check if we can jump 
            if move.up and self.on_ground: 
                self.yvel -= self.jump_speed

            # simple left/right movement
            if move.left: self.xvel = -self.move_speed
            if move.right: self.xvel = self.move_speed

            # if in the air, fall down
            if not self.on_ground:
                self.yvel += 0.3
                # but not too fast
                if self.yvel > max_gravity: self.yvel = max_gravity

            # if no left/right movement, x speed is 0, of course
            if not (move.left or move.right):
                self.xvel = 0

            # move horizontal, and check for horizontal collisions
            self.rect.left += self.xvel
            self.collide(self.xvel, 0, blocks)

            # move vertically, and check for vertical collisions
            self.rect.top += self.yvel
            self.on_ground = False;
            self.collide(0, self.yvel, blocks)

        def collide(self, xvel, yvel, blocks):
            # all blocks that we collide with
            for block in [blocks[i] for i in self.rect.collidelistall(blocks)]:

                # if xvel is > 0, we know our right side bumped 
                # into the left side of a block etc.
                if xvel > 0: self.rect.right = block.rect.left
                if xvel < 0: self.rect.left = block.rect.right

                # if yvel > 0, we are falling, so if a collision happpens 
                # we know we hit the ground (remember, we seperated checking for
                # horizontal and vertical collision, so if yvel != 0, xvel is 0)
                if yvel > 0:
                    self.rect.bottom = block.rect.top
                    self.on_ground = True
                    self.yvel = 0
                # if yvel < 0 and a collision occurs, we bumped our head
                # on a block above us
                if yvel < 0: self.rect.top = block.rect.bottom

    colliding = False
    Move = namedtuple('Move', ['up', 'left', 'right'])
    player=[]
    blocklist=[]
    font=pygame.font.Font(None,20)
    while True:
        screen.fill((25,30,90))
        mse = pygame.mouse.get_pos()
        key = pygame.key.get_pressed()
        if key[K_1]:
            blocksel="texture\\dirt.png"
        if key[K_2]:
            blocksel="texture\\stonetile.png"
        if key[K_3]:
            blocksel="texture\\sand.png"
        if key[K_ESCAPE]:
            exit()
        for event in pygame.event.get():
            if event.type == QUIT:
                exit()

            if key[K_LSHIFT]:
                if event.type==MOUSEMOTION:
                    if not any(block.rect.collidepoint(mse) for block in blocklist):
                        x=(int(mse[0]) / 32)*32
                        y=(int(mse[1]) / 32)*32
                        blocklist.append(Block(x+16,y+16,blocksel))
            else:
                if event.type == pygame.MOUSEBUTTONUP:
                    if event.button == 1:
                        to_remove = [b for b in blocklist if b.rect.collidepoint(mse)]
                        for b in to_remove:
                            blocklist.remove(b)

                        if not to_remove:
                            x=(int(mse[0]) / 32)*32
                            y=(int(mse[1]) / 32)*32
                            blocklist.append(Block(x+16,y+16,blocksel))

                    elif event.button == 3:
                        x=(int(mse[0]) / 32)*32
                        y=(int(mse[1]) / 32)*32
                        player=Player(x+16,y+16)

        move = Move(key[K_UP], key[K_LEFT], key[K_RIGHT])

        for b in blocklist:
                screen.blit(b.sprite, b.rect)

        if player:
            player.update(move, blocklist)
            screen.blit(player.sprite, player.rect)
        x=(int(mse[0]) / 32)*32
        y=(int(mse[1]) / 32)*32
        screen.blit(curs,(x,y))
        clock.tick(60)
        x=blocksel.replace('texture\\','')
        x=x.replace('.png','')
        words=font.render('Selected Block: '+str(x), True, (255,255,255))
        screen.blit(words,(1,1))
        pygame.display.flip()

任何帮助表示赞赏:)

4

2 回答 2

5

为了有一个滚动相机,你必须区分屏幕坐标世界坐标

在此图中,屏幕轮廓(和屏幕坐标)以红色显示。世界坐标为黑色。虚线箭头显示屏幕原点与世界原点(相机矢量相机位置)的偏移量。

世界与屏幕坐标

绘制对象时需要将世界坐标转换为屏幕坐标(相机变换),并在计算出鼠标指向哪个块时将屏幕坐标转换回世界坐标(逆相机变换)。

在二维中,这些变换很简单:相机变换是“世界坐标减去相机位置”,而逆相机变换是“屏幕坐标加上相机位置”。


如果您对代码的更一般性的评论感兴趣,您可以考虑将您的代码发布到Code Review(当您觉得它尽可能好时)。

关于我发现的问题的一些快速说明:

  1. 每次创建新块时加载纹理并制作精灵。这似乎有点浪费:为什么不只加载每个纹理一次并多次使用精灵呢?

  2. 玩家无法控制跳跃的高度。值得仔细研究像超级马里奥世界这样的经典平台游戏,看看跳跃是如何工作的。

  3. 速度以每帧像素表示,这意味着您无法改变帧速率。考虑以每秒像素表示速度并乘以时间步长。

  4. 这个变量max_gravity名字不好:它是玩家的终端向下速度。(此外,由于它是常量,您可能希望将其命名为大写。)

  5. 有很多重复的代码可以变成函数或方法。例如,像这样的代码:

    x=(int(mse[0]) / 32)*32
    y=(int(mse[1]) / 32)*32
    ... x+16,y+16 ...
    

    出现在四个地方。(当您修改代码以添加相机位置时,这将成为逆相机变换。)

  6. 如果您为常量命名,它将使代码更容易理解,例如:

    BACKGROUND_COLOR = 25, 30, 90
    TEXT_COLOR = pygame.Color('white')
    BLOCK_SIZE = 32, 32
    SCREEN_SIZE = 640, 480
    FRAMES_PER_SECOND = 60
    
于 2013-09-29T14:22:40.930 回答
3

我知道的最简单的方法是设置包含相机坐标的游戏变量(或单独的类)。这些充当绘制精灵的偏移量。

因此,如果 camera_x 和 camera_y 是所说的变量,您将更改所有 screen.blit 调用以包含偏移量 - 除了光标和 UI 文本,以及应该相对于屏幕原点而不是世界。

为了移动相机,您可以使用与移动角色大致相同的方法。如果按下 D 键,则将键处理设置为增加 camera_x,并为 A 减少它。请记住,如果您使用正 x 轴表示屏幕空间的轴,则应从 blit 调用中减去此偏移量(如果相机向右移动,则 x 增加,并且精灵在屏幕上向左移动。)

于 2013-09-29T08:02:06.977 回答