生活, 其他记录

用Pygame写球球大作战游戏

主要是参照网上这个代码进行改编的:Python 球球大作战简化版https://www.cnblogs.com/wenqiangblog/p/9469909.html。该代码实现了以下功能:点击一下鼠标,在鼠标的地方会出现一个随机大小的球,然后球随机移动,大球遇到小球,会吃掉小球。可玩性不高。

通过改编后,实现了这些功能:

  1. 根据鼠标的位置,自身球在地图中的位置会发生移动;
  2. 自身球固定在屏幕中心,其他球是会根据自身球相对移动。
  3. 当自身球显示大小不再增大时(因为屏幕大小有限,实际值还在增大),其他球显示的大小会根据自身球而等比例减小(实际值仍保持);
  4. 吃地上的豆豆可以慢慢发育起来;
  5. 大球吃小球, 会按一定比例吸收到自身的体积;
  6. 按住鼠标左键,球会加速;
  7. 敌方的球有一定几率改变运动轨迹;
  8. 敌方的球有一定几率会突然加速;
  9. 敌方的球有一定几率会增加自身球的体积的一部分(为了增加后期挑战性);
  10. 所有的球会按一定的比例减小体积,而且体积越大,减少越多。

未开发的功能:

  1. 分身;
  2. 吐球;
  3. 扎刺;
  4. 参数科学化;
  5. 设计和美工;
  6. 联网对战等。

这是游戏的界面截图:

以下是改编的球球大作战代码。可以复制到 Python 中试玩,也可以改参数后增加挑战性。前提是需要安装 pygame 库。安装方法是:win+R, 输入 cmd 回车, 输入 pip install pygame 后回车。需要说明的是:这里的代码没有做额外的优化,可能有些代码内容比较相似或重复,仅供参考。

"""
This code is supported by the website: https://www.guanjihuan.com
The newest version of this code is on the web page: https://www.guanjihuan.com/archives/703
"""

import pygame
import random
import math
import numpy as np

# 参数
screen_width = 1500  # 屏幕宽度
screen_height = 900  # 屏幕高度
map_width = screen_width*4  # 地图的大小
map_height = screen_height*4  # 地图的大小
number_enemy = map_width*map_height/500000  # 敌人的数量
number_dots = map_width * map_height / 50  # 点点的数量
max_show_size = 100  # 球显示的最大半径(屏幕有限,球再增大时,改变的地图比例尺寸)

my_value = 1000  # 我的初始值
enemy_value_low = 500  # 敌人的初始值(最低)
enemy_value_high = 1500  # 敌人的初始值(最高)
dot_value = 30  # 点点的值(地上的豆豆/食物值)
my_speed = 10  # 我的球运动的速度
speed_up = 20  # 按下鼠标时加速
speed_enemy = 10  # 敌人球正常运动速度
speed_enemy_anomaly = 20  # 敌人突然加速时的速度(速度异常时的速度)
anomaly_pro = 0.5  # 敌人加速的概率
change_pro = 0.05  # 敌人移动路径变化的概率,也就是1/change_pro左右会变化一次
eat_percent = 0.9  # 吃掉敌人的球,按多少比例并入自己的体积,1对应的是100%
loss = 0.001  # 按比例减小体重(此外越重的减少越多,10万体积损失值为loss的一倍)
enemy_bigger_pro = 0.0005  # 敌人的值增加了我的球的值的enemy_bigger_rate倍的几率
enemy_bigger_rate = 0.1  # 增加我的球的体积的enemy_bigger_rate倍


class Color(object):  # 定义颜色的类
    @classmethod  # 加了这个可以不需要把实例化,能直接调用类的方法
    def random_color(cls):  # cls, 即class,表示可以通过类名直接调用
        red = random.randint(0, 255)
        green = random.randint(0, 255)
        blue = random.randint(0, 255)
        return red, green, blue


class Ball(object):  # 定义球
    def __init__(self, x, y, sx, sy, color, value):  # 初始化
        self.x = x  # 球的地图位置参数
        self.y = y
        self.sx = sx  # 速度参数
        self.sy = sy
        self.color = color  # 颜色
        self.value = value  # 球的值,也就是球的大小(不是显示的大小)
        self.is_alive = True  # 球默认是存活状态


class My_Ball(Ball):  # 定义我的球,继承了Ball类的方法
    def __init__(self, x, y, sx, sy, color, value):
        # 注意:如果重写了__init__() 时,实例化子类,就不会调用父类已经定义的__init__()
        # 如果子类不重写__init__()方法,实例化子类后,会自动调用父类的__init__()的方法
        # 如果子类重写__init__()方法又需要调用父类的方法,则要使用super关键词。
        super().__init__(x, y, sx, sy, color, value)  # 调用父类Ball的初始化方法__init__()
        self.radius = int(self.value**0.5)  # 我的球的半径(不考虑系数pi)
        if self.radius >= max_show_size:  # 如果半径比规定的最大半径还大,则显示最大半径
            self.show_radius = max_show_size  # 我的球显示的半径
        else:
            self.show_radius = self.radius  # 如果半径没有超过规定最大的半径,则显示原来实际大小的半径
        self.position_x = int(screen_width/2)   # 把我的球固定在屏幕中间position_x,是屏幕显示的位置
        self.position_y = int(screen_height/2)  # 把我的球固定在屏幕中间position_y,是屏幕显示的位置

    def draw(self, window):  # 把我的球画出来
        self.radius = int(self.value ** 0.5)   # 这里重复上面的,因为除了初始化之后,还要更新
        if self.radius >= max_show_size:
            self.show_radius = max_show_size
        else:
            self.show_radius = self.radius
        self.position_x = int(screen_width / 2)
        self.position_y = int(screen_height / 2)
        pygame.draw.circle(window, self.color, (self.position_x , self.position_y), self.show_radius)

    def eat_ball(self, other):  # 吃别的球(包括小点点和敌人)
        if self != other and self.is_alive and other.is_alive:  # 如果other不是自身,自身和对方也都是存活状态,则执行下面动作
            distance = ((self.position_x - other.position_x) ** 2 + (self.position_y - other.position_y) ** 2) ** 0.5   # 两个球之间的距离
            if distance < self.show_radius and (self.show_radius > other.show_radius or (self.show_radius == other.show_radius and self.value > other.value)):  # 如果自身半径比别人大,而且两者距离小于自身半径,那么可以吃掉。
                other.is_alive = False  # 吃球(敌方已死)
                self.value += other.value*eat_percent   # 自己的值增大(体量增大)
                self.radius = int(self.value ** 0.5)  # 计算出半径
                if self.radius >= max_show_size:  # 我的球的显示半径
                    self.show_radius = max_show_size
                else:
                    self.show_radius = self.radius

    def move(self):  # 移动规则
        self.x += self.sx  # 地图位置加上速度
        self.y += self.sy
        # 横向出界
        if self.x < 0:  # 离开了地图左边
            self.x = 0
        if self.x > map_width:  # 离开了地图右边
            self.x = map_width
        # 纵向出界
        if self.y <= 0:  # 离开了地图下边
            self.y = 0
        if self.y >= map_height:  # 离开了地图上边
            self.y = map_height


class Enemy_Ball(Ball):  # 定义敌人的球,继承了Ball类的方法
    def __init__(self, x, y, sx, sy, color, value, host_ball):  # 初始化带上host_ball,也就是我的球
        super().__init__(x, y, sx, sy, color, value)
        self.host_ball = host_ball
        self.radius = int(self.value**0.5)
        if self.host_ball.radius >= max_show_size:  # 如果我的球比规定的最大尺寸还大,则敌人的球显示的比例要减小
            self.show_radius = max(10, int(self.radius/(self.host_ball.radius/max_show_size)))  # 敌人的球也不能太小,最小半径为10
            self.position_x = int((self.x - self.host_ball.x) / (self.host_ball.radius / max_show_size)) + int(
                screen_width / 2)  # 计算出敌人的球和我的球的相对位置,并且按比例减小
            self.position_y = int((self.y - self.host_ball.y) / (self.host_ball.radius / max_show_size)) + int(
                screen_height / 2)  # 计算出敌人的球和我的球的相对位置,并且按比例减小
        else:
            self.show_radius = self.radius  # 正常显示
            self.position_x = (self.x - self.host_ball.x) + int(screen_width / 2)  # 敌人和我的球的相对位置
            self.position_y = (self.y - self.host_ball.y) + int(screen_height / 2)  # 敌人和我的球的相对位置

    # 画出球
    def draw(self, window):
        self.radius = int(self.value ** 0.5)
        if self.host_ball.radius >= max_show_size:  # 这边把初始化的内容再写一遍,因为敌人的球初始化之后还要根据我的球而动态改变
            self.show_radius = max(10, int(self.radius/(self.host_ball.radius/max_show_size)))
            self.position_x = int((self.x - self.host_ball.x) / (self.host_ball.radius / max_show_size)) + int(
                screen_width / 2)
            self.position_y = int((self.y - self.host_ball.y) / (self.host_ball.radius / max_show_size)) + int(
                screen_height / 2)
        else:
            self.show_radius = self.radius
            self.position_x = (self.x - self.host_ball.x) + int(screen_width / 2)
            self.position_y = (self.y - self.host_ball.y) + int(screen_height / 2)
        pygame.draw.circle(window, self.color, (self.position_x, self.position_y), self.show_radius)

    def eat_ball(self, other):
        if self != other and self.is_alive and other.is_alive:
            distance = ((self.position_x - other.position_x) ** 2 + (self.position_y - other.position_y) ** 2) ** 0.5
            if distance < self.show_radius and (self.show_radius > other.show_radius or (self.show_radius == other.show_radius and self.value > other.value)):
                other.is_alive = False  # 吃球
                self.value += other.value*eat_percent
                self.radius = int(self.value ** 0.5)

    def move(self):  # 移动规则
        self.x += self.sx  # 地图位置加上速度
        self.y += self.sy
        # 横向出界
        if self.x < 0:  # 离开了地图左边
            self.sx = -self.sx
            self.x = 0
        if self.x > map_width:  # 离开了地图右边
            self.sx = -self.sx
            self.x = map_width
        # 纵向出界
        if self.y <= 0:  # 离开了地图下边
            self.sy = -self.sy
            self.y = 0
        if self.y >= map_height:  # 离开了地图上边
            self.sy = -self.sy
            self.y = map_height


class Dot_Ball(Ball):  # 定义地上的小点点,供自己的球和敌人的球吃,继承了Ball类的方法
    def __init__(self, x, y,  sx, sy, color, value, host_ball):
        super().__init__(x, y, sx, sy, color, value)
        self.host_ball = host_ball
        self.radius = 8  # 初始小点点大小
        if self.host_ball.radius >= max_show_size:
            self.show_radius = max(3, int(self.radius/(self.host_ball.radius/max_show_size)))  # 小点点显示也不能太小,最小显示半径为3
            self.position_x = int((self.x - self.host_ball.x) / (self.host_ball.radius / max_show_size)) + int(
                screen_width / 2)
            self.position_y = int((self.y - self.host_ball.y) / (self.host_ball.radius / max_show_size)) + int(
                screen_height / 2)
        else:
            self.show_radius = self.radius
            self.position_x = (self.x - self.host_ball.x) + int(screen_width / 2)
            self.position_y = (self.y - self.host_ball.y) + int(screen_height / 2)

    # 画出球
    def draw(self, window):
        if self.host_ball.radius >= max_show_size:  # 这边把初始化的内容再写一遍,因为小点点初始化之后还要根据我的球而动态改变
            self.show_radius = max(3, int(self.radius/(self.host_ball.radius/max_show_size)))
            self.position_x = int((self.x - self.host_ball.x) / (self.host_ball.radius / max_show_size)) + int(
                screen_width / 2)
            self.position_y = int((self.y - self.host_ball.y) / (self.host_ball.radius / max_show_size)) + int(
                screen_height / 2)
        else:
            self.show_radius = self.radius
            self.position_x = (self.x - self.host_ball.x) + int(screen_width / 2)
            self.position_y = (self.y - self.host_ball.y) + int(screen_height / 2)
        pygame.draw.circle(window, self.color, (self.position_x, self.position_y) , self.show_radius)


def creat_my_ball():  # 产生我的球
    x = random.randint(0, map_width)  # 我的球在地图中的位置,随机生成
    y = random.randint(0, map_height)
    value = my_value  # 我的球的初始值
    color = 255, 255, 255  # 我的球的颜色
    sx = 0  # 速度默认为0
    sy = 0
    host_ball = My_Ball(x, y, sx, sy, color, value)  # 调用My_Ball类
    return host_ball  # 返回我的球


def auto_creat_ball(balls, host_ball):  # 自动产生敌人的球
    if len(balls) <= number_enemy:  # 控制敌人的数量,如果个数够了,就不再生成
        x = random.randint(0, map_width)  # 敌人球在地图中的位置,随机生成
        y = random.randint(0, map_height)
        value = random.randint(enemy_value_low, enemy_value_high)  # 敌人的球初始值
        sx = random.randint(-speed_enemy, speed_enemy)  # 敌人的球移动速度
        i2 = random.randint(0, 1)  # y的移动方向
        if i2 == 0:
            sy = int((speed_enemy**2 - sx**2) ** 0.5)
        else:
            sy = -int((speed_enemy ** 2 - sx ** 2) ** 0.5)
        color = Color.random_color()  # 敌人的颜色随机生成
        enemy = Enemy_Ball(x, y, sx, sy, color, value, host_ball)
        balls.append(enemy)


def auto_creat_dots(dots, host_ball):  # 自动生成点点
    if len(dots) <= number_dots:  # 控制点点的数量
        x = random.randint(0, map_width)  # 随机生成点点的位置
        y = random.randint(0, map_height)
        value = dot_value  # 点点的值
        sx = 0  # 点点速度为0
        sy = 0
        color = Color.random_color()  # 颜色
        dot = Dot_Ball(x, y, sx, sy, color, value, host_ball)
        dots.append(dot)


def control_my_ball(host_ball):  # 控制我的球
    host_ball.move()
    host_ball.value = host_ball.value*(1-loss*host_ball.value/100000)
    for event in pygame.event.get():  # 监控事件(鼠标移动)
        # print(event)
        if event.type == pygame.MOUSEBUTTONDOWN:
            pos = event.pos
            speed = speed_up
        elif event.type == pygame.MOUSEMOTION:
            pos = event.pos
            if event.buttons[0] == 1:
                speed = speed_up
            if event.buttons[0] == 0:
                speed = my_speed
        elif event.type == pygame.MOUSEBUTTONUP:
            pos = event.pos
            speed = my_speed
        else:
            pos = [screen_width/2, screen_height/2]
            speed = my_speed
        if abs(pos[0] - screen_width/2) < 30 and abs(pos[1] - screen_height/2) < 30:
            host_ball.sx = 0
            host_ball.sy = 0
        elif pos[0] > screen_width/2 and pos[1] >= screen_height/2:
            angle = abs(math.atan((pos[1] - screen_height/2) / (pos[0] - screen_width/2)))
            host_ball.sx = int(speed * math.cos(angle))
            host_ball.sy = int(speed * math.sin(angle))
        elif pos[0] > screen_width/2 and pos[1] < screen_height/2:
            angle = abs(math.atan((pos[1] - screen_height/2) / (pos[0] - screen_width/2)))
            host_ball.sx = int(speed * math.cos(angle))
            host_ball.sy = -int(speed * math.sin(angle))
        elif pos[0] < screen_width/2 and pos[1] >= screen_height/2:
            angle = abs(math.atan((pos[1] - screen_height/2) / (pos[0] - screen_width/2)))
            host_ball.sx = -int(speed * math.cos(angle))
            host_ball.sy = int(speed * math.sin(angle))
        elif pos[0] < screen_width/2 and pos[1] < screen_height/2:
            angle = abs(math.atan((pos[1] - screen_height/2) / (pos[0] - screen_width/2)))
            host_ball.sx = -int(speed * math.cos(angle))
            host_ball.sy = -int(speed * math.sin(angle))
        elif pos[0] == screen_width/2:
            host_ball.sx = 0
            if pos[1] >= 0:
                host_ball.sy = speed
            else:
                host.ball.sy = -speed


def enemy_move(balls, host_ball):  # 敌人移动
    for enemy in balls:
        enemy.move()  # 移动
        enemy.value = enemy.value*(1-loss*enemy.value/100000)
        if random.randint(1, int(1/enemy_bigger_pro)) == 1:
            enemy.value += host_ball.value*enemy_bigger_rate
        if random.randint(1, int(1/anomaly_pro)) == 1:
            speed_enemy0 = speed_enemy_anomaly  # 敌人异常速度
        else:
            speed_enemy0 = speed_enemy  # 敌人正常速度
        i = random.randint(1, int(1/change_pro))  # 一定的概率改变轨迹
        if i == 1:
            enemy.sx = random.randint(-speed_enemy0, speed_enemy0)
            i2 = random.randint(0, 1)
            if i2 == 0:
                enemy.sy = int((speed_enemy0 ** 2 - enemy.sx ** 2) ** 0.5)
            else:
                enemy.sy = -int((speed_enemy0 ** 2 - enemy.sx ** 2) ** 0.5)


def eat_each_other(host_ball, balls, dots):  # 吃球
    for enemy in balls:
        for enemy2 in balls:
            enemy.eat_ball(enemy2)  # 敌人互吃
        for food in dots:
            enemy.eat_ball(food)  # 敌人吃点点
    for enemy in balls:
        host_ball.eat_ball(enemy)  # 我吃敌人
        enemy.eat_ball(host_ball)  # 敌人吃我
    for food in dots:
        host_ball.eat_ball(food)  # 我吃点点


def paint(host_ball, balls, dots, screen):
    screen.fill((0, 0, 0))  # 刷漆
    if host_ball.is_alive:
        host_ball.draw(screen)
    for enemy in balls:  # 遍历容器
        if enemy.is_alive:
            enemy.draw(screen)
        else:
            balls.remove(enemy)
    for food in dots:  # 遍历容器
        if food.is_alive:
            food.draw(screen)
        else:
            dots.remove(food)


def main():
    pygame.init()  # 初始化
    screen = pygame.display.set_mode((screen_width, screen_height))  # 设置屏幕
    pygame.display.set_caption("球球大作战")  # 设置屏幕标题
    balls = []  # 定义一容器  存放所有的敌方球
    dots = []  # 定义一容器 存放所有的点点
    is_running = True  # 默认运行状态
    host_ball = creat_my_ball()  # 产生我的球
    i00 = 0  # 一个参数
    while is_running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                is_running = False
        auto_creat_dots(dots, host_ball)  # 自动生成点点
        auto_creat_ball(balls, host_ball)  # 自动生成敌人
        paint(host_ball, balls, dots, screen)  # 把所有的都画出来 调用draw方法
        pygame.display.flip()  # 渲染
        pygame.time.delay(30)  # 设置动画的时间延迟

        control_my_ball(host_ball)  # 移动我的球
        enemy_move(balls, host_ball)  # 敌人的球随机运动
        eat_each_other(host_ball, balls, dots)  # 吃球 调用eat_ball方法
        i00 += 1
        if np.mod(i00, 50) == 0:
            print(host_ball.value)


if __name__ == '__main__':
    main()
3,619 次浏览

【说明:本站主要是个人的一些笔记和代码分享,内容可能会不定期修改。为了使全网显示的始终是最新版本,这里的文章未经同意请勿转载。引用请注明出处:https://www.guanjihuan.com

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code