【计算机游戏开发】碰撞检测与运动模拟

Alex_Shen
2021-08-10 / 0 评论 / 0 点赞 / 118 阅读 / 5,074 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-04-06,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

github项目地址

一、实验目的与要求

  1. 了解物理模拟、精灵的绘制与移动、触摸事件的应用等。
  2. 熟悉基于Box2D的物理引擎。
  3. 掌握Box2D触屏检测和碰撞检测机制。

二、实验内容与方法

1.完成游戏编译(60分)
成功编译并运行教材P150“游戏物理模拟实例-基于Box2D的游戏实例”。

2.修改击球方式 (10分)
修改教材源代码,使玩家在击球区域拖动鼠标左键,可以实现球杆的瞄准和能量的蓄积;当玩家松开鼠标左键,实现击球。

3.增加进洞口 (10分)
修改教材源代码,在角落增加(至少)一个进洞口,彩色球接触洞口后消失;白球接触洞口后消失并重启游戏。

4.修改球杆随距离变化的力度 (10分)
修改教材源代码,使球杆与台球之间的距离能够反映玩家击球力度大小。距离较远,则力度较大;距离较近,则力度较小。

5.其它优化功能 (10分)
修改教材源代码,使台球游戏性能更佳。

三、实验步骤与过程

1.完成游戏编译(60分)

  1. 导入文件,并完成编译
    在这里插入图片描述 在这里插入图片描述
    在这里插入图片描述

2.修改击球方式 (10分)

(1) 将击球方式更改为在击球区域拖动鼠标左键,实现瞄准以及蓄力。
(2) 首先更改鼠标调用函数

1.bool PhysicsBox2dScene::onTouchBegan(Touch* touch, Event* unused_event)  
2.{  
3.    if (_aimRect.containsPoint(touch->getLocation()) && _canAim)  
4.    {  
5.        _aim = true;  
6.        auto start_p = changePos(_mainBilliards->getPosition());  
7.        auto end_p = touch->getLocation();  
8.        _curPower = (end_p - start_p).getLength() / 2;  
9.        updateLine(start_p, end_p);  
10.        updatePowerSlider(_curPower);  
11.    }  
12.    _powerEnd = false;  
13.    return true;  
14.}  

根据鼠标点击位置与白球的相对位置,更新力度。

1.void PhysicsBox2dScene::onTouchMoved(Touch* touch, Event* unused_event)  
2.{  
3.    auto slider_bg = (Sprite*)getChildByTag(SLIDER_BG_TAG);  
4.    if (_aimRect.containsPoint(touch->getStartLocation()) && _canAim)  
5.    {  
6.        if (_aimRect.containsPoint(touch->getLocation()))  
7.        {  
8.            auto start_p = changePos(_mainBilliards->getPosition());  
9.            auto end_p = touch->getLocation();  
10.            updateLine(start_p, end_p);  
11.            _curPower = (end_p - start_p).getLength() / 2;  
12.  
13.            if (_curPower < 0) {  
14.                _curPower = 0;  
15.            }  
16.            else if (_curPower > slider_bg->getContentSize().height)  
17.            {  
18.                _curPower = slider_bg->getContentSize().height;  
19.            }  
20.            updatePowerSlider(_curPower);  
21.        }  
22.    }  
23.}  

鼠标滑动时,实时更新力度,并且更新力度条

1.void PhysicsBox2dScene::onTouchEnded(Touch* touch, Event* unused_event)  
2.{  
3.    if (_aim) {  
4.        auto slider_bg = (Sprite*)getChildByTag(SLIDER_BG_TAG);  
5.        _sliderSpeed = _curPower / slider_bg->getContentSize().height * 50.0f; 
6.  
7.        // 击球  
8.        auto start_p = changePos(_mainBilliards->getPosition());  
9.        auto end_p = _aimPos;  
10.        auto v = changePos((end_p - start_p).getNormalized());  
11.        float q = _curPower / slider_bg->getContentSize().height;    
12.        // 设置白球速度  
13.        _mainBilliards->SetLinearVelocity(b2Vec2(v.x * q * MAX_SPEED, v.y * q * MAX_SPEED));  
14.  
15.        auto dash_line = (Sprite*)getChildByTag(DASH_LINE_TAG);  
16.        dash_line->setVisible(false);  
17.        auto aim_icon = (Sprite*)getChildByTag(AIM_ICON_TAG);  
18.        aim_icon->setVisible(false);  
19.        auto aim_gan = (Sprite*)getChildByTag(AIM_GAN_TAG);  
20.        aim_gan->setVisible(false);    
21.        // 击球运动后,在所有物体都静止之前无法再进行瞄准和击球  
22.        _canAim = false;  
23.        _aim = false;    
24.        _powerEnd = true; // 进度条可以消除  
25.    }  
26.  
27.}  

鼠标释放时,进行击球动画,并且重置力度条。
在这里插入图片描述
注:需要注意修改碰撞检测中的代码,否则会出现对象丢失

1.void GameContactListener::BeginContact(b2Contact*contact)  
2.{  
3.    char* a = (char*)contact->GetFixtureA()->GetBody()->GetUserData();  
4.    char* b = (char*)contact->GetFixtureB()->GetBody()->GetUserData();  
5.    if (strcmp(a, "boundary") == 0 || strcmp(b, "boundary") == 0)  
6.        return;  
7.}  

3.增加进洞口 (10分)

在GameConfig中设置球洞的初始位置(四个角)

1.static const Vec2 holePos[] = {  
2.    Vec2(0,0),  
3.    Vec2(1024,768),  
4.    Vec2(1024,0),  
5.    Vec2(0,768)  
6.};  

在PhysicsBox2dScene::init()中创建球洞对象,并且将其其设置为静态物体(isStatic=True)。

1.for (int i = 0; i < 4; i++) {  
2.    auto holeSprite = BilliardSprite::create("hole", true, false, _world, "Chapter07/hole/hole.png", holePos[i]);  
3.    this->addChild(holeSprite);    
4.}  

进行碰撞检测
当白球碰到球洞时,设置bool值ballinhole(检测是否白球落袋),调用restart,重置画面

1.void GameContactListener::BeginContact(b2Contact*contact)  
2.{  
3.    BilliardSprite* ball1 = static_cast<BilliardSprite*>(contact->GetFixtureA()->GetBody()->GetUserData());  
4.    BilliardSprite* ball2 = static_cast<BilliardSprite*>(contact->GetFixtureB()->GetBody()->GetUserData());  
5.    if (strcmp("main", ball1->getName()) == 0 && strcmp("hole", ball2->getName()) == 0)  
6.        mainballinhole = true;  
7.}  
1.void PhysicsBox2dScene::restart(float t)  
2.{  
3.    CCDirector::sharedDirector()->replaceScene(PhysicsBox2dScene::createScene());  
4.}  
5.  
6.void PhysicsBox2dScene::update(float delta)  
7.{  
8.    if (_ContactListener->mainballinhole) {  
9.        PhysicsBox2dScene::restart(0.0);  
10.    }  
11.}  

其他普通球落袋时,将需要删除的球加入列表中。更新时,统一删除列表中的元素。

1.void GameContactListener::BeginContact(b2Contact*contact)  
2.{  
3.    if (strcmp("ball", ball1->getName()) == 0 && strcmp("hole", ball2->getName()) == 0)  
4.        RemoveList.insert(ball1);  
5.}  

1.void GameContactListener::deleteBody()  
2.{  
3.    set<BilliardSprite*>::iterator it = RemoveList.begin();  
4.    set<BilliardSprite*>::iterator end = RemoveList.end();  
5.  
6.    for (; it != end; it++) {  
7.        BilliardSprite* dyingBall = *it;  
8.        dyingBall->deleteBody();  
9.    }  
10.    RemoveList.clear();  
11.}  
1.void BilliardSprite::deleteBody()  
2.{  
3.    _body->GetWorld()->DestroyBody(_body);  
4.    removeChild(_sprite, true);  
5.    this->unscheduleAllSelectors();  
6.}  

具体效果见视频

4.修改球杆随距离变化的力度 (10分)

根据力度,修改球杆与球的相对位置。

1.void PhysicsBox2dScene::updateLine(Vec2 start_p, Vec2 end_p)  
2.{  
3.    // 计算球杆位置  
4.    auto aim_gan_pos = Vec2(  
5.        (((end_p - start_p).getLength() + (45 + _curPower / 2)) * start_p.x - (45 + _curPower / 2) * end_p.x) / (end_p - start_p).getLength(),  
6.        (((end_p - start_p).getLength() + (45 + _curPower / 2)) * start_p.y - (45 + _curPower / 2) * end_p.y) / (end_p - start_p).getLength());  
7.  
8.    auto aim_gan = (Sprite*)getChildByTag(AIM_GAN_TAG);  
9.    aim_gan->setPosition(aim_gan_pos);  
10.    aim_gan->setRotation(CC_RADIANS_TO_DEGREES(-_angle) - 90.0f);  
11.    aim_gan->setVisible(true);  
12.}  

具体效果见视频

0

评论区