OpenGL超级宝典(第7版)笔记13 前三章实例 下个五子棋 (上)

OpenGL超级宝典(第7版)笔记13 前三章实例 下个五子棋 (上)


前言

上一篇我们对前三章做了一个知识的梳理(知识图),还提了一下OpenGL扩展的相关内容。

这一篇是对前三章内容的综合应用,其中涉及了很多着色器的细节,会非常精彩,但可能篇幅会比较长,我们会尽量用更多之前学习的知识(有时候是不得已,有时候是更加方便快速)

1 初构建

首先我们要大体把程序的框架定出来,然后再去讨论细枝末节的具体内容。本身五子棋是个小游戏,其并不包含很多的部分,就是一个棋盘,然后两边下棋(同一个电脑操作)最终有人赢了之后重新开始,没有什么其他的模式,所以不需要很复杂的设计。其主要就是四大部分:输入,判断,绘制,数据。流程如下:
在这里插入图片描述
然后是具体的构建四部分内容,我比较喜欢先构建数据,因为程序总是在围绕这数据的修改来运行,所以先敲定数据的结构在对其他的部分进行构建比较好。

2 构建数据结构

首先我们肯定是要观察一下其余部分对数据结构的要求:

首先对输入模块要求我们有一个专门的变量来记录当前的棋子类型(是白子还是黑子)

判断模块需要我们进行是否五子连珠的判断,我们要针对棋盘来设计一个数据结构,要求既能保存棋子的布局,又能方便连珠的判断,这里我们决定用二维数组来解决(一维数组的缺点是在判断斜向的五子连珠时有点麻烦),用二维数组代表棋盘中的每个格子,无棋子为0,有黑子为1,有白子为2。除此以外还需要一个变量来记录当前是否有人获胜。

绘制模块需要我们分别绘制棋盘和棋子,棋盘我们可以用细分的方式来生成(用上了细分着色器、几何着色器),而棋子我们打算用点绘制来生成(将点的大小调大,作为棋子),这就要求我们有两个着色器程序(其实一个也可以但是我们还是想尝试一下新东西)。数据方面我们打算把棋盘的顶点数据直接写入顶点着色器中(用上了gl_VertexID),而棋子的位置我们用顶点属性来传入(用上了顶点属性),如果把上面的棋盘中每个格子都测试一遍就太慢了,尤其是刚开始的时候棋子的数量并不多,所以我们单独弄一个数组来安装顺序存放下的棋子(还可以通过它实现悔棋的功能),同时添加一个记录当前一共下了多少个棋子的变量。

综上暂时需要有:
记录棋子种类的变量
记录是否有人获胜的变量
记录棋盘中棋子情况的二维数组
记录下子顺序的数组(同时实现绘制)
记录一共下了多少子的变量

struct chess{
   
	GLchar chesskind=0;
};
chess chessboard[17][17];//二维数组表示棋盘中棋子情况,用于判断五子连珠
GLchar chess_draw_list[17*17][3]={
   0}; 
//下子顺序的数组,[0]表示种类[1]表示x[2]表示y,用于绘制
//不要用chessboard来绘制,因为每次绘制都要遍历每个格子,
//		刚开始的时候可能只有一两个格子有棋子
//		而chess_draw_list只需要依次绘制,
//		直到所有棋子绘制完成就行了,减少了判断的次数
GLint chess_number=0;//一共下的棋子数
GLint nowkind = 1;//黑为1白为2
int iswin = 0;//是否有人赢了

3 绘制

3.1 绘制准备

虽然绘制应该位于流程的后半段,但是如果我们不能绘制出棋盘,那么我们的输出就不能确定如何转化为对应的格子位置。而且当前最大的难点还是opengl绘制一块的。

由于这回我们要创建两个着色器程序,一个用于绘制棋盘(将用到细分着色器,几何着色器),另一个用于绘制棋子(就是点的绘制,只用顶点着色器和片段着色器),所以我们要稍微整理一下之前的函数,因为之前的函数只是绘制单一的着色器程序,现在我们可能需要各种各样的着色器,而且着色器可能还会有复用,所以我们把着色器的编译,和着色器程序的链接分成两个函数来进行封装。

因为有很多的着色器,所以我们把着色器对象放到全局变量中:

GLuint vs[3]={
   0};
GLuint tcs[3]={
   0};
GLuint tes[3]={
   0};
GLuint gs[3]={
   0};
GLuint fs[3]={
   0};
//着色器,我们准备了3个位置,可以放3个同种的着色器
GLuint rendering_program1;
GLuint rendering_program2;
//着色器程序,一共两个
GLuint vertex_array_object;
//当然这里还包含了顶点数组

然后我们要建立着色器的编译函数:

void compile_shader(void) {
   

#pragma region 编辑着色器内容
	static const GLchar* vertex_shader_source[] = {
   //着色器内容
	};
	static const GLchar* vertex_shader_source2[] = {
   //着色器内容
	};
	static const GLchar* fragment_shader_source2[] = {
   //着色器内容
	};
#pragma endregion


#pragma region 编译各种着色器并检查情况
	GLint shader_success;
	GLchar infoLog[512];//定义一个整型变量来表示是否成功编译,还定义了一个储存错误消息(如果有的话)的容器。
	
	vs[0] = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vs[0], 1, vertex_shader_source, NULL);
	glCompileShader(vs[0]);
	glGetShaderiv(vs[0], GL_COMPILE_STATUS, &shader_success);//检查编译是否成功
	if (!shader_success)
	{
   
	    glGetShaderInfoLog(vs[0], 512, NULL, infoLog);
	    std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
	}
	
	vs[1] = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vs[1], 1, vertex_shader_source2, NULL);
	glCompileShader(vs[1]);
	glGetShaderiv(vs[1], GL_COMPILE_STATUS, &shader_success);//检查编译是否成功
	if (!shader_success)
	{
   
	    glGetShaderInfoLog(vs[1], 512, NULL, infoLog);
	    std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
	}

	fs[0] = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fs[0]
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
作者对游戏的说明: 首先,您应当以一种批判的眼光来看待本程序。这个游戏是我制作 的第一部RPG游戏,无任何经验可谈,完全按照自己对游戏的理解进 行设计的。当我参照了《圣剑英雄2》的源码之后,才体会到专业游 戏引擎的博大精深。 该程序的内核大约有2000余行,能够处理人物的行走、对话、战斗, 等等。由于该程序的结构并不适于这种规模的程序,故不推荐您详 细研究该程序。所附地图编辑器的源程序我已经添加了详细的注释, 其程序结构也比较合理,可以作为初学VC的例子。 该程序在VC的程序向导所生成的SDI框架的基础上修改而成。它没有 使用任何关于VC底层的东西。程序的绝大部分都是在CgameView类中 制作的,只有修改窗口特征的一段代码在CMainFrm类中。其他的类 统统没有用到。另外添加的一个类是CEnemy类。 整个游戏的故事情节分成8段,分别由Para1.h ~ Para8.h八个文件 实现。由于程序仅仅能够被动的处理各种各样的消息,所以情节的 实现也只能根据系统的一些参数来判断当应当做什么。在程序中 使用了冗长的if……else if……结构来实现这种判断。 当然,在我的记录本上,详细的记录了每个事件的判断条件。这种 笨拙的设计当然是不可取的。成都金点所作《圣剑英雄II》采用了 剧本解读的方式,这才是正统的做法。但这也需要更多的编程经验 和熟练的code功夫。 下面列举的是程序编制过程中总结出来的经验和教训。 第一,对话方式应该采用《圣剑英雄II》的剧本方式。 现在的方式把一个段落中所有的对话都混在一个文件中,然后给每 句话一个号码相对应。这样做虽然降低了引擎的难度,却导致剧情的 编写极其繁琐。 第二,运动和显示应当完全分开。 现在的程序中,运动和显示是完全同步的。即:在定时器中调用所有 敌人的运动函数,然后将主角的动画向推一帧,接着绘制地图,调 用所有敌人的显示函数、重绘主角。这样的好处是不会掉帧,但带来 的问题是,如果要提高敌人的运动速度,那么帧数也跟着上去了。所 以当DEMO反馈说速度太慢的时候,我修改起来非常困难。而这个问 题到最后也仅仅是将4步一格该成了2步一格。 第三,VC中数组存在上限。如果用“int aaa[1000000000]”定义一个 数组,编译器肯定不会给分配那么大的内存空间。而在这个程序中, 地图矩阵、NPC矩阵都超过了VC中数组的上限。但这一点知道的太晚了。 在1.0本中已经发现地图最右端缺少了几行,但不知道是什么原因 造成的。(地图编辑器中未出现此问题,因为地图编辑器是用“序列 化”的方式存盘读盘的。)解决这个问题的方法是用“new”来分配 内存空间。 第四,由于不知道应该如何使用“new”和“delete”,几乎所有的DC 都使用了全局变量。这是完全没有必要的。程序运行期大约会耗用20 多M的内存空间,相当于一个大型游戏所使用的内存空间了。 另外,在游戏的剧情、美工方面也有许多问题,总之一个词“业余”。 我就不总结了。下一部作品,我将争取在程序上有一个质的飞跃。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值