当前位置: 首页 > 面试题库 >

在pygame中将滚动添加到平台游戏

巫欣荣
2023-03-14
问题内容

好的,所以我在下面包含了我项目的代码,我只是在做一些平台游戏方面对pygame做一些试验。我试图弄清楚如何按照播放器进行一些非常简单的滚动,因此播放器是相机的中心,它会弹跳/跟随他。谁能帮我?

import pygame
from pygame import *

WIN_WIDTH = 800
WIN_HEIGHT = 640
HALF_WIDTH = int(WIN_WIDTH / 2)
HALF_HEIGHT = int(WIN_HEIGHT / 2)

DISPLAY = (WIN_WIDTH, WIN_HEIGHT)
DEPTH = 32
FLAGS = 0
CAMERA_SLACK = 30

def main():
    global cameraX, cameraY
    pygame.init()
    screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
    pygame.display.set_caption("Use arrows to move!")
    timer = pygame.time.Clock()

    up = down = left = right = running = False
    bg = Surface((32,32))
    bg.convert()
    bg.fill(Color("#000000"))
    entities = pygame.sprite.Group()
    player = Player(32, 32)
    platforms = []

    x = y = 0
    level = [
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P       PPPPPPPPPPP              P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",]
    # build the level
    for row in level:
        for col in row:
            if col == "P":
                p = Platform(x, y)
                platforms.append(p)
                entities.add(p)
            if col == "E":
                e = ExitBlock(x, y)
                platforms.append(e)
                entities.add(e)
            x += 32
        y += 32
        x = 0

    entities.add(player)

    while 1:
        timer.tick(60)

        for e in pygame.event.get():
            if e.type == QUIT: raise SystemExit, "QUIT"
            if e.type == KEYDOWN and e.key == K_ESCAPE:
                raise SystemExit, "ESCAPE"
            if e.type == KEYDOWN and e.key == K_UP:
                up = True
            if e.type == KEYDOWN and e.key == K_DOWN:
                down = True
            if e.type == KEYDOWN and e.key == K_LEFT:
                left = True
            if e.type == KEYDOWN and e.key == K_RIGHT:
                right = True
            if e.type == KEYDOWN and e.key == K_SPACE:
                running = True

            if e.type == KEYUP and e.key == K_UP:
                up = False
            if e.type == KEYUP and e.key == K_DOWN:
                down = False
            if e.type == KEYUP and e.key == K_RIGHT:
                right = False
            if e.type == KEYUP and e.key == K_LEFT:
                left = False
            if e.type == KEYUP and e.key == K_RIGHT:
                right = False

        # draw background
        for y in range(32):
            for x in range(32):
                screen.blit(bg, (x * 32, y * 32))

        # update player, draw everything else
        player.update(up, down, left, right, running, platforms)
        entities.draw(screen)

        pygame.display.update()

class Entity(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)

class Player(Entity):
    def __init__(self, x, y):
        Entity.__init__(self)
        self.xvel = 0
        self.yvel = 0
        self.onGround = False
        self.image = Surface((32,32))
        self.image.fill(Color("#0000FF"))
        self.image.convert()
        self.rect = Rect(x, y, 32, 32)

    def update(self, up, down, left, right, running, platforms):
        if up:
            # only jump if on the ground
            if self.onGround: self.yvel -= 10
        if down:
            pass
        if running:
            self.xvel = 12
        if left:
            self.xvel = -8
        if right:
            self.xvel = 8
        if not self.onGround:
            # only accelerate with gravity if in the air
            self.yvel += 0.3
            # max falling speed
            if self.yvel > 100: self.yvel = 100
        if not(left or right):
            self.xvel = 0
        # increment in x direction
        self.rect.left += self.xvel
        # do x-axis collisions
        self.collide(self.xvel, 0, platforms)
        # increment in y direction
        self.rect.top += self.yvel
        # assuming we're in the air
        self.onGround = False;
        # do y-axis collisions
        self.collide(0, self.yvel, platforms)

    def collide(self, xvel, yvel, platforms):
        for p in platforms:
            if pygame.sprite.collide_rect(self, p):
                if isinstance(p, ExitBlock):
                    pygame.event.post(pygame.event.Event(QUIT))
                if xvel > 0:
                    self.rect.right = p.rect.left
                    print "collide right"
                if xvel < 0:
                    self.rect.left = p.rect.right
                    print "collide left"
                if yvel > 0:
                    self.rect.bottom = p.rect.top
                    self.onGround = True
                    self.yvel = 0
                if yvel < 0:
                    self.rect.top = p.rect.bottom


class Platform(Entity):
    def __init__(self, x, y):
        Entity.__init__(self)
        self.image = Surface((32, 32))
        self.image.convert()
        self.image.fill(Color("#DDDDDD"))
        self.rect = Rect(x, y, 32, 32)

    def update(self):
        pass

class ExitBlock(Platform):
    def __init__(self, x, y):
        Platform.__init__(self, x, y)
        self.image.fill(Color("#0033FF"))

if __name__ == "__main__":
    main()

问题答案:

绘制实体时,你需要将偏移量应用于实体的位置。我们称这个偏移量为 a camera,因为这是我们想要达到的效果。

首先,我们无法使用drawSprite群组的功能,因为Sprite不需要知道其位置(rect)并非要在屏幕上绘制的位置(最后,我们ll对该Group类进行子类化,并重新实现它draw以了解摄像头,但让我们开始慢一点)。

让我们从创建一个Camera类开始,以保存要应用于实体位置的偏移量的状态:

class Camera(object):
    def __init__(self, camera_func, width, height):
        self.camera_func = camera_func
        self.state = Rect(0, 0, width, height)

    def apply(self, target):
        return target.rect.move(self.state.topleft)

    def update(self, target):
        self.state = self.camera_func(self.state, target.rect)

这里要注意一些事情:

我们需要存储摄像机的位置以及水平的宽度和高度(以像素为单位)(因为我们要停止在水平边缘进行滚动)。我曾经使用a Rect来存储所有这些信息,但是你可以轻松地使用一些字段。

使用Rect派上用场的apply功能。在这里我们重新计算实体在屏幕上的位置以应用滚动。

每个主循环迭代一次,我们需要更新摄影机的位置,因此有了update功能。它只是通过调用camera_func函数来改变状态,这将为我们完成所有艰苦的工作。我们稍后实施。

让我们创建一个摄影机的实例:

for row in level:
    ...

total_level_width  = len(level[0])*32 # calculate size of level in pixels
total_level_height = len(level)*32    # maybe make 32 an constant
camera = Camera(*to_be_implemented*, total_level_width, total_level_height)

entities.add(player)
... 

并更改我们的主循环:

# draw background
for y in range(32):
    ...

camera.update(player) # camera follows player. Note that we could also follow any other sprite

# update player, draw everything else
player.update(up, down, left, right, running, platforms)
for e in entities:
    # apply the offset to each entity.
    # call this for everything that should scroll,
    # which is basically everything other than GUI/HUD/UI
    screen.blit(e.image, camera.apply(e)) 

pygame.display.update()

我们的摄像头课程已经非常灵活,但是非常简单。它可以使用不同种类的滚动(通过提供不同的camera_func功能),并且可以跟随任何变态的精灵,而不仅仅是播放器。你甚至可以在运行时更改此设置。

现在用于实施camera_func。一种简单的方法是将播放器(或我们要跟随的任何实体)居中放在屏幕上,实现很简单:

def simple_camera(camera, target_rect):
    l, t, _, _ = target_rect # l = left,  t = top
    _, _, w, h = camera      # w = width, h = height
    return Rect(-l+HALF_WIDTH, -t+HALF_HEIGHT, w, h)

我们只是将放在位置target,然后加上一半的总屏幕尺寸。你可以通过以下方式创建相机来尝试:

camera = Camera(simple_camera, total_level_width, total_level_height)

到目前为止,一切都很好。但是也许我们不想看到关卡外面的黑色背景?怎么样:

def complex_camera(camera, target_rect):
    # we want to center target_rect
    x = -target_rect.center[0] + WIN_WIDTH/2 
    y = -target_rect.center[1] + WIN_HEIGHT/2
    # move the camera. Let's use some vectors so we can easily substract/multiply
    camera.topleft += (pygame.Vector2((x, y)) - pygame.Vector2(camera.topleft)) * 0.06 # add some smoothness coolnes
    # set max/min x/y so we don't see stuff outside the world
    camera.x = max(-(camera.width-WIN_WIDTH), min(0, camera.x))
    camera.y = max(-(camera.height-WIN_HEIGHT), min(0, camera.y))

    return camera

在这里,我们仅使用min/ max函数以确保我们不会向外滚动。

像这样创建相机来尝试一下:

camera = Camera(complex_camera, total_level_width, total_level_height)

我们的最终滚动效果有点动画:

在此处输入图片说明

再次是完整的代码。注意我更改了一些内容:

  • 级别更大,并拥有更多平台
  • 使用python 3
  • 使用精灵组来处理相机
  • 重构了一些重复的代码
  • 由于Vector2 / 3现在很稳定,因此请使用它们来简化数学运算
  • 摆脱丑陋的事件处理代码,pygame.key.get_pressed而改用
 #! /usr/bin/python

import pygame
from pygame import *

SCREEN_SIZE = pygame.Rect((0, 0, 800, 640))
TILE_SIZE = 32 
GRAVITY = pygame.Vector2((0, 0.3))

class CameraAwareLayeredUpdates(pygame.sprite.LayeredUpdates):
    def __init__(self, target, world_size):
        super().__init__()
        self.target = target
        self.cam = pygame.Vector2(0, 0)
        self.world_size = world_size
        if self.target:
            self.add(target)

    def update(self, *args):
        super().update(*args)
        if self.target:
            x = -self.target.rect.center[0] + SCREEN_SIZE.width/2
            y = -self.target.rect.center[1] + SCREEN_SIZE.height/2
            self.cam += (pygame.Vector2((x, y)) - self.cam) * 0.05
            self.cam.x = max(-(self.world_size.width-SCREEN_SIZE.width), min(0, self.cam.x))
            self.cam.y = max(-(self.world_size.height-SCREEN_SIZE.height), min(0, self.cam.y))

    def draw(self, surface):
        spritedict = self.spritedict
        surface_blit = surface.blit
        dirty = self.lostsprites
        self.lostsprites = []
        dirty_append = dirty.append
        init_rect = self._init_rect
        for spr in self.sprites():
            rec = spritedict[spr]
            newrect = surface_blit(spr.image, spr.rect.move(self.cam))
            if rec is init_rect:
                dirty_append(newrect)
            else:
                if newrect.colliderect(rec):
                    dirty_append(newrect.union(rec))
                else:
                    dirty_append(newrect)
                    dirty_append(rec)
            spritedict[spr] = newrect
        return dirty            

def main():
    pygame.init()
    screen = pygame.display.set_mode(SCREEN_SIZE.size)
    pygame.display.set_caption("Use arrows to move!")
    timer = pygame.time.Clock()

    level = [
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                    PPPPPPPPPPP           P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P    PPPPPPPP                              P",
        "P                                          P",
        "P                          PPPPPPP         P",
        "P                 PPPPPP                   P",
        "P                                          P",
        "P         PPPPPPP                          P",
        "P                                          P",
        "P                     PPPPPP               P",
        "P                                          P",
        "P   PPPPPPPPPPP                            P",
        "P                                          P",
        "P                 PPPPPPPPPPP              P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",]


    platforms = pygame.sprite.Group()
    player = Player(platforms, (TILE_SIZE, TILE_SIZE))
    level_width  = len(level[0])*TILE_SIZE
    level_height = len(level)*TILE_SIZE
    entities = CameraAwareLayeredUpdates(player, pygame.Rect(0, 0, level_width, level_height))

    # build the level
    x = y = 0
    for row in level:
        for col in row:
            if col == "P":
                Platform((x, y), platforms, entities)
            if col == "E":
                ExitBlock((x, y), platforms, entities)
            x += TILE_SIZE
        y += TILE_SIZE
        x = 0

    while 1:

        for e in pygame.event.get():
            if e.type == QUIT: 
                return
            if e.type == KEYDOWN and e.key == K_ESCAPE:
                return

        entities.update()

        screen.fill((0, 0, 0))
        entities.draw(screen)
        pygame.display.update()
        timer.tick(60)

class Entity(pygame.sprite.Sprite):
    def __init__(self, color, pos, *groups):
        super().__init__(*groups)
        self.image = Surface((TILE_SIZE, TILE_SIZE))
        self.image.fill(color)
        self.rect = self.image.get_rect(topleft=pos)

class Player(Entity):
    def __init__(self, platforms, pos, *groups):
        super().__init__(Color("#0000FF"), pos)
        self.vel = pygame.Vector2((0, 0))
        self.onGround = False
        self.platforms = platforms
        self.speed = 8
        self.jump_strength = 10

    def update(self):
        pressed = pygame.key.get_pressed()
        up = pressed[K_UP]
        left = pressed[K_LEFT]
        right = pressed[K_RIGHT]
        running = pressed[K_SPACE]

        if up:
            # only jump if on the ground
            if self.onGround: self.vel.y = -self.jump_strength
        if left:
            self.vel.x = -self.speed
        if right:
            self.vel.x = self.speed
        if running:
            self.vel.x *= 1.5
        if not self.onGround:
            # only accelerate with gravity if in the air
            self.vel += GRAVITY
            # max falling speed
            if self.vel.y > 100: self.vel.y = 100
        print(self.vel.y)
        if not(left or right):
            self.vel.x = 0
        # increment in x direction
        self.rect.left += self.vel.x
        # do x-axis collisions
        self.collide(self.vel.x, 0, self.platforms)
        # increment in y direction
        self.rect.top += self.vel.y
        # assuming we're in the air
        self.onGround = False;
        # do y-axis collisions
        self.collide(0, self.vel.y, self.platforms)

    def collide(self, xvel, yvel, platforms):
        for p in platforms:
            if pygame.sprite.collide_rect(self, p):
                if isinstance(p, ExitBlock):
                    pygame.event.post(pygame.event.Event(QUIT))
                if xvel > 0:
                    self.rect.right = p.rect.left
                if xvel < 0:
                    self.rect.left = p.rect.right
                if yvel > 0:
                    self.rect.bottom = p.rect.top
                    self.onGround = True
                    self.yvel = 0
                if yvel < 0:
                    self.rect.top = p.rect.bottom

class Platform(Entity):
    def __init__(self, pos, *groups):
        super().__init__(Color("#DDDDDD"), pos, *groups)

class ExitBlock(Platform):
    def __init__(self, pos, *groups):
        super().__init__(Color("#0033FF"), pos, *groups)

if __name__ == "__main__":
    main()


 类似资料:
  • 我想在我的游戏中添加秒表。我计划修改此代码并添加到我的pygame中: 我是否应该在def init部分中添加一个计时器框,并为计时器代码创建一个新的def?我想让计时器不使用代码,因为我的游戏正在运行时钟。勾选(60),使其不会影响勾选 更新后,我的游戏代码如下:

  • 问题内容: 有没有一种方法可以将水平滚动条添加到HTML表格中?我实际上需要根据表的增长方式在垂直和水平方向上都可以滚动,但我无法显示任何滚动条。 问题答案: 您是否尝试过CSS 属性? 更新 正如其他用户指出的那样, 这不足以 添加滚动条。 因此,请参阅下面的评论和答案。

  • 注:看这篇文档之前,可先参考文档:http://docs.wex5.com/custom-icon/ 1.先修改文件IconWebPage.java 如图: 2.修改comp.min.css.xml 如图: 新增: <file>justep/lib/css2/dataControl.icons.css</file> 3.执行dist.bat 因为修改了comp.min.css.xml,所以要重新生

  • 问题内容: 我想增加一种滚动菜单项的方法,就像滚动滚动菜单项的列表一样。 假设我有10个菜单项。我想一次只显示5个菜单项,并且我会使用底部或顶部的垂直滚动按钮来显示未列出的菜单项并隐藏我刚刚看到的菜单项。 可能吗?我正在使用JIDE软件的,单击时会显示。我试图保持放置命令栏的外观,因此除非确实需要,否则我不想将其替换为。 问题答案: 可能是这个 http://www.javabeginner.co

  • 我一直在尝试制作一个包含可以选择和配置的字符串的滚动窗格。我一直在研究如何在滚动窗格中实现JList,但没有一种方法有效,这是因为指南一直在使用swing滚动窗格(

  • 我用JDK 7和插件Java ME SDK 3.2安装了NetBeans 7.2.1(它内部有Wireles Toolkit 2.5.2)。 我尝试通过单击File/new project/Java ME/Mobile Application创建新项目。然后我点击Next。在下一个窗口中,消息: 每个CLDC项目都需要为其分配CLDC兼容的SDK/Platform/Emulator。