Cocos2d-x之初级物理引擎

我连什么是重力都不懂,却让我开始用物理引擎。c++

zombie

一切故事发生的背景

同济大学软件学院每一个学期会要求学生独立或者组队完成一个大项目,因而2016年的大项目是用cocos2d-x这款引擎制做一个本身的游戏(我最终作的网游,请勿模仿)。xcode

以前写了一篇cocos2d-x关于键盘按住事件的教程,不足之处还请你们多多指出。app

在本身作练习的过程当中,逐渐接触到了碰撞,行走,降落一类的行为。我发现,若是所有由本身来实现,不只实现起来复杂,并且执行效率也不必定见得高,因此我决心开始学习cocos2d-x Physics 2D。函数

物理引擎的基础

两种物理引擎

根据官方文档的介绍,目前有两大重要的物理引擎, Box2D 和 Chipmunk,而且cocos2d-x已经集成了它们,在3.x版本中可基于Chipmunk的核心API的物理引擎使用。学习

一句话: cocos2d-x 3.x版本中使用Chipmunk物理引擎更加方便了,你们都升级成3.x吧=。=url

为何要用物理引擎

我为何要用物理引擎?举个开发中遇到的问题,在上一期的博文中,咱们成功建立了一个hero sprite(对,就是那个可爱的僵尸)。如今咱们要再建立一个坚果墙 sprite,叫作 wall sprite,它的做用是:hero会被wall而没法继续前进。spa

你们都知道,若是什么都不设置,咱们控制hero的时候,是会直接穿过wall sprite的。这确定不是咱们想要的,机智的霄同窗就想到了一个办法:获取hero要走的下一步位置(仔细看看上一期,就会发现这并不难),而后再判断这个位置(Point)是否是存在一个精灵,若是是,就强制取消移动命令。debug

好办法啊,思路清晰,我简直要为他鼓掌了。调试

方法虽好,惋惜我不会。以前提到过,我是一个呆呆呆呆的初学者,我只想用最方便的方式来实现我要的效我甚至不惜牺牲程序的效率来追求减小代码量,一想到要再次封装一大堆,我就烫烫烫。code

cocos2d-x physics 2D就能够完美解决这个问题,并且方法十分简单。

存在物理规律的世界?

咱们这个世界受着来自物理规律的支配,那么物理引擎建立出来的scene也一样要存在某种物理规律,身为造物主的你能够自由定义这些规律。

首先,咱们在Demo.cpp中建立一个物理世界。

Scene* Demo::createScene()
{
    auto scene = Scene::createWithPhysics();
    auto layer = HelloWorld::create();
    scene->addChild(layer);
    return scene;
}

很简单对吧?只须要将Scene::create改为Scene::createWithPhysics,在这里scene中的物理世界就算建立成功啦。

从新建立sprites

上一次教程中,咱们建立了一个hero sprite, 建立的方法是这样的:

auto hero = Sprite::create("hero.png");

太愚蠢了是否是?高贵的物理世界怎么能这样呢?...

很遗憾,这样的建立方式是没错的,咱们依然沿袭这个方法来建立sprite。

若是此时,你开启调试,就会发现sprite没有开始自由落体运动。那究竟是哪里出现了问题呢?

这样建立出来的sprite只是一个空壳而已,它没有任何灵魂和信仰的力量(Physics body),咱们此时须要给这只可怜的小家伙+1s信仰。

auto heroBody = PhysicsBody::createBox(hero->getContentSize());
hero->setPhysicsBody(bodyHero);

这样,它拥有了一个Physics body。如今再调试,你就发现咱们可爱的hero已经在自由落体了。

但是,你不想听听createBox究竟是什么意思吗?body存在一个边界,里面的空间表示sprite的实体。而这个边界有几种存在的形态:矩形、圆形和多边形。

在刚刚的例子中,咱们建立了一个矩形的边界,规定了边界范围是hero sprite的大小(png图片)。再说说以后要建立的坚果墙吧,它的形状基本趋近一个圆形,那么则可使用createCircle的方法来建立它的body.

setPhysicsBody顾名思义,就是将咱们的灵魂(body)赋给hero sprite.

掉...掉下去了

看着咱们的hero可以实现自由落体,我也很开心啊。但是...不一下子,它就掉到屏幕外面去了,怎么办?

恍然大悟,咱们的背景图片(它也是一个sprite,这不能忘啊),没有被添加body。可是咱们又发现一个问题,body是进不去的,因此,物理引擎专门提供了一个方法createEdgeBox,只建立边界。

# Demo.cpp

auto map = Sprite::create("background.png");
auto mapFrame = PhysicsBody::createEdgeBox(map->getContentSize());

map->setPhysicsBody(mapFrame);

再次调试,hero稳稳地落在了地面上。

支配个人世界

虽然我不知道什么是G = mg,可是我知道世界上必定是有重力的,嗯。因此咱们建立出来的scene中的物体也须要受到重力的做用。
API
看API文档了解到咱们须要传入一个Vec2类型的重力参数,第一个和第二个数值是什么意思呢?我经过xcode进行调试发现:
debug
第二个数值为默认的重力,98。那么咱们就能够经过setGravity方法来设置属于咱们本身的重力了。

# Demo.cpp

auto scene = Scene::createWithPhysics();
scene->getPhysicsWorld()->setGravity(Vec2(0.0f, -500.0f));
...

先使用物理scene中的getPhysicsWorld方法来获取咱们的物理世界,而后再设置重力,通过调试就能够看见hero sprite和wall sprite飞快地加速降低了。

还有不少好玩的功能强劲的API能够供你们使用,好比getAllBodies,都等着咱们去探索。

关系到具体body的属性

那么我想为hero sprite和wall sprite添加一些属于他们本身的物理属性,怎么作到呢?

就像现实世界中有人质量大,有人质量小同样,我要给坚果墙设置一个极大的质量以致于不可动摇,而僵尸(our hero)就能够自由行动(如何自由行动上一期已经说过啦~)。

就像API中所提到的,能够在建立Physics body的时候,就传入一个physics material进去。
API

以前咱们只是传入一个sprite content进去(第一个参数),如今要传入更多的参数,使hero sprite的physics body达到咱们预期的效果。

# Demo.cpp

auto hero = Sprite::create("hero.png");
auto heroBody = PhysicsBody::createBox(hero->getContentSize(), PhysicsMaterial(1.0f, 1.0f, 20.0f));

什么是Physics Material呢?physics material

根据API可知,咱们能够调用这个类的构造函数来建立一个physics material,使得physics body获取必定量的材质。密度,还原力和摩擦力。对于咱们的需求来讲,只要设置必要的摩擦力就够了。

第三个参数offset为偏移量,想要physics body和sprite的位置错开的话,能够填写这个参数。


heroBody->setDynamic(true);      //设置为静态的刚体,不受重力影响  
heroBody->setMass(999999);  //设置刚体不可动  
heroBody->setRotationEnable(false);      //设置刚体不可转动  
heroBody->getShape(0)->setRestitution(1.0f);

这些都是能够在API文档中找到设置physics body的方法,学会以后就能够为所欲为地建立属于本身的物理场景了。

没有重力的世界

不是全部游戏都是2D横版闯关的,好比上帝视角。
重力在这个场景中存在吗?存在,可是它不是明目张胆地表现出来。就好比一个个小棋子,定格在棋盘上,此时咱们不能为这个场景添加剧力,因而:

# Demo1.cpp

auto scene = Scene::createWithPhysics();
scene->getPhysicsWorld()->setGravity(Vec2(0.0f, 0.0f));
...

而且还要为sprite设置不受重力影响的效果。

# Demo1.cpp
auto sprite = Sprite::create("sprite.png");
auto spriteBody = PhysicsBody::createBox(sprite->getContentSize());
spriteBody->setGravityEnable(false);

这样,咱们的小棋子就定格在棋盘上了。

动动动动起来

既然没有了重力,咱们如何让它们在存在一个做用力的状况下,让它们停下来呢?

首先,你须要一个做用力。applyForce和applyImpulse这两个方法可以很好地帮助咱们建立给物体施加的力。

# Demo1.cpp

spriteBody->applyForce(Vec2(100.0f, 100.0f));
// spriteBody->applyImpulse(Vec2(100.0f, 100.0f));

咱们将物体发射到点100.0, 100.0的位置方向去。我还没来得及解释这两个方法是什么意思的时候,心急的朋友就立刻开始调试了,结果发现sprite并无按照预期的那样动起来。这是为何呢?

由于力不够大啊孩子,很神奇的是,咱们彷佛只规定了力的反向而力的大小并无被规定,可是又如何衡量一个力的大小呢?这是一个很使人纠结的问题。

force

且先来看文档, 参数只要求填入一个Vec2类型的数值,并且注释是force ...

会不会是默认了添加1N的力呢?因而我将代码改为下面这样:

# Demo1.cpp

spriteBody->applyForce(Vec2(100.0f, 100.0f) * 1000);
// spriteBody->applyImpulse(Vec2(100.0f, 100.0f) * 1000);

果真,精灵动了起来。可是问题又来了,不一会,我就发现精灵根本没有停下来的意思,它在不停地运动。

形成这种问题,主要有两个缘由:一、没有摩擦力;二、添加力的方式存在问题。

趁热打铁,咱们先来解决力的问题。applyForce有什么问题吗?咱们仔细看看API文档就会发现,这是添加了一个持续的力,这个力会不停地添加在sprite上,直到你手动地将其停下来。

那么咱们须要一个瞬间的力,就相似弹弓同样。applyImpulse这个时候就出场啦。这个方法能为物体添加一个瞬时的力。

impulse

# Demo1.cpp

spriteBody->applyImpulse(Vec2(100.0f, 100.0f) * 1000);
// spriteBody->applyForce(Vec2(100.0f, 100.0f) * 1000);

快快快快快停下

但是咱们发现精灵仍是不能很好地停下,可是至少它不会像以前那么飞奔了。

刚刚说了,摩擦力存在问题。咱们不是已经设置过摩擦力了吗?它会有什么问题呢?

摩擦力不表明空气阻力,在cocos2d-x的物理引擎建立的世界中,是默认不存在空气阻力这个高大上的属性的。

完蛋,物理引擎都没提供的功能,让我如何是好啊。很幸运的是,physics body提供了一个叫作setLinearDamping(设置线性阻尼)的方法。

linear damping

这个方法能够很好地使在无重力状态下的物体停下来。

# Demo1.cpp

...
spriteBody->setLinearDamping(5.0f);

我将sprite body的阻尼设置为5.0f,其所产生的具体效果,确定要在调试中才能看出。
很好,sprite在飞一段时间后,能很好地停下来了。

烂尾

固然物理引擎的魅力到这里还并无被彻底探索出来,只是给你们一个系统地学习方案而已。

仍是那句老话,我但愿将所学的一切用来解决实际的问题并将其转化为生产力,以上的所有都是我在学习中了解到的,每一章节都包含了不少小坑坑,不断地填补,以致刻骨铭心。

以上。

相关文章
相关标签/搜索