基于Cocos2d-x的2D光线效果雏形

前端之家收集整理的这篇文章主要介绍了基于Cocos2d-x的2D光线效果雏形前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

这个效果起初是在一篇blog看见的,效果很赞,是网页版的,然后想在Cocos2d上实现下,然后就去做了。

blog地址:http://huangwei.pro/2015-08/game-sight-light/
我的github:https://github.com/pepsigit/Cocos2d/tree/master/2DLighgt

首先,在 .h 里面定义一下需要的结构体:

typedef struct LIGHTLINE
{
   Point segA; // 光线的起始点
   Point segB; // 光线的终止点
   float t1;
}LightLine;
 
 
 
  
 

typedefstruct POINTANDANGLE
{
   Point point; // 光源打在墙上的点 
   float angle; // 相对于光源的角度
}PointAndAngle;


是的,我们只需要这些。再来 .cpp 里面,现在初始化下多边形的顶点:


boolHelloWorld::InitSegments()
{
   int subX =60;
   int subY =0;
   LightLine tmp;

    // Border
    tmp.segA = Vec2( origin.x,origin.y);
    tmp.segB = Vec2( origin.x,origin.y +visibleSize.height );
   m_segments.push_back(tmp);

    tmp.segA = Point( origin.x,origin.y );
    tmp.segB = Point( origin.x +visibleSize.width,origin.y );
   m_segments.push_back(tmp);

    tmp.segA = Point( origin.x,origin.y +visibleSize.height );
    tmp.segB = Point( origin.x +visibleSize.width,origin.y + visibleSize.height );
   m_segments.push_back(tmp);

    tmp.segA = Point( origin.x +visibleSize.width,origin.y + visibleSize.height );
   m_segments.push_back(tmp);

    // polygon #1
    tmp.segA =Point(100-subX,150-subY ); 
    tmp.segB =Point(120-subX,50-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(120-subX,50-subY );
    tmp.segB =Point(200-subX,80-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(200-subX,80-subY );
    tmp.segB =Point(140-subX,210-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(140-subX,210-subY );
    tmp.segB =Point(100-subX,150-subY );
   m_segments.push_back(tmp);

    // polygon #2
    tmp.segA =Point(100-subX,200-subY );
    tmp.segB =Point(120-subX,250-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(120-subX,250-subY );
    tmp.segB =Point( 60-subX,300-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(60-subX,300-subY );
    tmp.segB =Point(100-subX,200-subY );
   m_segments.push_back(tmp);

    // polygon #3
    tmp.segA =Point(200-subX,260-subY );
    tmp.segB =Point(220-subX,150-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(220-subX,150-subY );
    tmp.segB =Point(300-subX,200-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(300-subX,200-subY );
    tmp.segB =Point(350-subX,320-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(350-subX,320-subY );
    tmp.segB =Point(200-subX,260-subY );
   m_segments.push_back(tmp);

    // polygon #4
    tmp.segA =Point(340-subX,60-subY );
    tmp.segB =Point(360-subX,40-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(360-subX,40-subY );
    tmp.segB =Point(370-subX,70-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(370-subX,70-subY );
    tmp.segB =Point(340-subX,60-subY );
   m_segments.push_back(tmp);

    // polygon #5
    tmp.segA =Point(450-subX,190-subY );
    tmp.segB =Point(560-subX,170-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(560-subX,170-subY );
    tmp.segB =Point(540-subX,270-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(540-subX,270-subY );
    tmp.segB =Point(430-subX,290-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(430-subX,290-subY );
    tmp.segB =Point(450-subX,190-subY );
   m_segments.push_back(tmp);

    // polygon #6
    tmp.segA =Point(400-subX,95-subY );
    tmp.segB =Point(580-subX,50-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(580-subX,50-subY );
    tmp.segB =Point(480-subX,150-subY );
   m_segments.push_back(tmp);

    tmp.segA =Point(480-subX,150-subY );
    tmp.segB =Point(400-subX,95-subY );
   m_segments.push_back(tmp);
    
   if(0 < m_segments.size() )
    {
       vector<LightLine>::iterator it =m_segments.begin();
       for( ; it !=m_segments.end(); it++ )
        {
           drawNode->drawLine(it->segA,it->segB,Color4F(1,1,1) );
        }
    }

    return true;
}



效果



接下来添加光源,其实就是添加一个随便的精灵即可,这里我使用了自带的资源,CloseNormal.png,然后添加下触摸事件,你就可以移动这个光源了,这部分省略吧。


刚才初始化多边形的时候,已经将所有的线段保存在类成员m_segments里面了,下面要去除下重复的点,因为一个点连接2条线段:


void HelloWorld::GetEachPointAngle()
{
    vector<Point> tmpPoint;
    vector<LightLine>::iterator it =m_segments.begin();
    vector<Point>::iterator isFind;

    // 获得线段唯一点
   for( ; it !=m_segments.end(); it++ )
    {
        isFind =find( tmpPoint.begin(),tmpPoint.end(),it->segA);
       if( isFind == tmpPoint.end() )
        {
            tmpPoint.push_back(it->segA);
        }

        isFind =find( tmpPoint.begin(),it->segB);
       if( isFind == tmpPoint.end() )
        {
            tmpPoint.push_back(it->segB);
        }
    }    
  
// ......
}


现在获得了唯一点,不需要了很多重复的计算,接下来计算当前光源静止的位置到各个点的角度,并且额外增加两条 0.00001 角度的线,为了能让光源打在后面的墙上。



void HelloWorld::GetEachPointAngle()
{
    // .......

    //获得唯一点的角度,额外增加 2条射线
   vector<Point>::iterator it_point = tmpPoint.begin();
   for( ; it_point != tmpPoint.end(); it_point++ )
    {
       float angle =atan2(it_point->y -light->getPositionY(),it_point->x -light->getPositionX());
       m_angle.push_back(angle -0.00001);
       m_angle.push_back(angle);
       m_angle.push_back(angle +0.00001);
       
    }
    // .......
}


接下来开始计算打到墙上的点。将光源点分别与线段计算.公式看下原文。


void HelloWorld::GetEachPointAngle()
{
    // .........
    //循环个角度,找出最近的交点
   vector<float>::iterator it_angle =m_angle.begin();
   for( ; it_angle !=m_angle.end(); it_angle++ )
    {
       float dx =cos(*it_angle);
       float dy =sin(*it_angle);

        //CCLOG("%f,%f,%f",*it_angle,dx,dy);
        
       LightLine l;
        l.segA =Point(light->getPositionX(),light->getPositionY() );
        l.segB =Point(light->getPositionX() + dx,light->getPositionY() + dy );

       float minT1 =0;
        
        it =m_segments.begin();
       for( ; it !=m_segments.end(); it++ )
        {
           float tmpT1 =GetIntersection( l,*it );
           if( -1 == tmpT1 )
            {
               continue;
            }
           if(0 == minT1 || minT1 > tmpT1 )
            {
                minT1 = tmpT1;
            }
        }

        PointAndAngle p;
        p.point =Point(light->getPositionX() + minT1 * dx,light->getPositionY() + minT1 * dy );
        p.angle = *it_angle;
       m_intersects.push_back(p);
        //CCLOG("%f,p.point.x,p.point.y,*it_angle);
    }

    sort(m_intersects.begin(),m_intersects.end(),SortAngle );
    
}

floatHelloWorld::GetIntersection(LightLine ray,LightLine seg )
{
    //CCLOG("%f,ray.segA.x,ray.segA.y,ray.segB.x,ray.segB.y);
    
   int r_px = ray.segA.x;
   int r_py = ray.segA.y;
   float r_dx = ray.segB.x - ray.segA.x;
   float r_dy = ray.segB.y - ray.segA.y;

   int s_px = seg.segA.x;
   int s_py = seg.segA.y;
   float s_dx = seg.segB.x - seg.segA.x;
   float s_dy = seg.segB.y - seg.segA.y;

   float r_mag =sqrt( r_dx * r_dx + r_dy * r_dy );
   float s_mag =sqrt( s_dx * s_dx + s_dy * s_dy );
   if( ( r_dx / r_mag ) == ( s_dx / s_mag ) && ( r_dy / r_mag ) == ( s_dy / s_mag) )
    {
       return -1;
    }
    
    
   float t2 = ( r_dx * (s_py-r_py) + r_dy * (r_px - s_px)) / (s_dx*r_dy - s_dy*r_dx);
   float t1 = ( s_px + s_dx * t2 - r_px )/ r_dx;

   if( t1 <0 || t2 <0 || t2 >1 )
    {
       return -1;
    }

   CCLOG("%d,%d,r_px,r_py,r_dx,r_dy,s_px,s_py,s_dx,s_dy,t1);
   return t1;
}



最后对所有与光源配对的点根据角度进行排序,效果




最后循环填充三角形即可。

这里说下的是,光线和多边形不在一个layer上,因为在你每次移动的过程中,需要清楚旧光线,来画新光线,所以进行了分层,并且对旧数据进行清除,每次移动,重新计算再绘制。

这里可能会出现不准确的情况,效果



个人认为是移动时候,数值之间计算的误差导致。并非整数,所以我在 onTouchMoved 这样处理的:

/***********************************************************

 *   移动触摸会有误差值,不是整数,所以会出现三角形连接错误
 *   根据小数大小,取整运算    
            
***********************************************************/

subP.x = ( ( subP.x - (int)subP.x ) > 0.5 ) ? ceil(subP.x) : floor(subP.x);
subP.y = ( ( subP.y - (int)subP.y ) > 0.5 ) ? ceil(subP.y) : floor(subP.y);



效果有所改善:




大神们勿喷,谢谢,谢谢。

猜你在找的Cocos2d-x相关文章