让子弹飞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版的更多相关文章
- 转:基于开源项目OpenCV的人脸识别Demo版整理(不仅可以识别人脸,还可以识别眼睛鼻子嘴等)【模式识别中的翘楚】
文章来自于:http://blog.renren.com/share/246648717/8171467499 基于开源项目OpenCV的人脸识别Demo版整理(不仅可以识别人脸,还可以识别眼睛鼻子嘴 ...
- java springMVC 极致验证 非demo版
最近公司项目需要,做了个极致验证,自己在此做下记录. 先上效果图: 它的官网:http://www.geetest.com/ 里面有 身份验证.行为验证, 我这使用的为行为验证. 技术文档:ht ...
- 闯越自动签到demo版补充说明
demo代码:https://www.cnblogs.com/canmeng/p/11000548.html 定位出错是由于cookie 我重新登录账号过,cookies的值就变了 当时没注意cook ...
- 基于python3.7的一个闯越自动签到脚本--demo版
望指正demo的定位,有时候会抽风无法接受我的定位信息 #! /usr/bin/python3 # -*- coding:UTF- -*- # time : // : # file : chuangy ...
- Demo 版
Demo1 美食网站 Demo2 12301智慧旅游公共服务平台 Demo3 react_and_koa example
- 捕鱼达人Demo版下载
链接:https://pan.baidu.com/s/1ihHhikpFXiNJMxa26E8qBw 提取码:npj6
- 自制EF(iShare Demo版)
由于公司使用的所有技术都比较倾向于使用原生,不怎么借用其他第三方框架(无论是前端布局,样式,到后台的框架).公司也算比较小型的没有太大的项目 可以让我们进行团队合作项目,几乎是一人接手一个项目.然后自 ...
- 【Java】Swing+IO流实现一个简单的文件加密程序(demo版)
留着参考 EncrytService package com.my.service; import java.io.File; import java.io.FileInputStream; impo ...
- 【flask】使用配置类管理app测试环境-demo版
如果对app.config是什么还心有疑惑,或者对于这种配置方式很陌生,参考:flask项目配置 app.config classConfig.py: class BaseConfig(object) ...
随机推荐
- 【转】实践最有效的提高Android Studio运行、编译速度方案
原文:https://blog.csdn.net/xwh_1230/article/details/60961723 实践最有效的提高Android Studio运行.编译速度方案 最有效提升Andr ...
- mysql千万级表关联优化
MYSQL一次千万级连表查询优化(一) 概述: 交代一下背景,这算是一次项目经验吧,属于公司一个已上线平台的功能,这算是离职人员挖下的坑,随着数据越来越多,原本的SQL查询变得越来越慢,用户体验特别差 ...
- day5模块学习--yaml文件处理
yaml文件处理(http://pyyaml.org/wiki/PyYAMLDocumentation) 摘要: 本文讲的是yaml在python上的使用教程详解, YAML是一种容易人类阅读 ...
- 牛客网 桂林电子科技大学第三届ACM程序设计竞赛 D.寻找-树上LCA(树上a到b的路径上离c最近的点)
链接:https://ac.nowcoder.com/acm/contest/558/D来源:牛客网 寻找 小猫在研究树. 小猫在研究树上的距离. 给定一棵N个点的树,每条边边权为1. Q次询问,每次 ...
- 【原创】记一次MySQL大表高并发写入引发CPU飙升的排障过程
目录 一.故障现象... 1 二.初步分析... 2 三.排障过程... 2 1.排查是否QPS或insert并发请求上升导致问题发生... 2 2.排查是否锁资源等待或block导致了insert变 ...
- ssm框架常见问题
搭建SSM框架时,总是遇到这样那样的问题,有的一眼就能看出来,有的需要经验的积累.现将自己搭建SSM框架时遇到的典型问题总结如下: 一.Struts2框架下的action中无法使用@Autowired ...
- web服务端安全之暴力破解
一.暴力破解 指攻击者通过遍历或字典的方式,向目标发起大量请求,通过判断返回数据包的特征来找出正确的验证信息,从而绕过验证机制. 二.常见场景 用户登录处的账号密码暴力破解: 人机验证机制容易绕过,如 ...
- python opencv3 特征提取与描述 DoG SIFT hessian surf
git:https://github.com/linyi0604/Computer-Vision DoG和SIFT特征提取与描述 # coding:utf-8 import cv2 # 读取图片 im ...
- CF946D Timetable 动态规划
预处理出每一行去掉$k$个1能获得的最小代价 之后做一次分组背包$dp$即可 预处理可以选择暴力枚举区间... 复杂度$O(n^3)$ #include <set> #include &l ...
- BZOJ.3498.[PA2009]Cakes(三元环 枚举)
题目链接 感觉我可能学的假的(复杂度没问题,但是常数巨大). 一个比较真的说明见这儿:https://czyhe.me/blog/algorithm/3-mem-ring/3-mem-ring/. \ ...