dream的小站 - 游戏开发笔记
type: channel
link: https://www.resourch.com/category/%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91/
description: 少年时期游戏开发的笔记
items: 数独游戏开发及唯一解算法设计
items: 使用PIX for windows来debug你的工程
items: 【研究】关于gm8实例事件执行顺序 / 选取绘制在上层的实例
items: 银行家舍入导致的渲染插值问题
items: 【轨迹生成】三阶贝塞尔曲线详解
items: gm8图像渲染流程心得

数独游戏开发及唯一解算法设计
type: item
date: 2016-08-04T07:54:00+08:00
description: 唯一解生成算法数独中，有一个被称作唯一性技巧的东西，唯一性技巧基于这样一个事实——各类出版物上发布的数独题目都只有唯一解。事实上，绝大多数数独玩家有这样的共识：即合格的数独题目解应该是唯一的。因此，为了保证题目合格、有效，出题者在制作题目时，会将一些虽然不违反数独规则，但会导致题目出现双解或多解的特殊结构加以处理。这也就代表我们的游戏设计中，不能出现多解核心思路：回溯法+广度优先生成目前主要的思路就是按照1-9的顺序填数，把整个大九宫格划分为9个小的小九宫格，按照1-9的顺序在每个小九宫格内随机填数，并对不需要回溯的特殊分支进行剪枝处理。比如填1的时候，先将候选数更改为“1”，然后从左上的小九宫格内填候选数，按从左至右从上至下的顺序填到最后一个小九宫格，当所有的小九宫格内都包含了候选数字“1”时，将候选数更改为2，然后重复这个过程；如果填数过程中数字在横行，纵行有重复，则重新在这个小九宫格内选取空格（由于我们是把整个大九宫格分为了9个小九宫格，然后按照小九宫格的顺序填入候选数，所以无需检测在同一小九宫格内候选数是否重复）；如果这个小九宫格内没有可用空格，则回溯到上一个小九宫格，直到1-9的候选数填入完毕整个过程中数字1和9基本是不需要回溯的，并且无需检测小九宫内候选数是否重复，回溯次数可以大大减少之前做的生成例子，没有优化 http://tieba.baidu.com/p/4555720809ui设计思路想法是把上面三张图片的风格汇集在一起成品

使用PIX for windows来debug你的工程
type: item
date: 2016-04-25T12:58:00+08:00
description: 虽然gms的debuger功能相比gm8来说已经很强了，但有的时候图像上的问题还是十分难以解决，或者有的时候我们写了一个shader，需要查看顶点来帮助调试，而gms没有更多关于shader的功能，那么使用PIX可以大幅提高你的工作效率首先我们先打开一个工程，这里用的是yoyo官方提供的angry_cats的demo然后将其导出，导出时选择以zip方式导出然后将其解压，得到下面的文件打开开始菜单，在directx sdk目录下找到pix for windows（如果没有的请在这里下载http://www.microsoft.com/en-gb/download/details.aspx?id=6812）打开后我们会看到这样一个界面在program项中选择刚刚解压出来的游戏然后点More Options按钮，窗口会变成这样单击工具栏的“T”按钮，添加两个新的触发器，并更改Trigger Type如图设置，将新添加的两个触发器更改为按键触发，并设置按键为f5和f6（或者是其他你喜欢的）然后将两个触发器下的Action Type照图中设置保存项目，点击工具栏上的绿色箭头运行游戏，如果成功的话你的游戏左上角应该会显示这个按一下f5键（刚刚设置的触发器）开始捕捉画面等到你想停止捕捉的时候按下f6，会自动关闭游戏并弹出如下窗口把下面的两个窗口调整一下展开你刚刚捕捉到的Frame xxx点击render，过一段时间后，你会得到如下画面，确定你能正确看到画面如果说你在列表里找到了SetTexture()函数的调用，用鼠标左键双击十六进制参数，便可以查看纹理的使用了。除了纹理之外，在这个列表里有整个d3d渲染序列，您可以查看纹理、着色器，网格，顶点缓冲以及其他很多的东西，PIX是一个强大的调试工具，尤其是对于shader，你可以看到所有的数据交换，不仅如此，你还可以得到表面，纹理的创建顺序，以及他们是如何交换的，最后，你在这里得到的结果，到了其他平台也是一样的，灵活使用PIX可以让你的工作更有效率此外，PIX可以直接在反汇编模式下调试，如果是vs，在编译时指定D3DXSHADER_DEBUG(/Zi)和D3DXSHADER_SKIPOPTIMIZATION(/Od)可以直接查看代码

【研究】关于gm8实例事件执行顺序 / 选取绘制在上层的实例
type: item
date: 2016-02-24T00:06:00+08:00
description: 看了B老师 @blovestorm 的文章【论GameMakerStudio对象实例的执行顺序】http://blog.sina.com.cn/s/blog_c4f9d4230102v9no.html在实际操作过程中发现了一些问题，也就是在不断解散激活实例的过程中发生了一些问题实例的事件并没有按照我的ID大小执行新建两个对象，并在房间内放置多个实例，左侧数字为instance_id的所有元素instance_id为一个全局数组，储存着游戏内所有实例的id然后随机解散其中的一些实例并重新激活将instance_id的所有元素以顺序形式绘制出来，发现实例id被打乱中间的测试过程省略，根据测试发现，非draw事件的运行顺序和实例的id无关，和object_index与instance_id有关object_index越小的实例，事件执行越早如果object_index一样，则根据instance_id的顺序执行所以我个人认为gms的房间编辑器中，instance order顺序也是如此，object_index小的，排列顺序靠前，如果object_index一样，则在instance_id中靠前的实例，排列顺序靠前2016-02-25 更新：如何在多个同深度实例重叠时，鼠标选取绘制在上层的实例http://pan.baidu.com/s/1pKjdXrl之前由于实例事件执行顺序理解不通，走了许多弯路（http://tieba.baidu.com/p/4372501528）用了实例解散，碰撞检测等，把重叠实例id都写进列表，写了二三十行代码才达到的效果，现在用了三四行就写出来了，个人感觉效果不错，特来分享下

银行家舍入导致的渲染插值问题
type: item
date: 2015-10-01T00:15:00+08:00
description: 在我们进行小数取整运算时，可能会使用到round()函数，通常是为了取一个最接近的整数，但是却容易出现如图的情况实际用gm进行操作时，会出现“偏向双数的计算错误”，有的时候debug半天找不到问题，最后才发现问题原来在这里，用一句话总结就是：（图文无关)银行家舍入实际上，round使用的是一种银行家舍入算法，即【四舍六入五取偶】的舍入方法，其规则是：当舍去位的数值小于5时，直接舍去该位；当舍去位的数值大于5时，在舍去该位的同时向前位进一；当舍去位的数值等于5时，如果前位数值为奇，则在舍去该位的同时向前位进一，如果前位数值为偶，则直接舍去该位。因此，gm中的round()函数是符合规范的精灵渲染问题既然是四舍六入，那么问题就来了调整了两个紧邻的obj的坐标后（一个向上移动了0.5像素，另一个向下移动了0.5像素）发现，两个obj的碰撞盒的底端和顶端各自增加了1像素，这就是因为round取整的结果因此，有些时候一些喜欢追根究底的gm爱好者在研究gm的时候，经常会被round取整后的结果所迷惑，最典型的就是研究碰撞时，使用了小数，导致得到错误的研究结果。虽然有时未对精灵进行旋转和缩放操作，但精灵本身却被插值模糊，就是因为绘制精灵的坐标可能是小数，导致round使整个精灵的长度增加了1像素，发生了失真。（典型例子：设计游戏时允许主角移动速度为小数，并且在移动完毕后未手动修正坐标取整，绘制出的图像失真)因此，在需要四舍五入的情况下，最好自己写一个脚本，用来取整【完】

【轨迹生成】三阶贝塞尔曲线详解
type: item
date: 2015-08-27T20:27:00+08:00
description: *本文是本人于2015年发布游戏开发笔记，当时利用贝塞尔曲线仅在游戏开发领域，文章也仅发布于百度贴吧什么是贝塞尔曲线先上一段百度百科：贝塞尔曲线(Bézier curve)，又称贝兹曲线或贝济埃曲线，是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线，贝兹曲线由线段与节点组成，节点是可拖动的支点，线段像可伸缩的皮筋，我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线，在一些比较成熟的位图软件中也有贝塞尔曲线工具，如PhotoShop等。在Flash4中还没有完整的曲线工具，而在Flash5里面已经提供出贝塞尔曲线工具。成品展示曲线定义众所周知，gm中可以绘制出各种各样的形状，但却没有曲线绘制，因此功能受到了局限但是，虽然没有曲线绘制函数，但是gm中却有一种曲线，那就是路径当路径的连接方式设定为平滑曲线时，则绘制出来的路径是一条二次贝塞尔曲线，可以使用draw_path()函数绘制出来虽然这样可以画出一条曲线，但是由于路径属于资源在使用时不灵活、曲线精度较低、且我们实际使用时大多使用的是三次贝塞尔曲线，因此这种方式不提倡使用二次曲线看起来就是这样的：由于我们不使用二次曲线，所以在这里不多加讨论我们就直接开始讨论三次曲线三次贝塞尔曲线比二次曲线多了一个控制点，所以整个曲线由两个控制点（P1,P2）和起始终止点（P0,P3）控制。P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于P0走向P1，并从P2的方向来到P3。一般不会经过P1或P2；这两个点只是在那里提供方向资讯。P0和P1之间的间距，决定了曲线在转而趋进P3之前，走向P2方向的“长度有多长”。曲线的参数形式为：上图的曲线，是参数t从0到1时，上图中移动的黑点所走过的路径，那么看到这里，有些人就不太明白这个三次曲线是怎么定义的了，所以。我们还是得从头说起三阶贝塞尔曲线是怎么来的线性贝塞尔曲线给定点P0、P1，线性贝塞尔曲线只是一条两点之间的直线。这条线由下式给出：当参数t由0到1变化时，其过程如下：这个t值是曲线占总曲线长度的分量，你可以把总曲线长度看作1，当t为0.5时，图中的黑点就在这条曲线的中间（虽然一次贝塞尔曲线实际上就是条直线）二次曲线二次曲线实际上是由一次曲线得出的，为构建二次贝塞尔曲线，我们需要定义多条一次贝塞尔曲线由P0至P1的连续点Q0，描述一条线性贝塞尔曲线。由P1至P2的连续点Q1，描述一条线性贝塞尔曲线。由Q0至Q1的连续点Q1，描述一条线性贝塞尔曲线。由P0至P1的连续点B（t），描述一条二次贝塞尔曲线。点B(t)所走过的路径就是一条二次贝塞尔曲线同理，三次贝塞尔曲线也是由多条线性曲线定义的代码实现那么，知道了原理，我们就能绘制出曲线了，在这里，由于没办法直接绘制曲线，我使用的是原始绘制函数使用多段直线连接的方式绘制出近似曲线draw_primitive_begin(kind) //开始一个原始显示类型。 draw_vertex(x,y) //增加顶点 (x,y)到原始表中,使用之前设定的颜色和透明度 。 pr_linestrip //一组连续线，从第一个点到第二个点，再到第三个点，等等。最后一个没有 和第一个点连接。你需要指定一个额外的第一顶点拷贝。 draw_primitive_end() //结束描述原始绘制，这个函数是马上绘制它。用以上代码即可完成连续线的绘制，使用原始绘制直线也比直接用draw_line()+for循环绘制直线要快我们将他写成一个脚本以便于绘制///draw_bezier_cubic(x1,y1,x2,y2,x3,y3,x4,y4,complexity); //画一条三阶贝塞尔曲线 两个控制点(x2,y2,x3,y3) 两个起始点和中间点(x1,y1,x4,y4) 精度(complexity) var point_x,point_y; draw_primitive_begin(pr_linestrip) for(i = 0; i <= 1; i+= 1/argument8){ point_x = power(1-i,3)*argument0 + 3*power(1-i,2)*i*argument2+3*(1-i)*power(i,2)*argument4+power(i,3)*argument6; point_y = power(1-i,3)*argument1 + 3*power(1-i,2)*i*argument3+3*(1-i)*power(i,2)*argument5+power(i,3)*argument7; //由曲线函数得出当t==i时的点坐标 draw_vertex(point_x,point_y); //…

gm8图像渲染流程心得
type: item
date: 2015-05-10T09:01:00+08:00
description: draw的工作流程很特殊,在gm中,有一幅内置的画布会保存即将发生的绘制工作，我们所有的绘制任务都在这块画布上进行例如 draw_text(),draw_sprite()等这块画布不在屏幕上显示，只有在每步结束前，draw事件进行时，才会被绘制到屏幕上。这也是我们实际看到的东西,就像surface一样,你可以在surface上进行绘制工作,但只有将surface绘制到屏幕上才能被看见Gm中有两个关键函数screen_redraw() 调用绘图事件重绘房间。screen_refresh() 使用当前房间图像刷新显示（而不是使用绘图事件）。F1帮助文档中的解释是:第一个 redraw 函数重绘了内置画布，然后刷新屏幕图像。第二个函数 refresh 只是刷新了一次屏幕图像我们在其他的事件中进行绘制时并不会显示,是因为将东西都画到了内置的画布上,而我们在绘制到内置画布上后，并没有调用第一个函数刷新屏幕，等到每一步的最后，也就是绘制事件发生时，游戏执行了第二个函数，将内置画布重绘而gm在进行绘制时是按深度从大到小绘制贴图和精灵,绘制事件发生时,深度最大的是背景,所以首先会在内置画布上绘制出背景,覆盖你在上一步和这一步内所有绘制在内置画布上的东西,然后再执行其他的绘制任务,最后刷新屏幕,将内置画布上的东西绘制到屏幕。因此给人造成在其他事件中进行绘制操作时无效的假象step和draw的区别Step事件:只执行动作Draw事件:重绘画布 (执行默认的绘制动作,绘制房间背景和视角以及实例精灵,由深度决定绘制顺序)执行动作并刷新屏幕 (执行draw事件里的动作如绘制表面,精灵和粒子等,同时每进行一次绘制刷新一次屏幕)所以普通的步事件比draw事件执行要快如果要使用step进行绘制的话,必须使用set_automatic_draw(0)停止默认的绘制动作,否则在绘制事件发生时会清除全部内置画布的图像并且刷新屏幕,不仅会造成效率低下,还会发生闪屏表面和draw事件发生冲突的原因也很简单,新建表面属于新建了一张新画布,因此你使用时必须重新设定绘图目标到表面,并只能将表面绘制到屏幕上