cocos2d-x 游戏创作过程(三) .2
- 炮弹问题
- 敌人问题
- 炮弹与敌人回收问题
- 梯子问题
- 数据问题
炮弹问题
这个很难,但是解决了,至少花费了一个月,甚至翻阅的三角函数,来寻找答案。 最终完成了。炮弹的角度问题,和炮弹的发射点问题。 这两样非常重要。
至少对我来说太珍贵了,这几行代码。上一章说到了,骨骼动画 随意控制骨骼,可以控制骨骼的角度,自由的变换。而手枪发射的时候是根据你的胳膊的变化来自由调整角度。 那么问题就来了,如果计算出发射点,是很重要的。 而发射的角度,如果转化成真实发射的角度。 真实发射的角度是 横坐标,和纵坐标。(x,y)之间的比例。 也就是角度转弧度的问题。
其实他们的关系是。 角度 * ((2* pai)/360),就是弧度了。
而枪口的位置也非常重要,虽然我想了很久计算枪口的位置,但是我觉得还不如在骨骼上加一个隐藏的骨骼。当旋转胳膊的时候那条隐藏骨骼也在动。 这样就少了很多麻烦。
//代码的关系是
void BaseFSM::changeToAttack(){
b2Body *body=role->getb2Body();
if(role->state!=Role_ATTAK1){
role->state=Role_ATTAK1;
}
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
b2Body *body1 = world->CreateBody(&bodyDef);
//
spine::SkeletonAnimation *f = role->getSkeletonAnimation();
spBone *bone1 =f->findBone("rear-upper-arm");
spBone *bone3 =f->findBone("rear-upper-arm");
float rotation=bone1->rotation;
//转换为弧度
float radian = rotation*(2*3.1415926)/360;
float rotation_x = 20;
//转换为纵坐标,横坐标 * tan(弧度)
float rotation_y = 20*tan(radian);
float vo= 20;
float rotation1=bone3->rotation;
float radian1 = rotation1*(2*3.1415926)/360;
float rotation_x1 = vo*-1;
//转换为纵坐标,横坐标 * tan(弧度)
float rotation_y1 = vo*tan(radian1);
g->clear();
//隐藏骨骼
spBone *bone2 =f->findBone("Bullet");
spBone *bone4 =f->findBone("Bullet");
if(bone2){
log("log.....................%f",bone2->worldX/PTM_RATIO);
log("log.....................%f",bone2->worldY/PTM_RATIO);
g->clear();
}else{
log("log .........................false");
}
//子弹
//这个是右边结构。
if(role->getSkeletonAnimation()->getSkeleton()->flipX==0){
g->clear();
b2Vec2 b2vec2 = b2Vec2((body->GetPosition().x+bone2->worldX/PTM_RATIO),body->GetPosition().y+bone2->worldY/PTM_RATIO);
CCLOG("%f.............................xxxxxxxxxxxxxxxxxxx",bone1->rotation);
// g->drawDot(Vec2((b2vec2.x) *32,(b2vec2.y) *32),5,Color4F(1, 0, 0, 1));
initBullet(body,b2vec2,b2Vec2(rotation_x,rotation_y));
// Bullets(b2vec2,b2Vec2(rotation_x,rotation_y));
}
//对称的左边的结构
else if(role->getSkeletonAnimation()->getSkeleton()->flipX==-1){
//. 这一点很重要,子弹的位置是 骨骼设置好的。 一个隐藏没有图片的骨骼当手臂移动的时候骨骼也会移动,所以这个位置也是枪口位置
b2Vec2 b2vec1 = b2Vec2((body->GetPosition().x+bone4->worldX/PTM_RATIO),body->GetPosition().y
+bone4->worldY/PTM_RATIO);
CCLOG("%f.............................xxxxxxxxxxxxxxxxxxx",bone1->rotation);
// g->drawDot(Vec2((b2vec1.x) *32,(b2vec1.y) *32),5,Color4F(1, 0, 0, 1));
//. 这一点很重要,子弹的位置是 骨骼设置好的。 一个隐藏没有图片的骨骼当手臂移动的时候骨骼也会移动,所以这个位置也是枪口位置
initBullet(body,b2vec1,b2Vec2(rotation_x1,rotation_y1));
}
}
然后是设置子弹模块,上边已经保证了子弹的出弹位置,和出弹方向。 然后需要把子弹的刚体重力取消。 否则就不能以直线打出去了。会形成弧线。落地。 炮弹倒是可以,但是子弹要保证直线。
void BaseFSM::initBullet(b2Body *body,b2Vec2 b2vec,b2Vec2 b2vec1){
// 设置子弹类型,为了将来炮弹做准备,可以修改子弹的重量和图片,还有大小。有足够的改造空间
Sprite *sprite =Sprite::create("zidan.png");
sprite->setName("zidan");
sprite->setTag(20);
sprite->setCameraMask((unsigned short) CameraFlag::USER1);
this->context->addChild(sprite);
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.bullet=true;
b2Body *body1 = world->CreateBody(&bodyDef);
body1->SetTransform(b2vec,0.0f);
body1->SetBullet(true);
body1->SetUserData(sprite);
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(PTM_L*10,PTM_L*6);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
body1->CreateFixture(&fixtureDef);
body1->SetGravityScale(0);
//-------------------------------------------------------------
CCLOG("x=%f.........y=%f.................xxxxxxxzzzzzzzzzyyyhhhhhheeeeexxxxxxxxxxxx",b2vec1.x,b2vec1.y);
body1->SetLinearVelocity(b2vec1);
// body1->SetAwake(true);
}
然后就不是问题了。很基础的问题了。
敌人问题 ,炮弹与敌人回收的问题。
敌人的问题,应该不是很难的问题,基础的ai 和 射线检测上一章就有,现在重要的问题是如何回收敌人。由于碰撞检测类里边无法操作 word的问题。网络上大部分都一笔带过了,所以我想把我的代码摆一下。
思路是在碰撞监听b2ContactListener 里设置设置两个 vector 集合,把需要回收的精灵和刚体全部放在里边
if(spriteA->getTag()==39 ){
if(spriteB->getName()=="zidan"){
log("EndContact 方块消除");
b2body.push_back(bodyA);
sprite.push_back(spriteA);
}
}
if(spriteA->getName()=="zidan" ){
if(spriteB->getTag()==39){
log("EndContact 方块消除");
b2body.push_back(bodyB);
sprite.push_back(spriteB);
}
}
主界面中 update 下面进行删除, 删除的时候要注意一点,把Vector里边的内存也清空了,Vector可以自动扩容,但是不能自动缩小。就算删除了里边的元素也不能缩小。
void GameLayer::update(float dt){
//b2body是我定义的Vector别看错了。
if(!contactListener->b2body.empty()){
for(int i=0;i<contactListener->b2body.size();i++){
b2Body *b=contactListener->b2body[i];
world->DestroyBody(b);
}
}
//sprite是我定义的Vector别看错了。
if(!contactListener->sprite.empty()){
for(int i=0;i<contactListener->sprite.size();i++){
Sprite *sprite=contactListener->sprite[i];
this->removeChild(sprite,true);
}
}
}
所以需要
//清理干净
contactListener->b2body.clear();
contactListener->sprite.clear();
//制造一个什么都没有Vector 附在Vecotor 里边,以删除旧的Vector空间。
vector<b2Body *> f;
vector<Sprite *> s;
contactListener->b2body.swap(f);
contactListener->sprite.swap(s);
梯子问题
刚开始还傻傻的以为很简单,用碰撞检测去做,但是梯子问题,也是地图问题,所以射线是必选的。
思路是在主角中发射射线,如果检测到了梯子,那么上键的作用,就变成向梯子上楼梯,开启线程。 然后整个body进入0重力状态,按下向上,人就会位移向上走,在梯子附近人会持续检测,如果射线一直检测到,那么人就会0重力在梯子上,如果按左,或者按右,达到射线检测不到的位置, 开启的向上爬的线程就会关闭,然后人会落下来。
//检测梯子是否被检测到。 否则取消线程人物直接掉下来。
if(this->ladder){
CCLOG("攀爬true1111111111111");
}else{
CCLOG("攀爬false");
this->unschedule(schedule_selector(GameLayer::asyncUpdate2));
}
//检测梯子
if(sprite->getTag()==29){
//发射10条射线,保证全方位检测。
for(int i=0;i<10;i++){
auto a=i*20.0;
double x=cos(a* b2_pi/180);
double y=sin(a* b2_pi/180);
Vec2 v(x,y);
RaysCastCallback callback;
world->RayCast(&callback,b2Vec2(b->GetPosition().x,b->GetPosition().y)
,b2Vec2(b->GetPosition().x+v.x*2
, b->GetPosition().y+v.y*2));
Vec2 pos(b->GetPosition().x*PTM_RATIO,b->GetPosition().y*PTM_RATIO);
if(callback.fixture!=nullptr){
b2Body *b2= callback.fixture->GetBody();
auto sprite=(Sprite *)b2->GetUserData();
if(sprite!=nullptr){
//梯子的标记是17
if(sprite->getTag()==17){
g->drawLine(pos,Vec2(callback.point.x * PT_RATIO
,callback.point.y *PTM_RATIO)
,Color4F(134, 54, 78, 110));
//如果检测到跳出循环,甚至关闭线程也没问题。
this->ladder=true;
break;
}
}
}else{
// CCLOG("不确定------------------------------!");
this->ladder=false;
}
}
}
至于梯子与顶层的交接点还没写好,也没画好,估计是射线检测到了那个交界点然后直接跳到上层。
数据问题
有点困了,其实数据问题,主要是从 .plist中取出数据,比如人物的初始数据甚至是储存 人物获得的数据。然后实体化这些数据。
把.plist中数据取出来放进Map 里边,然后Map中取出数据,再放进实体里,然后,就可以用这些数据随控制什么了,属性也好,武器,也好。速度也好。名称也好。
具体的代码是
auto roleMap = FileUtils::getInstance()->getValueMapFromFile("propertyRole.plist");
//创建对象
PropertyData *propertyData = PropertyData::create();
propertyData->setID(roleMap["ID"].asInt());
propertyData->setHP(roleMap["HP"].asInt());
//这是血条。 血条的控制完全取决于数据的大小。
float hp = propertyData->getHP() * 0.01;
然后就没有了。 几个月的成果就这么少。 然后就是设计剧情,人像,音乐什么的。