使用Cocos2d-x 3.0和物理引擎制作简单的platformer游戏

来源:互联网 发布:张民弢 知乎 编辑:程序博客网 时间:2024/06/13 20:18
前言
本教程建立在上一篇教程的基础之上,所以,在继续之前,建议你先看完《使用Cocos2d-x 3.0和物理引擎制作滚动背景》。

内容大纲
我将在本教程中覆盖下面的内容:
1. 简单的面向对象游戏设计
2. 用户输入和touch检测
3. 给游戏对象施加力,使之移动和跳跃
4. 碰撞检测


简单的面向对象设计

这个教程是个非常简单的平台游戏,只有一些简单的平台和一个圆形的主角。说明一下,我在这里使用的是上一个教程中的TileMap编辑器来制作游戏地图的,所以,你可以参照上一篇教程。本游戏示例中只有两种对象。一种是player对象,另一种是platform对象。这两种对象都继承至GameObject类。而GameObject类又继承至Sprite类,同时它包含一个type属性,用来区分不同的游戏对象。把游戏对象都继承至同一基类有一个好处,就是在做碰撞检测的时候,可以直接强制转换过来。

下面是GameObject类的代码,它从Sprite类继承过来的。同时把它的type初始化为kGameObjectNone。(这是一个枚举类型,它定义在Constants.h中,本教程不会显示它的实现,但是大家可以在后面下载的源码中找到其实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// GameObject.h
#include "cocos2d.h"
#include "Constants.h"
 
USING_NS_CC;
 
classGameObject :publicSprite
{
public:
    GameObjectType  type;
 
    virtualbool init();
 
    CREATE_FUNC(GameObject);
};
 
////////////////////////////////////////////////////////
 
// GameObject.cpp
#include "GameObject.h"
 
boolGameObject::init()
{
    if(!Node::init())
    {
        returnfalse;
    }
 
    returntrue;
}

接下来是我们的Player类。


Player主要负责创建它自己的box2d世界中的body,当你调用createBox2dObject的时候,就会创建相应的body,同时把它加到world中去。同时,player类还包含使player往右移动和跳跃的方法,我们会在教程的后面讨论。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "cocos2d.h"
#include "GameObject.h"
 
USING_NS_CC;
 
classPlayer :publicGameObject
{
public:
    PhysicsBody* body;
 
    voidcreatedObject();
    voidjump();
    voidmoveRight();
 
    virtualbool init();
 
    CREATE_FUNC(Player);
};
 
///////////////////////////////////////////////////////
 
#include "Player.h"
#include "Constants.h"
 
boolPlayer::init()
{
    if(!GameObject::init())
    {
        returnfalse;
    }
 
    returntrue;
}
 
voidPlayer::createdObject()
{
    body = PhysicsBody::createCircle(0.7f);
    body->getShape(0)->setRestitution(1.0f);
    body->getShape(0)->setFriction(1.0f);
    body->getShape(0)->setDensity(1.0f);
    body->setRotationEnable(false);
    this->setPhysicsBody(body);
}
 
voidPlayer::moveRight()
{
    Vect impulse = Vect(50.0f, 0.0f);
    body->applyImpulse(impulse);
}
 
voidPlayer::jump()
{
    Vect impulse = Vect(0.0f, 100.0f);
    body->applyImpulse(impulse);
}


用户输入 / Touch检测

下面几行行代码定义在GameScene的onEnter方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
voidGameScene::onEnter()
{
    Layer::onEnter();
 
    auto listener = EventListenerTouchOneByOne::create();
    listener->setSwallowTouches(true);
 
    listener->onTouchBegan = CC_CALLBACK_2(GameScene::onTouchBegan, this);
 
    auto contactListener = EventListenerPhysicsContact::create();
    contactListener->onContactBegin = CC_CALLBACK_2(GameScene::onContactBegin, this);
 
 
    auto dispatcher = Director::getInstance()->getEventDispatcher();
 
    dispatcher->addEventListenerWithSceneGraphPriority(listener,this);
    dispatcher->addEventListenerWithSceneGraphPriority(contactListener,this);
}

下面的方法是在你鼠标点击模拟器或者手触摸设备屏幕的时候被调用的,也就是touch事件。我们需要把touch坐标点转换成cocos2d的坐标点。然后,我们判断,当单击的点在右半屏幕的时候,就让player往右移动。当单击左半边屏幕的时候,就让player跳起来。可以跳到platfrom上哦,呵呵。如果在下面起跳,会撞到头。这可能不是我们想要的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
boolGameScene::onTouchBegan(Touch *touch, Event *event)
{
    Point touchLocation = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));
 
    if(touchLocation.x >= screenSize.width / 2)
    {
        player->moveRight();
    }
    else
    {
        player->jump();
    }
    returnTRUE;
}

给游戏对象施加力,使之移动和跳跃

1
2
3
4
5
6
7
8
9
10
11
voidPlayer::moveRight()
{
    Vect impulse = Vect(50.0f, 0.0f);
    body->applyImpulse(impulse);
}
 
voidPlayer::jump()
{
    Vect impulse = Vect(0.0f, 100.0f);
    body->applyImpulse(impulse);
}

Wrap-up and Loose Ends

下面是GameScene类的实现。这里面使用的技术大家应该都接触过了,如果不清楚,可以参考我翻译的其它教程,或者在下方留言。可能需要指出来的是碰撞检测的代码,大家可以花点时间看看。


GameScene.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "cocos2d.h"
#include "Player.h"
 
USING_NS_CC;
 
classGameScene :publicLayer
{
public:
    Size screenSize;
 
    PhysicsWorld* m_world;
 
    Player* player;
 
    TMXTiledMap* tileMapNode;
 
    voidsetPhyWorld(PhysicsWorld* world){ m_world = world; };
 
    voidmakeBox2dObjAt(Point p, Size size, boold, floatr, floatfriction, floatdensity, floatrestitution);
 
    voiddrawCollisionTiles();
 
    voidaddScrollingBackgroundWithTileMap();
 
    voidvirtual update(floatdt);
 
    staticcocos2d::Scene* createScene();
 
    virtualbool init();
 
    virtualvoid onEnter();
 
    boolonTouchBegan(Touch *touch, Event *event);
 
    boolonContactBegin(EventCustom* event, constPhysicsContact& contact);
 
    CREATE_FUNC(GameScene);
};


GameScene.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include "GameScene.h"
 
Scene* GameScene::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::createWithPhysics();
 
    //scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    /*Vect gravity(0.0f, -600.0f);
    scene->getPhysicsWorld()->setGravity(gravity);*/
 
    // 'layer' is an autorelease object
    auto layer = GameScene::create();
 
    layer->setPhyWorld(scene->getPhysicsWorld());
 
    // add layer as a child to scene
    scene->addChild(layer);
 
    // return the scene
    returnscene;
}
 
boolGameScene::init()
{
    if(!Layer::init())
    {
        returnfalse;
    }
 
    // enable touches
 
    screenSize = Director::getInstance()->getWinSize();
 
    this->addScrollingBackgroundWithTileMap();
    this->drawCollisionTiles();
 
    player = (Player*)Sprite::create("Icon-Small.png");
    player->retain();
    player->setPosition(100.0f, 180.0f);
    player->createdObject();
 
    this->addChild(player);
 
    // Start main game loop
    this->scheduleUpdate();
 
    returntrue;
}
 
voidGameScene::makeBox2dObjAt(Point p, Size size, boold, floatr, floatfriction, floatdensity, floatrestitution)
{
    auto sprite = Sprite::create();
    auto body = PhysicsBody::createBox(size, PHYSICSBODY_MATERIAL_DEFAULT);
    body->getShape(0)->setDensity(density);
    body->getShape(0)->setFriction(friction);
    body->getShape(0)->setRestitution(restitution);
    body->setDynamic(d);
    sprite->setPosition(p);
    sprite->setPhysicsBody(body);
    this->addChild(sprite);
}
 
voidGameScene::drawCollisionTiles()
{
    TMXObjectGroup *objects = tileMapNode->objectGroupNamed("Collision");
    floatx, y, w, h;
    ValueVector objectsPoint = objects->getObjects();
    Value objPointMap;
    foreach(objPointMap in objectsPoint)
    {
        ValueMap objPoint = objPointMap.asValueMap();
        x = objPoint.at("x").asFloat();
        y = objPoint.at("y").asFloat();
        w = objPoint.at("width").asFloat();
        h = objPoint.at("height").asFloat();
 
        Point _point = Point(x + w / 2.0f, y + h / 2.0f);
        Size _size = Size(w, h);
 
        this->makeBox2dObjAt(_point, _size, false, 0, 0.0f, 0.0f, 0);
    }
}
 
voidGameScene::addScrollingBackgroundWithTileMap()
{
    tileMapNode = TMXTiledMap::create("scroller.tmx");
    tileMapNode->setAnchorPoint(Point(0, 0));
    this->addChild(tileMapNode);
}
 
voidGameScene::update(floatdt)
{
    Point pos = player->getPosition();
    this->setPosition(-1 * pos.x + 100, this->getPositionY());
}
 
voidGameScene::onEnter()
{
    Layer::onEnter();
 
    auto listener = EventListenerTouchOneByOne::create();
    listener->setSwallowTouches(true);
 
    listener->onTouchBegan = CC_CALLBACK_2(GameScene::onTouchBegan, this);
 
    auto contactListener = EventListenerPhysicsContact::create();
    contactListener->onContactBegin = CC_CALLBACK_2(GameScene::onContactBegin, this);
 
 
    auto dispatcher = Director::getInstance()->getEventDispatcher();
 
    dispatcher->addEventListenerWithSceneGraphPriority(listener,this);
    dispatcher->addEventListenerWithSceneGraphPriority(contactListener,this);
}
 
boolGameScene::onContactBegin(EventCustom* event, constPhysicsContact& contact)
{
    returntrue;
}
 
boolGameScene::onTouchBegan(Touch *touch, Event *event)
{
    Point touchLocation = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));
 
    if(touchLocation.x >= screenSize.width / 2)
    {
        player->moveRight();
    }
    else
    {
        player->jump();
    }
    returnTRUE;
}

这里有本教程的完整源代码


0 0
原创粉丝点击