cocos2dx在设计之初就集成了两套物理引擎,它们是box2d和chipmunk。我眼下使用的是最新版的cocos2dx 3.1.1。引擎中默认使用的是chipmunk。假设想要改使用box2d的话,须要改动相应的androidproject或者是iosproject的配置文件。

  在2.x版本号的cocos中。使用物理引擎的步骤十分繁琐。但在3.x版本号中变得很方便了。

我这次的学习目标是制作一个打砖块的小游戏。

  首先。如今的Scene类提供了一个静态工厂方法。用以创造一个集成物理引擎的场景。

  1. Scene::initWithPhysics()

这种方法能让你的场景具备创建物理世界的基本条件,接下来我们设置这个屋里世界的重力条件,由于打砖块游戏中不须要重力的影响,所以我们把该场景里的重力设置为0:

  1. PhysicsWorld* world = getPhysicsWorld();
  2. world->setGravity(Vec2(0,0));

  然后我们要给这个物理世界创造一个边界,便于我们观察效果,我的做法是把物理世界的scene和游戏逻辑的实现分开,新建一个继承自layer的类来写游戏逻辑:

  这是头文件:

  1. 1 #include "Util.h"
  2. 2
  3. 3 #ifndef __FristGame__GameLayer__
  4. 4 #define __FristGame__GameLayer__
  5. 5
  6. 6 class GameLayer:public Layer
  7. 7 {
  8. 8 public:
  9. 9 Sprite* ball;
  10. 10 Sprite* paddle;
  11. 11 Sprite* edgeSp;
  12. 12 Sprite* prop;
  13. 13 PhysicsBody* ballBody;
  14. 14 TMXTiledMap* map;
  15. 15
  16. 16 CREATE_FUNC(GameLayer);
  17. 17 bool init();
  18. 18
  19. 19 void loadPhysicsBody();//载入物理世界的边界
  20. 20 void loadTileMap();//载入使用tiledmap地图编辑器制作的地图
  21. 21 void loadProp();//载入碰撞特定砖块会掉落的道具
  22. 22 void update1(float dt);//打开一个定时器
  23. 23 void contact();//碰撞事件注冊
  24. 24 };
  25. 25 #endif /* defined(__FristGame__GameLayer__) */

  小提示:在3.2版本号的物理世界中我们不能使用scheduleupdate()函数,似乎body(刚体)的运动是在update里处理的,一旦我们重写了这个函数,物理世界中的小球就不再运动了。所以我们另外设置一个定时器update1来使用。

  这是cpp文件:

  1. 1 #include "GameLayer.h"
  2. 2 bool GameLayer::init()
  3. 3 {
  4. 4 loadTileMap();
  5. 5 loadPhysicsBody();
  6. 6 return true;
  7. 7 }
  8. 8 void GameLayer::loadPhysicsBody()
  9. 9 {
  10. 10 auto visibleSize = Director::getInstance()->getVisibleSize();//取得当前屏幕的尺寸size
  11. 11 auto origin = Director::getInstance()->getVisibleOrigin();
  12. 12
  13. 13 edgeSp = Sprite::create();//创建一个精灵
  14. 14 auto boundBody = PhysicsBody::createEdgeBox(visibleSize,PhysicsMaterial(0.0f,1.0f,0.0f),3);//edgebox是不受刚体碰撞影响的一种刚体,我们用它来设置物理世界的边界
  15. 15 edgeSp->setPosition(visibleSize.width/2, visibleSize.height/2);//位置设置在屏幕中央
  16. 16 edgeSp->setPhysicsBody(boundBody);//将精灵容纳的刚体设置为boundbody。注意这里不能确定刚体和精灵是不是父子节点的关系。有兴趣的朋友请自行研究。
  17. 17 addChild(edgeSp);//增加渲染树
  18. 18
  19. 19 ball = Sprite::create("game_ball_a.png");//创建小球的精灵
  20. 20 ball->setPosition(100,100);//设定位置在屏幕中下部
  21.      //PhysicsMaterial是设置刚体属性的类,三个參数分别相应三个属性:1、density(密度)2、restiution(弹性)3、friction(摩擦力),在这个游戏中我们须要小球无限碰撞,因此摩擦力和密度都设为1,弹力设为1。
  22. 21 ballBody = PhysicsBody::createCircle(ball->getContentSize().width/2,PhysicsMaterial(0.0f,1.0f,0.0f));
  23. 22 ballBody->setContactTestBitmask(0xFFFFFFFF);//接触掩码值---------标注1------------(见代码后)
  24. 23 Vect force = Vect(1000.0f,1000.0f);
  25. 24 ballBody->applyImpulse(force);//这种方法不会产生力,可是会让一个速度与body的速度叠加 产生新的速度(通过这种方法我们让小球匀速运动)
  26. 25 ballBody->setVelocity(Vec2(150,150));//设置小球速度
  27. 26 ball->setPhysicsBody(ballBody);
  28. 27 addChild(ball);
  29. 28
  30. 29 Sprite* batSprite = Sprite::create("game_av_d.png");//创建打砖块游戏中的砖块
  31. 30 PhysicsBody* batBody = PhysicsBody::createEdgeBox(batSprite->getContentSize(),PhysicsMaterial(0.0f,1.0f,0.0f));//创建相应的刚体并设置材质 32 batSprite->setPhysicsBody(batBody);
  32. 33 batSprite->setPosition(winSize.width/2,50);
  33. 34 addChild(batSprite);
  34. 35 batBody->setContactTestBitmask(0xFFFFFFFF);//设置接触掩码值
  35. 36 EventListenerTouchOneByOne* ev1 = EventListenerTouchOneByOne::create();//3.x版本号之后对触摸事件做了全盘的改动,这里不作具体描写叙述。这是创建一个单点触摸事件。
  36. 37 ev1->onTouchBegan = [](Touch* touch,Event* ev){return true;};//touchbegin不作不论什么处理。跳过
  37. 38 ev1->onTouchMoved = [=](Touch* touch,Event* ev){
  38. 39 float x = touch->getDelta().x;
  39. 40 batSprite->setPositionX(batSprite->getPositionX()+x);
  40. 41 };//在touchmove中移动挡板,依照触摸滑动的距离来移动挡板。
  41. 42 _eventDispatcher->addEventListenerWithSceneGraphPriority(ev1, this);//将触摸事件增加监听器
  42. 43 contact();//调用注冊碰撞事件的函数
  43. 44 schedule(schedule_selector(GameLayer::update1));//打开定时器
  44. 45
  45. 46 }
  46. 47 void GameLayer::contact()
  47. 48 {
  48. 49 EventListenerPhysicsContact* evContact = EventListenerPhysicsContact::create();//创建一个物理世界的碰撞事件
  49. 50 evContact->onContactBegin = [](PhysicsContact& contact){return true;};
  50. 51 evContact->onContactSeperate = [=](PhysicsContact& contact)//该函数在两个碰撞的刚体分离后调用
  51. 52 {
  52. 53 auto bodyA = (Sprite*)(contact.getShapeA()->getBody()->getNode());//两个碰撞刚体相相应的节点之A
  53. 54 auto bodyB = (Sprite*)(contact.getShapeB()->getBody()->getNode());//两个相碰撞刚体相应节点之B
  54. 55 if(!bodyA||!bodyB)//按理说碰撞发生之后不会发生有一个刚体的节点不存在的情况,可是实际測试时发现bodyA或bodyB有为NULL的情况。因此我们在这里做一个推断排除节点为空的情况
  55. 56 return;
  56. 57 int tagA = bodyA->getTag();
  57. 58 int tagB = bodyB->getTag();
  58. 59 if(tagA == 3)//假设碰撞两方刚体有一个是砖块。则把这个砖块连同节点一同删掉
  59. 60 {
  60. 61 bodyA->removeFromParentAndCleanup(true);
  61. 62 }
  62. 63 if(tagB == 3)
  63. 64 {
  64. 65 bodyB->removeFromParentAndCleanup(true);
  65. 66 }
  66. 67 prop->setVisible(true);
  67. 68 };
  68. 69 _eventDispatcher->addEventListenerWithSceneGraphPriority(evContact,this);//注冊碰撞事件
  69. 70 }
  70. 71 void GameLayer::loadTileMap()
  71. 72 {
  72. 73 map = TMXTiledMap::create("textmap.tmx");//从tmx创建一个TMXTileMap类
  73. 74 map->setPositionX(getPositionX() + 34);//设置位置 76 addChild(map);
  74. 77 TMXLayer* layer = map->getLayer("bricks");//从map中取出“bricks”图层
  75.      //这个循环嵌套是为了给每一个砖块精灵设置一个刚体
  76. 78 for(int x=0;x<13;x++)
  77. 79 {
  78. 80 for(int y=0;y<18;y++)
  79. 81 {
  80. 82 int gid = layer->getTileGIDAt(Vec2(x,y));//为了提高点效率。我们不是必需给每一个tile加上刚体。在创建地图时我们设定了空白处的gid值为12。因此我们仅仅对非12的tile加上刚体
  81. 83 if(gid != 12)
  82. 84 {
  83. 85 Sprite* sprite = layer->getTileAt(Vec2(x,y));//从tile的坐标取出相应的精灵
  84. 86 if(!sprite)//防止sprite为NULL
  85. 87 continue;
  86. 88 PhysicsBody* body = PhysicsBody::createEdgeBox(sprite->getContentSize(),PhysicsMaterial(1.0f,1.0f,0.0f));//给精灵设置一个刚体
  87. 89 sprite->setTag(3);//增加tag,方便碰撞时的推断
  88. 90 body->setContactTestBitmask(0xFFFFFFFF);//设置接触掩码值
  89. 91 sprite->setPhysicsBody(body);
  90. 92 }
  91. 93 }
  92. 94 }
  93. 95 loadProp();
  94. 96 }
  95. 97 void GameLayer::loadProp()
  96. 98 {
  97. 99 TMXObjectGroup* objects = map->getObjectGroup("prop");//从地图中取出对象层prop
  98. 100 CCASSERT(NULL != objects, "'Objects' object group not found");//防止为空
  99. 101 auto spawnPoint = objects->getObject("pop");//从对象层中取出对象pop,在创建地图时设置好的
  100. 102 CCASSERT(!spawnPoint.empty(), "spawnPoint object not found");
  101. 103 int x = spawnPoint["x"].asInt();//spwanPoint应该是一个map型容器。这方面我理解不深,不多描写叙述了。
  102. 104 int y = spawnPoint["y"].asInt();
  103. 105 prop = Sprite::create("game_energy_b.png");//依照从地图中取出的坐标创建一个精灵
  104. 106 prop->setPosition(x,y);
  105. 107 prop->setVisible(false);//一開始设置为不可见,当碰撞发生时设置为可见,并開始向下运动
  106. 108 prop->setZOrder(10);//防止被地图掩盖
  107. 109 map->addChild(prop);
  108. 110 }
  109. 111 void GameLayer::update1(float dt)
  110. 112 {
  111.      //-----------标注2----------------
  112. 113 float x = ballBody->getVelocity().x;
  113. 114 float y = ballBody->getVelocity().y;
  114. 115 if(x!=150&&x!=-150)
  115. 116 {
  116. 117 if(x<0)
  117. 118 x = -150;
  118. 119 else
  119. 120 x = 150;
  120. 121 }
  121. 122 if(y!=150&&y!=-150)
  122. 123 {
  123. 124 if(y<0)
  124. 125 y = -150;
  125. 126 else
  126. 127 y = 150;
  127. 128 }
  128. 129 ballBody->setVelocity(Vec2(x,y));
  129. 130
  130. 131 }

  这里对代码中的标注进行一些解释:

  标注1:关于接触掩码值。在3.0中的事件分发机制都由事件派发器管理,所以物理引擎的碰撞事件也不例外。

以下代码注冊碰撞响应事件和回调函数

  1. 1 auto contactListener = EventListenerPhysicsContact::create();
  2. 2 contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
  3. 3 _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

  每一次碰撞检測事件是有EventListenerPhysicsContact来进行监听的。监听到碰撞事件时,会回调响应事件onContactBegin()来进行碰撞事件的处理。_eventDispatcher是事件派发器,由它管理全部的注冊事件。

EventListenerPhysicsContact是碰撞检測中的一种,也能够运用EventListenerPhysicsContactWithBodies,EventListenerPhysicsContactWithShapes,EventListenerPhysicsContactWithGroup
来进行碰撞事件的注冊。对你你感兴趣的bodys,shape和group事件进行监听。   

  在上面说了这么多的东西,最重要的东西就是以下的,没有以下的东西。碰撞事件根本不起作用。这就是我第一次运用碰撞时遇到的问题。也就是设置物理接触相关的位掩码值,默认的接触事件不会被接受。须要设置一定的掩码值来使接触事件响应。 接触掩码值有三个值,各自是:

  1、CategoryBitmask,默认值为0xFFFFFFFF

  2、ContactTestBitmask,默认值为 0x00000000

  3、CollisionBitmask。默认值为0xFFFFFFFF 这三个掩码值都有相应的set/get方法来设置和获取。

  这三个掩码值由逻辑与来进行操作測试。

  一个body的CategoryBitmask和还有一个body的ContactTestBitmask的逻辑与的结果不等于0时,接触事件将被发出,否则不发送。

  一个body的CategoryBitmask和还有一个body的CollisionBitmask的逻辑与结果不等于0时,他们将碰撞,否则不碰撞 

  默认情况下的body属性会进行物理碰撞,但不会发送碰撞检測的信号,也就不会响应碰撞回调函数,这个能够看下默认情况下的掩码值的逻辑与

  CategoryBitmask = 0xFFFFFFFF;

  ContactTestBitmask = 0x00000000;

  CategoryBitmask & ContactTestBitmask = 0,所以不会发送碰撞信号

 

  CollisionBitmask = 0xFFFFFFFF。

  CategoryBitmask & CollisionBitmask = 0xFFFFFFFF,所以物体会碰撞。可是不会响应碰撞回调函数。

  上面介绍的掩码值是碰撞检測回调中最重要的。没有上面的掩码值,全部的碰撞回调函数都不会发生。 EventListenerPhysicsContact有四个接触回调函数:

  1、onContactBegin,在接触開始时被调用,仅调用一次。通过放回true或者false来决定两个物体是否有碰撞。

同一时候能够使用PhysicsContact::setData()来设置接触操作的用户数据。当返回false时。onContactPreSolve和onContactPostSolve将不会被调用,可是onContactSeperate将被调用一次。

  2、onContactPreSlove ,会在每一次被调用。通过放回true或者false来决定两个物体是否有碰撞,相同能够用ignore()来跳过兴许的onContactPreSolve和onContactPostSolve回调函数。(默认返回true)

  3、onContactPostSolve,在两个物体碰撞反应中的每一个步骤中被处理调用。能够在里面做一些兴许的接触操作。如销毁body

  4、onContactSeperate。在两个物体分开时被调用,在每次接触时仅仅调用一次,和onContactBegin配对使用。 上述中最重要的就是碰撞检測事件的解说,这是游戏中用到碰撞常常要用到的。 



  这里附上一篇博文,具体的解说了3.x的碰撞机制:http://www.tuicool.com/articles/2eI7Nv

  标注2:据我这两天的实验来看,在mac下和windows下使用物理引擎产生的效果有巨大的区别。非常多博客上的代码都是在windows上可以流畅执行,可是在mac上跑就会有非常多问题。

  比方,25行的ballBody->applyImpulse(force)。在windows下仅仅要使用applyForce就能够了,可是在mac下使用applyForce函数会让球的运动越来越快,没多久便会由于速度超过帧数飞出我们设置的边界。

  再比方。update1中的代码。这是为了保证能一直保持匀速运动而写的。在windows下全然不须要这些代码,可是在mac下假设没有。小球会在某次碰撞时候损失速度(随机的,有时不会损失),直至停下来。

本人cocos2dx 2.x和3.x的源代码淘宝地址(欢迎大家光顾):https://shop141567464.taobao.com/?spm=a313o.7775905.1998679131.d0011.pzUIU4

Cocos2d-x 3.1.1 学习日志13--物理引擎登峰造极之路的更多相关文章

  1. cocos2d-x学习日志(13) --A星寻路算法demo

    你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢?如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! A星算法简介: A*搜寻算法俗称A星 ...

  2. COCOS2DX学习之Box2d物理引擎使用之------动态物体的创建

    1.创建一个物理世界 首先要引入一个头文件#include "Box2D\Box2D.h" 之后利用b2word创建一个对象,而且指定这个物理世界中的加速度方向. word = n ...

  3. Python学习日志9月13日

    昨天的学习日志没有写,乱忙了一整天,政治电脑. 好奇心重,想要给电脑装上传说中LInux操作系统,各种小问题折腾到半夜,今天又折腾到晚上才真正的装上系统. 可是装上系统后又发现各种的不好用.虽然界面比 ...

  4. Cortex-M3学习日志(五) -- DAC实验

    终于逮了个忙里偷闲的机会,就再学一下LPC1768的外围功能吧,循序渐进是学习的基本规则,也许LPC1768的DAC与8位单片机16位单片机里面集成的DAC操作类似,但是既然这是懒猫的学习日志,就顺便 ...

  5. MobileForm控件的使用方式-用.NET(C#)开发APP的学习日志

    今天继续Smobiler开发APP的学习日志,这次是做一个title.toolbar.侧边栏三种效果 样式一 一.          Toolbar 1.       目标样式 我们要实现上图中的效果 ...

  6. composer的安装和使用 学习日志

    如果你做为一个phper,没有用过composer,那你真的不是一个合格的开发者.那么就来记录一下composer的学习日志 下面分享几个学习源头: composer中文网站:https://www. ...

  7. GRE学习日志

    发现开博客园真的很有督促作用,今天也顺便开个GRE学习日志吧 2015-02-09:单词 2015-02-10:单词 2015-02-11:单词 2015-03-02:阅读 2015-03-04:阅读 ...

  8. Ext.Net学习笔记13:Ext.Net GridPanel Sorter用法

    Ext.Net学习笔记13:Ext.Net GridPanel Sorter用法 这篇笔记将介绍如何使用Ext.Net GridPanel 中使用Sorter. 默认情况下,Ext.Net GridP ...

  9. Cortex-M3学习日志(六) -- ADC实验

    上一次简单的总结了一下DAC方面的知识,好吧,这次再来总结一下ADC方面的东东.ADC即Analog-to-Digital Converter的缩写,指模/数转换器或者模拟/数字转换器.现实世界是由模 ...

随机推荐

  1. code forces 994B

    B. Knights of a Polygonal Table time limit per test 1 second memory limit per test 256 megabytes inp ...

  2. Python实现队列

    队列的数据结构的主要结构:一个结点类和两个方法:出队列和进队列 class Node(object): def __init__(self,val): self.val = val self.next ...

  3. Mysql 索引原理(转自:张洋)

    摘要 本文以MySQL数据库为 研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据 库支持多种索引类型,如 ...

  4. js执行时间(调试)

    js 执行时间 function use_time(func) {    var start = new Date().getTime(); console.log(start);        fu ...

  5. Java并发容器--ConcurrentLinkedQueue

    概述 ConcurrentLinkedQueue是一种基于链表实现的无界非阻塞线程安全队列,遵循先入先出规则. 线程安全队列有两种实现方式: 阻塞方式:对入队和出队操作加锁.阻塞队列. 非阻塞方式:通 ...

  6. ubuntu安装ftp server并匿名访问

    $ sudo apt install vsftpd //修改添加以下配置 $ sudo vim /etc/vsftpd.conf #listen_ipv6=YES #注销ipv6监听 listen=Y ...

  7. log4j2 扩展日志级别,支持将系统日志与业务处理日志拆分

    项目中,有时候需要对系统中已处理的一些业务数据日志进行提取分析,通常log4j默认提供的日志级别可能不够用,这时候我们就需要对日志级别进行扩展,以满足我们的需求. 本文就简单介绍一下log4j2的日志 ...

  8. HDU 6300.Triangle Partition-三角形-水题 (2018 Multi-University Training Contest 1 1003)

    6300.Triangle Partition 这个题就是输出组成三角形的点的下标. 因为任意三点不共线,所以任意三点就可以组成三角形,直接排个序然后输出就可以了. 讲道理,没看懂官方题解说的啥... ...

  9. FZU-2216 The Longest Straight(尺取法)

     Problem 2216 The Longest Straight Accept: 523    Submit: 1663Time Limit: 1000 mSec    Memory Limit ...

  10. POJ 2155 Matrix(树状数组+容斥原理)

    [题目链接] http://poj.org/problem?id=2155 [题目大意] 要求维护两个操作,矩阵翻转和单点查询 [题解] 树状数组可以处理前缀和问题,前缀之间进行容斥即可得到答案. [ ...