OpenGL

一、OpenGL介绍

​OpenGL(Open Graphics Library)是一种跨语言、跨平台的应用程序编程接口(API),用于渲染2D和3D矢量图形。​它提供了一组函数,允许开发者与图形处理单元(GPU)交互,实现硬件加速的图形渲染。


OpenGL详解:GPU接口规范与图形渲染管线:https://blog.csdn.net/weixin_45449806/article/details/130207607


二、OpenGL组件

①顶点(Vertex)
②坐标(Coordinates)
③颜色(Color)
④纹理(Texture)
⑤着色器(Shader)
⑥帧缓冲(Frame Buffer)


三、OpenGL代码

1.2D白色矩形

(1)C++代码

#include <GL/glut.h>

void myDisplay(void)

{
     glClear(GL_COLOR_BUFFER_BIT);

     glRectf(-0.5f, -0.5f, 0.5f, 0.5f);

     glFlush();
}

int main(int argc, char *argv[])

{
     glutInit(&argc, argv);

     glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

     glutInitWindowPosition(100, 100);

     glutInitWindowSize(400, 400);

     glutCreateWindow("第一个OpenGL程序");

     glutDisplayFunc(&myDisplay);

     glutMainLoop();

     return 0;
}

(2)代码解释

该程序的作用是在一个黑色的窗口中央画一个白色的矩形。下面对各行语句进行说明。

首先,需要包含头文件#include <GL/glut.h>,这是GLUT的头文件,其已经包含了<GL/gl.h>和<GL/glu.h>

注意main函数中的各语句,除了最后的return之外,其余全部以glut开头。这种以glut开头的函数都是GLUT工具包所提供的函数,以下是这些函数的介绍:
①glutInit:
对GLUT进行初始化,这个函数必须在其它的GLUT使用之前调用一次。其格式比较死板,一般照抄这句glutInit(&argc, argv)就可以了。
②glutInitDisplayMode:
设置显示方式,其中GLUT_RGB表示使用RGB颜色,与之对应的还有GLUT_INDEX(表示使用索引颜色)。GLUT_SINGLE表示使用单缓冲,与之对应的还有GLUT_DOUBLE(使用双缓冲)
③glutInitWindowPosition:设置窗口在屏幕中的位置。
④glutInitWindowSize:设置窗口的大小
⑤glutCreateWindow:
根据前面设置的信息创建窗口。参数将被作为窗口的标题。
注意:窗口被创建后,并不立即显示到屏幕上。需要调用glutMainLoop才能看到窗口。
⑥glutDisplayFunc:
设置一个函数,当需要进行画图时,这个函数就会被调用
⑦glutMainLoop:
进行一个消息循环。(这个可能初学者也不太明白,现在只需要知道这个函数可以显示窗口,并且等待窗口关闭后才会返回,这就足够了。)

在glutDisplayFunc函数中,我们设置了“当需要画图时,请调用myDisplay函数”。于是myDisplay函数就用来画图。观察myDisplay中的三个函数调用,发现它们都以gl开头。这种以gl开头的函数都是OpenGL的标准函数,下面对用到的函数进行介绍。
①glClear:清除
GL_COLOR_BUFFER_BIT表示清除颜色,glClear函数还可以清除其它的东西,但这里不作介绍。
②glRectf:画一个矩形
四个参数分别表示了位于对角线上的两个点的横、纵坐标。
③glFlush:刷新屏幕
保证前面的OpenGL命令立即执行,而不是让它们在缓冲区中等待。作用类似 fflush stdout。


2.2D彩色三角形

(1)效果

在这里插入图片描述


(2)步骤

1.激活虚拟环境

conda activate pytorch_env1

2.安装pygame库

pip install pygame 
pip install PyOpenGL
pip install PyOpenGL_accelerate

①Pygame 是一个用于开发视频游戏的跨平台 Python 库。它提供了计算机图形(2D)和声音(音频)功能,使开发者能够轻松地创建游戏和多媒体应用。Pygame 提供了对图像、声音、事件处理、输入设备等方面的支持,适合用于制作简单到中等复杂度的游戏。
②PyOpenGL 是 Python 对 OpenGL 图形库的绑定,使 Python 程序能够使用 OpenGL 提供的功能进行高性能的 3D 图形渲染。OpenGL(Open Graphics Library)是一个跨语言、跨平台的图形渲染 API,广泛用于 3D 图形的开发。通过 PyOpenGL,开发者可以在 Python 中访问 OpenGL 的功能,进行复杂的 3D 图形处理。
③PyOpenGL_accelerate 是 PyOpenGL 的一个扩展模块,旨在通过使用 Cython 对关键部分进行编译优化,以提高性能。它提供了对 PyOpenGL 的加速,使得在进行大量图形计算时,能够获得更好的性能表现。需要注意,安装 PyOpenGL_accelerate 需要有一个正常工作的 Python 扩展编译环境。


(3)python代码

import pygame                #导入Pygame库, 用于创建游戏窗口和处理事件
from pygame.locals import *  #导入Pygame的本地模块, 包含常用的变量和函数

from OpenGL.GL import *      #导入OpenGL的核心功能
from OpenGL.GLUT import *    #导入OpenGL的实用工具库
from OpenGL.GLU import *     #导入OpenGL的实用工具库

#triangle vertices
vertices = [
    [0, 1, 0],   #vertex0
    [-1, -1, 0], #vertex1
    [1, -1, 0]   #vertex2
]

#triangle colors
colors = [
    [1, 0, 0],  #red
    [0, 1, 0],  #green
    [0, 0, 1]   #blue
]

#绘制三角形
def Triangle():
    glBegin(GL_TRIANGLES)
    for i, vertex in enumerate(vertices):
        glColor3fv(colors[i]) #config color
        glVertex3fv(vertex)   #config vertex
    glEnd()

def main():
    pygame.init()
    display = (800, 600)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)     #创建窗口

    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0) #设置透视参数
    glTranslatef(0.0, 0.0, -5)                        #平移视图

    while True:  #主循环
        for event in pygame.event.get():   #处理事件
            if event.type == pygame.QUIT:  #如果是退出事件,则退出程序
                pygame.quit()
                quit()

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)  #清除屏幕和深度缓冲

        Triangle()  #绘制三角形

        pygame.display.flip()  #刷新屏幕
        pygame.time.wait(10)   #稍微等待一下,减少CPU占用

main()  #调用主函数,启动程序

(4)代码解释

在这个例子中,我们首先定义了三角形的顶点和颜色。然后,我们定义了一个函数 Triangle,它使用 OpenGL 的函数 glBeginglColor3fvglVertex3fv 来绘制三角形。

在 main 函数中,我们初始化 Pygame,并创建一个 800x600 的窗口。

然后,我们设置 OpenGL 的视口和透视参数。

接着,我们进入一个无限循环,在这个循环中,我们处理事件(例如,检测到窗口关闭事件时退出程序),清除屏幕和深度缓冲,然后调用 Triangle 函数绘制三角形,最后刷新屏幕。

pygame.display.set_mode(display, DOUBLEBUF|OPENGL) 在这行代码中,pygame.display.set_mode() 是 Pygame 的函数,用于创建游戏窗口。
参数 DOUBLEBUF|OPENGL 表示我们希望窗口支持双缓冲和 OpenGL。双缓冲可以防止画面闪烁,OpenGL 是我们要使用的图形库。


3.3D旋转球体

(1)效果

在这里插入图片描述


(2)python代码

import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *

# 初始化光照参数
def init_lighting():
    # 设置光源位置和亮度
    glLight(GL_LIGHT0, GL_POSITION, (1, 1, 1, 0))  # 设置光源位置
    glLight(GL_LIGHT0, GL_DIFFUSE, (1.0, 1.0, 1.0, 1))   # 增强光源的漫反射光强度
    glLight(GL_LIGHT0, GL_SPECULAR, (1.0, 1.0, 1.0, 1))  # 增强光源的高光反射光强度
    glEnable(GL_LIGHT0)  # 启用光源0
    glEnable(GL_LIGHTING)  # 启用光照

    # 启用阴影
    glEnable(GL_DEPTH_TEST)

    # 设置环境光(全局光照),使得球体的中间部分变亮
    glLightfv(GL_LIGHT0, GL_AMBIENT, (0.3, 0.3, 0.3, 1))  # 设置环境光,弱光源用于全局照亮

    # 设置材质反射率,适应多种光源
    glMaterial(GL_FRONT, GL_DIFFUSE, (1.0, 0.8, 0.2, 1))  # 设置物体的漫反射光反射率 (橙色)
    glMaterial(GL_FRONT, GL_SPECULAR, (1.0, 1.0, 1.0, 1))  # 设置物体的高光反射光反射率
    glMaterial(GL_FRONT, GL_SHININESS, 100)  # 设置高光反射光的强度 (强光)

    # 添加第二个光源
    glLight(GL_LIGHT1, GL_POSITION, (-1, -1, 1, 0))  # 第二个光源的位置
    glLight(GL_LIGHT1, GL_DIFFUSE, (1.0, 1.0, 1.0, 1))   # 增加光源的强度
    glLight(GL_LIGHT1, GL_SPECULAR, (1.0, 1.0, 1.0, 1))  # 第二个光源的反射强度
    glEnable(GL_LIGHT1)  # 启用第二个光源

# 绘制球体
def draw_sphere():
    # 使用OpenGL的GLU库绘制一个球体
    quadric = gluNewQuadric()  # 创建一个四面体对象
    gluSphere(quadric, 1, 32, 32)  # 绘制球体,半径为1,细分为32x32

def main():
    pygame.init()
    display = (800, 600)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
    gluPerspective(45, (display[0] / display[1]), 0.1, 50.0)  # 设置透视视角
    glTranslatef(0.0, 0.0, -5)  # 平移视图

    # 启用光照
    init_lighting()

    # 主循环
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 退出事件
                pygame.quit()
                quit()

        glRotatef(1, 3, 1, 1)  # 旋转球体
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 清除颜色缓冲和深度缓冲
        draw_sphere()  # 绘制球体
        pygame.display.flip()  # 刷新屏幕
        pygame.time.wait(10)  # 稍微等待一下,减少CPU占用

if __name__ == "__main__":
    main()

(3)效果2:彩色2.5D球体

颜色随时改变
在这里插入图片描述


(4)python代码2

import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import math

def Sphere():
    slices = 50  # 经度细分数
    stacks = 50  # 纬度细分数
    radius = 1.0 # 球体半径

    # 遍历每个纬度层
    for i in range(stacks):
        # 计算当前纬度和下一纬度的角度
        theta1 = i * math.pi / stacks          # 当前层纬度
        theta2 = (i + 1) * math.pi / stacks    # 下一层纬度

        glBegin(GL_TRIANGLE_STRIP)  # 使用三角形条带构建球面
        # 遍历每个经度点
        for j in range(slices + 1):
            phi = j * 2 * math.pi / slices  # 经度角
            # 为当前纬度层和下一纬度层生成顶点
            for theta in [theta1, theta2]:
                # 球面坐标转换为笛卡尔坐标
                x = radius * math.sin(theta) * math.cos(phi)
                y = radius * math.sin(theta) * math.sin(phi)
                z = radius * math.cos(theta)

                # 根据坐标位置计算颜色(映射到0-1范围)
                r = (x + 1) / 2  # X轴方向颜色分量
                g = (y + 1) / 2  # Y轴方向颜色分量
                b = (z + 1) / 2  # Z轴方向颜色分量

                glColor3f(r, g, b)    # 设置顶点颜色
                glVertex3f(x, y, z)   # 添加顶点坐标
        glEnd()

def main():
    pygame.init()
    display = (800, 600)
    # 创建OpenGL显示窗口
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)

    # 设置透视投影
    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)
    glTranslatef(0.0, 0.0, -5)  # 将视点向后移动5个单位

    glEnable(GL_DEPTH_TEST)  # 启用深度测试

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        glRotatef(1, 3, 1, 1)  # 绕(3,1,1)轴旋转,每次1度
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 清除缓冲
        Sphere()                # 绘制球体
        pygame.display.flip()   # 刷新显示
        pygame.time.wait(10)    # 控制渲染速度

if __name__ == "__main__":
    main()

4.3D彩色旋转立方体

(1)效果

在这里插入图片描述


(2)python代码

import pygame                #导入Pygame库,用于创建游戏窗口和处理事件
from pygame.locals import *  #导入Pygame的本地模块,包含常用的变量和函数
from OpenGL.GL import *      #导入OpenGL的核心功能
from OpenGL.GLUT import *    #导入OpenGL的实用工具库
from OpenGL.GLU import *     #导入OpenGL的实用工具库

#Cube vertices
vertices = (
    (1, -1, -1), (1, 1, -1),   #前面的两个顶点
    (-1, 1, -1), (-1, -1, -1), #左面的两个顶点
    (1, -1, 1), (1, 1, 1),     #后面的两个顶点
    (-1, -1, 1), (-1, 1, 1)    #右面的两个顶点
)

#定义立方体的面
faces = (
    (0, 1, 2, 3),  #前面的四个顶点
    (3, 2, 7, 6),  #左面的四个顶点
    (6, 7, 5, 4),  #后面的四个顶点
    (4, 5, 1, 0),  #右面的四个顶点
    (1, 5, 7, 2),  #上面的四个顶点
    (4, 0, 3, 6)   #下面的四个顶点
)

#定义面的颜色
colors = (
    (1, 0, 0),  #红色
    (0, 1, 0),  #绿色
    (0, 0, 1),  #蓝色
    (1, 1, 0),  #黄色
    (1, 0, 1),  #紫色
    (0, 1, 1),  #青色
    (1, 1, 1),  #白色
    (0, 0, 0)   #黑色
)
#绘制立方体
def Cube():
    glBegin(GL_QUADS)   #开始绘制四边形
    for face in faces:
        x = 0
        for vertex in face:
            x += 1
            glColor3fv(colors[x])          #设置顶点颜色
            glVertex3fv(vertices[vertex])  #设置顶点坐标
    glEnd()             #结束绘制四边形

def main():
    pygame.init()          #初始化Pygame
    display = (800, 600)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)      #创建窗口
    gluPerspective(45, (display[0] / display[1]), 0.1, 50.0)  #设置透视参数
    glTranslatef(0.0, 0.0, -5)                           #平移视图

    # Enable depth testing
    glEnable(GL_DEPTH_TEST)  #启用深度测试

    #主循环(无限循环)
    while True:
        for event in pygame.event.get():   #处理事件
            if event.type == pygame.QUIT:  #如果是退出事件,则退出程序
                pygame.quit()
                quit()

        glRotatef(1, 3, 1, 1)                 #旋转立方体
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) #清除屏幕和深度缓冲
        Cube()                 #绘制立方体
        pygame.display.flip()  #刷新屏幕
        pygame.time.wait(10)   #稍微等待一下,减少 CPU 占用

main()  #调用主函数,启动程序

(3)代码解释

这段代码使用了许多图形编程和计算机图形学的技术和概念,包括OpenGL,深度缓冲,顶点数组,颜色数组,透视变换,模型视图变换,渲染循环等。

Pygame:
Pygame 是一个 Python 编写的跨平台的视频游戏开发库。它包括计算机图形学和声音库,设计用于创建视频游戏。在这个程序中,Pygame 主要用于创建显示窗口和处理用户输入(如点击关闭按钮)。
OpenGL:
OpenGL(Open Graphics Library)是一个跨语言、跨平台的应用程序编程接口(API),用于渲染2D、3D矢量图形。在这段代码中,OpenGL 负责实际的立方体绘制工作。
GL_QUADS:
GL_QUADS是OpenGL中的一个绘制模式,表示每四个顶点构成一个独立的四边形。在glBegin和glEnd之间定义的顶点会被组织成四边形进行渲染。
glColor3fv 和 glVertex3fv:
这两个函数分别用于设置顶点的颜色和坐标。3fv表示这个函数接受一个包含三个浮点数的数组,f表示浮点数,v表示向量(在这里指的是数组)。
深度缓冲(Depth Buffer):
深度缓冲是3D渲染中的一个技术,用于判断一个像素是否应该被绘制到屏幕上。当有多个物体在同一个位置渲染时,深度缓冲可以帮助我们判断哪一个物体在前面,哪一个物体在后面。这个程序中通过调用glEnable(GL_DEPTH_TEST)来启用深度测试,然后在每一帧的开始时清除深度缓冲。
透视变换(Perspective Projection):
透视变换是3D计算机图形中的一种技术,用于将3D世界转换成2D图像。在这个程序中,通过gluPerspective函数设置透视变换的参数。
模型视图变换(Model View Transformation): 模型视图变换是3D计算机图形中的一种技术,用于设置和改变物体在3D世界中的位置和方向。在这个程序中,通过glTranslatef和glRotatef函数来移动和旋转立方体。
渲染循环(Rendering Loop): 渲染循环是所有图形应用程序的核心,它控制图形的更新和渲染。在这个程序中,渲染循环是主循环,包含事件处理,立方体旋转,绘制和缓冲区交换等操作。


5.小球在旋转的正六边形中,受重力影响。键盘按←→键控制旋转速度

(1)效果

在这里插入图片描述


(2)python代码

import pygame
import math

# Initialize Pygame
pygame.init()

# Constants
WIDTH, HEIGHT = 800, 600
CENTER = (WIDTH // 2, HEIGHT // 2)
HEX_RADIUS = 250
BALL_RADIUS = 15
GRAVITY = 0.5
FRICTION = 0.995
RESTITUTION = 0.8
ROTATION_SPEED = 0.02  #控制正六边体的旋转速度

# Set up display
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Bouncing Ball in Spinning Hexagon")
clock = pygame.time.Clock()

# Generate base hexagon vertices
base_hexagon = []
for i in range(6):
    angle = math.pi/3 * i
    x = HEX_RADIUS * math.cos(angle)
    y = HEX_RADIUS * math.sin(angle)
    base_hexagon.append((x, y))

# Initialize ball
ball_pos = [CENTER[0], CENTER[1] - HEX_RADIUS + 50]
ball_vel = [2, 0]

def main():
    global ROTATION_SPEED
    current_angle = 0
    running = True

    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        #键盘按← →键控制正六边体的旋转速度
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            ROTATION_SPEED = max(0, ROTATION_SPEED - 0.001)   #限制最小值
        if keys[pygame.K_RIGHT]:
            ROTATION_SPEED += 0.001  #右方向键加速旋转

        # Update rotation angle
        current_angle = (current_angle + ROTATION_SPEED) % (2 * math.pi)

        # Apply physics
        ball_vel[1] += GRAVITY
        ball_vel[0] *= FRICTION
        ball_vel[1] *= FRICTION
        ball_pos[0] += ball_vel[0]
        ball_pos[1] += ball_vel[1]

        # Generate rotated hexagon
        current_hex = []
        for x, y in base_hexagon:
            rot_x = x * math.cos(current_angle) - y * math.sin(current_angle)
            rot_y = x * math.sin(current_angle) + y * math.cos(current_angle)
            current_hex.append((rot_x + CENTER[0], rot_y + CENTER[1]))

        # Collision detection
        for i in range(6):
            a = current_hex[i]
            b = current_hex[(i+1) % 6]

            # Calculate wall midpoint and normal
            mid = ((a[0]+b[0])/2, (a[1]+b[1])/2)
            norm = (CENTER[0] - mid[0], CENTER[1] - mid[1])
            length = math.hypot(*norm)
            if length == 0:
                continue
            norm = (norm[0]/length, norm[1]/length)

            # Find closest point on wall to ball
            ap = (ball_pos[0]-a[0], ball_pos[1]-a[1])
            ab = (b[0]-a[0], b[1]-a[1])
            t = max(0, min(1, (ap[0]*ab[0] + ap[1]*ab[1])/(ab[0]**2 + ab[1]**2 + 1e-8)))
            closest = (a[0] + t*ab[0], a[1] + t*ab[1])

            # Check collision
            dx = ball_pos[0] - closest[0]
            dy = ball_pos[1] - closest[1]
            distance = math.hypot(dx, dy)

            if distance < BALL_RADIUS:
                # Calculate wall point velocity
                wall_vel = (-ROTATION_SPEED*(closest[1]-CENTER[1]),
                            ROTATION_SPEED*(closest[0]-CENTER[0]))

                # Calculate relative velocity
                rel_vel = (ball_vel[0]-wall_vel[0], ball_vel[1]-wall_vel[1])
                v_dot_n = rel_vel[0]*norm[0] + rel_vel[1]*norm[1]

                if v_dot_n < 0:
                    # Reflect velocity
                    rel_vel = (rel_vel[0] - 2*v_dot_n*norm[0],
                               rel_vel[1] - 2*v_dot_n*norm[1])

                    # Apply restitution and update velocity
                    ball_vel[0] = rel_vel[0]*RESTITUTION + wall_vel[0]
                    ball_vel[1] = rel_vel[1]*RESTITUTION + wall_vel[1]

                    # Reposition ball
                    overlap = BALL_RADIUS - distance
                    ball_pos[0] += norm[0] * overlap
                    ball_pos[1] += norm[1] * overlap

        # Draw everything
        screen.fill((0, 0, 0))
        pygame.draw.lines(screen, (255, 255, 255), True, current_hex, 2)
        pygame.draw.circle(screen, (255, 0, 0),
                           (int(ball_pos[0]), int(ball_pos[1])), BALL_RADIUS)
        pygame.display.flip()
        clock.tick(60)

    pygame.quit()

if __name__ == "__main__":
    main()



四、用Pygame开发小游戏

1.贪吃蛇

蛇身长度为1时,可以按P键暂停调整位置。蛇身长度>1时再按P会死亡。

import pygame
import time
import random
import os

# 初始化pygame
pygame.init()

# 游戏窗口尺寸
window_width = 600
window_height = 400
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("贪吃蛇游戏")

# 颜色定义
white = (255, 255, 255)
yellow = (255, 255, 102)
black = (0, 0, 0)
red = (213, 50, 80)
light_green = (144, 238, 144)
dark_green = (34, 139, 34)
blue = (50, 153, 213)

# 基本参数
snake_block = 15
snake_speed = 15

# 背景图路径
bg_path = "/mnt/data/image.png"  # 这里修改为你的图片路径

# 默认背景颜色
background = blue  # 如果未加载图片,则使用蓝色背景

# 尝试加载背景图像
try:
    if os.path.exists(bg_path):
        background = pygame.image.load(bg_path)
        background = pygame.transform.scale(background, (window_width, window_height))  # 调整大小
    else:
        raise FileNotFoundError(f"未找到图片文件: {bg_path}")
except Exception as e:
    print(f"错误: {e},使用默认蓝色背景。")

# 字体定义
score_font = pygame.font.SysFont("comicsansms", 40)
msg_font = pygame.font.SysFont("impact", 50)

# 显示分数(右上角,黑色)
def Your_score(score):
    value = score_font.render(f"Score: {score}", True, black)
    window.blit(value, [window_width - value.get_width() - 20, 20])

# 显示Game Over消息,分为两行显示
def message_centered(msg1, msg2, color):
    text1 = msg_font.render(msg1, True, color)
    text2 = msg_font.render(msg2, True, color)

    # 计算两行消息的显示位置
    text1_rect = text1.get_rect(center=(window_width // 2, window_height // 3))
    text2_rect = text2.get_rect(center=(window_width // 2, window_height // 2 + 40))

    window.blit(text1, text1_rect)
    window.blit(text2, text2_rect)

# 画蛇(尾到头,最后一个是蛇头)
def draw_snake(snake_block, snake_list):
    for i, pos in enumerate(snake_list):
        color = dark_green if i == len(snake_list) - 1 else light_green
        pygame.draw.rect(window, color, [pos[0], pos[1], snake_block, snake_block])

# 画食物(圆形)
def draw_food(x, y):
    pygame.draw.circle(window, yellow, (x + snake_block // 2, y + snake_block // 2), snake_block // 2)

# 蛇是否吃到食物(精确碰撞)
def check_food_collision(x1, y1, fx, fy):
    return abs(x1 - fx) < snake_block and abs(y1 - fy) < snake_block

# 游戏开始界面
def game_start_screen():
    window.fill(blue)
    message_centered("Press Any Key to Start", " ", white)
    pygame.display.update()

    waiting = True
    while waiting:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                waiting = False

# 游戏主循环
def gameLoop():
    game_over = False
    game_close = False
    game_paused = False

    # 初始蛇的位置和方向
    x1 = window_width / 2
    y1 = window_height / 2
    x1_change = 0
    y1_change = 0

    snake_list = []
    snake_length = 1

    foodx = round(random.randrange(0, window_width - snake_block) / 10.0) * 10.0
    foody = round(random.randrange(0, window_height - snake_block) / 10.0) * 10.0

    while not game_over:
        while game_close:
            window.fill(blue)
            message_centered("Game Over!", "Press Q to Quit or C to Restart", red)
            Your_score(snake_length - 1)
            pygame.display.update()

            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_q:
                        game_over = True
                        game_close = False
                    if event.key == pygame.K_c:
                        gameLoop()

        # 暂停功能
        if game_paused:
            window.fill(blue)
            message_centered("Paused!", "Press P to Resume", red)
            Your_score(snake_length - 1)
            pygame.display.update()

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    game_over = True
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_p:  # 按 P 键继续游戏
                        game_paused = False
                    elif event.key == pygame.K_q:
                        game_over = True
                        game_close = False

        # 处理所有键盘事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT and not game_paused:
                    x1_change = -snake_block
                    y1_change = 0
                elif event.key == pygame.K_RIGHT and not game_paused:
                    x1_change = snake_block
                    y1_change = 0
                elif event.key == pygame.K_UP and not game_paused:
                    y1_change = -snake_block
                    x1_change = 0
                elif event.key == pygame.K_DOWN and not game_paused:
                    y1_change = snake_block
                    x1_change = 0
                elif event.key == pygame.K_p:  # 按 P 键暂停游戏
                    game_paused = True

        # 暂停时蛇不再移动,方向不变
        if game_paused:
            x1_change = 0
            y1_change = 0

        # 判断是否撞到墙壁
        if x1 >= window_width or x1 < 0 or y1 >= window_height or y1 < 0:
            game_close = True

        # 更新蛇的位置
        x1 += x1_change
        y1 += y1_change

        # 如果背景是图片,使用blit,否则直接填充颜色
        if isinstance(background, pygame.Surface):
            window.blit(background, (0, 0))  # 保证背景图在最底层
        else:
            window.fill(background)  # 如果是颜色背景,直接填充颜色

        draw_food(foodx, foody)  # 食物绘制

        # 更新蛇身
        head = [x1, y1]
        snake_list.append(head)
        if len(snake_list) > snake_length:
            del snake_list[0]

        # 如果蛇的头部碰到自己的身体
        for segment in snake_list[:-1]:
            if segment == head:
                game_close = True

        draw_snake(snake_block, snake_list)
        Your_score(snake_length - 1)
        pygame.display.update()

        # 吃到食物
        if check_food_collision(x1, y1, foodx, foody):
            foodx = round(random.randrange(0, window_width - snake_block) / 10.0) * 10.0
            foody = round(random.randrange(0, window_height - snake_block) / 10.0) * 10.0
            snake_length += 1

        pygame.time.Clock().tick(snake_speed)

    pygame.quit()
    quit()

# 启动游戏前的开始界面
game_start_screen()

# 启动游戏
gameLoop()

2.俄罗斯方块

import pygame
import random

# 初始化 Pygame
pygame.init()

# 定义常量
WIDTH = 300
HEIGHT = 600
BLOCK_SIZE = 30
GRID_WIDTH = WIDTH // BLOCK_SIZE
GRID_HEIGHT = HEIGHT // BLOCK_SIZE

# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
CYAN = (0, 255, 255)
YELLOW = (255, 255, 0)
MAGENTA = (255, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
ORANGE = (255, 165, 0)

# 定义方块形状
SHAPES = [
    [[1, 1, 1, 1]],
    [[1, 1], [1, 1]],
    [[1, 1, 0], [0, 1, 1]],
    [[0, 1, 1], [1, 1, 0]],
    [[1, 1, 1], [0, 1, 0]],
    [[1, 1, 1], [1, 0, 0]],
    [[1, 1, 1], [0, 0, 1]]
]

COLORS = [CYAN, YELLOW, MAGENTA, GREEN, RED, BLUE, ORANGE]

# 创建游戏窗口
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("俄罗斯方块")

# 初始化网格
grid = [[0] * GRID_WIDTH for _ in range(GRID_HEIGHT)]


# 生成新方块
def new_piece():
    shape = random.choice(SHAPES)
    color = random.choice(COLORS)
    x = GRID_WIDTH // 2 - len(shape[0]) // 2
    y = 0
    return shape, color, x, y


# 检查方块是否可以移动到指定位置
def can_move(shape, x, y):
    for i in range(len(shape)):
        for j in range(len(shape[0])):
            if shape[i][j]:
                new_x = x + j
                new_y = y + i
                if new_x < 0 or new_x >= GRID_WIDTH or new_y >= GRID_HEIGHT or (
                        new_y >= 0 and grid[new_y][new_x]):
                    return False
    return True


# 将方块固定到网格上
def fix_piece(shape, color, x, y):
    for i in range(len(shape)):
        for j in range(len(shape[0])):
            if shape[i][j]:
                grid[y + i][x + j] = color


# 检查并消除满行
def clear_lines():
    full_lines = []
    for i in range(GRID_HEIGHT):
        if all(grid[i]):
            full_lines.append(i)
    for line in full_lines:
        del grid[line]
        grid.insert(0, [0] * GRID_WIDTH)
    return len(full_lines)


# 绘制网格
def draw_grid():
    for i in range(GRID_HEIGHT):
        for j in range(GRID_WIDTH):
            if grid[i][j]:
                pygame.draw.rect(screen, grid[i][j],
                                 (j * BLOCK_SIZE, i * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
                pygame.draw.rect(screen, BLACK,
                                 (j * BLOCK_SIZE, i * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 1)


# 绘制方块
def draw_piece(shape, color, x, y):
    for i in range(len(shape)):
        for j in range(len(shape[0])):
            if shape[i][j]:
                pygame.draw.rect(screen, color,
                                 ((x + j) * BLOCK_SIZE, (y + i) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
                pygame.draw.rect(screen, BLACK,
                                 ((x + j) * BLOCK_SIZE, (y + i) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 1)


# 主游戏循环
clock = pygame.time.Clock()
shape, color, x, y = new_piece()
fall_time = 0
fall_speed = 0.3
score = 0

running = True
while running:
    screen.fill(BLACK)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                if can_move(shape, x - 1, y):
                    x -= 1
            elif event.key == pygame.K_RIGHT:
                if can_move(shape, x + 1, y):
                    x += 1
            elif event.key == pygame.K_DOWN:
                if can_move(shape, x, y + 1):
                    y += 1
            elif event.key == pygame.K_UP:
                rotated_shape = list(map(list, zip(*reversed(shape))))
                if can_move(rotated_shape, x, y):
                    shape = rotated_shape

    fall_time += clock.get_rawtime()
    clock.tick()

    if fall_time / 1000 >= fall_speed:
        if can_move(shape, x, y + 1):
            y += 1
        else:
            fix_piece(shape, color, x, y)
            score += clear_lines()
            shape, color, x, y = new_piece()
            if not can_move(shape, x, y):
                running = False
        fall_time = 0

    draw_grid()
    draw_piece(shape, color, x, y)

    # 显示分数
    font = pygame.font.Font(None, 36)
    text = font.render(f"Score: {score}", 1, WHITE)
    screen.blit(text, (10, 10))

    pygame.display.flip()

pygame.quit()

3.飞机坦克大战

import pygame
import random

# 初始化 Pygame
pygame.init()

# 窗口大小
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 700
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

# 载入背景图片
background = pygame.image.load("./background.png")
background = pygame.transform.scale(background, (SCREEN_WIDTH, SCREEN_HEIGHT))  #让屏幕大小和背景图片大小一致

# 载入游戏音乐
pygame.mixer.init()
#pygame.mixer.music.load("bgm.mp3")
#pygame.mixer.music.play(-1)  # 无限循环播放

# 颜色
WHITE = (255, 255, 255)

# 载入字体
#font = pygame.font.Font("font.ttf", 24)
font = pygame.font.SysFont(None, 24)  # 默认字体
#large_font = pygame.font.Font("font.ttf", 48)
large_font = pygame.font.Font(None, 48)

# 玩家类
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("./player.png")
        self.rect = self.image.get_rect()
        self.rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT - 80)
        self.speed = 5
        self.hp = 3
        self.score = 0
        self.bullets = pygame.sprite.Group()
        self.super_bullets = pygame.sprite.Group()
        self.shoot_delay = 300                    #普通子弹发射间隔(ms)
        self.last_shot = pygame.time.get_ticks()  #普通子弹上次射击时间
        self.super_shoot_delay = 500   #大招冷却时间0.5秒
        self.last_super_shot = 0       #记录上次大招发射时间

    def update(self, keys):
        if keys[pygame.K_LEFT] and self.rect.left > 0:
            self.rect.x -= self.speed
        if keys[pygame.K_RIGHT] and self.rect.right < SCREEN_WIDTH:
            self.rect.x += self.speed
        if keys[pygame.K_UP] and self.rect.top > 0:
            self.rect.y -= self.speed
        if keys[pygame.K_DOWN] and self.rect.bottom < SCREEN_HEIGHT:
            self.rect.y += self.speed

        # 发射普通子弹
        now = pygame.time.get_ticks()
        if keys[pygame.K_SPACE] and now - self.last_shot > self.shoot_delay:
            self.last_shot = now
            bullet = Bullet(self.rect.centerx, self.rect.top)
            self.bullets.add(bullet)

        # 按B键 发射大招(0.5秒冷却)
        if self.score >= 10 and keys[pygame.K_b] and now - self.last_super_shot > self.super_shoot_delay:
            self.last_super_shot = now  # 记录大招发射时间
            bullet = SuperBullet(self.rect.centerx, self.rect.top)
            self.super_bullets.add(bullet)
            self.score -= 10  # 每次使用大招消耗 10 分

# 玩家普通子弹类
class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.image.load("./bullet.png")
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.speed = -8

    def update(self):
        self.rect.y += self.speed
        if self.rect.bottom < 0:
            self.kill()

# 玩家大招子弹类
class SuperBullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.image.load("./super_bullet.png")
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.speed = -12

    def update(self):
        self.rect.y += self.speed
        if self.rect.bottom < 0:
            self.kill()

# 敌机类
class Enemy(pygame.sprite.Sprite):
    def __init__(self, enemy_type):
        super().__init__()
        self.enemy_type = enemy_type
        self.image = pygame.image.load(f"enemy_{enemy_type}.png")
        self.rect = self.image.get_rect()
        self.rect.x = random.randint(0, SCREEN_WIDTH - self.rect.width)
        self.rect.y = random.randint(-150, -50)
        self.speed = random.randint(5-enemy_type, 6-enemy_type)  #敌机飞行速度
        self.hp = enemy_type * 1      #敌机生命值 =  等级 * 1
        self.bullets = pygame.sprite.Group()
        self.shoot_delay = random.randint(1000, 3000)
        self.last_shot = pygame.time.get_ticks()

    def update(self):
        self.rect.y += self.speed
        if self.rect.top > SCREEN_HEIGHT:
            self.kill()

        # 随机发射子弹
        now = pygame.time.get_ticks()
        if now - self.last_shot > self.shoot_delay:
            self.last_shot = now
            bullet = EnemyBullet(self.rect.centerx, self.rect.bottom)
            self.bullets.add(bullet)

# 敌机子弹类
class EnemyBullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.image.load("./enemy_bullet.png")
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.speed = 5

    def update(self):
        self.rect.y += self.speed
        if self.rect.top > SCREEN_HEIGHT:
            self.kill()

# 游戏主循环
def game_loop():
    running = True
    clock = pygame.time.Clock()
    player = Player()
    enemies = pygame.sprite.Group()
    enemy_spawn_delay = 1000
    last_spawn = pygame.time.get_ticks()

    while running:
        clock.tick(60)
        screen.blit(background, (0, 0))

        # 事件处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        keys = pygame.key.get_pressed()
        player.update(keys)

        # 生成敌机
        now = pygame.time.get_ticks()
        if now - last_spawn > enemy_spawn_delay:
            last_spawn = now
            enemy = Enemy(random.randint(1, 4))
            enemies.add(enemy)

        # 更新元素
        player.bullets.update()
        player.super_bullets.update()
        enemies.update()
        for enemy in enemies:
            enemy.bullets.update()

        # 碰撞检测:普通子弹
        for bullet in player.bullets:
            hit_enemies = pygame.sprite.spritecollide(bullet, enemies, False)
            for enemy in hit_enemies:
                enemy.hp -= 1
                bullet.kill()
                if enemy.hp <= 0:
                    enemy.kill()
                    player.score += 10

        # 碰撞检测:超级子弹(大招),秒杀所有碰撞的敌机
        for super_bullet in player.super_bullets:
            hit_enemies = pygame.sprite.spritecollide(super_bullet, enemies, True)  # True 表示敌机直接消失
            if hit_enemies:
                super_bullet.kill()  # 大招击中敌人后自己消失
                player.score += len(hit_enemies) * 10  # 每杀一个敌机加 10 分

        # 绘制
        screen.blit(player.image, player.rect)
        player.bullets.draw(screen)
        player.super_bullets.draw(screen)
        enemies.draw(screen)
        for enemy in enemies:
            enemy.bullets.draw(screen)

        # 显示得分
        score_text = font.render(f"Score: {player.score}", True, WHITE)
        screen.blit(score_text, (10, 10))

        pygame.display.flip()

    pygame.quit()

# 运行游戏
game_loop()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员爱德华

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

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

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

打赏作者

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

抵扣说明:

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

余额充值