让子弹飞是我非常喜欢的一款游戏。今天的目标就是利用cocos2dx 3.0 和box2d 打造一款这样的类型游戏的Demo版。本来cocos2dx 3.0 已经封装了physicals模块,可是我在使用的过程中遇到了一些问题,比方子弹速度过快时候会出屏等,所以就认为还是直接封装box2d API来完毕这款Demo。

我主要封装了两个类,一个叫Box2dHandler, 这个类继承自Node和b2ContactListener, 是用来和box2d打交道的。能够用来创建方形,圆形的静态或者动态刚体。创建物理世界,以及处理碰撞的检測重任都交给这个类了。另外一个类叫B2Sprite, 也继承自Node,本来是想继承自Sprite的,可是在实现过程中发现有问题。就改成继承自Node。它的功能是用来粘合cocos2dx和Box2D, 起到一个中间的桥梁作用。这里要感谢一下<<cocos2d-x高级开发教程>>一书,这两个类基本从它移植而来,可是原书使用的是2.0版本号。所以还是要做一些改动。

好了,闲话到此为止。上代码。

首先是Box2dHandler。

#ifndef __BOX2DHANDLER_H__
#define __BOX2DHANDLER_H__ #include "cocos2d.h"
#include "Box2D.h"
#include "Box2dHandlerDelegate.h"
#include "B2Sprite.h" USING_NS_CC; enum shape
{
box=1,
circle=2,
}; class Box2dHandler : public cocos2d::Node, public b2ContactListener
{ private:
b2World *m_world;
typedef std::pair<b2Fixture*, b2Fixture*> MyContact;
std::set<MyContact> m_contacts;
public:
bool init();
bool initBox2D();
void addBodyForSprite(B2Sprite* sprite, double density = 1.0, double friction = 0.9, double restituion = 0.1, shape type=box);
void addFixtureForSprite(B2Sprite* sprite, double density = 1.0, double friction = 0.9, double restituion = 0.1, shape type=box);
void addStaticBodyForSprite(B2Sprite* sprite, double density = 0.0);
void dealCollision(); public:
virtual void BeginContact(b2Contact * contact);
virtual void EndContact(b2Contact * contact); static Box2dHandler * handler();
//void draw();
void update(float dt);
CC_SYNTHESIZE(Box2dHandlerDelegate*, m_delegate, Delegate); }; #endif // __BOX2DHANDLER_H__

熟悉box2d的话,非常easy看清楚这个类就是封装了主要的Box2D操作。包含创建刚体,以及监听碰撞。

接下来是实现代码。

#include "Box2dHandler.h"
#include "HelloWorldScene.h"
#define PTM_RATIO 32 Box2dHandler * Box2dHandler::handler()
{
static Box2dHandler * handler = NULL;
if(handler == NULL)
{
handler = new Box2dHandler();
handler->init();
return handler;
}
else
{
return handler;
}
} bool Box2dHandler::init()
{
this->initBox2D();
this->scheduleUpdate();
return true;
} bool Box2dHandler::initBox2D()
{
Size s = Director::getInstance()->getWinSize();
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f); m_world = new b2World(gravity);
m_world->SetAllowSleeping(true);
m_world->SetContinuousPhysics(true);
m_world->SetContactListener(this); b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); b2Body* groundBody = m_world->CreateBody(&groundBodyDef); b2EdgeShape groundBox; //Bottom
//groundBox.Set(b2Vec2(0, 0), b2Vec2(s.width/PTM_RATIO, 0));
//groundBody->CreateFixture(&groundBox, 0); //Top
groundBox.Set(b2Vec2(0, s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO, s.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox, 0); //Left
groundBox.Set(b2Vec2(0, s.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox, 0); //Right
groundBox.Set(b2Vec2(s.width/PTM_RATIO, s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO, 0));
groundBody->CreateFixture(&groundBox, 0); return true;
} void Box2dHandler::addFixtureForSprite(B2Sprite* sprite, double density, double friction, double restitution, shape type)
{
b2PolygonShape spriteShape;
Size size = sprite->getB2Sprite()->getContentSize() * sprite->getScale();
spriteShape.SetAsBox(size.width / PTM_RATIO / 2, size.height / PTM_RATIO / 2); b2CircleShape circle;
circle.m_radius = (sprite->getB2Sprite()->getContentSize().width * sprite->getScale())/2/PTM_RATIO; b2FixtureDef spriteShapeDef;
if(type == box)
spriteShapeDef.shape = &spriteShape;
else
spriteShapeDef.shape = &circle;
spriteShapeDef.density = density;
spriteShapeDef.restitution = restitution;
spriteShapeDef.friction = friction; b2Body * spriteBody = sprite->getB2Body();
spriteBody->CreateFixture(&spriteShapeDef);
} void Box2dHandler::addBodyForSprite(B2Sprite* sprite, double density, double friction, double restitution, shape type)
{
b2BodyDef spriteBodyDef;
spriteBodyDef.type = b2_dynamicBody;
spriteBodyDef.position.Set(sprite->getPosition().x / PTM_RATIO, sprite->getPosition().y / PTM_RATIO);
spriteBodyDef.userData = sprite; b2Body* spriteBody = m_world->CreateBody(&spriteBodyDef);
sprite->setB2Body(spriteBody);
this->addFixtureForSprite(sprite, density, friction, restitution, type);
} void Box2dHandler::addStaticBodyForSprite(B2Sprite* sprite, double density)
{
b2BodyDef spriteBodyDef;
spriteBodyDef.type = b2_staticBody;
spriteBodyDef.position.Set(sprite->getPosition().x / PTM_RATIO, sprite->getPosition().y / PTM_RATIO);
//spriteBodyDef.userData = sprite; b2Body* spriteBody = m_world->CreateBody(&spriteBodyDef);
sprite->setB2Body(spriteBody);
this->addFixtureForSprite(sprite, density);
} void Box2dHandler::update(float dt)
{
m_world->Step(dt, 8, 8);
std::vector<b2Body*> toDestory;
for(b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if(b->GetUserData() != NULL)
{
B2Sprite* sprite = static_cast<B2Sprite*>(b->GetUserData());
b2Vec2 pos = b->GetPosition();
float rotation = b->GetAngle() / 0.01745329252;
sprite->setPosition(pos.x * PTM_RATIO, pos.y * PTM_RATIO);
sprite->setRotation(rotation);
if(b->GetPosition().y*PTM_RATIO<= -25)
{
toDestory.push_back(b);
}
}
} if(toDestory.size()>0)
{
for(int i = 0; i < toDestory.size(); i++)
{
B2Sprite* sp=static_cast<B2Sprite*>( toDestory.at(i)->GetUserData());
if(sp != NULL)
sp->removeFromParentAndCleanup(true);
m_world->DestroyBody(toDestory.at(i));
}
toDestory.clear();
} this->dealCollision();
} void Box2dHandler::BeginContact(b2Contact * contact)
{
CCLog("start");
B2Sprite* spa = static_cast<B2Sprite*>(contact->GetFixtureA()->GetBody()->GetUserData());
B2Sprite* spb = static_cast<B2Sprite*>(contact->GetFixtureB()->GetBody()->GetUserData()); if(spa != NULL && spb != NULL)
{
MyContact myContact(contact->GetFixtureA(), contact->GetFixtureB());
m_contacts.insert(myContact);
}
} void Box2dHandler::EndContact(b2Contact* contact)
{
CCLog("end");
MyContact myContact(contact->GetFixtureA(), contact->GetFixtureB());
m_contacts.erase(myContact);
} void Box2dHandler::dealCollision()
{
if(m_delegate != NULL && m_contacts.size()>0)
{
std::set<MyContact>::iterator it;
for(it = m_contacts.begin(); it != m_contacts.end(); ++it)
{
B2Sprite* bullet = static_cast<B2Sprite*>(it->first->GetBody()->GetUserData());
B2Sprite* actor = static_cast<B2Sprite*>(it->second->GetBody()->GetUserData());
if(bullet->getTag() == kTagBulletBase && (actor->getTag() == kTagRedEnemy || actor->getTag() == kTagBlueEnemy || actor->getTag() == kTagYellowEnemy))
m_delegate->CollisionEvent(bullet, actor);
else if((bullet->getTag() == kTagRedEnemy || bullet->getTag() == kTagBlueEnemy || bullet->getTag() == kTagYellowEnemy) && actor->getTag() == kTagBulletBase )
m_delegate->CollisionEvent(actor, bullet);
}
}
m_contacts.clear();
}

简单解释下:

Box2dHandler * Box2dHandler::handler() 

构造函数,内存管理交给cocos2dx

bool Box2dHandler::initBox2D()

设置好重力场,创建物理世界m_world, 以及定义好屏幕边界为可碰撞的静态刚体。

void Box2dHandler::addBodyForSprite(B2Sprite* sprite, double density, double friction, double restitution, shape type)

创建动态刚体,有方形和圆形两种选择。

void Box2dHandler::addFixtureForSprite(B2Sprite* sprite, double density, double friction, double restitution, shape type)

为刚体创建外观并设置刚体属性。

void Box2dHandler::update(float dt)

更新物理世界,并同步刚体位置到cocos2dx中精灵,当中的桥梁就是B2Sprite。

void Box2dHandler::BeginContact(b2Contact * contact)
void Box2dHandler::EndContact(b2Contact* contact)

物理碰撞检測的回调方法。

void Box2dHandler::dealCollision()

自己定义的碰撞处理方法。

就这么多。非常easy明了。接下来看看B2Sprite的代码

#include "cocos2d.h"
#include "Box2D.h"
USING_NS_CC; enum Enemy_Color
{
k_red = 0,
k_blue = 1,
k_yellow = 2,
}; class B2Sprite : public cocos2d::Node
{
public: static B2Sprite* create(CCTexture2D * texture);
static B2Sprite* create(const char* pngFile);
bool init(const char* pngFile);
bool init(CCTexture2D* texture);
CCActionInterval* createAnimation(const char* plist,int frames);
CCActionInterval* createZombileAnimation(const char* plist, int frames, Enemy_Color color); static B2Sprite* create(const char* plist, int frames, Enemy_Color color);
bool init(const char* plist, int frames, Enemy_Color color); CC_SYNTHESIZE_READONLY(Sprite*, m_sprite, B2Sprite);
CC_SYNTHESIZE(b2Body*, m_b2Body, B2Body); // 物理实际的“物体”
CC_SYNTHESIZE(bool, m_isDead, IsDead);
CC_SYNTHESIZE(bool, m_isAlive, IsAlive);
}; #endif

主要内容在这里

CC_SYNTHESIZE_READONLY(Sprite*, m_sprite, B2Sprite);
CC_SYNTHESIZE(b2Body*, m_b2Body, B2Body); // 物理实际的“物体”

我们能够看到B2Sprite一方面挂接了一个Sprite用于显示刚体,另外一方面有挂接了一个刚体对象m_b2Body, 所以它就起着一个桥梁的作用。

实现代码就不给出了。由于都是些细节性的方法,是用来写这个Demo的。大家全然能够依据自己须要来封装自己的B2Sprite版本号。

好了完毕了这两个类的封装。接下来就是完毕我们的Demo了。

我建立一个叫HelloWorldScene的Layer来承载这个游戏Demo。

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__ #include "cocos2d.h"
#include "Box2D.h"
#include "VisibleRect.h"
#include "cocos-ext.h"
#include "Box2dHandler.h"
using namespace cocos2d; enum {
kTagParentNode = 1,
kTagBulletParentNode=2,
kTagHandler = 3,
kTagFloor = 100,
kTagFloor2 = 101,
kTagRedEnemy = 102,
kTagBlueEnemy = 103,
kTagYellowEnemy = 104,
kTagBulletBase = 500,
}; class HelloWorld : public cocos2d::Layer, public Box2dHandlerDelegate
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
HelloWorld();
~HelloWorld(); // Touch process
bool onTouchBegan(Touch* touch, Event* pEvent);
void onTouchEnded(Touch* touch, Event* pEvent); virtual void CollisionEvent(B2Sprite*, B2Sprite*);
void HelloWorld::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity); }; #endif // __HELLOWORLD_SCENE_H__

我将创建刚体的任务都放在构造函数中,CollisionEvent是处理碰撞的回调方法。LauchBomb是发射子弹的方法。

接下来看实现。

#include "HelloWorldScene.h"

#define PTM_RATIO 32
USING_NS_CC; Scene* HelloWorld::createScene()
{
auto scene = Scene::create();
auto layer = new HelloWorld();
scene->addChild(layer);
layer->release();
return scene;
} HelloWorld::HelloWorld()
{
auto dispatcher = Director::getInstance()->getEventDispatcher();
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
//touchListener->onTouchMoved = CC_CALLBACK_2(MapLayer::onTouchMoved, this);
touchListener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
dispatcher->addEventListenerWithSceneGraphPriority(touchListener, this); Sprite* bg = Sprite::create("bg3.png");
addChild(bg, -1);
bg->setPosition(ccp( VisibleRect::center().x, VisibleRect::center().y)); Box2dHandler * handler = Box2dHandler::handler();
handler->setDelegate(this);
handler->setTag(kTagHandler);
this->addChild(handler); B2Sprite * floor = B2Sprite::create("floor.png");
floor->setTag(kTagFloor);
floor->setPosition(300, 350);
handler->addStaticBodyForSprite(floor);
addChild(floor); B2Sprite * floor2 = B2Sprite::create("floor2.png");
floor2->setTag(kTagFloor2);
floor2->setPosition(450, 250);
handler->addStaticBodyForSprite(floor2);
addChild(floor2); B2Sprite * redEnemy = B2Sprite::create("Zombie", 16, k_red);
redEnemy->setTag(kTagRedEnemy);
redEnemy->setPosition(442,500);
handler->addBodyForSprite(redEnemy);
addChild(redEnemy); B2Sprite * blueEnemy = B2Sprite::create("Zombie", 16, k_blue);
blueEnemy->setTag(kTagBlueEnemy);
blueEnemy->setPosition(310,500);
handler->addBodyForSprite(blueEnemy);
addChild(blueEnemy); B2Sprite * yellowEnemy = B2Sprite::create("Zombie", 16, k_yellow);
yellowEnemy->setTag(kTagYellowEnemy);
yellowEnemy->setPosition(330,500);
handler->addBodyForSprite(yellowEnemy);
addChild(yellowEnemy);
} HelloWorld::~HelloWorld()
{
} void HelloWorld::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity)
{
B2Sprite * sprite = B2Sprite::create("bullet.png");
sprite->setScale(0.3);
sprite->setPosition(ccp(position.x, position.y));
Box2dHandler * handler = (Box2dHandler*)(this->getChildByTag(kTagHandler));
handler->addBodyForSprite(sprite, 1.0, 0.3, 0.8, circle);
sprite->getB2Body()->SetLinearVelocity(velocity);
sprite->getB2Body()->SetBullet(true);
sprite->setTag(kTagBulletBase);
addChild(sprite);
} bool HelloWorld::onTouchBegan(Touch* touch, Event* pEvent)
{
return true;
} void HelloWorld::onTouchEnded(Touch* touch, Event* pEvent)
{
Point p = touch->getLocation();
b2Vec2 target(p.normalize().x * 100, p.normalize().y*100);
b2Vec2 v = target;
b2Vec2 source(0, 0);
LaunchBomb(source, v);
} // a is bullet
void HelloWorld::CollisionEvent(B2Sprite*a, B2Sprite*b)
{
if(a->getPositionX() < b->getPositionX())
b->getB2Body()->ApplyLinearImpulse(b2Vec2(100,0), b->getB2Body()->GetPosition(), true);
else if(a->getPositionX() > b->getPositionX())
b->getB2Body()->ApplyLinearImpulse(b2Vec2(-100,0), b->getB2Body()->GetPosition(), true); }

HelloWorld::HelloWorld() 

中我放置了两个镜头刚体作为floor, 然后在上面放了几个敌人。当然,假设要做一个正式的游戏,关卡数据要在外面编辑好,然后读取进来。这里只写个Demo,就没有编辑关卡了。

void HelloWorld::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity)

创建了一个子弹刚体。这种方法在每次触摸屏幕都会触发。

  void HelloWorld::CollisionEvent(B2Sprite*a, B2Sprite*b)

碰撞检測回调方法,这里做的处理是: 假设子弹和敌人产生碰撞,假设敌人在子弹坐标,就给它一个水平向左的冲量,反之给它一个向右的冲量,让它掉下平台。

能够看到,真正的Demo代码是很少的。

接下来上图。

设计一颗子弹

好了,这就是本章的所有内容。源代码已经上传到群 216208142 空间,有须要的读者能够加群来获取。

让子弹飞Demo版的更多相关文章

  1. 转:基于开源项目OpenCV的人脸识别Demo版整理(不仅可以识别人脸,还可以识别眼睛鼻子嘴等)【模式识别中的翘楚】

    文章来自于:http://blog.renren.com/share/246648717/8171467499 基于开源项目OpenCV的人脸识别Demo版整理(不仅可以识别人脸,还可以识别眼睛鼻子嘴 ...

  2. java springMVC 极致验证 非demo版

    最近公司项目需要,做了个极致验证,自己在此做下记录. 先上效果图: 它的官网:http://www.geetest.com/   里面有 身份验证.行为验证,  我这使用的为行为验证. 技术文档:ht ...

  3. 闯越自动签到demo版补充说明

    demo代码:https://www.cnblogs.com/canmeng/p/11000548.html 定位出错是由于cookie 我重新登录账号过,cookies的值就变了 当时没注意cook ...

  4. 基于python3.7的一个闯越自动签到脚本--demo版

    望指正demo的定位,有时候会抽风无法接受我的定位信息 #! /usr/bin/python3 # -*- coding:UTF- -*- # time : // : # file : chuangy ...

  5. Demo 版

    Demo1   美食网站 Demo2   12301智慧旅游公共服务平台 Demo3   react_and_koa example

  6. 捕鱼达人Demo版下载

    链接:https://pan.baidu.com/s/1ihHhikpFXiNJMxa26E8qBw 提取码:npj6

  7. 自制EF(iShare Demo版)

    由于公司使用的所有技术都比较倾向于使用原生,不怎么借用其他第三方框架(无论是前端布局,样式,到后台的框架).公司也算比较小型的没有太大的项目 可以让我们进行团队合作项目,几乎是一人接手一个项目.然后自 ...

  8. 【Java】Swing+IO流实现一个简单的文件加密程序(demo版)

    留着参考 EncrytService package com.my.service; import java.io.File; import java.io.FileInputStream; impo ...

  9. 【flask】使用配置类管理app测试环境-demo版

    如果对app.config是什么还心有疑惑,或者对于这种配置方式很陌生,参考:flask项目配置 app.config classConfig.py: class BaseConfig(object) ...

随机推荐

  1. ASP.NET:插件化机制

    概述 nopCommerce的插件机制的核心是使用BuildManager.AddReferencedAssembly将使用Assembly.Load加载的插件程序集添加到应用程序域的引用中.具体实现 ...

  2. ASP.NET MVC5+ 路由特性

    概述 ASP.NET MVC 5支持一种新的路由协议,称为路由特性. MVC5也支持以前定义路由的方式,你可以在一个项目中混合使用这两种方式来定义路由. 案例 1.使用Visual Studio 20 ...

  3. 【POJ】2165.Gunman

    题解 把直线的斜率分解成二维,也就是随着z的增加x的增量和y的增量 我们发现一条合法直线向上移一点一定能碰到一条横线 知道了这条横线可以算出y的斜率 我们旋转一下,让这条横线碰到两条竖线,就可以算出x ...

  4. atomikos + druid 连接超时失效

    atomikos + druid 连接超时失效,需要多次连接才能成功. 首次连接会报异常: 2018-01-08 16:58:12 DEBUG [com.jpcar.model.dao.jpcar.A ...

  5. 四、django rest_framework源码之频率控制剖析

    1 绪言 权限判定之后的下一个环节是访问频率控制,本篇我们分析访问频率控制部分源码. 2 源码分析 访问频率控制在dispatch方法中的initial方法调用check_throttles方法开始. ...

  6. HTML基础-DAY2

    表单标签form 功能:表单用于向服务器传输数据,从而实现用户与Web服务器的交互 表单能够包含input系列标签,比如文本字段.复选框.单选框.提交按钮等等. 表单还可以包含textarea.sel ...

  7. [ 原创 ]学习笔记- 数据在Activity之间的传递的情况

    情况一:一个Activity跳转到另一个Activity时,将第一个Activity的数据传递到第二个Activity里面. 分析:当一个界面跳转到另一个界面的同时还要讲数据传递过去,这种情况需要用I ...

  8. [BZOJ4557][JLOI2016]侦察守卫(树形DP)

    首先可以确定是树形DP,但这里存在跨子树的信息传递问题,这里就需要“借”的思想. f[i][j]表示i子树内所有点都被覆盖到,且i以外j层内的点都能被覆盖到 的方案数. g[i][j]表示i子树内离i ...

  9. 利用meterpreter下的Venom免杀后门

    转载请注明作者:admin-神风 下载地址:https://github.com/r00t-3xp10it/venom .从Github上下载框架: tar.gz OR zip OR git clon ...

  10. [CC-XYHUMOQ]A humongous Query

    [CC-XYHUMOQ]A humongous Query 题目大意: 有一个长度为\(n(n\le32)\)的以\(1\)开头,\(0\)结尾的\(01\)序列\(S\).令\(f(S)\)表示序列 ...