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

スクロール カメラを使用するには、スクリーン座標ワールド座標を区別する必要があります。

この図では、画面の輪郭 (および画面座標) が赤で示されています。ワールド座標は黒です。破線の矢印は、ワールド原点 (カメラ ベクトルまたはカメラ位置) からのスクリーン原点のオフセットを示します。

ワールドとスクリーンの座標

オブジェクトを描画するときはワールド座標からスクリーン座標に変換し (カメラ変換)、マウスが指しているブロックを特定するときはスクリーン座標からワールド座標に戻す必要があります (逆カメラ変換)。

2 次元では、これらの変換は簡単です。カメラ変換は「ワールド座標からカメラ位置を引いたもの」であり、逆カメラ変換は「画面座標とカメラ位置を足したもの」です。


コードに関するより一般的なコメントに興味がある場合は、コードをCode Reviewに投稿することを検討してください(自分で作成したものと同じくらい良いと感じた場合)。

私が見つけた問題に関する簡単なメモ:

  1. 新しいブロックを作成するたびに、テクスチャを読み込んでスプライトを作成します。これは少し無駄に思えます: 各テクスチャを 1 回だけロードして、スプライトを複数回使用しないのはなぜですか?

  2. プレイヤーはジャンプの高さを制御できません。スーパー マリオ ワールドのような古典的なプラットフォーム ゲームを注意深く見て、ジャンプがどのように機能するかを確認する価値があります。

  3. 速度はフレームあたりのピクセル数で表されます。つまり、フレームレートを変更することはできません。速度を 1 秒あたりのピクセル数で表し、タイムステップを掛けることを検討してください。

  4. 変数max_gravityの名前は不適切です。これは、プレーヤーの端末の下向き速度です。(また、定数であるため、大文字で名前を付けることもできます。)

  5. 関数やメソッドに変換できる反復的なコードがたくさんあります。たとえば、次のようなコードです。

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

    4ヶ所に登場。(コードを修正してカメラ位置を追加すると、これは逆カメラ変換になります。)

  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 呼び出しにオフセットを含めるように変更します。世界。

カメラを動かすには、キャラクターを動かすのとほぼ同じ方法を使用できます。D キーが押された場合は camera_x を増やし、A の場合は減らすようにキー処理を設定します。正の x 軸を使用してスクリーン スペースの軸を表す場合は、ブリット呼び出しからこのオフセットを差し引く必要があることに注意してください (カメラが右に移動すると、x が増加し、スプライトは画面上で左に移動します)。

于 2013-09-29T08:02:06.977 に答える