用python编写的《兔子猎手》小游戏的完整代码

这里提供一个简单的兔子猎手游戏,使用Pygame库编写。游戏玩法是:玩家要尽可能地击杀小动物,每击杀一只小动物得到随机分数,如果玩家未能击杀小动物,相应的血量变少。本游戏一共有4个文件并提供文件和有关素材下载,下面是游戏代码:

game.py

import pygame, random, math, sys
from settings import Settings
from mclass import *
from scoreboard import Scoreboard

"""主函数"""
def main():

    """初始化pygame,设置展示窗口"""
    pygame.init()
    pygame.mixer.init()
    settings = Settings()
    screen = pygame.display.set_mode(settings.SCREEN_SIZE)
    pygame.display.set_caption("兔子猎手")
    clock = pygame.time.Clock()

    # 字体加载
    font = pygame.font.Font("resources/font/msyhbd.ttc", 18)

    # 加载必要的游戏素材
    game_images = {}
    for key, value in settings.IMAGE_PATHS.items():
        game_images[key] = pygame.image.load(value)

    game_sounds = {}
    for key, value in settings.SOUNDS_PATHS.items():
        if key != 'moonlight':
            game_sounds[key] = pygame.mixer.Sound(value)

    # 播放背景音乐
    pygame.mixer.music.load(settings.SOUNDS_PATHS['moonlight'])
    pygame.mixer.music.play(-1, 0.0)

    # 定义兔子
    bunny = BunnySprite(settings, position=(100, 240))

    # 跟踪玩家的精度变量,记录了射出的箭头数和被击中的獾的数量
    acc_record = [0, 0]

    # 生命值
    healthvalue = 194

    # 弓箭
    arrow_sprites_group = pygame.sprite.Group()

    # 獾
    badguy_sprites_group = pygame.sprite.Group()
    mnum1 = random.randint(0, len(settings.MONSTER_RECT) - 1)
    badguy = BadguySprite(settings.MONSTER_RECT[mnum1], position=(640, 100))
    badguy_sprites_group.add(badguy)

    # 定义了一个定时器,使得游戏里经过一段时间后就新建一支獾
    badtimer = 100
    badtimer1 = 0

    # 游戏主循环,running变量会跟踪游戏是否结束,
    running, exitcode = True, False
    while running:
        # 在给屏幕画任何东西之前用黑色进行填充
        screen.fill(settings.SCREEN_COLOR)

        # 添加的风景也需要画在屏幕上
        for x in range(settings.SCREEN_SIZE[0] // game_images['grass'].get_width() + 1):
            for y in range(settings.SCREEN_SIZE[1] // game_images['grass'].get_height() + 1):
                screen.blit(game_images['grass'], (x * 100, y * 100))
        for i in range(4):
            screen.blit(game_images['castle'], (0, 30 + 105 * i))

        # 倒计时信息
        countdown_text = font.render(str((90000 - pygame.time.get_ticks()) // 60000) + ":" + str((90000 - pygame.time.get_ticks()) // 1000 % 60).zfill(2), True, (0, 0, 0))
        countdown_rect = countdown_text.get_rect()
        countdown_rect.topright = [635, 5]
        screen.blit(countdown_text, countdown_rect)

        # 退出
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                # 更换兔子状态
                settings.RABBIT_STATE = 1
                game_sounds['shoot'].play()
                acc_record[1] += 1
                mouse_pos = pygame.mouse.get_pos()
                angle = math.atan2(mouse_pos[1] - (bunny.rotated_position[1] + 32), mouse_pos[0] - (bunny.rotated_position[0] + 26))
                arrow = ArrowSprite(game_images.get('arrow'), (angle, bunny.rotated_position[0] + 32, bunny.rotated_position[1] + 26))
                arrow_sprites_group.add(arrow)
            elif event.type == pygame.MOUSEBUTTONUP:
                settings.RABBIT_STATE = 0

        # 移动兔子(上下左右按键)
        key_pressed = pygame.key.get_pressed()

        # 处理键盘事件(移动兔子位置)
        if key_pressed[pygame.K_w] or key_pressed[pygame.K_UP]:
            bunny.move(settings.SCREEN_SIZE, 'up')
            bunny.move_up()
        elif key_pressed[pygame.K_s] or key_pressed[pygame.K_DOWN]:
            bunny.move(settings.SCREEN_SIZE, 'down')
            bunny.move_down()
        elif key_pressed[pygame.K_a] or key_pressed[pygame.K_LEFT]:
            bunny.move(settings.SCREEN_SIZE, 'left')
            bunny.move_left()
        elif key_pressed[pygame.K_d] or key_pressed[pygame.K_RIGHT]:
            bunny.move(settings.SCREEN_SIZE, 'right')
            bunny.move_right()

        # 更新弓箭
        for arrow in arrow_sprites_group:
            if arrow.update(settings.SCREEN_SIZE):
                arrow_sprites_group.remove(arrow)

        # 更新獾
        if badtimer == 0:
            mnum2 = random.randint(0, len(settings.MONSTER_RECT) - 1)
            badguy = BadguySprite(settings.MONSTER_RECT[mnum2], position=(640, random.randint(50, 430)))
            badguy_sprites_group.add(badguy)
            badtimer = 100 - (badtimer1 * 2)
            badtimer1 = 20 if badtimer1 >= 20 else badtimer1 + 2
        badtimer -= 1
        for badguy in badguy_sprites_group:
            if badguy.update():
                game_sounds['hit'].play()
                healthvalue -= random.randint(4, 8)
                badguy_sprites_group.remove(badguy)

        # 碰撞检测
        for arrow in arrow_sprites_group:
            for badguy in badguy_sprites_group:
                if pygame.sprite.collide_mask(arrow, badguy):
                    game_sounds['enemy'].play()
                    arrow_sprites_group.remove(arrow)
                    badguy_sprites_group.remove(badguy)
                    acc_record[0] += 1
                    settings.SCORE += random.randint(100, 600)

        # 画出弓箭
        arrow_sprites_group.draw(screen)

        # 画出獾
        badguy_sprites_group.draw(screen)

        # 画出兔子
        bunny.draw(screen, settings.RABBIT_STATE, pygame.mouse.get_pos())

        # 画出城堡健康值,首先画了一个全红色的生命值条,然后根据城堡的生命值往生命条里面添加绿色
        screen.blit(game_images.get('healthbar'), (5, 5))
        for i in range(healthvalue):
            screen.blit(game_images.get('health'), (i + 8, 8))

        # 创建记分牌和显示当前得分和最高分数
        sb = Scoreboard(settings.SCORE, screen)
        sb.show_score()

        # 判断游戏是否结束
        if pygame.time.get_ticks() >= 90000:
            running, exitcode = False, True
        if healthvalue <= 0:
            running, exitcode = False, False

        # 更新屏幕
        pygame.display.flip()
        clock.tick(settings.FPS)

    # 计算准确率
    accuracy = acc_record[0] / acc_record[1] * 100 if acc_record[1] > 0 else 0
    accuracy = '%.2f' % accuracy
    showEndGameInterface(screen, exitcode, settings.SCORE, game_images)

"""游戏跑起来"""
if __name__ == '__main__':
    main()

settings.py

"""设置类"""
import os
import pygame

class Settings():

    def __init__(self):
        """初始化设置"""
        self.SCREEN_SIZE = (640, 480)
        self.SCREEN_COLOR = (0, 0, 0)

        # 游戏循环帧率设置
        self.FPS = 100

        # 初始化分数和最高分数
        self.SCORE = 0
        self.HIGH_SCORE = 0

        # 更换兔子状态
        self.RABBIT_STATE = 0

        # 兔子图片
        self.PLAYER_IMG1 = pygame.image.load('resources/images/dude.png')
        self.PLAYER_IMG2 = pygame.image.load('resources/images/dude2.png')
        self.PLAYER_RECT = []
        self.PLAYER_RECT.append(self.PLAYER_IMG1)
        self.PLAYER_RECT.append(self.PLAYER_IMG2)

        # 怪物图片
        self.MONSTER_IMG1 = pygame.image.load('resources/images/monster/img1.png')
        self.MONSTER_IMG2 = pygame.image.load('resources/images/monster/img2.png')
        self.MONSTER_IMG3 = pygame.image.load('resources/images/monster/img3.png')
        self.MONSTER_IMG4 = pygame.image.load('resources/images/monster/img4.png')
        self.MONSTER_IMG5 = pygame.image.load('resources/images/monster/img5.png')
        self.MONSTER_IMG6 = pygame.image.load('resources/images/monster/img6.png')
        self.MONSTER_IMG7 = pygame.image.load('resources/images/monster/img7.png')
        self.MONSTER_IMG8 = pygame.image.load('resources/images/monster/img8.png')
        self.MONSTER_IMG9 = pygame.image.load('resources/images/monster/img9.png')
        self.MONSTER_IMG10 = pygame.image.load('resources/images/monster/img10.png')
        self.MONSTER_RECT = []
        self.MONSTER_RECT.append(self.MONSTER_IMG1)
        self.MONSTER_RECT.append(self.MONSTER_IMG2)
        self.MONSTER_RECT.append(self.MONSTER_IMG3)
        self.MONSTER_RECT.append(self.MONSTER_IMG4)
        self.MONSTER_RECT.append(self.MONSTER_IMG5)
        self.MONSTER_RECT.append(self.MONSTER_IMG6)
        self.MONSTER_RECT.append(self.MONSTER_IMG7)
        self.MONSTER_RECT.append(self.MONSTER_IMG8)
        self.MONSTER_RECT.append(self.MONSTER_IMG9)
        self.MONSTER_RECT.append(self.MONSTER_IMG10)

        # 游戏图片路径
        self.IMAGE_PATHS = {
            'rabbit': os.path.join(os.getcwd(), 'resources/images/dude.png'),
            'grass': os.path.join(os.getcwd(), 'resources/images/grass.png'),
            'castle': os.path.join(os.getcwd(), 'resources/images/castle.png'),
            'arrow': os.path.join(os.getcwd(), 'resources/images/bullet.png'),
            'healthbar': os.path.join(os.getcwd(), 'resources/images/healthbar.png'),
            'health': os.path.join(os.getcwd(), 'resources/images/health.png'),
            'gameover': os.path.join(os.getcwd(), 'resources/images/gameover.png'),
            'youwin': os.path.join(os.getcwd(), 'resources/images/youwin.png')
        }

        # 游戏声音路径
        self.SOUNDS_PATHS = {
            'hit': os.path.join(os.getcwd(), 'resources/audio/explode.wav'),
            'enemy': os.path.join(os.getcwd(), 'resources/audio/enemy.wav'),
            'shoot': os.path.join(os.getcwd(), 'resources/audio/shoot.wav'),
            'moonlight': os.path.join(os.getcwd(), 'resources/audio/moonlight.wav')
        }

mclass.py

import pygame, sys, math, codecs

"""定义兔子类"""
class BunnySprite(pygame.sprite.Sprite):
    def __init__(self, settings, position):
        pygame.sprite.Sprite.__init__(self)
        self.settings = settings
        self.image = []
        for i in range(len(self.settings.PLAYER_RECT)):
            self.image.append(self.settings.PLAYER_RECT[i].convert_alpha())
        self.rect = self.settings.PLAYER_IMG1.get_rect()
        self.rect.left, self.rect.top = position
        self.speed = 5
        self.rotated_position = position
        self.settings = settings

    """移动兔子"""
    def move(self, screensize, direction):
        if direction == 'left':
            self.rect.left = max(self.rect.left - self.speed, 0)
        elif direction == 'right':
            self.rect.left = min(self.rect.left + self.speed, screensize[0])
        elif direction == 'up':
            self.rect.top = max(self.rect.top - self.speed, 0)
        elif direction == 'down':
            self.rect.top  = min(self.rect.top + self.speed, screensize[1])

    # 向上移动,需要判断边界
    def move_up(self):
        if self.rect.top <= 30:
            self.rect.top = 30
        else:
            self.rect.top -= self.speed

    # 向下移动,需要判断边界
    def move_down(self):
        if self.rect.top >= self.settings.SCREEN_SIZE[1] - self.rect.height + 10:
            self.rect.top = self.settings.SCREEN_SIZE[1] - self.rect.height + 10
        else:
            self.rect.top += self.speed

    # 向左移动,需要判断边界
    def move_left(self):
        if self.rect.left <= 30:
            self.rect.left = 30
        else:
            self.rect.left -= self.speed

    # 向右移动,需要判断边界
    def move_right(self):
        if self.rect.left >= self.settings.SCREEN_SIZE[0] - self.rect.width + 30:
            self.rect.left = self.settings.SCREEN_SIZE[0] - self.rect.width + 30
        else:
            self.rect.left += self.speed

    """画到屏幕上"""
    def draw(self, screen, rabbit_state, mouse_pos):
        angle = math.atan2(mouse_pos[1] - (self.rect.top + 32), mouse_pos[0] - (self.rect.left + 26))
        if rabbit_state == 1:
            image_rotate = pygame.transform.rotate(self.settings.PLAYER_IMG1, 360 - angle * 57.29)
        else:
            image_rotate = pygame.transform.rotate(self.settings.PLAYER_IMG2, 360 - angle * 57.29)
        bunny_pos = (self.rect.left - image_rotate.get_rect().width / 2, self.rect.top - image_rotate.get_rect().height / 2)
        self.rotated_position = bunny_pos
        screen.blit(image_rotate, bunny_pos)

"""定义弓箭类"""
class ArrowSprite(pygame.sprite.Sprite):
    def __init__(self, image, position):
        pygame.sprite.Sprite.__init__(self)
        self.angle = position[0]
        self.image = pygame.transform.rotate(image, 360 - position[0] * 57.29)
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)
        self.rect.left, self.rect.top = position[1:]
        self.speed = 10

    """更新弓箭"""
    def update(self, screensize):
        velx = math.cos(self.angle) * self.speed
        vely = math.sin(self.angle) * self.speed
        self.rect.left += velx
        self.rect.top += vely
        if self.rect.right < 0 or self.rect.left > screensize[0] or self.rect.top > screensize[1] or self.rect.bottom < 0:
            return True
        return False

"""定义獾类"""
class BadguySprite(pygame.sprite.Sprite):
    def __init__(self, image, position):
        pygame.sprite.Sprite.__init__(self)
        self.image = image
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)
        self.rect.left, self.rect.top = position
        self.speed = 2 # 设置獾的移动速度

    """更新獾"""
    def update(self):
        self.rect.left -= self.speed
        if self.rect.left < 64:
            return True
        return False

""" 
对文件的操作
写入文本:
传入参数为content,strim,path;content为需要写入的内容,数据类型为字符串。
path为写入的位置,数据类型为字符串。strim写入方式
传入的path需如下定义:path= r’ D:\text.txt’
f = codecs.open(path, strim, 'utf8')中,codecs为包,需要用impor引入。
strim=’a’表示追加写入txt,可以换成’w’,表示覆盖写入。
'utf8'表述写入的编码,可以换成'utf16'等。
"""
def write_txt(content, strim, path):
    f = codecs.open(path, strim, 'utf8')
    f.write(str(content))
    f.close()

"""
读取txt:
表示按行读取txt文件,utf8表 示读取编码为utf8的文件,可以根据需求改成utf16,或者GBK等。
返回的为数组,每一个数组的元素代表一行,
若想返回字符串格式,可以将改写成return ‘\n’.join(lines)
"""
def read_txt(path):
    with open(path, 'r', encoding='utf8') as f:
        lines = f.readlines()
    return lines

"""游戏结束界面"""
def showEndGameInterface(screen, exitcode, settings_score, game_images):
    font = pygame.font.Font("resources/font/msyhbd.ttc", 24)
    text = font.render(f"当前得分: {settings_score}", True, (255, 0, 0))
    text_rect = text.get_rect()
    text_rect.centerx = screen.get_rect().centerx
    text_rect.centery = screen.get_rect().centery + 24

    # 判断得分更新最高分
    # 临时的变量在到排行榜的时候使用
    j = 0

    # 获取文件中内容转换成列表使用“|”分割开内容
    arrayscore = read_txt(r'resources/txt/score.txt')[0].split('|')

    # 循环分数在列表里的排序
    for i in range(0, len(arrayscore)):
        # 判断当前获得的分数是否大于排行榜上的分数
        if settings_score > int(arrayscore[i]):
            # 大于排行榜上的内容,把分数和当前分数进行替换
            j = arrayscore[i]
            arrayscore[i] = str(settings_score)
            settings_score = 0
        # 替换下来的分数下移动一位
        if int(j) > int(arrayscore[i]):
            k = arrayscore[i]
            arrayscore[i] = str(j)
            j = k

    # 循环分数列表 写入文档
    for i in range(0, len(arrayscore)):
        # 判断列表中第一个分数
        if i == 0:
            # 覆盖写入内容追加“|”方便分割内容
            write_txt(arrayscore[i] + '|', 'w', r'resources/txt/score.txt')
        else:
            # 判断是否为最后一个分数
            if i == 9:
                # 最近添加内容最后一个分数不添加“|”
                write_txt(arrayscore[i], 'a', r'resources/txt/score.txt')
            else:
                # 不是最后一个分数,添加的时候添加“|”
                write_txt(arrayscore[i] + '|', 'a', r'resources/txt/score.txt')

    while True:
        screen.fill(0)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        if exitcode:
            screen.blit(game_images['youwin'], (0, 0))
        else:
            screen.blit(game_images['gameover'], (0, 0))
        screen.blit(text, text_rect)
        pygame.display.flip()

scoreboard.py

import pygame.font
from pygame.sprite import Group
from mclass import *

class Scoreboard():
    """显示得分信息的类"""

    def __init__(self, settings_score, screen):
        """初始仳显示得分涉及的属性"""
        self.screen = screen
        self.screen_rect = screen.get_rect()
        self.score = settings_score

        # 显示得分信息时使用的字体设置
        self.text_color_green = (0, 255, 0)
        self.text_color_red = (255, 0, 0)
        self.text_color_blue = (0, 0, 255)
        self.score_font = pygame.font.Font("resources/font/msyhbd.ttc", 18)

        # 准备包含最高得分和当前得分的图像
        self.prep_score()
        self.prep_high_score()

    def prep_score(self):
        """将得分转换为渲染的图像"""
        # 绘制当前得分
        self.score_text = self.score_font.render(str(self.score), True, self.text_color_green)

        # 将得分放在屏幕右上角
        self.text_rect = self.score_text.get_rect()
        self.text_rect.centerx = self.screen_rect.centerx
        self.text_rect.top = self.text_rect.top + 5

    def prep_high_score(self):
        """将最高得分转换为渲染的图像"""
        # 获取最高分数
        self.arrayhighscore = read_txt(r'resources/txt/score.txt')[0].split('|')
        self.highscore = self.arrayhighscore[0]
        self.high_score_image = self.score_font.render(str("最高分:" + self.highscore), True, self.text_color_blue)

        # 将最高分放在屏幕顶部中央
        self.high_score_rect = self.high_score_image.get_rect()
        self.high_score_rect.right = self.screen_rect.right - 10
        self.high_score_rect.bottom = self.screen_rect.bottom - 10

    def show_score(self):
        """在屏幕上 显示当前得分和最高得分"""
        self.screen.blit(self.score_text, self.text_rect)
        self.screen.blit(self.high_score_image, self.high_score_rect)

运行效果如下:

相关说明:

1、终身VIP会员无限制任意下载,免积分。即前往开通>>

2、下载积分可通过日常 签到 以及 积分兑换 等途径获得!

3、本站资源无解压密码.

4、本站资源大多存储在云盘,如出现链接失效请评论反馈。

5、本站提供的免费源码、模板、软件工具等其他资源,均不包含技术服务,请大家谅解!资源仅供参考学习只用,请勿用于任何商业用途,请支持正版。

6、源码、模板等资源会随着技术、环境的升级而存在部分问题,还请慎重选择。

版权声明:本文为 “南方小强” 原创文章,转载请附上原文出处链接及文本声明;
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。
南方小强 » 用python编写的《兔子猎手》小游戏的完整代码

发表评论