让子弹飞Demo版

让子弹飞是我非常喜欢的一款游戏。今天的目标就是利用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版

时间: 2024-10-13 00:52:01

让子弹飞Demo版的相关文章

Demo 版

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

iOS开发应用上架必读最新苹果审核规则(史上最全版)

学习交流及技术讨论可新浪微博关注:极客James 1. 条款和条件 ? 1.1 为App Store开发程序,开发者必须遵守 Program License Agreement (PLA).人机交互指南(HIG)以及开发者和苹果签订的任何其他协议和合同.以下规则和例证旨在帮助开发者的程序能获得App Store的认可,而不是修改或删除任何其他协议中的条款. 2. 功能 ? 2.1 崩溃的程序将会被拒绝. ? 2.2 存在错误的程序将会被拒绝. ? 2.3 跟开发者宣传不符的程序将会被拒绝. ?

App Store 审核指南最新中英文参照版

1.1 As a developer of Apps for the App Store you are bound by the terms of theProgram License Agreement (PLA), Human Interface Guidelines (HIG), and any other licenses or contracts between you and Apple. The following rules and examples are intended

大数据基本的 增删改差 Demo(资源来自网络本人属于收藏总结)

今天第一天接触大数据 做一些基本的增删改差,来这总结一下 ,资源来自网络本人属于收藏总结 这一篇只有demo合运行结果 ,下一篇有一点对代码的分析合自己的理解 先看一下数据库 的数据 查询数据 1 public static void main(String args[]) throws TRSException { 2 TRSConnection conn = new TRSConnection("http://127.0.0.1:5555", "admin",

软件保护技术--- 常见保护技巧

(1)序列号保护机制 数学算法一项都是密码加密的核心,但在一般的软件加密中,它似乎并不太为人们关心,因为大多数时候软件加密本身实现的都是一种编程的技巧.但近几年来随着序列号加密程序的普及,数学算法在软件加密中的比重似乎是越来越大了.     我们先来看看在网络上大行其道的序列号加密的工作原理.当用户从网络上下载某个shareware--共享软件后,一般都有使用时间上的限制,当过了共享软件的试用期后,你必须到这个软件的公司去注册后方能继续使用.注册过程一般是用户把自己的私人信息(一般主要指名字)连

jar包版本介绍(beta,alpha,release),软件的版本介绍

α(Alpha) 此版本表示该软件仅仅是一个初步完成品,通常只在软件开发者内部交流,也有很少一部分发布给专业测试人员.一般而言,该版本软件的bug(漏洞)较多,普通用户最好不要安装.主要是开发者自己对产品进行测试,检查产品是否存在缺陷.错误,验证产品功能与说明书.用户手册是否一致. β(beta) 该版本相对于α版已有了很大的改进,消除了严重的错误,但还是存在着一些缺陷,需要经过大规模的发布测试来进一步消除.这一版本通常由软件公司免费发布,用户可从相关的站点下载.通过一些专业爱好者的测试,将结果

【转】高通平台android 环境配置编译及开发经验总结

原文网址:http://blog.csdn.net/dongwuming/article/details/12784535 1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通开发板上烧录文件系统 建立高通平台开发环境 高通平台,android和 modem 编译流程分析 高通平台 7620 启动流程分析 qcril 流程分析,设置sim卡锁 python scons 语法学习 Python 语言之 scons 工具流程分析: 1.2 搭建高通平台环境开发环境 高通and

软件工程实践----初步接触软件工程的总结

这学期的软件工程课即将结束,下面我就对本学期的软件工程课做一下基本的总结. 首先,这是我学期初在阅读了相关资料后提的一些问题:http://www.cnblogs.com/bsdbch/p/4027935.html 这些问题,有的在实际的课程实践中碰到了,因此得到了更深一步的了解.比如关于如何进行需求分析.产品定位,再比如如何控制代码的质量,如何架构整体框架从而让子函数.子类变得更小.但是也有些问题,由于我们的产品没有达到用那些高级方法的高度,所以没有过多的了解.比如关于程序模块间的耦合. 下面

Chrome Extension 检查视图(无效)处理方法

最近闲来无事,简单看了下Chrome扩展的开发,并且开发一个小小的翻译插件(TranslateBao)作为练手,开发细节不详述了,如果有新学习chrome extension开发的新人,可以参考源码,并欢迎提出宝贵意见,共同进步. 闲话不多说,在一个Demo版开发完成后,载入到Chrome中进行体验,一切感觉良好...(-.-),但是,遇到一个感觉很不爽的东西,见下图: 没错,问题就是在“检查视图:background.html”后面有一个“无效”,虽然没有发现具体影响到什么地方,但是这么一个负