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