Pygame学习笔记13:Dungeon角色扮演游戏

这一次就来学习设计一款ASCII 文本的RPG——Dungeon角色扮演游戏,这一章的内容会结合前面所学的知识。

Dungeon游戏

地图的构建

关于地图构建的相关的操作都封装在一个Dungeon类中,存储在名为Dungeon.py文件中:

import random
import sys
import pygame
from pygame.locals import *
from MyLibrary import *


class Dungeon(object):
    def __init__(self, offsetx, offsety):
        self.text = MySprite()
        self.text.load("ascii8x12.png", 8, 12, 32)
        self.tiles = list()
        self.rooms = list()
        for n in range(0, 80 * 45):
            self.tiles.append(-1)

        self.offsetx = offsetx
        self.offsety = offsety
        self.generate()

    def getCharAt(self, x, y):
        if x < 0 or x > 79 or y < 0 or y > 44:
            print("error: x,y = ", x, y)
            return
        index = y * 80 + x
        if index < 0 or index > 80*45:
            print("error: index = ", index)
            return
        return self.tiles[index]

    def setCharAt(self, x, y, char):
        if x < 0 or x > 79 or y < 0 or y > 44:
            print("error: x,y = ", x, y)
            return
        index = y * 80 + x
        if index < 0 or index > 80*45:
            print("error: index = ", index)
            return
        self.tiles[index] = char

    def draw(self, surface):
        for y in range(0,45):
            for x in range(0,80):
                char = self.getCharAt(x, y)
                if char >= 0 and char <= 255:
                    self.draw_char(surface, x, y, char)

    # 将字符随机放入不同的地方
    def putCharInRandomRoom(self, targetChar, itemChar):
        x = random.randint(0, 79)
        y = random.randint(0, 44)
        tile = self.getCharAt(x, y)
        while tile != targetChar:
            x = random.randint(0, 79)
            y = random.randint(0, 44)
            tile = self.getCharAt(x, y)
        self.setCharAt(x, y, itemChar)

    def generate(self, emptyChar=175, roomChar=218, hallChar=177):
        self.emptyChar = emptyChar
        self.roomChar = roomChar
        self.hallChar = hallChar
        for index in range(0, 80 * 45):
            self.tiles[index] = emptyChar
        PL = 4
        PH = 8
        SL = 5
        SH = 14
        self.createRoom(0, 0, PL, PH, SL, SH)
        self.createRoom(20, 0, PL, PH, SL, SH)
        self.createRoom(40, 0, PL, PH, SL, SH)
        self.createRoom(60, 0, PL, PH, SL, SH)
        self.createRoom(0, 22, PL, PH, SL, SH)
        self.createRoom(20, 22, PL, PH, SL, SH)
        self.createRoom(40, 22, PL, PH, SL, SH)
        self.createRoom(60, 22, PL, PH, SL, SH)

        for room in self.rooms:
            for y in range(room.y, room.y + room.height):
                for x in range(room.x, room.x + room.width):
                    self.setCharAt(x, y, roomChar)

        for n in range(0, 7):
            self.createHallRight(self.rooms[n], self.rooms[n + 1], hallChar)

        # 随机选择上半部分的一个房间连接下半部分
        choice = random.randint(0, 3)
        print("choice:" + str(choice) + "," + str(choice + 4))
        self.createHallDown(self.rooms[choice], self.rooms[choice + 4], hallChar)

        # 选择一个房间作为入口
        choice = random.randint(0, 7)
        self.entrance_x = self.rooms[choice].x + self.rooms[choice].width // 2
        self.entrance_y = self.rooms[choice].y + self.rooms[choice].height // 2
        # 入口位置
        self.setCharAt(self.entrance_x, self.entrance_y, 29)
        print("entrance:", choice, self.entrance_x, self.entrance_y)

        # 选择一个房间作为出口
        choice2 = random.randint(0, 7)
        while choice2 == choice:
            choice2 = random.randint(0, 7)
        x = self.rooms[choice2].x + self.rooms[choice2].width // 2
        y = self.rooms[choice2].y + self.rooms[choice2].height // 2
        # 出口位置
        self.setCharAt(x, y, 30)
        print("exit:", choice2, x, y)

        # 金子随机生成
        drops = random.randint(5, 20)
        for n in range(1, drops):
            self.putCharInRandomRoom(roomChar, 70)  # 'G'

        # 添加武器,护甲和生命药水
        self.putCharInRandomRoom(roomChar, 86)  # 'W'
        self.putCharInRandomRoom(roomChar, 64)  # 'A'
        self.putCharInRandomRoom(roomChar, 71)  # 'H'
        self.putCharInRandomRoom(roomChar, 71)  # 'H'

        # 添加一些怪兽
        num = random.randint(5, 10)
        for n in range(0, num):
            self.putCharInRandomRoom(roomChar, 20)

    # 生成位置和大小随机的房间
    def createRoom(self, x, y, rposx, rposy, rsizel, rsizeh):
        room = Rect(x + random.randint(1, rposx),
                    y + random.randint(1, rposy),
                    random.randint(rsizel, rsizeh),
                    random.randint(rsizel, rsizeh))
        self.rooms.append(room)

    # 创造水平通道
    def createHallRight(self, src, dst, hallChar):
        pathx = src.x + src.width
        pathy = src.y + random.randint(1, src.height - 2)
        self.setCharAt(pathx, pathy, hallChar)
        if pathy > dst.y and pathy < dst.y + dst.height:
            while pathx < dst.x:
                pathx += 1
                self.setCharAt(pathx, pathy, hallChar)
        else:
            while pathx < dst.x + 1:
                pathx += 1
                self.setCharAt(pathx, pathy, hallChar)
                if pathy < dst.y + 1:
                    self.setCharAt(pathx, pathy, hallChar)
                    while pathy < dst.y:
                        pathy += 1
                        self.setCharAt(pathx, pathy, hallChar)
                else:
                    self.setCharAt(pathx, pathy, hallChar)
                    while pathy < dst.y + dst.height:
                        pathy -= 1
                        self.setCharAt(pathx, pathy, hallChar)

    # 创造垂直通道
    def createHallDown(self, src, dst, hallChar):
        pathx = src.x + random.randint(1, src.width - 2)
        pathy = src.y + src.height
        self.setCharAt(pathx, pathy, hallChar)
        if pathx > dst.x and pathx < dst.x + dst.width:
            while pathy < dst.y:
                pathy += 1
                self.setCharAt(pathx, pathy, hallChar)
        else:
            while pathy < dst.y + 1:
                pathy += 1
                self.setCharAt(pathx, pathy, hallChar)
            if pathx < dst.x + 1:
                self.setCharAt(pathx, pathy, hallChar)
                while pathx < dst.x:
                    pathx += 1
                    self.setCharAt(pathx, pathy, hallChar)
            else:
                self.setCharAt(pathx, pathy, hallChar)
                while pathx > dst.x + dst.width:
                    pathx -= 1
                    self.setCharAt(pathx, pathy, hallChar)

    def draw_radius(self, surface, rx, ry, radius):
        left = rx - radius
        if left < 0:
            left = 0
        top = ry - radius
        if top < 0:
            top = 0
        right = rx + radius
        if right > 79:
            right = 79
        bottom = ry + radius
        if bottom > 44:
            bottom = 44

        for y in range(top, bottom):
            for x in range(left, right):
                char = self.getCharAt(x, y)
                if char >= 0 and char <= 255:
                    self.draw_char(surface, x, y, char)

    def draw_char(self, surface, tilex, tiley, char):
        self.text.X = self.offsetx + tilex * 8
        self.text.Y = self.offsety + tiley * 12
        self.text.frame = char
        self.text.last_frame = char
        self.text.update(0)
        self.text.draw(surface)

角色的构建

角色包括玩家操作的角色和随机生成的怪兽,都存放在Play.py文件中:

from chapter13.Dungeon import *


# 用于模拟色子滚动并产生随机数
def Die(faces):
    roll = random.randint(1, faces)
    return roll


class Player(object):
    def __init__(self, dungeon, level, name):
        self.dungeon = dungeon
        self.alive = True
        self.x = 0
        self.y = 0
        self.name = name
        self.gold = 0
        self.experience = 0
        self.level = level
        self.weapon = level
        self.weapon_name = "Club"
        self.armor = level
        self.armor_name = "Rags"
        self.roll()

    def roll(self):
        self.str = 6 + Die(6) + Die(6)
        self.dex = 6 + Die(6) + Die(6)
        self.con = 6 + Die(6) + Die(6)
        self.int = 6 + Die(6) + Die(6)
        self.cha = 6 + Die(6) + Die(6)
        self.max_health = 10 + Die(self.con)
        self.health = self.max_health

    def levelUp(self):
        self.str += Die(6)
        self.dex += Die(6)
        self.con += Die(6)
        self.int += Die(6)
        self.cha += Die(6)
        self.max_health += Die(6)
        self.health = self.max_health

    def draw(self, surface, char):
        self.dungeon.draw_char(surface, self.x, self.y, char)

    def move(self, movex, movey):
        char = self.dungeon.getCharAt(self.x + movex, self.y + movey)
        if char not in (self.dungeon.roomChar, self.dungeon.hallChar):
            return False
        else:
            self.x += movex
            self.y += movey
            return True

    def moveUp(self):
        return self.move(0, -1)

    def moveDown(self):
        return self.move(0, 1)

    def moveLeft(self):
        return self.move(-1, 0)

    def moveRight(self):
        return self.move(1, 0)

    def addHealth(self, amount):
        self.health += amount
        if self.health < 0:
            self.health = 0
        elif self.health > self.max_health:
            self.health = self.max_health

    def addExperience(self, xp):
        cap = math.pow(10, self.level)
        self.experience += xp
        if self.experience > cap:
            self.levelUp()

    # 获取攻击值
    def getAttack(self):
        attack = self.str + Die(20)
        return attack

    # 获取防御值
    def getDefense(self):
        defense = self.dex + self.armor
        return defense

    # 根据防御值获取伤害值
    def getDamage(self, defense):
        damage = Die(8) + self.str + self.weapon - defense
        return damage


class Monster(Player):
    def __init__(self, dungeon, level, name):
        Player.__init__(self, dungeon, level, name)
        self.gold = random.randint(1, 4) * level
        self.str = 1 + Die(6) + Die(6)
        self.dex = 1 + Die(6) + Die(6)


游戏启动文件:

有了前面两个文件之后,就可以实现后面的操作了:

from chapter13.Player import *


# 游戏的一些初始化操作
def game_init():
    global screen, backbuffer, font1, font2, timer
    pygame.init()
    screen = pygame.display.set_mode((700, 650))
    backbuffer = pygame.Surface((700, 650))
    pygame.display.set_caption("Dungeon Game")
    font1 = pygame.font.SysFont("Courier New", size=18, bold=True)
    font2 = pygame.font.SysFont("Courier New", size=14, bold=True)
    timer = pygame.time.Clock()


def playerCollision(stepx, stepy):
    global TILE_EMPTY, TILE_ROOM, TILE_HALL, dungeon, player, level
    yellow = (220, 220, 0)
    green = (0, 220, 0)

    # 获取玩家的位置
    char = dungeon.getCharAt(player.x + stepx, player.y + stepy)

    # 到达入口
    if char == 29:
        message("portal up")
    # 到达出口
    elif char == 30:
        message("portal down")
    # 到墙里面
    elif char == TILE_EMPTY:
        message("You ran into the wall--ouch!")
    # 拾取到金子
    elif char == 70:
        gold = random.randint(1, level)
        player.gold += gold
        dungeon.setCharAt(player.x + stepx, player.y + stepy, TILE_ROOM)
        message("You found " + str(gold) + " gold!", yellow)
    # 拾取到武器
    elif char == 86:
        weapon = random.randint(1, level + 2)
        if level <= 5:
            temp = random.randint(0, 2)
        else:
            temp = random.randint(3, 6)
        # 根据temp决定是什么武器
        if temp == 0:
            name = "Dagger"
        elif temp == 1:
            name = "Short Sword"
        elif temp == 2:
            name = "Wooden Club"
        elif temp == 3:
            name = "Long Sword"
        elif temp == 4:
            name = "War Hammer"
        elif temp == 5:
            name = "Battle Axe"
        elif temp == 6:
            name = "Halberd"
        # 玩家默认使用更高级的武器
        if weapon >= player.weapon:
            player.weapon = weapon
            player.weapon_name = name
            message("You found a " + name + " +" + str(weapon) + "!", yellow)
        else:
            player.gold += 1
            message("You discarded a worthless " + name + ".")
        dungeon.setCharAt(player.x + stepx, player.y + stepy, TILE_ROOM)
    # 拾取到盔甲
    elif char == 64:
        armor = random.randint(1, level + 2)
        if level <= 5:
            temp = random.randint(0, 2)
        else:
            temp = random.randint(3, 7)
        # 根据temp决定盔甲的种类
        if temp == 0:
            name = "Cloth"
        elif temp == 1:
            name = "Patchwork"
        elif temp == 2:
            name = "Leather"
        elif temp == 3:
            name = "Chain"
        elif temp == 4:
            name = "Scale"
        elif temp == 5:
            name = "Plate"
        elif temp == 6:
            name = "Mithril"
        elif temp == 7:
            name = "Adamantium"
        # 玩家默认选择更高级的盔甲
        if armor >= player.armor:
            player.armor = armor
            player.armor_name = name
            message("You found a " + name + " +" + str(armor) + "!", yellow)
        else:
            player.gold += 1
            message("You discarded a worthless " + name + ".")
        dungeon.setCharAt(player.x + stepx, player.y + stepy, TILE_ROOM)
    # 拾取到血包
    elif char == 71:
        heal = 0
        for n in range(0, level):
            heal += Die(6)
        player.addHealth(heal)
        dungeon.setCharAt(player.x + stepx, player.y + stepy, TILE_ROOM)
        message("You drank a healing potion worth " + str(heal) + " points!", green)
    # 遇到怪兽,则需要进行攻击
    elif char == 20:
        attack_monster(player.x + stepx, player.y + stepy, 20)


# 攻击怪兽
def attack_monster(x, y, char):
    global dungeon, TILE_ROOM
    monster = Monster(dungeon, level, "Grue")
    # 玩家的攻击
    defense = monster.getDefense()
    attack = player.getAttack()
    damage = player.getDamage(defense)
    battle_text = "You hit the " + monster.name + " for "
    if attack == 20 + player.str:
        damage *= 2
        battle_text += str(damage) + " CRIT points!"
        dungeon.setCharAt(x, y, 70)
    elif attack > defense:
        if damage > 0:
            battle_text += str(damage) + " points."
            dungeon.setCharAt(x, y, 70)
        else:
            battle_text += "no damage!"
            damage = 0
    else:
        battle_text = "You missed the " + monster.name + "!"
        damage = 0

    # 怪兽的攻击
    defense = player.getDefense()
    attack = monster.getAttack()
    damage = monster.getDamage(defense)
    if attack > defense:
        if damage > 0:
            # 若造成的伤害超出玩家的最大生命值,则减半
            if damage > player.max_health:
                damage /= 2
            battle_text += " It hit you for " + str(damage) + " points."
            player.addHealth(-damage)
        else:
            battle_text += " It no damage to you."
    else:
        battle_text += " It missed you."

    # 展示攻击后的结果
    message(battle_text)
    # 判断玩家是否还活着
    if player.health <= 0:
        player.alive = False


# 移动所有的怪兽
def move_monsters():
    # 遍历所有的怪兽
    for y in range(0, 44):
        for x in range(0, 79):
            tile = dungeon.getCharAt(x, y)
            # 如果是怪兽,则移动
            if tile == 20:
                move_monster(x, y, 20)


# 移动怪兽
def move_monster(x, y, char):
    global TILE_ROOM
    movex = 0
    movey = 0
    dir = random.randint(1, 4)
    if dir == 1:
        movey = -1
    elif dir == 2:
        movey = 1
    elif dir == 3:
        movex = -1
    elif dir == 4:
        movex = 1
    c = dungeon.getCharAt(x + movex, y + movey)
    if c == TILE_ROOM:
        dungeon.setCharAt(x, y, TILE_ROOM)  # 将原位置填充默认字符
        dungeon.setCharAt(x + movex, y + movey, char)  # 在新的位置上填充代表怪兽的字符


# 打印出各种信息
def print_stats():
    fmt = "{:3.0f}"
    print_text(font2, 0, 615, "STR:" + fmt.format(player.str))
    print_text(font2, 40, 615, "DEX:" + fmt.format(player.dex))
    print_text(font2, 80, 615, "CON:" + fmt.format(player.con))
    print_text(font2, 120, 615, "INT:" + fmt.format(player.int))
    print_text(font2, 160, 615, "CHA:" + fmt.format(player.cha))
    print_text(font2, 200, 615, "DEF:" + fmt.format(player.getDefense()))

    # 获取平均伤害
    global att, attlow, atthigh
    att[0] = att[1]
    att[1] = att[2]
    att[2] = att[3]
    att[3] = att[4]
    att[4] = (player.getAttack() + att[0] + att[1] + att[2] + att[3]) // 5
    if att[4] < attlow:
        attlow = att[4]
    elif att[4] > atthigh:
        atthigh = att[4]
    print_text(font2, 240, 615, "ATT:" + str(attlow) + "-" + str(atthigh))

    print_text(font2, 300, 615, "LVL:" + fmt.format(player.level))
    print_text(font2, 360, 615, "EXP:" + str(player.experience))
    print_text(font2, 440, 615, "WPN:" + str(player.weapon) + ":" + player.weapon_name)
    print_text(font2, 560, 615, "ARM:" + str(player.armor) + ":" + player.armor_name)
    print_text(font2, 580, 570, "GOLD:" + str(player.gold))
    print_text(font2, 580, 585, "HLTH:" + str(player.health) + "/" + str(player.max_health))


def message(text, color=(255, 255, 255)):
    global message_text, message_color
    message_text = text
    message_color = color


# 定义用于地下城的ASCII码
TILE_EMPTY = 177
TILE_ROOM = 31
TILE_HALL = 31


if __name__ == "__main__":
    game_init()
    game_over = False
    last_time = 0
    dungeon = Dungeon(30, 30)
    dungeon.generate(TILE_EMPTY, TILE_ROOM, TILE_HALL)
    player = Player(dungeon, 1, "Player")
    player.x = dungeon.entrance_x + 1
    player.y = dungeon.entrance_y + 1
    level = 1
    message_text = "Welcome, brave adventurer!"
    message_color = 0, 200, 50
    draw_radius = False
    # 估计攻击伤害
    att = list(0 for n in range(0, 5))
    attlow = 90
    atthigh = 0

    while True:
        timer.tick(30)
        ticks = pygame.time.get_ticks()

        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    sys.exit()
                # 用于控制地图模式的开关
                elif event.key == K_TAB:
                    draw_radius = not draw_radius
                # 按下空格键重新生成地图
                elif event.key == K_SPACE:
                    dungeon.generate(TILE_EMPTY, TILE_ROOM, TILE_HALL)
                    player.x = dungeon.entrance_x + 1
                    player.y = dungeon.entrance_y + 1
                # 上下左右键移动
                elif event.key == K_UP or event.key == K_w:
                    if player.moveUp() == False:
                        playerCollision(0, -1)
                    else:
                        move_monsters()
                elif event.key == K_DOWN or event.key == K_s:
                    if player.moveDown() == False:
                        playerCollision(0, 1)
                    else:
                        move_monsters()
                elif event.key == K_RIGHT or event.key == K_d:
                    if player.moveRight() == False:
                        playerCollision(1, 0)
                    else:
                        move_monsters()
                elif event.key == K_LEFT or event.key == K_a:
                    if player.moveLeft() == False:
                        playerCollision(-1, 0)
                    else:
                        move_monsters()

        backbuffer.fill((20, 20, 20))

        # 绘制地下城
        if draw_radius:
            dungeon.draw_radius(backbuffer, player.x, player.y, 6)
        else:
            dungeon.draw(backbuffer)

        # 绘制玩家
        player.draw(backbuffer, 0)

        screen.blit(backbuffer, (0, 0))
        print_text(font1, 0, 0, "Dungeon Level " + str(level))
        print_text(font1, 600, 0, player.name)
        # 打印出message_text中的内容
        print_text(font2, 30, 570, message_text, message_color)
        print_stats()

        pygame.display.update()


运行结果如下:
在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花无凋零之时

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值