簡體   English   中英

Pygame 平台游戲 2D 中的敵人移動

[英]Enemy Movement in Pygame Platformer 2D

在我的第一個項目上工作,我正在嘗試對敵人的移動進行排序,下面的代碼是我當前的實現。 敵人使用玩家 position (target.pos.x) 與其 pos.x 之間的距離。 要敵人向左移動 20 步然后改變方向並向右移動 20 步,沖洗並重復。

self.target = game.player
def movement(self):
    self.acc = vec(0, BOSS_GRAVITY)
    if (-17 >= (self.pos.x - self.target.pos.x) >= -200) and self.target.hit_rect.y == self.hit_rect.y:
        self.vel.x -= BOSS_SPEED * -1
        self.enemy_direction = 'R'

    if (200 >= (self.pos.x - self.target.pos.x) >= 17) and self.target.hit_rect.y == self.hit_rect.y:
        self.vel.x += BOSS_SPEED * -1
        self.enemy_direction = 'L'
self.acc.x += self.vel.x * BOSS_FRICTION

    self.vel += self.acc
    self.pos += self.vel
    self.pos += self.vel + 0.5 * self.acc

我希望我的敵人向右移動一定量然后改變速度和 go 相反的方式不要保持空閑。

我想讓敵人向左移動 20 步然后改變方向並向右移動 20 步,沖洗並重復。

好的,那么我們如何實現呢? 首先是一些定義:

什么是“節奏”? 讓我們從5 pixels開始。 左邊是-x ; 正確的是+x

還有一些額外的事情需要照顧。 當 object無法向所需方向移動時怎么辦? 它可以turn around

所以我們需要保留一堆關於這個敵人的統計數據:位置、步數、行進方向。 一旦你有了一些數據點,想想:數據結構 現在我要將所有這些放入 Python class 中,但也可以將 go 放入一個簡單的列表中。 但是,如果數據多於幾個點,這些就會變得笨拙。

# As a list
enemy_image = pygame.image.load( "doomba.png" ).convert_alpha()
enemy_rect  = enemy_image.get_rect()
enemy_rect.center = ( x, y )
enemy1 = [ enemy_rect, enemy_image, PACE_SIZE, TURN_SIZE ]

作為 class 更好:

# As a sprite class
class Enemy( pygame.sprite.Sprite ):
    def __init__( self, x, y, bitmap, pace=5, turn_after=20 ):
            """ Create a new Enemy at that is drawn at (x,y) as the /bitmap/.
                It moves /pace/ pixels each step, left, then right     """
        pygame.sprite.Sprite.__init__( self )
        self.image = pygame.image.load( bitmap ).convert_alpha()
        self.rect  = self.image.get_rect()
        self.rect.center = ( x, y )         # location
        self.pace_size   = pace             # How big each step is
        self.pace_count  = 0                # distance moved
        self.direction   = -1               # Start moving left (-x)
        self.turn_after  = turn_after       # distance limit

(我制作了基於 PyGame Sprite 的數據結構,因為它只需要 2 行代碼,並且提供了許多預構建的功能。)

所以現在我們有一個數據結構(名為Enemy ),它包含一個位置、大小、bitmap,並記住它走了多遠,以及朝哪個方向。 但是它還沒有實現任何類型的運動算法。 所以讓我們添加這個。

Sprite class 希望將此算法寫入名為update()的 function 中。 幀調用此 function 以決定該幀的移動。 這可能是no-movement或其他。 它可以是任何東西。

在這里,您可以看到我們正在計算移入self.pace_count的步數,然后將位圖的x position(保存在self.rect中)調整為步長( self.pace_size )。 如果敵人向左移動,則需要減去步伐大小,向右移動則需要添加。 我們可以通過將添加的數量乘以self.direction-11 )來自動執行此操作。 每當敵人轉身時,就會設置方向值。

    def update( self ):
        """ Implement the movement algorithm """
        # Walk pace in the current direction
        self.pace_count += 1
        self.rect.x     += self.direction * self.pace_size     # Move some pixels

        # We need to turn around if walked enough paces in the same direction
        if ( self.pace_count >= self.turn_after ):
            # Turn around!
            self.direction *= -1           # reverses the pixel distance
            self.pace_count = 0            # reset the pace count

        # We also should change direction if we hit the screen edge
        if ( self.rect.x <= 0 ):
            self.direction  = 1             # turn right
            self.pace_count = 0
        elif ( self.rect.x >= WINDOW_WIDTH - self.rect.width ):
            self.direction  = -1
            self.pace_count = 0

因此,當敵人走設定的步數時,方向反轉,步數歸零。 但是如果我們撞到屏幕的一側,我們也需要轉身。 當這種情況發生時,只有一個明顯的轉向方式,所以方向是絕對改變的,而不是被顛倒過來。 這段代碼可能會變得更簡單,因為它基本上每次都做幾乎相同的事情。 但為了清楚地說明所涉及的步驟,我留了一點時間。

就是這樣,算法實現了。 看demo,太快 所以讓我們也加入一個實時速度。

蘑菇太快了

控制移動速度的一種簡單方法是在步驟之間設置延遲。 首先決定敵人移動的頻率(例如:每 100 毫秒),存儲在self.speed中,然后決定最后一步的時間在self.pace_time中。 然后到了更新時間,查看 PyGame 時鍾,看看是否經過了足夠的毫秒,然后才移動敵人。 否則什么都不做。

def update( self ):
    """ Implement the movement algorithm """
    time_now = pygame.time.get_ticks()               # what time is it
    if ( time_now > self.pace_time + self.speed ):   # time to move again?
        self.pace_time = time_now                    # remember move time

        # Walk pace in the current direction
        self.pace_count += 1

這給出了一個更穩重的運動。 我調整了敵人以更頻繁地移動,但步幅更小。 所以現在它也不會遍歷 window 的那么多。 將速度控制為時間而不是幀速率是很重要的。 例如,如果我剛剛將pace大小設置為0.2像素以上,那么肯定會使蘑菇減速到某個速度。 但它只在我的電腦上准確。 如果幀速率只有 21 FPS,突然又慢了 2/3。 如果幀速率是 160 FPS 呢? 它會回到超快,就是這樣。 因此,保持任何速度和運動都由實時毫秒控制,而不是幀速率和距離。

慢蘑菇

無論如何,這應該足以讓你繼續自己的運動算法。 如果對代碼有疑問,請發表評論。

參考代碼:

import pygame

# Window size
WINDOW_WIDTH    = 600
WINDOW_HEIGHT   = 400
WINDOW_SURFACE  = pygame.HWSURFACE|pygame.DOUBLEBUF

DARK_BLUE = (   3,   5,  54)

class Enemy( pygame.sprite.Sprite ):
    def __init__( self, x, y, pace, bitmap, turn_after=20, speed=100 ):
        """ Create a new Enemy at that is drawn at (x,y) as the /bitmap/.
            It moves /pace/ pixels left, then right   """
        pygame.sprite.Sprite.__init__( self )    
        self.image = pygame.image.load( bitmap ).convert_alpha()
        self.rect  = self.image.get_rect()
        self.rect.center = ( x, y )         # location
        self.pace_size   = pace             # How big each step is
        self.pace_count  = 0                # distance moved
        self.direction   = -1               # Start moving left (-x)
        self.turn_after  = turn_after       # distance limit
        self.speed       = speed            # Milliseconds per pace
        self.pace_time   = 0                # time of last step

    def update( self ):
        """ Implement the movement algorithm """
        time_now = pygame.time.get_ticks()               # what time is it
        if ( time_now > self.pace_time + self.speed ):   # is it time to move again
            self.pace_time = time_now

            # Walk pace in the current direction
            self.pace_count += 1
            self.rect.x     += self.direction * self.pace_size     # Move some pixels

            # We need to turn around if walked enough paces in the same direction
            if ( self.pace_count >= self.turn_after ):
                # Turn around!
                self.direction *= -1           # reverses the pixel distance
                self.pace_count = 0            # reset the pace count

            # We also should change direction if we hit the screen edge
            if ( self.rect.x <= 0 ):
                self.direction  = 1             # turn right
                self.pace_count = 0
            elif ( self.rect.x >= WINDOW_WIDTH - self.rect.width ):
                self.direction  = -1
                self.pace_count = 0


### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption("Movement Algorithm Example")

### Sprite and Sprite Group
pos_x     = WINDOW_WIDTH//2
pos_y     = WINDOW_HEIGHT//2
pace_size = 7
enemy = Enemy( pos_x, pos_y, pace_size, "mushroom.png" )

all_sprites_group = pygame.sprite.Group()
all_sprites_group.add( enemy )


### Main Loop
clock = pygame.time.Clock()
done = False
while not done:

    # Handle user-input
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True
        elif ( event.type == pygame.MOUSEBUTTONUP ):
            # On mouse-click
            pass
        elif ( event.type == pygame.KEYUP ):
            pass

    # Movement keys
    #keys = pygame.key.get_pressed()
    #if ( keys[pygame.K_UP] ):
    #    print("up")

    # Update the window, but not more than 60fps
    all_sprites_group.update()
    window.fill( DARK_BLUE )
    all_sprites_group.draw( window )
    pygame.display.flip()

    # Clamp FPS
    clock.tick_busy_loop(60)

pygame.quit()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM