当前位置:   article > 正文

【Program with me】python开发Minecraft | 不借助图形引擎 | 从底层的一行行代码搭建起来_moderngl

moderngl

工具准备:

1.IDE: PyCharm Download PyCharm: The Python IDE for data science and web development by JetBrainsDownload the latest version of PyCharm for Windows, macOS or Linux.icon-default.png?t=N7T8https://www.jetbrains.com/pycharm/download/?section=windows

2.编程语言:python 3.11

Download Python | Python.orgThe official home of the Python Programming Languageicon-default.png?t=N7T8https://www.python.org/downloads/

第三方库下载安装:

在整个IDE的下方找到Terminal,如下,打开它。

1.安装几个第三方库 输入以下命令回车下载

pip install pygame moderngl PyGLM numba

他们分别为

pygame:用来创建openGL上下文和事件处理和纹理加载

moderngl:用python语言重新编写并包装了openGL,使我们可以用更简单的语法和函数使用复杂的图形接口。

Numpy:可以使用各种数据类型的n维数组,并用于形成网格顶点数据

PyGLM:用于矩阵和向量相关的数学运算。

numba:提升python的性能和速度

2.创建相关资源文件夹

如图所示,创建以下文件:

用于存放 各种资源的assets文件夹

用于存放 多边形网格的meshes文件夹

用于存放 着色器的shaders文件夹

用于存放 世界物体的world_objects文件夹

主py文件和设置py文件

3.引入各种第三方包

位置:在settings.py文件中

引入以下代码

  1. from numba import njit
  2. import numpy as np
  3. import glm
  4. import math
  5. # resolution
  6. WIN_RES = glm.vec2(1600,900)

我做了什么?

我从numba包中引入了njit函数;

引入numpy重命名为np;

引入了glm和math包 用于数学运算

并将屏幕的分辨率设置为1600*900


位置:在main.py文件中

引入以下代码

  1. from settings import *
  2. import moderngl as mgl
  3. import pygame as pg
  4. import sys

我做了什么:

把settings文件中所有东西引入进来

引入moderngl包并重命名为mgl

引入pygame包并重命名为pg

引入sys包, sys包提供python对操作系统的访问

4.开始构造引擎

  I.我们开始构造自己的引擎

        位置:在main.py文件中,

  1. class VoxelEngine:
  2. def __init__(self):
  3. pass
  4. def update(self):
  5. pass
  6. def render(self):
  7. pass
  8. def handle_events(self):
  9. pass
  10. def run(self):
  11. pass
'
运行

这就是引擎的主体,这些方法或函数意味着什么:

__init__()为VoxelEngine类的构造方法;

update()用于更新对象的状态;

render()用于渲染图形或对象

handle_events()用于处理事件

run()用于执行游戏的主循环;众所周知,计算机会一行一行执行代码,代码执行完就会停止,所以必须将其置于不断循环中,否则游戏就停了,对吧。


 II.初始化并实例化引擎

位置:main.py中,在引擎代码的下面

  1. if __name__ == '__main__':
  2. app = VoxelEngine()
  3. app.run()

 III. 填充引擎的构造方法

        位置:在__init__()中

  1. pg.init()
  2. pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION,3)
  3. pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION,3)
  4. pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE)
  5. pg.display.gl_set_attribute(pg.GL_DEPTH_SIZE,24)
  6. pg.display.set_mode(WIN_RES, flags=pg.OPENGL | pg.DOUBLEBUF)
  7. self.ctx = mgl.create_context()
  8. self.ctx.enable(flags=mgl.DEPTH_TEST | mgl.CULL_FACE | mgl.BLEND)
  9. self.ctx.gc_mode = 'auto'
  10. self.clock = pg.time.Clock()
  11. self.delta_time = 0
  12. self.time = 0
  13. self.is_running = True

我们一行行代码解释:从上往下

 ①初始化pg

 ②设置openGL的上下文的主版本号,主版本号表示OpenGL的主要版本,即OpenGL 3.x系列,在这里,我们将主版本号设置为3,表明我们希望使用OpenGL 3.x的特性。

 ③设置openGL的上下文的次版本号,次版本号表示OpenGL主要版本中的次要版本,即OpenGL 3.3,在这里,我们将次版本号设置为3,表明我们希望使用OpenGL 3.3的特性。

 ④设置GL_CONTEXT_PROFILE_MASK为核心配置;

        设置GL_CONTEXT_PROFILE_CORE仅支持核心特性,而不支持过时的特性。

        总之,我设置GL_CONTEXT_PROFILE_MASK作为OpenGL上下文,其中只包含OpenGL 3.3 核心规范中定义的特性,而不使用过时的特性。

 ⑤将深度缓冲区设置为24

 ⑦该函数用于创建一个窗口,窗口大小为一个包含了两个整数的元组变量,即WIN_RES;

        flags参数用于指定窗口的特性,中间的 | 意思是将左右两个参数合并在一起用;

        OPENGL参数表示创建的窗口将使用openGL渲染3D图形,该窗口拥有openGL上下文;

        DOUBLEBUF参数表示使用双缓冲技术。双缓冲是一种渲染技术,它可以减少屏幕撕裂(screen tearing)现象的发生。通过使用双缓冲,我们可以在后台渲染图像,然后一次性将完整的图像显示在屏幕上,从而避免了部分图像显示在屏幕上时可能出现的撕裂现象。

⑧调用moderngl创建一个openGL上下文并赋值给ctx,使我们可以借助ctx操作openGL上下文

⑩激活moderngl的一些功能:

DEPTH_TEST:深度测试,深度测试是OpenGL中用于确定哪些像素应该被绘制在屏幕上的一种技术。它通过比较每个像素的深度值与已经绘制的像素的深度值来确定是否绘制该像素。启用深度测试可以确保远处的物体不会覆盖近处的物体
CULL_FACE:用面剔除。面剔除是OpenGL中的一种技术,用于剔除不可见的多边形面,从而提高渲染性能。启用面剔除可以使OpenGL只绘制可见的多边形面,而忽略那些不可见的面。
BLEND:启用颜色混合。混合是OpenGL中的一种技术,用于将新绘制的像素与已经存在的像素进行混合,从而实现半透明效果。启用混合可以使OpenGL绘制半透明的物体。

 (11)启用垃圾回收机制,自动回收不再被使用的openGL对象

 (13-15)用于计时和追踪时间

 (17)标识游戏是否正在运行

 IV.填充更新方法

        位置在:update()里面

  1. self.delta_time = self.clock.tick()
  2. self.time = pg.time.get_ticks() * 0.001
  3. pg.display.set_caption(f'{self.clock.get_fps() :.0f}')

代码解释,从上往下:

①获取上一帧和当前帧的时间间隔(以毫秒为单位),赋值给delta_time变量,这个时间间隔通常用于控制游戏的运行速度,确保游戏在不同的设备上以相同的速度运行,或者用于计算物体移动、动画等的变化

②获取自游戏开始以来的总毫秒数,然后乘以 0.001 将其转换为秒,并将结果赋值给 self.time 变量,这个时间通常用于记录游戏的运行时间。

③clock.get_fps()获取当前帧率并使用set_caption()函数显示在窗口标题栏上; 

f'{self.clock.get_fps() :.0f}' 表示将当前帧率四舍五入为整数,并转换为字符串格式。

V.填充渲染方法

         位置在render()方法内

  1. self.ctx.clear()
  2. pg.display.flip()

①清除当前的帧缓冲区和深度缓冲区,清除帧缓冲区意味着将缓冲区中的所有像素数据重置为指定的颜色值,通常是清除为背景色或透明色,以准备进行新的渲染。

②显示新的帧,前文提到了双缓冲技术,这意味着在渲染新一帧之前,首先将渲染结果绘制到后台缓冲区中,然后通过调用 display.flip() 方法将后台缓冲区的内容切换到前台缓冲区,从而更新屏幕显示。

VI.填充事件处理方法

位置:handle_events()中

  1. for event in pg.event.get():
  2. if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
  3. self.is_running = False

监听窗口的关闭键也就是X键和escape键,立刻将is_running状态设为False。

VII.填充运行方法

位置:run方法内

  1. while self.is_running:
  2. self.handle_events()
  3. self.update()
  4. self.render()
  5. pg.quit()
  6. sys.exit()

 当is_running变量处于true时,不断地运行和处理handle_events()、update()和render()方法

当is_running变量处于false时,触发pg.quit()和sys.exit()方法以关闭游戏和回收资源

5.实验下游戏,是否成功

 所以,的确是成功了,左上角显示有帧率,非常高,但同时也在消耗过多的资源,因为没有让他sleep,但之后会解决这个问题。

注意:可能你启动游戏后,游戏画面太大了把整个屏幕都包裹了,那么要改下settings.py里面的WIN_RES分辨率参数,改小点,哈哈。


main.py的整体代码如下

  1. from settings import *
  2. import moderngl as mgl
  3. import pygame as pg
  4. import sys
  5. class VoxelEngine:
  6. def __init__(self):
  7. pg.init()
  8. pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 3)
  9. pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3)
  10. pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE)
  11. pg.display.gl_set_attribute(pg.GL_DEPTH_SIZE, 24)
  12. pg.display.set_mode(WIN_RES, flags=pg.OPENGL | pg.DOUBLEBUF)
  13. self.ctx = mgl.create_context()
  14. self.ctx.enable(flags=mgl.DEPTH_TEST | mgl.CULL_FACE | mgl.BLEND)
  15. self.ctx.gc_mode = 'auto'
  16. self.clock = pg.time.Clock()
  17. self.delta_time = 0
  18. self.time = 0
  19. self.is_running = True
  20. def update(self):
  21. self.delta_time = self.clock.tick()
  22. self.time = pg.time.get_ticks() * 0.001
  23. pg.display.set_caption(f'{self.clock.get_fps() :.0f}')
  24. def render(self):
  25. self.ctx.clear()
  26. pg.display.flip()
  27. def handle_events(self):
  28. for event in pg.event.get():
  29. if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
  30. self.is_running = False
  31. def run(self):
  32. while self.is_running:
  33. self.handle_events()
  34. self.update()
  35. self.render()
  36. pg.quit()
  37. sys.exit()
  38. if __name__ == '__main__':
  39. app = VoxelEngine()
  40. app.run()

settings.py的代码如下

  1. from numba import njit
  2. import numpy as np
  3. import glm
  4. import math
  5. # resolution
  6. WIN_RES = glm.vec2(1600, 900)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/796976
推荐阅读
相关标签
  

闽ICP备14008679号