bool player::playinit(){
if(!Sprite::init())
return false;
Size size=Director::getInstance()->getWinSize();
//精灵中添加两个精灵的对象,作为player的child。
Sprite*sp=Sprite::create("4.0.png");
sp->setPosition(size.width/2,size.height/2);
this->addChild(sp,1,tag_p1);
Sprite*sp2=Sprite::create(“5.0.png");
sp2->setPosition(size.width/2,size.height/2);
this->addChild(sp2,1,tag_p1);
return true;
}
=========================
精灵中添加精灵有什么好处,来看两个例子:
例一,精灵有血条,精灵也有自己的运动,我们当然希望精灵的运动和血条是不干涉的,也就是血条的加减血和精灵的运动是两个不同的精灵,各自负责自己的运动,但是又有一些运动希望他们同步,比如,一起走,一起跳,等等;
例二,英雄的运动和英雄的皮肤分开,英雄升级换一套皮肤,然后运动相同;比如有10级,10套皮肤,英雄的动作由20张图片合成,如果不分离,我们可能需要10*20=200(每种皮肤一套动作)张图片,但是分离,我们只需要20+10张(一套动作,套上10套皮肤)。
先来看两个初始化精灵的常用函数,
通过精灵图片创建:
==============================
Sprite* Sprite::create(const std::string& filename)
{
Sprite *sprite = new (std::nothrow) Sprite();
if (sprite && sprite->initWithFile(filename)) //注意init
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return nullptr;
bool Sprite::initWithFile(const std::string& filename)
{
CCASSERT(filename.size()>0, "Invalid filename for sprite");
Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
if (texture)
{
Rect rect = Rect::ZERO;
rect.size = texture->getContentSize();
return initWithTexture(texture, rect);
}
return false;
}
Sprite* Sprite::create()
{
Sprite *sprite = new (std::nothrow) Sprite();
if (sprite && sprite->init()) //注意init
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return nullptr;
}
bool Sprite::init(void)
{
return initWithTexture(nullptr, Rect::ZERO );
Sprite*sp1=Sprite::create("1483.0.png");
sp1->setAnchorPoint(ccp(0.5,1));
sp1->setPosition(this->getContentSize().width,this->getContentSize().height);
addChild(sp1);
============================
auto sp=(Player*)this->getChildByTag(tag_hp);
int add=p->hp_n+k;
if (add>10) {
add=10;
}
float sca=(float)add/10;
CCLOG("-----%f",sca);
auto p=(Player*)this->getChildByTag(tag_player);
auto sp=(Player*)p->getChildByTag(tag_hp);
int add=p->hp_n+k;
if (add>10) {
add=10;
}
float sca=(float)add/10;
CCLOG("-----%f",sca);
sp->setScaleY(sca);
============================
sp->runAction(Sequence::create(JumpTo::create(1.0,ccp(200,100),300, 1),CallFunc::create(this,callfunc_selector(Player::gowolk)),CallFunc::create(this,callfunc_selector(Player::set_speed)),NULL));
============================
这将发生什么,你操作的是精灵的容器,那么这个精灵中的所有子精灵,都将执行相同的运动,这就实现了案例一的要求。
现在来说说实现共同运动的几种方式:
一,直接通过initwithfile创建出实体玩家,在玩家这个精灵上,添加血条精灵,这是一个比较好用的方法,你可以很好的实现碰撞。共同运动;但是,架构不好,因为你有一个主精灵this,还有一堆child,你的主精灵是个实在的实体。
二,通过不带参的creat创建空精灵,在适当位置initwithfile,这和方法一相同。也好实现碰撞
三,在player中实现每个精灵的共同运动,比如按键,所有精灵一起跳,
auto vec=this->get children(),他返回一个Vector(Node*),你add child都在这里面,然后每个jumpTo。
for(auto sp:vec){ sp->runaction(Jumpto::create(100,ccp(100,100)))}
这也能实现共同运动,但这是比较垃圾的方法。
四,创建空精灵,并且不实例化,就操作这个”不存在“的精灵。
但是对于碰撞,和坐标,该如何操作。这就是难点,也是本文讨论的重点所在。
比如我们创建一个精灵,这个精灵里面有一堆精灵,这个精灵是用方法四创建的,如下
============================
player* player::createplayer(){
player *pRet = new(std::nothrow) player();
if (pRet && pRet->init())
{
pRet->autorelease();
return pRet;
}
else
{
delete pRet;
pRet = NULL;
return NULL;
}
}
bool player::playinit(){
if(!Sprite::init())
return false;
score=0;
Size size=Director::getInstance()->getWinSize();
//只列举一个
Sprite*sp=Sprite::create("4.0.png");
sp->setPosition(568,160);
this->addChild(sp,1,tag_p1);
...
return true;
}
============================
player*p=player::createplayer();
p->setPosition(0,0);
p->playinit();
addChild(p,1,tag_player);
============================
这时setPosition为(0,0),精灵的坐标设置了两次 sp->setPosition(568,160); p->setPosition(0,0);
这样,对于layer来说,这个player中的sp1的位置就是(0+568,0+160),这和坐标转换converToWorldspace函数的意思有点类似,然后我们操作精灵移动,改变player*p的位置,很好,添加的所有子精灵都跟着移动了,但是
这个“但是”很重要,碰撞失效了。当我们精灵的位置为0,0时还好,可以碰撞,但是一旦移动了,或者player的setposition不是0,0.碰撞压根就不存在(其实是存在的,只是不合效果)。
这是什么原因:
============================
auto pla=(player*)this->getChildByTag(tag_player);
Sprite* sp=(Sprite*)pla->getChildByTag(tag_p1);
if(i->boundingBox().intersectsRect(sp->boundingBox())){…}
//sp是pla内部的子精灵,是pla的孩子节点
============================
是sp没取到,还是这样跨级取不合理,还是boundingbox这个函数实效了?
通过CCLOG sp的精灵坐标,pla的坐标以及,sp的contentSize,可以发现一切没有问题。
问题出在intersectsRect。
操作sp的移动,直接在layer中sp->setposition,但是,我们这不是一个精灵,而是一组,当然你也可以一个个setposition,那么写精灵组合的意义就没有了;所以,我们在Layer中操作pla的移动,所有的pla中的child都会跟着移动。
这里将引进一个概念,模型坐标,当pla的坐标变化了,精灵的位置也变了,但是sp的坐标一直没变,而intersecrsRect用的判断坐标恰恰是你的sp坐标,一个相对你精灵组的模型坐标,所以,无论你如何改变你的pla坐标,你的sp坐标没变,虽然你的位置变了,但是碰撞点的判断的位置一直没变(568,160),一直在根据你的sp在player中setposition的那个点在判断。
问题找到,如何解决。
这里总结了4套方案:
方案一,将精灵通过createwithfile的方式创建,再在这个精灵内添加一些装饰精灵,一主多辅的方案,此方案较为简单,也不容易出错,boundingbox也是根据你的file的精灵来的,位置上精灵和模型统一,底层的碰撞函数可用,不存在模型坐标问题。
方案二,创建精灵空容器player,里面有个精灵,sp1,将碰撞写在自己的成员函数里,这样的话,碰撞的对象就需要转换成你的player模型坐标,更加容易出错,不推荐。
方案三,将你的精灵容器位置设置为(0,0),移动时候将player 的所有自精灵同时移动,也可以达到效果,但这是个糟糕的做法,第一,这么做,还不如layer,完全没有用到精灵的特性,第二,精灵容器child越多,代码量越多,当然循环也可以搞定,但是内存优化方面却不能及;
方案四:就是上面所说的,player就是空的,里面有很多精灵,运动通过操作player完成,但是碰撞有问题,其实该方案比较符合精灵容器的观点,解决碰撞问题的方案,就是重写碰撞函数。
如下:sp坐标+pla坐标+图片半径,(当然,也可以用Poin p=pla->converToWorldSpace(sp->getposition),p代替sp坐标+pla坐标)
if (i->getPositionY()<sp->getPositionY()+pla->getPositionY()+sp->getContentSize().height/2
&&i->getPositionY()>sp->getPositionY()+pla->getPositionY()-sp->getContentSize().height/2
&&i->getPositionX()<sp->getPositionX()+pla->getPositionX()+sp->getContentSize().width/2
&&i->getPositionX()>sp->getPositionX()+pla->getPositionX()-sp->getContentSize().width/2)
或者改写底层的intersecrsRect函数
bool GameLayer::myintersecrsRect(Sprite* sp1,Sprite* sp2){
Rect Rect1=sp1->boundingBox();
Rect Rect2=sp2->boundingBox();
auto position1=sp1->getParent()->getPosition();
auto position2=sp2->getParent()->getPosition();
return !(Rect1.getMaxX()+position1.x<Rect2.getMinX()+position2.x ||
Rect2.getMaxX()+position2.x<Rect1.getMinX()+position1.x ||
Rect1.getMaxY()+position1.y<Rect2.getMinY()+position2.y ||
Rect2.getMaxY()+position2.y<Rect1.getMinY()+position1.y);
}