【Cocos2d-x】视线和光线:如何创建 2D 视觉范围效果

来源:互联网 发布:安卓博德之门2修改数据 编辑:程序博客网 时间:2024/05/01 15:15

【Cocos2d-x】视线和光线:如何创建 2D 视觉范围效果

    论坛看到的一片很好的文章,果断收藏,转过来方便大家查看~

sight_and_light.gif

Android客户端下载:sight_and_light-debug.rar


以下翻译转载自:indienova

原文:http://ncase.github.io/sight-and-light/

注意:手机浏览可能会不能加载内嵌网页


今天,我要介绍大家制作如下这个效果:(鼠标移动可看不同效果,可在原文中尝试交互效果)

1413776665590325.gif

这个效果被用在了我最新的开源游戏《Nothing To Hide》里面。您可能知道,有不少游戏都有类似的效果,比如《摩纳哥:你的就是我的(Monaco: What’s Yours Is Mine)》、《爆破杰克(Dynamite Jack)》以及《史莱姆吉什(Gish)》等。说不定也会出现在你的游戏中!

1413774826226688.png

我会将自己的实现步骤以及在开发过程中犯过的错误展示出来。首先,是一些预热的内容,下面的演示展示了绘制一些线段并且跟踪鼠标的位置。(提示:一个方形由四条线段组成,以此类推)

1413777460947041.gif

接下来是比较数学的一部分。别担心,只是回忆一下之前学过的代数。


我们需要找到视线(也可以是光线,我们用视线来统一代替)和这些线段之间最近的交点。任何一条线都可以这样来表示:

1
Point + Direction * T

这样,我们就可以得到关于视线和线段的四个等式:

1
2
3
4
Ray X = r_px+r_dx*T1
Ray Y = r_py+r_dy*T1
Segment X = s_px+s_dx*T2
Segment Y = s_py+s_dy*T2

注意:在我们开始之前,要注意检查视线和相对应的线段是否平行,也就是方向是否相同。如果平行,就没有交点,不用处理。


如果它们(视线和相对应的线段)相交,那么交点的 X 和 Y 应该相等,也就是:

1
2
r_px+r_dx*T1 = s_px+s_dx*T2
r_py+r_dy*T1 = s_py+s_dy*T2


我们将用下面的方法取得 T1 和 T2

1
2
3
4
5
6
7
8
9
10
11
// Isolate T1 for both equations, getting rid of T1
T1 = (s_px+s_dx*T2-r_px)/r_dx = (s_py+s_dy*T2-r_py)/r_dy
 
// Multiply both sides by r_dx * r_dy
s_px*r_dy + s_dx*T2*r_dy - r_px*r_dy = s_py*r_dx + s_dy*T2*r_dx - r_py*r_dx
 
// Solve for T2!
T2 = (r_dx*(s_py-r_py) + r_dy*(r_px-s_px))/(s_dx*r_dy - s_dy*r_dx)
 
// Plug the value of T2 to get T1
T1 = (s_px+s_dx*T2-r_px)/r_dx

要确保 T1>0 和 0<T2<1。如果不是这样,那么预计中的交点并不在视线或者线段上,其实也就是无交点。如果由交点,那很好!你找到一个交点。现在用同样一条视线和所有线段计算,以便找到最近的交点(应该是 T1 值最小的那个)。


下面是看起来的样子:(移动鼠标的效果)

1413777664308096.gif

很好,现在让我们发射出 50 条视线来:

1413777759966659.gif

然后,我想,我只要连接起这些交点来,就可以得到一个看起来不错的多边形,结果看起来是这样:

1413777775495297.gif

见鬼。就算我发出 360 条视线,也还是看起来不对劲儿。这个问题困扰了我半天,直到我意识到:我不用向所有方向发出视线,我只需要向每个线段的端点发出视线就可以。

针对每一条(不同的)线段的端点,我直接发出视线,另外增加两条偏移量为 +/- 0.00001 弧度的视线。这两条额外的视线用来去和线段后面的墙来相交。

1413777886953394.gif

接下来,我按照这些视线的角度将这些交点排序,这样我就可以简单的按照顺时针顺序将它们连接起来,可以绘制出一个看起来满意的多边形。

1413777905805719.gif

最后!看起来确实不错了。我又添加了一些效果,看起来就像下面这样,带有一些模糊的阴影效果。红点代表的是 11 个初始点——是的,11 个多边形。

1413778053998003.gif

最后,做了些改进,我放了两幅图。然后,将这两幅图混合,就成了我们在开始的时候看到的那个效果那样。

然后我又添加了额外的光源,就成了下面这样

1413774960618722.png

1413774987371822.png

1413778033317909.gif

多光源、投射阴影、激光感应炸弹、显示你或者敌人的可视范围……这种 2D 的效果有很强的可扩展性,如果应用得当,加上好的创意,可以大大增强你的游戏的吸引力。

 

Cocos2d-x实现过程

这个效果最初是在indienova看到的相关介绍,于是就开始琢磨如何在Cocos2d-x中实现这个效果。


第一次尝试:

这个效果是由 背景图(骷髅)和前景(美女)图 叠加形成的,在光线变动的情况下背景保持不变,而前景根据光线的明暗相应的显示对应部分的明暗度,而没有被光线照射到的地方隐藏掉。

首先想到的自然是 DrawNode 绘画出光照射到的多边形区域,然后用ClippingNode 设置DrawNode 为模板来绘制前景图。

但是从效果中可以看出影子中还会区分透明度,越偏离视线的方向的光线越暗,并与前景叠加,前景被分成了几个层次的透明度。

ClippingNode在绘制模板的时候会把模板扣抠掉,如果可以保留模板的绘制的话,就可以将前景与模板的颜色做blend处理,形成多层次透明度。

查阅 ClippingNode 代码,发现ClippingNode在做模板设置的时候设置了 模板测试 从不通过,所以模板怎么都不会被绘制出来。

1
2
glStencilFunc(GL_NEVER, mask_layer, mask_layer);
glStencilOp(!_inverted ? GL_REPLACE : GL_ZERO, GL_KEEP, GL_KEEP);

可以修改为:

1
2
glStencilFunc(GL_GEQUAL, mask_layer, mask_layer);
glStencilOp(GL_KEEP, GL_KEEP, !_inverted ? GL_REPLACE : GL_ZERO);

然后设置前景的blend func 为:

1
2
3
auto fore = Sprite::create("foreground.png");
BlendFunc func = {GL_DST_COLOR, GL_NONE};
fore->setBlendFunc(func);

此时测试模板和前景是能很好的叠加效果的。


但是接下来使用DrawNode绘制多边形的时候却出现了问题,DrawNode对于处理复杂多边形并不能胜任,画出来的形状都是乱飞的。


第二次尝试:

这次尝试了使用了 前一篇文章 介绍的nanovg 来绘制多边形,首先建了个NvgNode,重载draw 里面写绘制函数,将这个node作为模板,最后发现新问题:

1.nanovg 绘制的效率比较低,简单测试同为使用多边形填充整个屏幕,nanovg 30帧,DrawNode 60帧,差的还是比较多。

2.nanovg 处理多边形是 也是 使用 模板来处理 填充颜色,修改了一番还是不能够很好的和cocos结合,没有再往深处继续研究。


第三次尝试:

这次还是回到了DrawNode上,对于opengl 绘制 复杂多边形上进行资料搜索。

看到几种解决方案(参考):

第一种解决方案:多边形网格化法

对于非简单多边形、非凸多边形或有洞的多边形,OpenGL在GLU库中提供了一个多边形网格化对象GLUtesselator,对多边形进行网格化————将它们分解成一组简单的、能够进行渲染的OpenGL多边形。

经测试这种方法对凹凸多边形和自交、带孔多边形都能正确的渲染。


第二种解决方案:模板缓冲法stencil Buffer

可以参考nanovg中的实现方法


第三种解决方案: 凹多边形凸分解法

思路: 使用算法将凹多边形分解为多个凸多边形或一系列的三角形,然后进行渲染。

这里 列出了许多三角形划分多边形算法的实现,这里选了一个比较靠谱的  Triangulate 来使用。

将最后输出的多边形分解为三角形后调用DrawNode::drawTriangle 来绘制,成功!


项目地址: https://github.com/2youyouo2/sight_and_night


来源网址:http://cocokele.com/sight-and-light/

0 0
原创粉丝点击