在你的 Python 平台游戏中加入战利品

在本期关于使用 Python 的 Pygame 模块进行视频游戏编程的文章中,为你的玩家提供一些宝藏来收集并提高他们的分数。
135 位读者喜欢这篇文章。
Hearts, stars, and dollar signs

Opensource.com

这是关于使用 Python 3Pygame 模块创建视频游戏的系列文章的一部分。之前的文章有:

  1. 学习如何通过构建一个简单的骰子游戏来用 Python 编程
  2. 使用 Pygame 模块用 Python 构建游戏框架
  3. 如何为你的 Python 游戏添加玩家
  4. 使用 Pygame 在游戏中移动你的角色
  5. 没有反派的英雄算什么?如何为你的 Python 游戏添加一个反派
  6. 在你的 Python 平台游戏中放置平台
  7. 在你的 Python 游戏中模拟重力
  8. 为你的 Python 平台游戏添加跳跃功能
  9. 让你的 Python 游戏玩家能够向前和向后跑

如果你一直关注本系列之前的文章,那么你已经了解了视频游戏机制编程的所有基础知识。你可以基于这些基础知识创建完全属于你自己的功能齐全的视频游戏。当你刚开始学习时,遵循像本系列代码示例这样的“食谱”很有帮助,但最终,食谱会变成一种约束。现在是时候运用你所学的原则,并以新的方式应用它们了。

如果这听起来说起来容易做起来难,那么本文演示了一个如何利用你已经知道的知识来实现新目的的示例。具体来说,它涵盖了如何实现战利品系统

使用你从之前的课程中学到的关于平台的知识。

在大多数视频游戏中,你都有机会“掠夺”,或收集游戏世界中的宝藏和其他物品。战利品通常会增加你的分数或生命值,或提供指向你的下一个任务的信息。

在你的游戏中包含战利品类似于编程平台。与平台一样,战利品没有用户控件,随游戏世界滚动,并且必须检查与玩家精灵的碰撞。

在开始之前,你必须有一个战利品图形,例如硬币或宝箱。如果你已经下载了我推荐的图块集,即 来自 Kenney.nl 的 simplified-platformer-pack,那么你可以使用其中的钻石或钥匙。

创建战利品函数

战利品与平台非常相似,你甚至不需要 Loot 类。你可以直接重用 Platform 类,并将结果称为战利品。

由于战利品类型和位置可能因关卡而异,因此在你的 Level 类中创建一个名为 loot 的新函数(如果你还没有的话)。由于战利品物品不是平台,你还必须创建一个新的 loot_list 组,然后将战利品对象添加到其中。与平台、地面和敌人一样,此组用于检查碰撞

    def loot(lvl):
        if lvl == 1:
            loot_list = pygame.sprite.Group()
            loot = Platform(tx*9, ty*5, tx, ty, 'loot_1.png')
            loot_list.add(loot)

        if lvl == 2:
            print(lvl)

        return loot_list

在此代码中,我将战利品的位置表示为图块大小的倍数:X 轴上的 tx 和 Y 轴上的 ty 。我这样做是因为我在方格纸上绘制了我的关卡,因此只需计算地图上的方格,然后将其乘以图块大小即可,而不是计算像素数,这很容易。对于非常长的关卡尤其如此。如果你愿意,可以硬编码像素数。

你可以添加任意数量的战利品对象;只需记住将每个对象添加到你的战利品列表中即可。Platform 类的参数是 X 位置、Y 位置、战利品精灵的宽度和高度(通常最容易保持你的战利品精灵与所有其他图块大小相同)以及你想用作战利品的图像。战利品的放置可能与映射平台一样复杂,因此请在使用关卡时使用你创建的关卡设计文档。

在你的脚本的 Setup 部分调用你的新战利品函数。在以下代码中,前三行是上下文,因此只需添加第四行

loot_list = Level.loot(1)

正如你现在所知,除非你将其包含在主循环中,否则战利品不会被绘制到屏幕上。将此行添加到你的循环中

    loot_list.draw(world)

启动你的游戏以查看会发生什么。

Loot

你的战利品对象已生成,但当你的玩家碰到它们时,它们不会执行任何操作,当你的玩家跑过它们时,它们也不会滚动。接下来解决这些问题。

滚动战利品

与平台一样,当玩家在游戏世界中移动时,战利品必须滚动。逻辑与平台滚动相同。要向前滚动战利品,请添加最后两行

        for e in enemy_list:
            e.rect.x -= scroll
        for l in loot_list:     # loot scroll
            l.rect.x -= scroll  # loot scroll

要向后滚动它,请添加最后两行

        for e in enemy_list:
            e.rect.x += scroll
        for l in loot_list:     # loot scroll
            l.rect.x += scroll  # loot scroll

再次启动你的游戏,看看你的战利品对象现在是否像它们游戏世界中一样,而不仅仅是绘制在它的顶部。

检测碰撞

与平台和敌人一样,你可以检查战利品和你的玩家之间的碰撞。逻辑与其他碰撞相同,只是碰撞不(一定)影响重力或生命值。相反,碰撞会导致战利品消失并增加玩家的分数。

当你的玩家触摸战利品对象时,你可以从 loot_list 中删除该对象。这 意味着当你的主循环重绘 loot_list 中的所有战利品项时,它不会重绘该特定对象,因此看起来就像玩家抓住了战利品。

在你的 Player 类的 update 函数中,在平台碰撞检测之上添加以下代码(最后一行仅用于上下文)

		loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False)
		for loot in loot_hit_list:
			loot_list.remove(loot)
			self.score += 1
		print(self.score)
 
        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)

碰撞发生时,你不仅从其组中删除了战利品对象,还奖励了你的玩家分数提升。你还没有创建分数变量,因此将其添加到你在 Player 类的 __init__ 函数中创建的玩家属性中。在以下代码中,前两行是上下文,因此只需添加分数变量

        self.frame = 0
        self.health = 10
        self.score = 0

应用你所知道的知识

正如你所看到的,你已经掌握了所有基础知识。你现在要做的就是以新的方式使用你所知道的知识。例如,如果你还没有将敌人放置在合理的位置,请花一些时间使用与放置平台和战利品相同的方法来执行此操作。

下一篇文章中还有一些技巧,但与此同时,使用你所学到的知识制作一些简单的单级游戏。限制你尝试创建的范围很重要,这样你就不会让自己不知所措。这也使得更容易获得看起来和感觉都完整的成品。

这是你目前为止为这个 Python 平台游戏编写的所有代码

#!/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])
forwardx  = 600
backwardx = 120

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.score = 0
        self.is_jumping = True
        self.is_falling = True
        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):
        if self.is_jumping:
            self.movey += 3.2

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

    def jump(self):
        if self.is_jumping is False:
            self.is_falling = False
            self.is_jumping = True

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

        # moving left
        if self.movex < 0:
            self.is_jumping = True
            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.is_jumping = True
            self.frame += 1
            if self.frame > 3 * ani:
                self.frame = 0
            self.image = self.images[self.frame // ani]

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

        ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
        for g in ground_hit_list:
            self.movey = 0
            self.rect.bottom = g.rect.top
            self.is_jumping = False  # stop jumping

        # fall off the world
        if self.rect.y > worldy:
            self.health -=1
            print(self.health)
            self.rect.x = tx
            self.rect.y = ty

        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
        for p in plat_hit_list:
            self.is_jumping = False  # stop jumping
            self.movey = 0
            if self.rect.bottom <= p.rect.bottom:
               self.rect.bottom = p.rect.top
            else:
               self.movey += 3.2

        if self.is_jumping and self.is_falling is False:
            self.is_falling = True
            self.movey -= 33  # how high to jump

        loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False)
        for loot in loot_hit_list:
            loot_list.remove(loot)
            self.score += 1
            print(self.score)

        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)

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

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((550, 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

    def loot(lvl):
        if lvl == 1:
            loot_list = pygame.sprite.Group()
            loot = Platform(tx*5, ty*5, tx, ty, 'loot_1.png')
            loot_list.add(loot)

        if lvl == 2:
            print(lvl)

        return loot_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)
enemy_list = Level.bad( 1, eloc )
loot_list = Level.loot(1)


'''
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'):
                player.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)

    # scroll the world forward
    if player.rect.x >= forwardx:
        scroll = player.rect.x - forwardx
        player.rect.x = forwardx
        for p in plat_list:
            p.rect.x -= scroll
        for e in enemy_list:
            e.rect.x -= scroll
        for l in loot_list:
            l.rect.x -= scroll

    # scroll the world backward
    if player.rect.x <= backwardx:
        scroll = backwardx - player.rect.x
        player.rect.x = backwardx
        for p in plat_list:
            p.rect.x += scroll
        for e in enemy_list:
            e.rect.x += scroll
        for l in loot_list:
            l.rect.x += scroll

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

 

接下来阅读什么
标签
Seth Kenlon
Seth Kenlon 是一位 UNIX 极客、自由文化倡导者、独立多媒体艺术家和 D&D 爱好者。他曾在电影和计算机行业工作,而且经常同时从事这两个行业。
User profile image.
Jess Weichler 是一位数字艺术家,使用开源软件和硬件在 CyanideCupcake.com 上创作数字和物理世界的作品。

6 条评论

解释得很好,信息量很大。

谢谢

代码有效!!!!非常感谢!

感谢发布这个!

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.