在你的 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 条评论

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

谢谢

代码运行了!!!!非常感谢!

感谢发布!

© . All rights reserved.