好了,今天想要讲的是如何让精灵按照自己定义的路径去运动,官方给我们了一个action类,里面有很多运动的类型,比如bezier曲线运动,比如jump运动等等,设计好了运动之后,我们只要让精灵调运runAction()即可。 本人呢是十足的秦时明月粉丝,特别喜欢里面 的 少司命!!!简直是我的女神呀,所以自然想和少司命搭上一点关系那,就记得当初少司命 搭救 盗跖 那次 耍了一招 用树叶画八卦阵放大招技能来着。 然后就让我各种各种激动(原谅我爱少司命爱的那么深沉!)
好的,转入正题,首先我们可以看看利用 粒子系统 + 运动轨迹做出来效果如何。。。
这个是我静态的粒子系统,是树叶绕着中心转,有一点点散开的效果:
这个是粒子系统在在八卦路线的时候样子,如果有少司命的纤细小手的话技能就好看了:
由于本人用了粒子系统有散开效果,所有早先的路径下叶子就有点散开了,也可以通过叶子浓度看出叶子经过的路径方向。
首先我们来分析原理,最一开始,我们画了一个 半径为R,中心点为(a,b)的圆,等圆画完以后紧接着画一个半斤为 R/2 ,中心点为(a-R/2 , b)的半个圆(上半个圆弧),再接着,我们就画半径为 R/2 ,中心点为 (a+R/2 , b )的半圆弧(在图中由于截图过早的问题还没有画完整)。这样,我们要的八卦效果就出来了。。。
由上面分析可知,我们必须要解决画圆算法:由初中还是高中知识可知,x^2 + y^2 = R^2。然后再做适当平移(a,b)就可以得到我们指定中心点为(a,b),半径为R的圆了。 当然 x^2 + y^2 = R^2 求解由于正负的关系考虑比较复杂 ,所以我把它转化成为 : (R*cos(α))^2 + (R*sin(α))^2 = R^2 所以我们的圆上点坐标为 (a+R*cos(α) ,a+R*sin(α) ), 其中 α > 0 && α <2 * Pi , 这样就是我们要画的正圆了。通过 对 α 范围控制,我们也可以画我们指定想要的半圆。
//定义一个结构体,包含里中心点坐标以及要画圆的半径长度
typedef struct _WuzhuangConfig
{
cocos2d::Point centerPosition;//中心点坐标
float rudius;//半径
}WuzhuangConfig;
//根据画圆进度t以及圆的配置信息计算出圆上点坐标
static inline Vec2 PointAt(float t /*这里的t代表画圆进度,若单纯顺时针画一个圆时,与动作进度相同
,若逆时针或者画半圆等需要一定映射关系*/
, const WuzhuangConfig c )
{
float x = - c.rudius * cos(3.1415926 * t * 2 ) + c.centerPosition.x ;
//上面加负号时因为从左手边开始画起
float y = c.rudius * sin(3.1415926 * t * 2 ) + c.centerPosition.y;
return Vec2(x,y);
}
好,上面解决了画圆问题,下面我们来剖析我们画圆的顺序与 动作进度 t (0代表动作起点,1代表动作结束,0.5代表动作执行到了刚刚好一半) 之间关系 。
上面大圆的周长是全路径的 2/3 (初中知识不解释),小半圆分别占了全路径的 1/6 。
故 t < 2.0/3 && t>0 时 , 我们执行画大圆操作。因为我们要画的整个大圆 ,而不是2/3个圆,故我们需要把动作进度映射到画圆进度上 即画大圆的进度 p = t * 3/2 。这样就可以画出整个大圆了。
同理 t < 5/6 && t >2/3 时,p = (t- 2.0/3) * 3 ; 当 t >5/6 && t < 1 时,p = 7.0/2 - t * 3 ;
知道了这个映射关系以后,我们就可以写出整个画八卦的根据 动作进度 t 的函数
void Wuzhuang::update(float t)
{
if(_target)
{
if(t < 2.0/3)
{
_target->setPosition(PointAt( 3.0 / 2 * t, m_config ));
}
else
{
if(t < 5.0/6)
{
_target->setPosition(PointAt((t- 2.0/3) * 3 , m_config1/*小圆1配置信息*/ ));
}
else
{
_target->setPosition(PointAt( 7.0/2 - t * 3, m_config2/*小圆2配置信息*/ ));
}
}
}
}
介绍完了画圆核心的,剩下的就是简单设置配置属性和定义一个调用接口了。
//小圆的配置信息
WuzhuangConfig m_config1,m_config2;
//初始化
bool Wuzhuang::initWithDuration(float t, const WuzhuangConfig& c)
{
if (ActionInterval::initWithDuration(t))
{
m_config = c;
m_config1.rudius = c.rudius/2;
m_config2.rudius = c.rudius/2;
m_config1.centerPosition = c.centerPosition - Vec2(c.rudius/2,0);
m_config2.centerPosition = c.centerPosition + Vec2(c.rudius/2,0);
return true;
}
return false;
}
//外部调用的静态接口
Wuzhuang* Wuzhuang::actionWithDuration(float t,const WuzhuangConfig& c)
{
Wuzhuang* pWuzhuang = new Wuzhuang();
pWuzhuang ->initWithDuration(t, c);
pWuzhuang ->autorelease();
return pWuzhuang;
}
基本整个代码就是这样了。。这样外部就可以调用了。调用的话就像调用bezierby一样调用就好了。
WuzhuangConfig m_config;
m_config.centerPosition = Vec2(370, 500);
m_config.rudius = 200;
Wuzhuang* pAction = Wuzhuang::actionWithDuration(4, m_config);
p->runAction(pAction);
所有想要自定义的动作都可以通过这样实现。如果不懂可以去看看BezierBy这样的类他们时怎么实现的,仿照他们这样写就可以了。。。