1
0
mirror of https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git synced 2025-08-23 04:35:28 +08:00
This commit is contained in:
Meow J
2016-08-29 01:43:33 +08:00
parent c95ab5a95f
commit 18dc11fb82
4 changed files with 50 additions and 44 deletions

View File

@@ -6,101 +6,100 @@
翻译 | [ZMANT](https://github.com/Itanq)
校对 | 暂无
## 设置
在开始真正写游戏机制之前,我们首先需要配置一个简单的框架,用来存放这个游戏,这个游戏将会用到几个第三方库,它们的大多数都已经在前面的教程中介绍过了。在需要用到新的库的时候,我会作出适当的介绍。
在我们开始实际构造这个游戏之前,我们首先需要设置一些简单的框架来处理这个游戏,这个游戏将会用到几个第三方库,它们大多数都已经在前面的教程中介绍过了。不管在那个地方需要用到新的库的时候,我们都会作出适当的介绍
首先,我们定义一个所谓的<def>超级</def>(Uber)游戏类它会包含所有相关的渲染和游戏代码。这个游戏类的主要作用是简单管理你的游戏代码并与此同时将所有的窗口代码从游戏中解耦。这样子的话你就可以把相同的类迁移到完全不同的窗口库比如SDL或SFML而不需要做太多的工作
首先,我们定义一个叫做`Game`的类来包含所有有关的渲染和游戏设置代码。有了这个类,我们就可以用这个类把游戏代码(稍稍的)有组织的嵌入到游戏窗口代码中。用这种方式,你就可以把相同的代码迁移到完全不同的窗口库(比如 SDL或SFML)而不需要做太多的工作。
!!! important
> 存在有成千上万的方式来抽象并概括游戏/图形代码然后封装到类和对象中。在这个教程中,你刚好会看到有一个方法来解决这个问题。如果你感到有一个更好的方法,尽量实现你的改进
抽象并归纳游戏图形代码至类与对象中有成千上万种方式。在这个系列教程中你所看到的仅是其中的一种。如果你觉得能有更好的方式进行实现,你可以尝试改进我的这个实现
这个`Game`类封装了一个初始化函数一个更新函数一个处理输入函数以及一个渲染函数:
这个游戏类封装了一个初始化函数一个更新函数一个处理输入函数以及一个渲染函数:
```C++
```c++
class Game
{
public:
// Game state
// 游戏状态
GameState State;
GLboolean Keys[1024];
GLuint Width, Height;
// Constructor/Destructor
// 构造函数/析构函数
Game(GLuint width, GLuint height);
~Game();
// Initialize game state (load all shaders/textures/levels)
// 初始化游戏状态(加载所有的着色器/纹理/关卡)
void Init();
// GameLoop
// 游戏循环
void ProcessInput(GLfloat dt);
void Update(GLfloat dt);
void Render();
};
```
这个类可能就是你期望中的游戏类。我们通过一个`width`和`height`(对应于你玩游戏时的设备分辨率)来初始化个游戏实例并且使用`Init`函数来加载着色器、纹理以及初始化游戏状态。我们可以通过调用`ProcessInput`函数来使用存储在`Keys`数组里的数据来处理输入并且在`Update`函数里面更新游戏设置状态(比如玩家/球的移动)。最后,我们还可以在`Render`函数里面渲染游戏。注意,我们在渲染逻辑里面分离出了运动逻辑
这个类应该包含了所有在一个游戏类中会出现的东西。我们通过给定一个宽度和高度(对应于你玩游戏时的分辨率来初始化个游戏并且使用<fun>Init</fun>函数来加载着色器、纹理并且初始化所有的游戏状态。我们可以通过调用<fun>ProcessInput</fun>函数,并使用存储在<var>Keys</var>数组里的数据来处理输入并且在<fun>Update</fun>函数里面我们可以更新游戏设置状态比如玩家/球的移动。最后,我们还可以调用<fun>Render</fun>函数来对游戏进行渲染。注意,我们将运动逻辑与渲染逻辑分开了
这个`Game`类同样了封装了一个叫做`State`的变量,它的类型`GameState`如下定义
这个<fun>Game</fun>类同样了封装了一个叫做<var>State</var>的变量,它的类型是<def>GameState</def>,定义如下
```C++
// Represents the current state of the game
```c++
// 代表了游戏的当前状态
enum GameState {
GAME_ACTIVE,
GAME_MENU,
GAME_WIN
};
};
```
可以使我们跟踪游戏当前状态。这样我们就可以根据当前游戏的状态来决定渲染或者处理不同的元素(比如当我们在游戏菜单界面的时候就可能需要渲染和处理不同的元素)
个类可以帮助我们跟踪游戏当前状态。这样的话我们就可以根据当前游戏的状态来决定渲染和/或者处理不同的元素(Item)了(比如当我们在游戏菜单界面的时候就可能需要渲染和处理不同的元素了)
目前为止,这个`Game`类的函数还完全是空的,因为我们还没有写实际的实现代码,但这里是`Game`类的[header](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/game_setting-up.h)和[code](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/game_setting-up)文件
目前为止,这个游戏类的函数还完全是空的,因为我们还没有写游戏的实际代码,但这里是`Game`类的[头文件](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/game_setting-up.h)和[代码文件](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/game_setting-up)。
## 通用
## 工具类
因为我们正在开发一个大型应用,所以我们将不得不频繁重用一些OpenGL的概念比如纹理和着色器等。因此为这两个元素(items)创建一个更加易用的接口是情理之中的事了,就像在我们前面一个教程中创建的那个`shader`类一样。
因为我们正在开发一个大型应用,所以我们将不得不频繁重用一些OpenGL的概念比如纹理和着色器等。因此为这两个元素创建一个更加易用的接口是情理之中的事了,就像在我们前面教程中创建的那个着色器类一样。
定义一个着色器类,它接收两个或三个字符串(如果有几何着色器)然后生成一个编译好的着色器(或者如果失败的话生成一条错误信息)。这个着色器类同样也包含大量实用的函数来快速设置`uniform`变量。同样也定义一个纹理类它从给定的字节数组中生成一个2D纹理图像(基于它的内容)。并且这个纹理类同样也封装了许多实用的函数。
着色器类会接受两个或三个如果有几何着色器)字符串,并生成一个编译好的着色器如果失败的话生成错误信息。这个着色器类也包含很多工具(Utility)函数来帮助快速设置uniform值。纹理类会接受一个字节(Byte)数组以及宽度和高度根据设定的属性生成一个2D纹理图像。同样这个纹理类也会封装一些工具函数。
我们并不打算钻研这些类实现细节,因为学到这里你应该可以很容易理解它们是如何工作的了。出于这个原因你可以找到它们的头文件和实现的代码文件,有详细的注释,如下
我们并不会深入讨论这些类实现细节,因为学到这里你应该可以很容易理解它们是如何工作的了。出于这个原因你可以在下面找到它们的头文件和代码文件,有详细的注释:
>* Shader : [header](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/shader.h),[code](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/shader)
>* Texture: [header](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/texture.h),[code](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/texture)
- **着色器**[头文件](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/shader.h)[代码](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/shader)
- **纹理**[头文件](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/texture.h)[代码](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/texture)
注意当前的这个纹理类仅仅设置成单一的`2D`纹理,但你可以很容易的通过可选的纹理类型来扩展
注意当前的纹理类仅是为2D纹理设计的,但你很容易就可以将其扩张至更多的纹理类型。
## 资源管理
## 资源管理
着色器纹理类的函数被他们自己使用的时候,他们确实需要一个字节数组或是几个字符串来初始化他们。我们可以很容易的在他们自己的类里面嵌入文件加载代码,但这稍微有点违反`单一职责原则`(也就是说)在这个类里我们应该仅仅关注纹理或着色器本身而不需要关注他们的文件加载结构
尽管着色器纹理类的函数本身就很棒了,它们仍需要一个字节数组或一些字符串来调用它们。我们可以很容易文件加载代码嵌入到它们自己的类中,但这稍微有点违反了<def>单一功能原则</def>(Single Responsibility Principle),即这两个类应当分别仅仅关注纹理或着色器本身而不是它们的文件加载机制
出于这个原因,通常用一个更加有组织的方法(来实现文件的加载),就是创建一个叫做`resource manager`的单独实体类来加载游戏相关的资源。这里有好几个方法来实现`resouce manager`在这个教程我们选择使用一个单独的静态`resouce manager`(就是给它静态属性),也就是说可以在整个工程中使用它来加载资源以及使用和她相关加载功能。
出于这个原因,我们通常用一个更加有组织的方法(译注:来实现文件的加载,就是创建一个所谓<def>资源管理器</def>的实体,专门加载游戏相关的资源。创建一个资源管理器有多种方法。在这个教程我们选择使用一个单一实例(Singleton)的静态资源管理器,(由于它静态的本质)它在整个工程中都可以使用,它会封装所有的已加载资源以及一些相关加载功能。
使用具有静态属性的单独的类有很多好处,但它主要的坏处就是会失去`OOP`特性以及控制结构/破坏。不过,这些对于小项目来说是很容易处理的。
使用一个具有静态属性的单一实例类有很多优点也有很多缺点。它主要的缺点就是这样会损失OOP属性并且丧失构造与析构的控制。不过对于我们这种小项目来说是这些问题也是很容易处理的。
就像其他的类文件一样,这个资源管理器的列表如下:
和其它类的文件一样,这个资源管理器的代码如下:
>* Resource Manager: [header](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/resource_manager.h), [code](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/resource_manager)
- **资源管理器**[头文件](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/resource_manager.h)[代码](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/resource_manager)
使用资源管理我们可以很容易把着色器加载到程序里面,比如
通过使用资源管理器,我们可以很容易把着色器加载到程序里面:
```C++
```c++
Shader shader = ResourceManager::LoadShader("vertex.vs", "fragment.vs", nullptr, "test");
// then use it
// 接下来使用它
shader.Use();
// or
// 或者
ResourceManager::GetShader("test").Use();
```
通过定义的`Game`类和`resouce manager`类一起就可以很很容易管理`Shader`和`Texture2D`,基于这个基础,在以后的教程里我们将会广泛使用这些类来实现这个`Breakout`游戏。
<fun>Game</fun>类、资源管理器类,以及很容易管理的<fun>Shader</fun><fun>Texture2D</fun>类一起组成了之后教程的基础,我们之后会广泛使用这些类来实现我们的Breakout游戏。
## 程序
对这个游戏,我们仍然需要创建一个窗口并且设置OpenGL的初状态。我们确保使用OpenGL的[面剔除](http://learnopengl.com/#!Advanced-OpenGL/Face-culling)功能和它的[混合](http://learnopengl.com/#!Advanced-OpenGL/Blending)功能。我们不需要使用深度测试因为这个游戏完全是一个二维的,所有顶点的`z`坐标都具有相同的值。因此开启深度测试并没有什么用还有可能造成`z-fighting`现象
我们仍然需要为这个游戏创建一个窗口并且设置一些OpenGL的初状态。我们确保使用OpenGL的[面剔除](../../04 Advanced OpenGL/04 Face culling.md)功能和[混合](../../04 Advanced OpenGL/03 Blending.md)功能。我们不需要使用深度测试因为这个游戏完全是2D的,所有顶点都有相同的z值所以开启深度测试并没有什么用,反而可能造成深度冲突(Z-fighting)
这个`Breakout`游戏开始时候的代码相当的简单:我们用`GLFW`创建一个窗口,注册一些回调函数,创建一个`Game`实例并且调用了`Game`类所有相关的函数。这个代码如下:
这个Breakout游戏的起始代码非常简单我们用GLFW创建一个窗口注册一些回调函数创建一个Game对象,并将所有相关的信息都传到游戏类中。代码如下:
>* Program: [code](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/program)
- **程序**[代码](http://learnopengl.com/code_viewer.php?code=in-practice/breakout/program)
运行这个代码,你能得到下面的输出:
运行这个代码,你应该能得到下面的输出:
![ans](http://learnopengl.com/img/in-practice/breakout/setting-up.png)
目前为止我们已经有了一个后面的教程需要的固定框架;我们将会持续的扩展这个`Game`类来封装一些新的功能。如果你准备好了就点击[下一个](http://learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites)教程。
![](../../img/06/Breakout/02/setting-up.png)
现在我们已经为之后的教程构建了一个坚实的框架,我们将不断地拓展这个游戏类,封装新的功能。如果你准备好了,就可以开始[下一节](03 Rendering Sprites.md)的学习了。

View File

@@ -167,7 +167,7 @@ void SpriteRenderer::DrawSprite(Texture2D &texture, glm::vec2 position,
当试图在一个场景中用旋转矩阵和伸缩矩阵放置一个对象的时候,建议首先做伸缩变换,然后旋转最后是移动变换。因为矩阵乘法是从右向左执行的,所以我们变换的矩阵顺序是相反的:移动,旋转,缩放。
旋转变换可能看起来稍微有点让人望而却步。我们从[变换](../../01 Getting started/07 Transformations/)教程里面知道旋转总是围绕原点`(0,0)`转动的。因为我们指定了四边形的左上角为`(0,0)`,所有的旋转都是围绕`(0,0)`的。主要是这个旋转的原点是四边形的左上角,这样就会造成不太友好的旋转效果。我们想要做的就是把旋转的点移到四边形的中心,这样旋转就是围绕四边形中心而不是左上角了。我们通过在旋转之前把旋转点移动到四边形中心解决了这个问题。
旋转变换可能看起来稍微有点让人望而却步。我们从[变换](../../01 Getting started/07 Transformations.md)教程里面知道旋转总是围绕原点`(0,0)`转动的。因为我们指定了四边形的左上角为`(0,0)`,所有的旋转都是围绕`(0,0)`的。主要是这个旋转的原点是四边形的左上角,这样就会造成不太友好的旋转效果。我们想要做的就是把旋转的点移到四边形的中心,这样旋转就是围绕四边形中心而不是左上角了。我们通过在旋转之前把旋转点移动到四边形中心解决了这个问题。
![](../../img/06/Breakout/03/rotation-origin.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -147,4 +147,11 @@
- Tessellation Evaluation Shader细分评估着色器
- Compute Shader计算着色器
- Attachment附件
- Render Buffer Object渲染缓冲对象
- Render Buffer Object渲染缓冲对象
## 06-03-01
- Level关卡
- Gameplay State游戏状态
- Singleton单一实例
- Single Responsibility Principle单一功能原则