源码分析使用Cocos2d-x实现2D光线效果

源码分析使用Cocos2d-x实现2D光线效果,第1张

概述我要介绍的,就是这样的效果:(创意和素材都来自于原文:http://ncase.me/sight-and-light/) 由于原文介绍的过于简练,导致像我这样的小白根本看不懂,所以我想要介绍的更易懂一点。 一、画线段 在Cocos2d-x中,已经封装了通过Opengl ES的画线函数,只需要创建一个DrawNode对象,就可以画线了,画几条线段,就像这样: 二、画射线和线段的交点及轨迹 这里需要一

我要介绍的,就是这样的效果:(创意和素材都来自于原文:http://ncase.me/sight-and-light/)


由于原文介绍的过于简练,导致像我这样的小白根本看不懂,所以我想要介绍的更易懂一点。


一、画线段

在Cocos2d-x中,已经封装了通过Opengl ES的画线函数,只需要创建一个DrawNode对象,就可以画线了,画几条线段,就像这样:

二、画射线和线段的交点及轨迹

这里需要一点点几何知识了。


直线的参数表示:

直线可以用直线上的一点P0和方向向量v表示,直线上的所有点P满足 P = P0 + tv。

参数方程最方便的地方在于直线、射线、线段的方程形式是一样的,区别在于参数t。直线的t没有范围限制,射线的t>0,线段的t在0~1之间(t >=0 && t <= 1)。


直线交点:

设直线分别为 P+t1v 和 Q+t2w,设向量u=QP,设cross(x,y)为向量x和y的叉积,则:

t1 = cross(w,u) / cross(v,w)

t2 = cross(v,w)

当cross(v,w) == 0时,两直线平行,无交点。

所以把屏幕中心作为光源,方向指向鼠标所在的位置,画一条射线,t即是光源与交点的距离,选一个最近的交点(即t最小),连接光源和这个点,就会得到这样的效果:

主要代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 bool HelloWorld::getIntersection( const line& ray, const line& segment, Point& point, float & distance) { Vec2 v1(ray.p2 - ray.p1); Vec2 v2(segment.p2 - segment.p1); float cross = getCross(v1,v2); if (cross == 0) { return false ; } Vec2 u(ray.p1 - segment.p1); float t1 = getCross(v2,u) / cross; float t2 = getCross(v1,u) / cross; if (t1 < 0 || t2 < 0 || t2 > 1) { return false ; } point = v1 * t1 + ray.p1; distance = t1; return true ; } 射线与线段的交点


三、以鼠标为光源,画射向线段端点的光线

改动一下刚才的代码,以鼠标作为光源,画射向每个端点的光线,在每条光线的两侧同时画出极角偏移1e-4的两条光线,用来穿过线段端点,与端点后面的线段相交。看起来就像这样:


四、画多边形,标记出光亮区域

上一步画的光线表示出了光亮区域,还需要画出填充多边形来标记一下,但是opengl只能画凸多边形。所以为了画出需要的不规则多边形,要分割成三角形来画。

容易看出,任意相邻的两个交点与光源,可以组成一个三角形,接下来就是找相邻的点。所以极角排序一下,依次取相邻的点就可以了。画完三角形后的效果就像这样:


五、实现本文开头的效果

Cocos2d-x提供了ClipPingNode类,可以做出不规则的裁剪图形,以上一步画的多边形为模板裁剪就可以了,不多赘述,代码中有详细。


六、附上Cocos2d-x写的主要代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @H_101_404@ 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 @H_597_502@ 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 #include "HelloWorldScene.h" USING_NS_CC; Scene* HelloWorld::createScene() { auto scene = Scene::create(); auto layer = HelloWorld::create(); scene->addChild(layer); return scene; } bool HelloWorld::init() { if (!Layer::init()) { return false ; } // 添加背景图 auto visSize = Director::getInstance()->getVisibleSize(); auto background = Sprite::create( "background.png" ); background->setposition(visSize.wIDth / 2,visSize.height / 2); addChild(background,1); // 创建两个DrawNode,一个用来画静态线段,一个画动态线段 _staticDraw = DrawNode::create(); addChild(_staticDraw,100); _touchDraw = DrawNode::create(); @H_101_404@ //addChild(_touchDraw,100); // 创建ClipPingNode,设置底板和模板 _clip = ClipPingNode::create(); _clip->setInverted( false ); _clip->setAlphaThreshold(255.0f); auto foreground = Sprite::create( "foreground.png" ); foreground->setposition(visSize.wIDth / 2,visSize.height / 2); _clip->addChild(foreground,1); _clip->setStencil(_touchDraw); addChild(_clip,101); // 画线段,并保存所有不重复的端点 initSegments(); initPoints(); // 触摸监听 auto Listener = EventListenertouchOneByOne::create(); Listener->ontouchBegan = [=](touch* touch,Event* event) { ontouchmoved(touch,event); return true ; }; Listener->ontouchmoved = CC_CALLBACK_2(HelloWorld::ontouchmoved, this ); getEventdispatcher()->addEventListenerWithSceneGraPHPriority(Listener, this ); return true ; } voID HelloWorld::ontouchmoved(touch* touch,Event* event) { Point tar(0,0); // 光线的端点 Point cur(0,0); // 光线与线段的交点 float distance = 0; // 光源与交点的距离 _touchDraw->clear(); auto pos = touch->getLocation(); //_touchDraw->drawDot(pos,5,color4F::RED); // 计算极角,并添加两个偏移1e-4的极角 initAngles(pos); // 极角排序 std::sort(_angles.begin(),_angles.end(),[]( float x, float y) { return x < y; }); // 找最近的交点 std::vector<Point> vertex; for (auto angle : _angles) { Vec2 dlt( cos (angle), sin (angle)); @H_597_502@ float closest = -1; for (auto s : _segments) { if (getIntersection(line(pos,pos + dlt),s,cur,distance)) { if (closest == -1 || closest > distance) { closest = distance; tar = cur; } } } if (closest != -1) { vertex.push_back(tar); } } // 画三角形 // 下面2个循环第3个参数可以写为 vertex[(i+1) % vertex.size()],合并成1个循环 // 但是显然,取余 *** 作效率太低,分开写更好一些 int limit = vertex.size() - 1; for ( int i = 0; i < limit; i++) { _touchDraw->drawTriangle(pos,vertex[i],vertex[i+1],color4F::WHITE); } if (limit > 0) { _touchDraw->drawTriangle(pos,vertex[limit],vertex[0],color4F::WHITE); } //画三角形的边,DeBUG用 /*for(auto v : vertex) { _touchDraw->drawSegment(pos,v,0.5f,color4F::RED); _touchDraw->drawDot(v,3,color4F::RED); }*/ } // 画线段 voID HelloWorld::initSegments() { _segments.clear(); _segments.push_back(line(Point(0,360),Point(840,360))); _segments.push_back(line(Point(840,0))); _segments.push_back(line(Point(840,0),Point(0,0))); _segments.push_back(line(Point(0,360))); _segments.push_back(line(Point(100,210),Point(120,310))); _segments.push_back(line(Point(120,310),Point(200,280))); _segments.push_back(line(Point(200,280),Point(140,150))); _segments.push_back(line(Point(140,150),Point(100,210))); _segments.push_back(line(Point(100,160),110))); _segments.push_back(line(Point(120,110),Point(60,60))); _segments.push_back(line(Point(60,60),160))); _segments.push_back(line(Point(200,100),Point(220,210))); _segments.push_back(line(Point(220,Point(300,160))); _segments.push_back(line(Point(300,Point(350,40))); _segments.push_back(line(Point(350,40),100))); _segments.push_back(line(Point(540,300),Point(560,320))); _segments.push_back(line(Point(560,320),Point(570,290))); _segments.push_back(line(Point(570,290),Point(540,300))); _segments.push_back(line(Point(650,170),Point(760,190))); _segments.push_back(line(Point(760,190),Point(740,90))); _segments.push_back(line(Point(740,90),Point(630,70))); _segments.push_back(line(Point(630,70),Point(650,170))); _segments.push_back(line(Point(600,265),Point(780,310))); _segments.push_back(line(Point(780,Point(680,210))); _segments.push_back(line(Point(680,Point(600,265))); for (auto s : _segments) { _staticDraw->drawSegment(s.p1,s.p2,color4F::WHITE); } } // 找不重复端点 voID HelloWorld::initPoints() { for (auto segment : _segments) { if (_points.find(segment.p1) == _points.end()) { _points.insert(segment.p1); } if (_points.find(segment.p2) == _points.end()) { _points.insert(segment.p2); } } } // 初始化极角 voID HelloWorld::initAngles( const Point& touchPos) { _angles.clear(); const float eps = static_cast < float >(1e-4); for (auto p : _points) { auto angle = atan2 (p.y - touchPos.y,p.x - touchPos.x); _angles.push_back(angle); _angles.push_back(angle - eps); _angles.push_back(angle + eps); } } // 向量的叉积 float HelloWorld::getCross( const Vec2& v1, const Vec2& v2) { return (v1.x * v2.y - v1.y * v2.x); } // 射线与线段的交点 bool HelloWorld::getIntersection( const line& ray, const line& segment, Point& point, float & distance) { Vec2 v1(ray.p2 - ray.p1); Vec2 v2(segment.p2 - segment.p1); float cross = getCross(v1,v2); if (cross == 0) { return false ; } Vec2 u(ray.p1 - segment.p1); float t1 = getCross(v2,u) / cross; float t2 = getCross(v1,u) / cross; if (t1 < 0 || t2 < 0 || t2 > 1) { return false ; } point = v1 * t1 + ray.p1; distance = t1; return true ; }

PS. 目前还有两个问题:1、没有实现出原文中的阴影效果 2、编译到安卓看不到效果。还希望大家与原作者交流讨论

总结

以上是内存溢出为你收集整理的源码分析使用Cocos2d-x实现2D光线效果全部内容,希望文章能够帮你解决源码分析使用Cocos2d-x实现2D光线效果所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/web/1008015.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-22
下一篇 2022-05-22

发表评论

登录后才能评论

评论列表(0条)

保存