新年如何写一个小游戏代码。
花了2周,研究一下重新研究Cocos2d 的游戏代码,里边没有刚体,也没有物理引擎。 只是用Sprite精灵和, 数组写一个小程序。
刚刚上传了。 最大的愿望是今年能赚到钱。 我觉得我又有进步了。不知道可以用它谋生吗?
首先我凭空写一个控制按钮。
控制按钮分为上下左右,左上, 右上,左下、右下。
这是个复合的 按钮,分为,中心点 和8个点。 跟大部分的游戏的控制杆差不多。 计算 圆心圆球,移动以后和中心点的弧度,去判断上下左右。
//获得弧度
float HRocker::getRad(Vec2 pos1, Vec2 pos2){
float px1 =pos1.x;
float py1 =pos1.y;
float px2 =pos2.x;
float py2 =pos2.y;
float x=px2-px1;
float y=py2-py1;
float xie=sqrt(pow(x,2)+pow(y,2));
float cosAngle=x/xie;
float rad=acos(cosAngle);
if(py2<py1){
rad=-rad;
}
return rad;
}
如果你想变成角度的化。只要
float angle_1=angle *180.0/PI;
也可以通过角度去判断。
枚举或者数字,控制杆的值,然后在控制主角运动的时候,根据控制杆的值,去左判断。
typedef enum {
rocker_stay=0,
rocker_left=1,
rocker_right=2,
rocker_up=3,
rocker_down=4,
rocker_LeftUp=5,
rocker_RightUp=6,
rocker_LeftDown=7,
rocker_RightDown=8
}tagDirection;
\\把rocker的状态传进去。
baseFSM->switchMoveState(rocker->rockerDirection);
剩下的就是上下左右。 自己加自己。 当时我想用Camera 去制作地图的移动。 但是当我用了Camera移动的同时。游戏杆不会跟着移动,而且就算移动,也会浪费内存。没有意义。
最后只要让地图跟着移动。
void BaseFSM::changeUp(){
mapLayer->setPosition(Vec2(mapLayer->getPositionX(),mapLayer->getPositionY()-SPEED));
};
void BaseFSM::changeDown(){
mapLayer->setPosition(Vec2(mapLayer->getPositionX(),mapLayer->getPositionY()+SPEED));
};
上下是地图移动,左右是主角移动。剩下的混合移动。
其实有的时候可以让主角更加灵活的移动。 通过精确计算,中心小球的弧度,算出上下左右的移动比例。现在仅仅是纯粹的左右上下移动。
核心代码
有些时候用std::Vector 总是很麻烦,以前也是这么使用的。 问题是 首先查找一个特定的元素,很麻烦, 然后删除它。删除特定的元素。首先遍历他们 ,然后根据指针查找到类, 然后什么的。我遍历了。 删除了特定的 元素。其实很麻烦。
vector<int>::iterator iter = vec.begin()+5;
for(;it != vec.end();)
{
if(*it == 5)
//删除指定元素,返回指向删除元素的下一个元素的位置的迭代器
it = vec.erase(it);
else
//迭代器指向下一个元素位置
++it;
}
其实 Cocos2d 里边有自带的Vector,
Vector < Sprite *>
这个Vector,只要vector.eraseObject(sprite1);
就可以做到上述的事情。 我最开始找了eraseObject 在std::Vector 里,后来才发现找错了。
这个Vector的作用充分发挥中。
子弹的发射
首先对于子弹的发射 很重要。 我设置了一个发射按钮 。 这个很简单。 然后,我在主角中间的位置创建了一个子弹。
最开始我创建子弹的时候,仅仅是用 MoveTo 到固定的坐标。 但是我发现回收的时候很不好弄。 当碰撞到了敌人,且超过预定界面以后 子弹精灵并不能,随时随地的计算 和删除。
所有我在创建子弹的时候,把子弹放进这个动态数组中, 然后持续遍历这个数组,当数组满足了条件。就对他进行调整。
首先创建的是 子弹的 发射数组
for(int i=0;i<bean_vector.size();i++){
if(bean_vector.at(i)!=nullptr){
Sprite *sprite = bean_vector.at(i);
sprite->setPosition(sprite->getPositionX()+f_x,sprite->getPositionY()+f_y);
}
}
其实可以调整发射的速度。 但是默认固定的,也可以调整发射。速度和方向。
当子弹碰撞到了敌人以后, 删除敌人,且删除子弹。
Sprite *sprite1=mapLayer->_data.at(j);
//取出敌人的大小
float spri= sprite1->getContentSize().width;
float spriH=sprite1->getContentSize().height;
//取出子弹的大小
float spriteBulletW=sprite->getContentSize().width/2;
float spriteBulletH=sprite->getContentSize().height/2;
//----------------------------------------------------------------------------------------------------------
//---- 这里要注意是 敌人的位置需要减去 主角移动的方向。这点遇到过坑
if((abs(sprite->getPositionX()-sprite1->getPositionX()+spriteBulletW)<spri/2)
&& (abs(sprite->getPositionY()-sprite1->getPositionY()-mapLayer->getPositionY()+spriteBulletH)<spriH/2)){
}
---//当然最重要的是把元素从集合中删除
bean_vector.eraseObject(sprite);
这并没有什么难点。 如果面试的时候问难点,我也会忘记。
然后是敌人的攻击与移动了。
//创建瓦片 获取位置信息 和属性。
auto tileMap = TMXTiledMap::create("untitled.tmx");
TMXObjectGroup *group = tileMap->getObjectGroup("enemy");
ValueVector group1 = group->getObjects();
for(int i=0;i<group1.size();i++){
ValueMap spawnPoint = group1[i].asValueMap();
std::string name = spawnPoint["enemy"].asString();
float pro = spawnPoint["x"].asFloat();
float pro_y=spawnPoint["y"].asFloat();
EnemySprite *sprite = EnemySprite::create();
sprite->setProperty(spawnPoint["gift"].asString());
sprite->setMapLayer(this);
sprite->setGameRole(gameRole);
sprite->setAnchorPoint(Vec2(0.5, 0.5));
sprite->setPosition(pro, pro_y);
// CCLOG("精灵。。。。。。。。。。。%f",pro);
// CCLOG("精灵。。。。。。。。。。。%f",pro_y);
addChild(sprite);
_data.pushBack(sprite);
从瓦片中取出敌人。 和各种需要的元素。放入一数组中 再遍历敌人。 首先敌人的攻击是需要延迟的。 不能像冲锋枪一样嘟嘟嘟。 在 更新状态的时候 ,每20毫秒 ,启动一次。虽然还是很快,但是可以调整。
if(a>20){
moveEnemy();
a=0;
}
a++;
然后进入 最重要的两个核心代码中的其中之一。如何让子弹不停的追踪主角。 这需要测算一下。敌人和主角之间的 sin 和cos值。 计算他们的初始化位置。 去攻击主角。
我在子弹的类里 把主角引用进去。
//首先计算弧度
void BulletLayer::move_init(){
float angle = getRed(gameRole->getPosition(),this->getPosition()) *180.0/PI;
float angle_1 = 2* PI/360 * (angle+180);
x_x1= 30 *cos(angle_1);
y_y1= 30 * sin(angle_1);
this->schedule(schedule_selector(BulletLayer::updateMove));
}
void BulletLayer::updateMove(float dt)
{
this->setPosition(this->getPositionX()+x_x1,this->getPositionY()+y_y1);
}
核心代码2
如何仅仅是简单的子弹攻击敌人,然后敌人消失,这个游戏的没有任何可玩性。所以 射击游戏最 主要一个卖点就是 礼物包。武器包。
我把属性写在了敌人里边。
\\如果检测到了gift属性
EnemySprite *sprite = EnemySprite::create();
sprite->setProperty(spawnPoint["gift"].asString());
if(spriteEnemy->str=="1"){
Sprite *sprite = Sprite::create("gift.png");
sprite->setTag(21);
sprite->setPosition(spriteEnemy->getPositionX()
,spriteEnemy->getPositionY()+mapLayer->getPositionY());
addChild(sprite);
//动力源
gift_vector.pushBack(sprite);
}
当特定敌人受到了碰撞,gift精灵 放进 gift_vector动态数组Vector。
if(sprite1->getTag()==21){
float game_Gouyin=gameRole->gouyin;
gameRole->gouyin=game_Gouyin+1;
gameRole->setControlLayer(this);
CCLOG("gouyin %f",gameRole->gouyin);
this->removeChild(sprite1);
gift_vector.eraseObject(sprite1);
}
当获得Gift以后。如果被 主角碰撞,那么 主角当 一个特定当变化就会打开。
主角周围,有两颗球围绕着变化。这两个球,可以充当子弹去攻击敌人。礼物的增多,这个小球就会增多。
圆环的公式是
x2 y2是半径的坐标,x0 y0是圆心坐标。
float _x=(_x2-x0)*cos(angle_2) - (_y2-y0)*sin(angle) +x0;
float _y=(_x2-x0)*sin(angle_2) + (_y2-y0)*cos(angle_2) +y0;
只要不断的更新弧度,就可以让小球不断的围着圆心修改位置。
本来核心代码有3个的。
失败的位置。
首先我想让敌人的移动就像有刚体 一样。 然后追击这主角。 在没有刚体的情况下,所有的精灵都会以最短的距离攻击主角。然后就会形成一块区域叠加这好几层精灵。 但是我需要让敌人之间有碰撞的距离,当敌人相互碰撞的时候不是重合。而是相互挤在一起。由于敌人的属性是相同的,只能识别与自己不相同的主角。发生了很多抖动,就像烂的物理引擎一样。所有放弃了。或许是懒得写吧。 刚体的具体原理我还没有清楚,所有以后再说吧。
游戏结束
这个游戏结束的标准是 打败所有敌人。 或者被敌人打败。
#判断被敌人打死。 我写了一个血条类,当被子弹攻击到,血条下降%1的血。
所有当攻击完所有敌人。或者被打败就会发出对话框。 内容是success 和Fail 。 然后弹出按钮表示是否重启,或者退出。
void WinSuccessLayer::Fail(){
Director::sharedDirector()->end();
exit(0);
}
总结:
- 射击类,游戏。
- 首先,创建主角。
- 利用动态数组去管理 所有的运动精灵。
- 弧度和角度的换算。 float angle_1=angle *180.0/PI;
- 圆环的计算。 半径和弧度要找对。不需要更新半径和圆心。 而是更新角度。!!!!
- 增加一定要增加攻击时候的音乐。 auto audio = SimpleAudioEngine::getInstance();
- 在管理每一个精灵的动态选择中。要在精灵类的内部进行管理。而不是在数组上。尽量少迭代数组。
- 地图的移动尽量移动地图类。而不是用Gamera。 管理好层级管关系。按钮不能被盖住。