首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Cocos2d-x 整合 Box2D 打造彈弓遊戲完整教程(上)

     編輯:關於C語言

本教程系列將引導你使用 Cocos2d-x 1.0.1-x-0.11.0 版本與 Box2D 物理引擎,從零構建一款酷炫的彈弓射擊遊戲。教程改編自 Ray Wenderlich 經典 iOS 教程,將原 Objective-C 代碼移植為 C++ 版本,並使用 Vicki Wenderlich 繪製的彈弓、橡子、狗、貓與憤怒松鼠等美術素材。

學習目標

  • 掌握旋轉關節的創建與配置
  • 理解焊接關節在子彈掛載中的應用
  • 實現視角跟隨拋射體的攝影機邏輯
  • 基於碰撞衝量判斷敵人銷毀條件
  • 完整遊戲場景搭建與物理模擬流程

先備知識: 需熟悉 Box2D 入門教程 或具備同等物理引擎基礎。

專案初始化與靜態場景搭建

創建 HelloWorld 專案並清空預設內容,勾選 Box2D 支援。聲明 Catapult 類繼承自 CCLayer。在 init 方法中加載背景、彈弓基座、地面、松鼠等靜態精靈,將錨點統一設為左下角 CCPointZero 以便精確定位:

CCSprite* sprite = CCSprite::spriteWithFile("bg.png");
sprite->setAnchorPoint(CCPointZero);
this->addChild(sprite, -1); // 背景層

#define FLOOR_HEIGHT 62.0f // 地面高度宏,便於統一調整

sprite = CCSprite::spriteWithFile("catapult_base_2.png");
sprite->setAnchorPoint(CCPointZero);
sprite->setPosition(ccp(181.0, FLOOR_HEIGHT));
this->addChild(sprite, 0); // 彈弓底座後層

// 依序添加左松鼠、彈弓前座、右松鼠、冰面地面...

靜態場景效果

物理世界與彈弓臂創建

在類中聲明 b2World* m_world、b2Body* m_groundBody、b2Body* m_armBody、b2Fixture* m_armFixture。初始化物理世界,重力設為 (0, -10),啟用睡眠機制與連續碰撞檢測。創建地面剛體並添加四邊邊界:

b2EdgeShape groundBox;
// 底部邊界
groundBox.Set(b2Vec2(0, FLOOR_HEIGHT/PTM_RATIO),
              b2Vec2(screenSize.width*2.0f/PTM_RATIO, FLOOR_HEIGHT/PTM_RATIO));
m_groundBody->CreateFixture(&groundBox, 0);
// 頂部、左側、右側邊界同理...

創建彈弓臂動態剛體,位置設在基座上方,夾具尺寸略小於精靈圖片實際顯示區域:

CCSprite* arm = CCSprite::spriteWithFile("catapult_arm.png");
this->addChild(arm, 1);

b2BodyDef armBodyDef;
armBodyDef.type = b2_dynamicBody;
armBodyDef.linearDamping = 1;
armBodyDef.angularDamping = 1;
armBodyDef.position.Set(230.0f/PTM_RATIO, (FLOOR_HEIGHT+91.0f)/PTM_RATIO);
armBodyDef.userData = arm;
m_armBody = m_world->CreateBody(&armBodyDef);

b2PolygonShape armBox;
b2FixtureDef armBoxDef;
armBoxDef.shape = &armBox;
armBoxDef.density = 0.3f;
armBox.SetAsBox(11.0f/PTM_RATIO, 91.0f/PTM_RATIO);
m_armFixture = m_armBody->CreateFixture(&armBoxDef);

夾具與精靈尺寸對比

旋轉關節:賦予彈弓臂真實擺動

使用 b2RevoluteJoint 將彈弓臂釘在地面剛體上,限制旋轉角度 9°~75°,並啟用馬達模擬彈性回彈:

b2RevoluteJointDef armJointDef;
armJointDef.Initialize(m_groundBody, m_armBody,
    b2Vec2(233.0f/PTM_RATIO, FLOOR_HEIGHT/PTM_RATIO));
armJointDef.enableMotor = true;
armJointDef.enableLimit = true;
armJointDef.motorSpeed = -10; // 負值順時針旋轉
armJointDef.lowerAngle = CC_DEGREES_TO_RADIANS(9);
armJointDef.upperAngle = CC_DEGREES_TO_RADIANS(75);
armJointDef.maxMotorTorque = 700;
m_armJoint = (b2RevoluteJoint*)m_world->CreateJoint(&armJointDef);

加入旋轉關節後效果

鼠標關節:實現滑鼠/觸控拉動彈弓

在類中聲明 b2MouseJoint* m_mouseJoint 並實現觸控回調:

void Catapult::ccTouchesBegan(CCSet* touches, CCEvent* event) {
    if (m_mouseJoint != NULL) return;
    CCTouch* touch = (CCTouch*)touches->anyObject();
    CCPoint location = touch->locationInView(touch->view());
    location = CCDirector::sharedDirector()->convertToGL(location);
    b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);

    // 僅在觸點位於彈弓臂左側區域時創建關節
    if (locationWorld.x < m_armBody->GetWorldCenter().x + 150.0/PTM_RATIO) {
        b2MouseJointDef md;
        md.bodyA = m_groundBody;
        md.bodyB = m_armBody;
        md.target = locationWorld;
        md.maxForce = 2000; // 需足夠大以對抗旋轉關節馬達扭矩
        m_mouseJoint = (b2MouseJoint*)m_world->CreateJoint(&md);
    }
}

void Catapult::ccTouchesMoved(CCSet* touches, CCEvent* event) {
    if (m_mouseJoint == NULL) return;
    // 同步更新鼠標關節目標點
    CCTouch* touch = (CCTouch*)touches->anyObject();
    CCPoint location = touch->locationInView(touch->view());
    location = CCDirector::sharedDirector()->convertToGL(location);
    b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
    m_mouseJoint->SetTarget(locationWorld);
}

void Catapult::ccTouchesEnded(CCSet* touches, CCEvent* event) {
    if (m_mouseJoint != NULL) {
        m_world->DestroyJoint(m_mouseJoint);
        m_mouseJoint = NULL;
    }
}

運行測試,滑鼠左鍵拖拽彈弓臂後釋放,臂會在馬達驅動下快速回彈。

拉動彈弓效果

子彈系統:預生成、焊接關節與發射邏輯

為避免運行時頻繁創建銷毀,啟動時預生成多顆橡子子彈並停用:

std::vector m_bullets;
int m_currentBullet;

void Catapult::createBullets(int count) {
    m_currentBullet = 0;
    float pos = 62.0f;
    for (int i=0; iaddChild(sprite, 1);
        b2BodyDef bulletBodyDef;
        bulletBodyDef.type = b2_dynamicBody;
        bulletBodyDef.bullet = true; // 關鍵:標記為高速子彈,啟用 CCD 防止穿透
        bulletBodyDef.position.Set(pos/PTM_RATIO, (FLOOR_HEIGHT+15)/PTM_RATIO);
        bulletBodyDef.userData = sprite;
        b2Body* bullet = m_world->CreateBody(&bulletBodyDef);
        bullet->SetActive(false); // 初始停用

        b2CircleShape circle;
        circle.m_radius = 15.0/PTM_RATIO;
        b2FixtureDef ballShapeDef;
        ballShapeDef.shape = &circle;
        ballShapeDef.density = 0.8f;
        ballShapeDef.restitution = 0.2f;
        ballShapeDef.friction = 0.99f;
        bullet->CreateFixture(&ballShapeDef);
        m_bullets.push_back(bullet);
    }
}

發射前調用 attachBullet() 將當前子彈移至彈弓尖端,用 b2WeldJoint 焊接固定:

bool Catapult::attachBullet() {
    if (m_currentBullet < m_bullets.size()) {
        m_bulletBody = m_bullets.at(m_currentBullet++);
        m_bulletBody->SetTransform(b2Vec2(230/PTM_RATIO, (155+FLOOR_HEIGHT)/PTM_RATIO), 0);
        m_bulletBody->SetActive(true);

        b2WeldJointDef weldJointDef;
        weldJointDef.Initialize(m_bulletBody, m_armBody,
            b2Vec2(230/PTM_RATIO, (155+FLOOR_HEIGHT)/PTM_RATIO));
        weldJointDef.collideConnected = false; // 避免子彈與彈弓臂碰撞
        m_bulletJoint = (b2WeldJoint*)m_world->CreateJoint(&weldJointDef);
        return true;
    }
    return false;
}

在 tick 更新循環中檢測彈弓臂回彈角度,當接近初始位置(≤10°)時銷毀焊接關節,子彈隨即飛出:

if (m_releasingArm && m_bulletJoint != NULL) {
    if (m_armJoint->GetJointAngle() <= CC_DEGREES_TO_RADIANS(10)) {
        m_releasingArm = false;
        m_world->DestroyJoint(m_bulletJoint);
        m_bulletJoint = NULL;
    }
}

調整旋轉關節 maxMotorTorque 為 700 可獲得適中的發射速度。

跟隨攝影機:讓視角隨子彈移動

在 tick 中添加視角跟隨邏輯:

if (m_bulletBody && m_bulletJoint == NULL) { // 子彈已發射且未焊接
    b2Vec2 pos = m_bulletBody->GetPosition();
    CCPoint myPos = this->getPosition();
    CCSize screenSize = CCDirector::sharedDirector()->getWinSize();
    if (pos.x > screenSize.width/2/PTM_RATIO) {
        myPos.x = -MIN(screenSize.width*2 - screenSize.width,
                       pos.x*PTM_RATIO - screenSize.width/2);
        this->setPosition(myPos);
    }
}

跟隨攝影機效果

第一部分完成彈弓核心機制,第二部分將實現敵人佈局、碰撞銷毀、粒子特效與完整遊戲循環。資源打包見附件,原文地址:Ray Wenderlich 教程原文

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved