在你的 Python 游戏中模拟重力

学习如何使用 Python 的 Pygame 模块编写视频游戏,并开始控制重力。
211 位读者喜欢这篇文章。
Star Trek: inspiring people and their tech since 1964

公共领域

现实世界充满了运动和生命。使现实世界如此繁忙和动态的原因是物理学。物理学是物质在空间中运动的方式。由于视频游戏世界没有物质,它也没有物理学,因此游戏程序员必须模拟物理学。

对于大多数视频游戏而言,基本上只有两个重要的物理方面:重力和碰撞。

当您向游戏中添加敌人时,您已经实现了一些碰撞检测,但本文添加了更多内容,因为重力需要碰撞检测。思考一下为什么重力可能涉及碰撞。如果您想不出任何理由,请不要担心——当您完成示例代码时,它会变得显而易见。

现实世界中的重力是具有质量的物体相互吸引的趋势。物体越大,它施加的引力影响就越大。在视频游戏物理学中,您不必创建质量大到足以证明引力是合理的物体;您只需对物体编程,使其具有向视频游戏世界中假定最大的物体(世界本身)坠落的趋势。

添加重力函数

请记住,您的玩家已经有一个属性来确定运动。使用此属性将玩家精灵拉向屏幕底部。

在 Pygame 中,数字越大,越靠近屏幕底部边缘。

在现实世界中,重力影响一切。然而,在平台游戏中,重力是选择性的——如果您将重力添加到整个游戏世界,您的所有平台都会掉到地面上。相反,您只需将重力添加到玩家和敌人精灵。

首先,在您的 Player 类中添加一个 gravity 函数

    def gravity(self):
        self.movey += 3.2 # how fast player falls

这是一个简单的函数。首先,您将玩家设置为垂直运动,无论您的玩家是否想要运动。换句话说,您已将玩家编程为始终处于坠落状态。这基本上就是重力。

为了使重力函数生效,您必须在主循环中调用它。这样,Python 每隔一个时钟周期将坠落运动应用于您的玩家一次。

在此代码中,将第一行添加到您的循环中

    player.gravity() # check gravity
    player.update()

启动您的游戏以查看会发生什么。注意观察,因为它发生得很快:您的玩家从空中掉下来,直接掉出您的游戏屏幕。

您的重力模拟正在工作,但可能太好了。

作为实验,尝试更改玩家坠落的速度。

为重力添加地板

您的角色从世界中掉落的问题在于您的游戏无法检测到它。在某些游戏中,如果玩家从世界中掉落,精灵将被删除并在新的地方重生。在其他游戏中,玩家会失去积分或生命。无论您希望在玩家从世界中掉落时发生什么,您都必须能够检测到玩家何时消失在屏幕外。

在 Python 中,要检查条件,您可以使用 if 语句。

您必须检查if您的玩家是否正在坠落以及您的玩家坠落了多远。如果您的玩家坠落得太远以至于到达屏幕底部,那么您可以做某些事情。为了保持简单,将玩家精灵的位置设置为屏幕底部边缘上方 20 像素。

使您的 gravity 函数看起来像这样

    def gravity(self):
        self.movey += 3.2 # how fast player falls
        
        if self.rect.y > worldy and self.movey >= 0:
            self.movey = 0
            self.rect.y = worldy-ty

然后启动您的游戏。您的精灵仍然会坠落,但它会在屏幕底部停止。您可能无法看到地面图层后面的精灵。一个简单的解决方法是通过在精灵击中游戏世界底部后在其新的 Y 位置添加另一个 -ty 来使您的玩家精灵弹得更高

    def gravity(self):
        self.movey += 3.2 # how fast player falls
        
        if self.rect.y > worldy and self.movey >= 0:
            self.movey = 0
            self.rect.y = worldy-ty-ty

现在您的玩家在屏幕底部弹跳,就在您的地面精灵后面。

您的玩家真正需要的是一种对抗重力的方法。重力的问题在于,除非您有可以推开的东西,否则您无法对抗它。因此,在下一篇文章中,您将添加地面和平台碰撞以及跳跃能力。 在此期间,尝试将重力应用于敌人精灵。

这是到目前为止的所有代码

#!/usr/bin/env python3
# by Seth Kenlon

# GPLv3
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://gnu.ac.cn/licenses/>.

import pygame
import sys
import os

'''
Variables
'''

worldx = 960
worldy = 720
fps = 40
ani = 4
world = pygame.display.set_mode([worldx, worldy])

BLUE = (25, 25, 200)
BLACK = (23, 23, 23)
WHITE = (254, 254, 254)
ALPHA = (0, 255, 0)

'''
Objects
'''

# x location, y location, img width, img height, img file
class Platform(pygame.sprite.Sprite):
    def __init__(self, xloc, yloc, imgw, imgh, img):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(os.path.join('images', img)).convert()
        self.image.convert_alpha()
        self.image.set_colorkey(ALPHA)
        self.rect = self.image.get_rect()
        self.rect.y = yloc
        self.rect.x = xloc

class Player(pygame.sprite.Sprite):
    """
    Spawn a player
    """

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.movex = 0
        self.movey = 0
        self.frame = 0
        self.health = 10
        self.images = []
        for i in range(1, 5):
            img = pygame.image.load(os.path.join('images', 'hero' + str(i) + '.png')).convert()
            img.convert_alpha()
            img.set_colorkey(ALPHA)
            self.images.append(img)
            self.image = self.images[0]
            self.rect = self.image.get_rect()

    def gravity(self):
        self.movey += 3.2
        if self.rect.y > worldy and self.movey >= 0:
            self.movey = 0
            self.rect.y = worldy-ty-ty

    def control(self, x, y):
        """
        control player movement
        """
        self.movex += x
        self.movey += y

    def update(self):
        """
        Update sprite position
        """

        self.rect.x = self.rect.x + self.movex
        self.rect.y = self.rect.y + self.movey

        # moving left
        if self.movex < 0:
            self.frame += 1
            if self.frame > 3 * ani:
                self.frame = 0
            self.image = pygame.transform.flip(self.images[self.frame // ani], True, False)

        # moving right
        if self.movex > 0:
            self.frame += 1
            if self.frame > 3 * ani:
                self.frame = 0
            self.image = self.images[self.frame // ani]

        hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
        for enemy in hit_list:
            self.health -= 1
            print(self.health)


class Enemy(pygame.sprite.Sprite):
    """
    Spawn an enemy
    """

    def __init__(self, x, y, img):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(os.path.join('images', img))
        self.image.convert_alpha()
        self.image.set_colorkey(ALPHA)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.counter = 0

    def move(self):
        """
        enemy movement
        """
        distance = 80
        speed = 8

        if self.counter >= 0 and self.counter <= distance:
            self.rect.x += speed
        elif self.counter >= distance and self.counter <= distance * 2:
            self.rect.x -= speed
        else:
            self.counter = 0

        self.counter += 1


class Level:
    def ground(lvl, gloc, tx, ty):
        ground_list = pygame.sprite.Group()
        i = 0
        if lvl == 1:
            while i < len(gloc):
                ground = Platform(gloc[i], worldy - ty, tx, ty, 'tile-ground.png')
                ground_list.add(ground)
                i = i + 1

        if lvl == 2:
            print("Level " + str(lvl))

        return ground_list

    def bad(lvl, eloc):
        if lvl == 1:
            enemy = Enemy(eloc[0], eloc[1], 'enemy.png')
            enemy_list = pygame.sprite.Group()
            enemy_list.add(enemy)
        if lvl == 2:
            print("Level " + str(lvl))

        return enemy_list

    # x location, y location, img width, img height, img file
    def platform(lvl, tx, ty):
        plat_list = pygame.sprite.Group()
        ploc = []
        i = 0
        if lvl == 1:
            ploc.append((200, worldy - ty - 128, 3))
            ploc.append((300, worldy - ty - 256, 3))
            ploc.append((500, worldy - ty - 128, 4))
            while i < len(ploc):
                j = 0
                while j <= ploc[i][2]:
                    plat = Platform((ploc[i][0] + (j * tx)), ploc[i][1], tx, ty, 'tile.png')
                    plat_list.add(plat)
                    j = j + 1
                print('run' + str(i) + str(ploc[i]))
                i = i + 1

        if lvl == 2:
            print("Level " + str(lvl))

        return plat_list


'''
Setup
'''

backdrop = pygame.image.load(os.path.join('images', 'stage.png'))
clock = pygame.time.Clock()
pygame.init()
backdropbox = world.get_rect()
main = True

player = Player()  # spawn player
player.rect.x = 0  # go to x
player.rect.y = 30  # go to y
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10

eloc = []
eloc = [300, 0]
enemy_list = Level.bad(1, eloc)

gloc = []
tx = 64
ty = 64

i = 0
while i <= (worldx / tx) + tx:
    gloc.append(i * tx)
    i = i + 1

ground_list = Level.ground(1, gloc, tx, ty)
plat_list = Level.platform(1, tx, ty)

'''
Main Loop
'''

while main:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            try:
                sys.exit()
            finally:
                main = False

        if event.type == pygame.KEYDOWN:
            if event.key == ord('q'):
                pygame.quit()
                try:
                    sys.exit()
                finally:
                    main = False
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(-steps, 0)
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(steps, 0)
            if event.key == pygame.K_UP or event.key == ord('w'):
                print('jump')

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(steps, 0)
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps, 0)

    world.blit(backdrop, backdropbox)
    player.gravity()
    player.update()
    player_list.draw(world)
    enemy_list.draw(world)
    ground_list.draw(world)
    plat_list.draw(world)
    for e in enemy_list:
        e.move()
    pygame.display.flip()
    clock.tick(fps)

这是关于使用 Pygame 模块在 Python 3 中创建视频游戏的系列文章的第 6 部分。之前的文章是

  1. 通过构建一个简单的骰子游戏来学习如何在 Python 中编程
  2. 使用 Pygame 模块使用 Python 构建游戏框架
  3. 如何将玩家添加到您的 Python 游戏中
  4. 使用 Pygame 移动您的游戏角色
  5. 没有反派的英雄算什么?如何将反派添加到您的 Python 游戏中
  6. 使用 Pygame 在 Python 游戏中放置平台
接下来阅读什么
标签
Seth Kenlon
Seth Kenlon 是一位 UNIX 极客、自由文化倡导者、独立多媒体艺术家和 D&D 爱好者。他曾在电影和计算机行业工作,而且经常同时从事这两个行业。
User profile image.
Jess Weichler 是一位数字艺术家,她使用开源软件和硬件在 CyanideCupcake.com 上以数字方式和物理世界中创作作品。

6 条评论

有用的信息,谢谢

我很高兴这个系列又回来了!

在关于反派的第 5 部分和关于模拟重力的第 6 部分之间,您向 Level 类添加了一个 Platform 类和一个平台函数,但没有完全解释原因。我一直在按照我的反派/敌人代码进行操作,并添加了重力代码,但未定义“ty”变量。查看第 6 部分中的完整代码添加了之前文章中没有的一些内容。是否缺少关于平台的文章?

重力中存在错误。为了在窗口底部停止,它应该检查

if self.rect.y > worldy-ty ...

您忘记了 `-ty`

或者您可以使用 `rect. bottom` 代替 `rect.y`,并在两个地方使用 `ty` - 不使用 `ty`

if self.rect.bottom > worldy and self.movey >= 0
self.movey = 0
self.rect.bottom = worldy

© . All rights reserved.