用cocos2d-x模拟单摆运动的程序

需求

物理原理课需要制作关于单摆在在不同摆角下运动状态的课程报告,所以就想着做出一个有图形界面的单摆演示程序。由于图形界面只有cocos2d-学的还算可以,所以就用这个来做(实际上用MFC或者用WPF做效果会更好一点?……)

理论基础

周期求解

单摆在偏角不太大的情况(高中课本认为小于5°均可)下,单摆的运动可以近似地视为简谐运动。
周期公式
T0=2πlg
但是在摆角较大时该公式误差很大。所以需要引入其他的公式。
在刘凤祥《单摆运动周期的近似解》中得出一个近似周期公式
T=T010.062Θ2
程序使用该公式求得单摆的近似周期。

瞬时角速度

推导出瞬时角速度的微分方程,然后用龙格库塔法求解,在程序中求得瞬时角速度。
直接上图……

有了瞬时角速度之后就可以用update函数移动摆球并表现在图形界面中。

程序代码

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include "ui/CocosGUI.h"
#include "Consts.h"

class HelloWorld : public cocos2d::Layer
{
public:
    enum kState
    {
        kState_Stop,kState_Running,kState_Paused
    };
    HelloWorld();
    ~HelloWorld();
    static cocos2d::Scene* createScene();
    virtual bool init();
    CREATE_FUNC(HelloWorld);

public:
    void sliderEvent(cocos2d::Ref* sender,cocos2d::ui::Slider::EventType type);
    void textFieldEvent(cocos2d::Ref* sender,cocos2d::ui::TextField::EventType type);
    void StartTouchEvent(cocos2d::Ref* sender,cocos2d::ui::Widget::TouchEventType type);
    void PauseTouchEvent(cocos2d::Ref* sender,cocos2d::ui::Widget::TouchEventType type);


public:
    virtual void update(float delta);

protected:
    cocos2d::CustomCommand _customCommand;

private:
    double _startAngel,_l,_T;
    int _halfT;
    double _currentAngel,_currentW,_currentTime;

    kState _state;

    cocos2d::Vec2 _ballPos,_vertexPos,_graphVertexPos;
private:
    cocos2d::ui::Text * m_TText;
    cocos2d::ui::Text * m_WText;
    cocos2d::ui::Text * m_AngelText;
    cocos2d::ui::Text * m_CurrentAngelText;
    cocos2d::ui::Slider * m_AngelSlider;
    cocos2d::ui::TextField * m_LTextField;
    cocos2d::ui::Button * m_PauseButton;
    cocos2d::Sprite * m_ball;

    cocos2d::DrawNode * m_drawNode;
    cocos2d::DrawNode * m_graphNode;
private:
    double getW();
    double getT();

};

#endif // __HELLOWORLD_SCENE_H__
#include "HelloWorldScene.h"
#include "cocostudio/CocoStudio.h"
#include "ui/CocosGUI.h"
// draw
#include "renderer/CCRenderer.h"
#include "renderer/CCCustomCommand.h"

USING_NS_CC;

using namespace cocostudio::timeline;
using namespace cocos2d::ui;


HelloWorld::HelloWorld()
{
    m_TText = nullptr;
    m_WText = nullptr;
    m_AngelText = nullptr;
    m_CurrentAngelText = nullptr;
    m_AngelSlider = nullptr;
    m_LTextField = nullptr;

    _vertexPos = Vec2(300,550);
    _graphVertexPos = Vec2(300,120);
}

HelloWorld::~HelloWorld()
{
}

Scene* HelloWorld::createScene()
{
    auto scene = Scene::create();

    auto layer = HelloWorld::create();

    scene->addChild(layer);

    return scene;
}


bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    // init variables
    _state = kState_Stop;
    // DrawNode
    m_drawNode = DrawNode::create();
    this->addChild(m_drawNode,10);
    m_graphNode = DrawNode::create();
    this->addChild(m_graphNode,9);
    m_graphNode->drawLine(Vec2(0,120),Vec2(600,Color4F::RED);
    m_graphNode->drawLine(Vec2(300,0),Vec2(300,240),Color4F::RED);
    m_graphNode->drawPoint(_vertexPos,20,Color4F::BLUE);


    // init UI
    auto rootNode = CSLoader::createNode("MainScene.csb");
    addChild(rootNode);

    m_TText = static_cast<ui::Text*>(rootNode->getChildByName("Panel_RT")->getChildByName("Text_T_Data"));
    m_WText = static_cast<ui::Text*>(rootNode->getChildByName("Panel_RT")->getChildByName("Text_W_Data"));
    m_CurrentAngelText = static_cast<ui::Text*>(rootNode->getChildByName("Panel_RT")->getChildByName("Text_CurrentAngel_Data"));
    m_AngelText = static_cast<ui::Text*>(rootNode->getChildByName("Panel_RB")->getChildByName("Text_Angel_Data"));
    m_AngelSlider = static_cast<ui::Slider*>(rootNode->getChildByName("Panel_RB")->getChildByName("Slider_Angel"));
    m_AngelSlider->setMaxPercent(90);
    m_AngelSlider->addEventListener(CC_CALLBACK_2(HelloWorld::sliderEvent,this));
    m_LTextField = static_cast<ui::TextField*>(rootNode->getChildByName("Panel_RB")->getChildByName("TextField_l"));
    m_LTextField->addEventListener(CC_CALLBACK_2(HelloWorld::textFieldEvent,this));

    auto startBtn = static_cast<ui::Button*>(rootNode->getChildByName("Panel_RB")->getChildByName("Button_Start"));
    startBtn->addTouchEventListener(CC_CALLBACK_2(HelloWorld::StartTouchEvent,this));
    m_PauseButton = static_cast<ui::Button*>(rootNode->getChildByName("Panel_RB")->getChildByName("Button_Pause"));
    m_PauseButton->addTouchEventListener(CC_CALLBACK_2(HelloWorld::PauseTouchEvent,this));

    // init Sprite
    m_ball = Sprite::create("ball.png");
    m_ball->setAnchorPoint(Vec2(0.5,0.5));
    m_ball->setPosition(_vertexPos);
    this->addChild(m_ball);

    return true;
}

void HelloWorld::sliderEvent(Ref *pSender,cocos2d::ui::Slider::EventType type)
{
    if(type == Slider::EventType::ON_PERCENTAGE_CHANGED)
    {
        Slider* slider = dynamic_cast<Slider*>(pSender);
        int percent = slider->getPercent();
        m_AngelText->setString(StringUtils::format("%d",percent));
        _startAngel = (double)percent / 180 * PI;

        _currentAngel = _startAngel;
        _ballPos = Vec2(_vertexPos.x + _l * 100 * sin(_currentAngel),_vertexPos.y - _l * 100 * cos(_currentAngel));
        m_ball->setPosition(_ballPos);
    }
}

void HelloWorld::textFieldEvent(Ref *pSender,cocos2d::ui::TextField::EventType type)
{
    switch (type)
    {
    case TextField::EventType::ATTACH_WITH_IME:
    {
        TextField* textField = dynamic_cast<TextField*>(pSender);
/* Size screenSize = Director::getInstance()->getWinSize(); textField->runAction(MoveTo::create(0.225f,Vec2(screenSize.width / 2.0f,screenSize.height / 2.0f + textField->getContentSize().height / 2.0f))); */
    }
    break;

    case TextField::EventType::DETACH_WITH_IME:
    {
        TextField* textField = dynamic_cast<TextField*>(pSender);
/* Size screenSize = Director::getInstance()->getWinSize(); textField->runAction(MoveTo::create(0.175f,screenSize.height / 2.0f))); */
    }
    break;
    case TextField::EventType::INSERT_TEXT:
    {
        TextField * textField = dynamic_cast<TextField*>(pSender);
        std::string str = textField->getString();
        int num = atoi(str.c_str());
        log("%d",num);
        _l = num;
    }
        break;
    case TextField::EventType::DELETE_BACKWARD:
        break;
    default:
        break;
    }
}

void HelloWorld::StartTouchEvent(Ref *pSender,Widget::TouchEventType type)
{
    switch (type)
    {
    case Widget::TouchEventType::BEGAN:
        break;

    case Widget::TouchEventType::MOVED:
        break;

    case Widget::TouchEventType::ENDED:
    {
        _currentAngel = _startAngel;
        _currentW = 0;
        _currentTime = 0;
        _state = kState_Running;
        m_PauseButton->setTitleText("PAUSE");
        // set T
        _T = getT();
        m_TText->setText(StringUtils::format("%lf",_T));
        this->scheduleUpdate();
    }
    break;

    case Widget::TouchEventType::CANCELED:
        break;

    default:
        break;
    }
}
void HelloWorld::PauseTouchEvent(Ref *pSender,Widget::TouchEventType type)
{
    switch (type)
    {
    case Widget::TouchEventType::BEGAN:
        break;

    case Widget::TouchEventType::MOVED:
        break;

    case Widget::TouchEventType::ENDED:
    {
        if(_state == kState_Running)
        {
            _state = kState_Paused;
            this->unscheduleUpdate();
            auto button = dynamic_cast<ui::Button*>(pSender);
            button->setTitleText("RESUME");
        }
        else if(_state == kState_Paused)
        {
            _state = kState_Running;
            this->scheduleUpdate();
            auto button = dynamic_cast<ui::Button*>(pSender);
            button->setTitleText("PAUSE");
        }
    }
    break;

    case Widget::TouchEventType::CANCELED:
        break;

    default:
        break;
    }
}


void HelloWorld::update(float delta)
{
    // double W = getW();
    // update data
    double k1,k2,k3,k4,l1,l2,l3,l4;
    {
        k1 = _currentW;
        l1 = -(G / _l) * sin(_currentAngel);
        k2 = _currentW + delta * l1 / 2.0;
        l2 = -(G / _l) * sin(_currentAngel + delta * k1 / 2.0);
        k3 = _currentW + delta * l2 / 2.0;
        l3 = -(G / _l) * sin(_currentAngel + delta * k2 / 2.0);
        k4 = _currentW + delta * l3;
        l4 = -(G / _l) *sin(_currentAngel * delta * k3);

        _currentTime += (double)delta;
        _currentAngel += delta * (k1 + 2 * k2 + 2 * k3 + k4) / 6.0;
        log("%lf",_currentAngel);
        _currentW += delta * (l1 + 2 * l2 + 2 * l3 + l4) / 6.0;
    }
    m_WText->setText(StringUtils::format("%.4lf",_currentW));
    m_CurrentAngelText->setText(StringUtils::format("%.1lf",_currentAngel / PI * 180));

    // draw
    _ballPos = Vec2(_vertexPos.x + _l * 100 * sin(_currentAngel),_vertexPos.y - _l * 100 * cos(_currentAngel));
    m_ball->setPosition(_ballPos);

    m_drawNode->clear();
    m_drawNode->drawLine(_vertexPos,_ballPos,Color4F::BLUE);

    // graph
    m_graphNode->drawPoint(Vec2(_graphVertexPos.x + _currentAngel * 200,_graphVertexPos.y + _currentW * 50),2,Color4F::RED);
}

double HelloWorld::getW()
{
    double W;
    W = sqrt(2 / _l*_l) * sqrt(9.8*_l*(cos(_currentAngel) - cos(_startAngel)));
    if (_halfT % 2 == 0)
        W = -W;
    return W;
}

double HelloWorld::getT()
{
    double T;
// double T1,T2,T3;
// T3 = sqrt(_l / G);
// T1 = 2 * PI * sqrt(_l / G);
// T2 = 1 - 0.062*_startAngel*_startAngel;
    T = (2 * PI * sqrt(_l / G)) /
        (1 - 0.062*_startAngel*_startAngel);
// T = T1 / T2;
    return T;
}

程序截图以及下载


下载地址:
http://download.csdn.net/detail/jjhfen00/9330825

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...