2

ブロックを配置してレベルを作り、それをプレイする小さなプラットフォーマー ゲームに取り組んでいます。

重力、ジャンプ、左右の移動はできましたが、左右に移動するときにプレイヤーを壁に衝突させる方法がわかりません。

私がそれを機能させたい方法は次のようなものです-

if key[K_LEFT]:

if not block to the left:

move to the left

これを行うにはどうすればよいですか(このソースに関連して):

import pygame,random
from pygame.locals import *
import itertools
pygame.init()
screen=pygame.display.set_mode((640,480))
class Block(object):
    sprite = pygame.image.load("texture\\dirt.png").convert_alpha()
    def __init__(self, x, y):
        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)

blocklist = []
player = []
colliding = False

while True:
    screen.fill((25,30,90))
    mse = pygame.mouse.get_pos()
    key=pygame.key.get_pressed()

    if key[K_LEFT]:
        p.rect.left-=1
    if key[K_RIGHT]:
        p.rect.left+=1
    if key[K_UP]:
        p.rect.top-=10

    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))
        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))

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

    for b in blocklist:
        screen.blit(b.sprite, b.rect)
    for p in player:
        if any(p.rect.colliderect(block) for block in blocklist):
            #collide
            pass
        else:
            p.rect.top += 1
        screen.blit(p.sprite, p.rect)
    pygame.display.flip()
4

2 に答える 2

8

一般的なアプローチは、水平方向と垂直方向の衝突処理を 2 つの別々のステップに分けることです。

これを行い、プレイヤーの速度も追跡すると、どちら側で衝突が発生したかを簡単に知ることができます。

まず、速度を追跡するために、プレーヤーにいくつかの属性を与えましょう。

class Player(object):
    ...
    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 = 10
        self.move_speed = 8

ここで、実際に衝突をチェックするメソッドが必要です。すでに述べたように、物事を簡単にするために、xvelandを使用yvelして、左側または右側などと衝突したかどうかを確認します。これはPlayerクラスに入ります。

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

次に、移動処理をPlayerクラスに移動します。それでは、入力を追跡する on オブジェクトを作成しましょう。ここでは、 を使用しnamedtupleます。

from collections import namedtuple
...
max_gravity = 100
Move = namedtuple('Move', ['up', 'left', 'right'])
while True:
    screen.fill((25,30,90))
    mse = pygame.mouse.get_pos()
    key = pygame.key.get_pressed()

    for event in pygame.event.get():
       ...

    move = Move(key[K_UP], key[K_LEFT], key[K_RIGHT])
    for p in player:
        p.update(move, blocklist)
        screen.blit(p.sprite, p.rect)

blocklistupdateメソッドにを渡して、Player衝突をチェックできるようにします。オブジェクトを使用してmove、プレイヤーがどこに移動する必要があるかがわかったので、次を実装しましょうPlayer.update

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)

あとは a を使用しClockてフレームレートを制限し、ゲームを一定の速度で実行できるようにするだけです。それでおしまい。

完全なコードは次のとおりです。

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

pygame.init()
clock=pygame.time.Clock()
screen=pygame.display.set_mode((640,480))

max_gravity = 100

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

class Player(object):
    sprite = pygame.image.load("dirt.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 = 10
        self.move_speed = 8

    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

blocklist = []
player = []
colliding = False
Move = namedtuple('Move', ['up', 'left', 'right'])
while True:
    screen.fill((25,30,90))
    mse = pygame.mouse.get_pos()
    key = pygame.key.get_pressed()

    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))
        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))

                elif event.button == 3:
                    x=(int(mse[0]) / 32)*32
                    y=(int(mse[1]) / 32)*32
                    player=[]
                    player.append(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)
    for p in player:
        p.update(move, blocklist)
        screen.blit(p.sprite, p.rect)
    clock.tick(60)
    pygame.display.flip()

イメージ名を変更したので、これをテストするために必要なイメージ ファイルは 1 つだけです。また、プレイヤーをリストに保持する理由はわかりませんが、ゲームの動作を示す素晴らしいアニメーションを次に示します。

ここに画像の説明を入力

于 2013-09-24T07:35:29.150 に答える
1

pygame を使用しているため、pygame を使用rect.colliderect()して、プレイヤーのスプライトがブロックと衝突しているかどうかを確認できます。次に、特定の四角形が他の四角形に対して相対的にある側を返す関数を作成します。

def rect_side(rect1, rect2): # Returns side of rect1 relative to rect2.
    if rect1.x > rect2.x:
        return "right"
    else:
        return "left"
    # If rect1.x == rect2.x the function will return "left".

rect_side()戻る場合よりも"right"、左への動きを制限し、その逆も同様です。

PS: 垂直方向の動きを含めて同じものが必要な場合は、出力とを比較rect1.yして処理できます。水平方向と垂直方向を表すタプルを作成できます。rect2.y"up""down"

于 2013-09-23T20:36:28.400 に答える