在你的 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本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.