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

好的,所以我在下面包含了我项目的代码,我只是在做一些平台游戏方面对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中将滚动添加到平台游戏 的全部内容, 来源链接: utcz.com/qa/405419.html

回到顶部