我要介绍的,就是这样的效果:(创意和素材都来自于原文: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
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
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();
//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));
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光线效果的更多相关文章

  1. HTML实现代码雨源码及效果示例

    这篇文章主要介绍了HTML实现代码雨源码及效果示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. 源码推荐:简化Swift编写的iOS动画,iOS Material Design库

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  3. swift皮筋弹动发射飞机ios源码

    这是一个款采用swift实现的皮筋弹动发射飞机游戏源码,游戏源码比较详细,大家可以研究学习一下吧。

  4. swift 写的app 源码,保存一下下

    http://www.topthink.com/topic/3345.htmlhttp://www.csdn.net/article/2015-01-09/2823502-swift-open-source-libs

  5. swift 源码网站 code4app

    http://code4app.com/ios/HTHorizontalSelectionList/54cb2c94933bf0883a8b4583http://123.th7.cn/code/DMPagerViewController_2522.html

  6. OpenStack Swift源码导读:业务整体架构和Proxy进程

    OpenStack的源码分析在网上已经非常多了,针对各个部分的解读亦是非常详尽。其中proxy是前端的业务接入进程。account、container和object目录分别是账户、容器和对象的业务处理逻辑进程。各个业务进程或模块之间的逻辑关系可以参考《OpenstackSwift简介》文中的架构图。在《OpenstackSwift简介》从理论上面介绍了具体的节点寻找过程。

  7. 源码推荐(7.21):顶部滑动菜单FDSlideBar,Swift版无限循环轮播图

    顶部滑动菜单FDSlideBarFDSlideBar是一个顶部滑动菜单,如常见的网易、腾讯新闻等样式。菜单间切换流畅,具有较好的体验性。测试环境:Xcode6.2,iOS6.0以上Swift版无限循环轮播图无限循环轮播图片点击代理可设置图片Url的数组Url和本地图片混合轮播测试环境:Xcode6.2,iOS7.0以上弹幕系统实现--QHDanumuDemo说明:QHDanmu文件夹下是主要的弹幕模块系统,QHDanmuSend文件夹下是简单的发射弹幕的界面。

  8. openstack swift和wsgi源码分析1 HTTP请求处理过程

    分析proxy-server代理服务的执行流程,其他的三个主要服务accountserver,containerserver,objectserver执行过程通proxyserver类似。入口函数调用run_wsgi,此函数完成以下工作:下面重点研究下process_request函数是如何把消息转化为HTTP的request对象这一过程。process_request函数,生成HttpProtocol对象,并执行init操作,注意,HttpProtocol对象自身没有init函数,所以会调用父类的父类的

  9. fir.im Weekly - 进击的 Swift

    最近Swift开源了,众开发者们欢呼雀跃。本期fir.imWeekly准备了一些关于Swift的“新鲜”干货分享,也包括一些优秀的GitHub源码、开发工具和技术文章等等。同时,苹果启用了新的官网:Swift.org,Swift的GitHub主页:https://github.com/apple/swiftSwift3API设计准则勤快的@星夜暮晨翻译了苹果Swift官方网站博客的一篇文章:Swift3APIDesignGuidelines,了解Swift3特性,希望对你有所帮助。如何在iOS中实现一个可

  10. 苹果贴放出Swift语言的源码

    前一段时间苹果贴放出Swift语言的源码,宣布该语言正式开源。其中还包括Swfit核心库项目和全新的Swift包管理器项目。Swift的开源是程序开发者的又一个福音,攻城狮们可以利用SWIFT语言做更多的事情。Swift语言项目的代码分为几个开源库,全部托管在GitHub上。

随机推荐

  1. 【cocos2d-x 3.x 学习笔记】对象内存管理

    Cocos2d-x的内存管理cocos2d-x中使用的是上面的引用计数来管理内存,但是又增加了一些自己的特色。cocos2d-x中通过Ref类来实现引用计数,所有需要实现内存自动回收的类都应该继承自Ref类。下面是Ref类的定义:在cocos2d-x中创建对象通常有两种方式:这两中方式的差异可以参见我另一篇博文“对象创建方式讨论”。在cocos2d-x中提倡使用第二种方式,为了避免误用第一种方式,一般将构造函数设为protected或private。参考资料:[1]cocos2d-x高级开发教程2.3节[

  2. 利用cocos2dx 3.2开发消灭星星六如何在cocos2dx中显示中文

    由于编码的不同,在cocos2dx中的Label控件中如果放入中文字,往往会出现乱码。为了方便使用,我把这个从文档中获取中文字的方法放在一个头文件里面Chinese.h这里的tex_vec是cocos2dx提供的一个保存文档内容的一个容器。这里给出ChineseWords,xml的格式再看看ChineseWord的实现Chinese.cpp就这样,以后在需要用到中文字的地方,就先include这个头文件然后调用ChineseWord函数,获取一串中文字符串。

  3. 利用cocos2dx 3.2开发消灭星星七关于星星的算法

    在前面,我们已经在GameLayer中利用随机数初始化了一个StarMatrix,如果还不知道怎么创建星星矩阵请回去看看而且我们也讲了整个游戏的触摸事件的派发了。

  4. cocos2dx3.x 新手打包APK注意事项!

    这个在编译的时候就可以发现了比较好弄这只是我遇到的,其他的以后遇到再补充吧。。。以前被这两个问题坑了好久

  5. 利用cocos2dx 3.2开发消灭星星八游戏的结束判断与数据控制

    如果你看完之前的,那么你基本已经拥有一个消灭星星游戏的雏形。开始把剩下的两两互不相连的星星消去。那么如何判断是GameOver还是进入下一关呢。。其实游戏数据贯穿整个游戏,包括星星消除的时候要加到获得分数上,消去剩下两两不相连的星星的时候的加分政策等,因此如果前面没有做这一块的,最好回去搞一搞。

  6. 利用cocos2dx 3.2开发消灭星星九为游戏添加一些特效

    needClear是一个flag,当游戏判断不能再继续后,这个flag变为true,开始消除剩下的星星clearSumTime是一个累加器ONE_CLEAR_TIME就是每颗星星消除的时间2.连击加分信息一般消除一次星星都会有连击信息和加多少分的信息。其实这些combo标签就是一张图片,也是通过控制其属性或者runAction来实现。源码ComboEffect.hComboEffect.cpp4.消除星星粒子效果消除星星时,为了实现星星爆裂散落的效果,使用了cocos2d提供的粒子特效引擎对于粒子特效不了

  7. 02 Cocos2D-x引擎win7环境搭建及创建项目

    官网有搭建的文章,直接转载记录。环境搭建:本文介绍如何搭建Cocos2d-x3.2版本的开发环境。项目创建:一、通过命令创建项目前面搭建好环境后,怎样创建自己的Cocos2d-x项目呢?先来看看Cocos2d-x3.2的目录吧这就是Cocos2d-x3.2的目录。输入cocosnew项目名–p包名–lcpp–d路径回车就创建成功了例如:成功后,找到这个项目打开proj.win32目录下的Hello.slnF5成功了。

  8. 利用cocos2dx 3.2开发消灭星星十为游戏添加音效项目源码分享

    一个游戏,声音也是非常的重要,其实cocos2dx里面的简单音效引擎的使用是非常简单的。我这里只不过是用一个类对所有的音效进行管理罢了。Audio.hAudio.cpp好了,本系列教程到此结束,第一次写教程如有不对请见谅或指教,谢谢大家。最后附上整个项目的源代码点击打开链接

  9. 03 Helloworld

    程序都有一个入口点,在C++就是main函数了,打开main.cpp,代码如下:123456789101112131415161718#include"main.h"#include"AppDelegate.h"#include"cocos2d.h"USING_NS_CC;intAPIENTRY_tWinMain{UNREFERENCED_ParaMETER;UNREFERENCED_ParaMETER;//createtheapplicationinstanceAppDelegateapp;return

  10. MenuItemImage*图标菜单创建注意事项

    学习cocos2dx,看的是cocos2d-x3.x手游开发实例详解,这本书错误一大把,本着探索求知勇于发现错误改正错误的精神,我跟着书上的例子一起调试,当学习到场景切换这个小节的时候,出了个错误,卡了我好几个小时。

返回
顶部