cocos2dx3.2开发 RPG《Flighting》(八)开始战斗

一、前言
前面我们已经基本学会了怎么让角色走起路来而且也做了很多的优化。下面是时候开始战斗了。不过还是不要急,我们先暂时不引入英雄和怪物这两个类(毕竟只要是Role就可以打架,哪怕是英雄打英雄)

二、正文
先大致说一下整个思路:
1.选择角色并且可以拉出一条导航线出来(之前已经做了)
2.判断导航线的终点,如果终点刚好是在一个Role的身上,那么…嘻嘻就打他了。
3.角色移动到目标身边
4.角色播放攻击动画
5.被打的人播放被打的动画
1.2两步之前已经基本做了,这里只需要做一点点修改。

void FlightLayer::onTouchEnded(Touch* touch,Event* event){  
    if(m_cur_control){  
        m_cur_control->onTouchEnded(touch,event);  
        Point tp = Director::getInstance()->convertToGL(touch->getLocationInView());  
        if(!m_cur_control->getBoundingBox().containsPoint(tp)){  
            m_cur_control->setDesPoint(m_cur_control->getEndPoint());  
        }  
        Role_Ptr temp = m_cur_control->getAttackTarget();//原来的攻击目标  
  
        for(auto it = m_rolesArray.begin();it!=m_rolesArray.end();++it){  
            if((**it)->getBoundingBox().containsPoint(tp)){       
                //攻击原来的人  
                if(**it == m_cur_control){      //如果想要自己攻击自己,变为攻击原本正在攻击的那个人  
                    m_cur_control->setAttackTarget(temp);  
                    break;  
                }  
                
             m_cur_control->setAttackTarget((*it));  
                break;  
}else{m_cur_control->setAttackTarget(nullptr);}}}}

```

并且这里再一次利用m_rolesArray,找出要攻击谁。并且赋值给m_attackTarget。不啰嗦了,大家应该可以看懂。
注意,这里的判断操作是在FlightLayer中执行而不是在Role中,毕竟FlightLayer比Role高一个维度,一个Role对象不能管其他Role对象,都是交给FlightLayer处理的。

3.角色移动到目标身边
对了现在Role类里面又多了一个m_attackTargetPtr的Role**二级指针了(Role_Ptr),和一个m_attackTarget的Role* ,关于二级指针蒙了的话可以回去看前面的。
protected:  
    /*Role的成员变量*/  
    Role* m_attackTarget;  
    Role** m_attackTargetPtr;//攻击目标  

```

void Role::setAttackTarget(Role** targetPtr){  
    m_attackTargetPtr = targetPtr;  
    if(targetPtr){  
        m_attackTarget = *m_attackTargetPtr;  
    }else{  
        m_attackTarget = nullptr;  
    }  
}  

```

刚刚FlightLayer已经为我们设置了攻击目标(m_attackTarget),接下来只需要在Role的update函数里面动一下手脚就可以了。
void Role::update_attackTarget(){  
    if(m_attackTargetPtr){  
        m_attackTarget = *m_attackTargetPtr;          
    }else{  
        m_attackTarget = nullptr;  
    }  
}  

```

update_attackTarget保证让我们的攻击目标指针的正确性

下面是带攻击目标的update_pos函数,在讲简单移动控制的时候我们已经给出了一个简化版的。
void Role::update_pos(){  
  
    if(m_attackTarget){  
        m_desPoint.y = m_attackTarget->getPositionY();  
        m_desPoint.x = m_attackTarget->getPositionX();  
    }  
    if(m_attackTarget){  
        if(m_attackTarget->getPositionX() > getPositionX()){  
            m_armFaceTo = false;  
        }else{  
            m_armFaceTo = true;  
        }  
        if(m_armFaceTo){  
            m_arm->setVisible(false);  
            m_arm->setPosition(m_arm_offsetX,m_arm_offsetY);  
            m_arm->setScaleX(1.0f);  
            m_arm->setVisible(true);  
        }else{  
            m_arm->setVisible(false);  
            m_arm->setScaleX(-1.0f);  
            m_arm->setPosition(-m_arm_offsetX,m_arm_offsetY);  
            m_arm->setVisible(true);  
        }  
        if(!Rect(getPositionX()-m_attackDistance,getPositionY()-m_attackDistance,2*m_attackDistance,2*m_attackDistance).containsPoint(m_desPoint)){//就是这个if  
            this->move();  
            float distance = ccpDistance(getPosition(),m_desPoint);  
            float t = distance / m_speed;  
            float speed_x = (m_desPoint.x - getPositionX()) / t;  
            float speed_y = (m_desPoint.y - getPositionY()) / t;  
  
            setPositionX(getPositionX() + speed_x);  
            setPositionY(getPositionY() + speed_y);  
        }else{  
            this->attack();  
            //this->setDesPoint(getPosition());  
        }  
    }else{  
        if(m_desPoint.x > this->getPosition().x && m_armFaceTo == true){    
            m_armFaceTo = false;  
        }  
        if(m_desPoint.x < this->getPosition().x && m_armFaceTo == false){  
            m_armFaceTo = true;  
        }  
        if(m_armFaceTo){  
            m_arm->setVisible(false);  
            m_arm->setPosition(m_arm_offsetX,m_arm_offsetY);  
            m_arm->setScaleX(1.0f);  
            m_arm->setVisible(true);  
        }else{  
            m_arm->setVisible(false);  
            m_arm->setScaleX(-1.0f);  
            m_arm->setPosition(-m_arm_offsetX,m_arm_offsetY);  
            m_arm->setVisible(true);  
        }  
        if(!Rect(m_desPoint.x-m_speed/2,m_desPoint.y-m_speed/2,m_speed,m_speed).containsPoint(getPosition())){  
            this->move();  
            float distance = ccpDistance(getPosition(),m_desPoint);  
            float t = distance / m_speed;  
            float speed_x = (m_desPoint.x - getPositionX()) / t;  
            float speed_y = (m_desPoint.y - getPositionY()) / t;  
            setPositionX(getPositionX() + speed_x);  
            setPositionY(getPositionY() + speed_y);  
        }else{  
            this->stand();  
        }  
    }  
      
}  

```

这里的逻辑没有理得太清楚所以很多重复代码,请见谅。
但是我们对比之前没有攻击目标的update_pos,可以知道只不过加了一个if判断现在有没有攻击目标,没有的话,还是按照简单移动那一套来。
重点看有攻击目标,看代码就知道,如果有攻击目标的话,终点m_desPoint就设置为目标所在的坐标。还有那些转身的代码也很容易看懂。
我们重点看那个if的代码
if(!Rect(getPositionX()-m_attackDistance,getPositionY()-m_attackDistance,2*m_attackDistance,2*m_attackDistance).containsPoint(m_desPoint)){  
            this->move();  
            float distance = ccpDistance(getPosition(),m_desPoint);  
            float t = distance / m_speed;  
            float speed_x = (m_desPoint.x - getPositionX()) / t;  
            float speed_y = (m_desPoint.y - getPositionY()) / t;  
  
            setPositionX(getPositionX() + speed_x);  
            setPositionY(getPositionY() + speed_y);  
        }else{  
            this->attack();  
            //this->setDesPoint(getPosition());  
        }  

```

好长的判断语句,但是先让我解释几个变量就很容易理解了
m_attackDistance:攻击距离
2*m_attackDistance:因为我的攻击距离是一个正方形区域,所以正方形的变长等于两边的攻击距离
好了判断条件的基本意思就是,if(目标还没进入攻击范围)一直向目标移动
else 攻击!
4.角色播放攻击动画
void Role::attack(){  
    if(m_arm && en_stat!=ROLE_ATTACK){  
        en_stat = ROLE_ATTACK;  
        //攻击过程中速度为O  
        m_speed = 0;  
        m_arm->getAnimation()->setSpeedScale(m_atkSpeed);  
        m_arm->getAnimation()->play("attack");  
        this->setDesPoint(getPosition());  
    }  
}  

```

为什么攻击的时候速度为0?很简单,你总不能让角色一边平移一边播放攻击动画吧。
而且在攻击的时候(攻击动画没播放完),攻击目标移出了攻击范围,update函数知道之后就调用move函数,导致这次攻击不成功。我不喜欢这样子所以我的update函数还会加上这一个判断
void Role::update_pos(){  
    if(en_stat == ROLE_ATTACK){  
        //如果是攻击状态 位置属性不需改变  
        return;  
    }//后面的省略了  
}  

```

这样子,只要达到了攻击条件了,最起码播放一次攻击动作。
但是,加了这个判断之后,role攻击完之后就一直站在那里不动了?因为Role现在速度是0啊。那什么时候变回原来的速度?
很简单,攻击完呗。骨骼动画的Animation给我们提供了一套很好的回调机制,我们可以绑定当动画播放前要做什么,动画播放完要做什么,甚至动画播放到哪一帧我们要做什么
首先在Role::init添加一下初始化绑定回调函数的语句
m_arm->getAnimation()->setMovementEventCallFunc(CC_CALLBACK_3(Role::onBondAnimationFinish,this));  

```

我们看看onBondAnimationFinish是怎样的
void Role::onBondAnimationFinish(Armature* arm,MovementEventType type,const std::string& name){  
    if(type == COMPLETE){  
        if(name == "attack"){  
            CCLOG("ATTACK COMPLETE");  
            //恢复速度  
            m_speed = m_initSpeed;  
            this->stand();  
        }  
    }  
  
}  

```

看代码应该就知道是怎么一回事吧。type常用的有COMPLETE 和START 分别代表播放完和准备开始播放

好了,如无意外,现在,我们的角色可以攻击对方了。被攻击的人播放受伤动画这个我打算迟点结合子弹和子弹效果那里将。这里先暂时跳过吧。

我的csdn地址:http://blog.csdn.net/hezijian22
邮箱地址:578690286@qq.com
如有问题或指教,欢迎与我交流,谢谢。