4

私はC++でSDL、OpenGLなどを自己学習しています。オンラインのチュートリアルは、画面の周りを移動するボックスと、ボックスが通過できない棚を作成するのに役立ちました...

ボックスをジャンプまたはホップする方法を説明および示すチュートリアルを見つけることができませんでした. 理想的には、古い学校のマリオのように、箱が移動して棚に飛び上がるようにしたい.

これがSDLでできるかどうかわかりませんか? 上を押した後にボックスを地面に戻すには、何かを追加する必要があると思います... up/downキーを試して、押した後にボックスを地面に戻そうとしましたが、そうではありませんでした仕事。ボックスを地面に置いてみましたが、上キーが機能しません。また、上が押されてボックスが移動し、ボックスが同じ距離だけ下に移動したことも試しました...どのように実装する必要があるかを理解しています(私は思います)、私は十分なC++を知らないだけですそれを書くか、それができるかどうか。

現在、私は左右上下に移動できますが、彼は空中に浮いたままです:(

これが私のコードです:

    #include "SDL.h"
    #include "SDL_image.h"
    #include <string>

    const int SCREEN_WIDTH = 480;
    const int SCREEN_HEIGHT = 480;
    const int SCREEN_BPP = 32;
    const int FRAMES_PER_SECOND = 20;
    const int SQUARE_WIDTH = 20;
    const int SQUARE_HEIGHT = 20;

    SDL_Surface *square = NULL;
    SDL_Surface *screen = NULL;
    SDL_Event event;
    SDL_Rect wall;

    class Square{
        private:
        SDL_Rect box;
        int xVel, yVel;
        public:
        Square();
        void handle_input();
        void move();
        void show();};

    class Timer{
        private:
        int startTicks;
        int pausedTicks;
        bool paused;
        bool started;
        public:
        Timer();
        void start();
        void stop();
        void pause();
        void unpause();
        int get_ticks();
        bool is_started();
        bool is_paused();};

    SDL_Surface *load_image( std::string filename ){
        SDL_Surface* loadedImage = NULL;
        SDL_Surface* optimizedImage = NULL;
        loadedImage = IMG_Load( filename.c_str() );
        if( loadedImage != NULL )
        {
            optimizedImage = SDL_DisplayFormat( loadedImage );
            SDL_FreeSurface( loadedImage );
            if( optimizedImage != NULL )
            {
                SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( optimizedImage->format, 237, 145, 33 ) );//optimizedImage->format, 0, 0xFF, 0xFF
            }
        }
        return optimizedImage;}

    void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL ){
        SDL_Rect offset;
        offset.x = x;
        offset.y = y;
        SDL_BlitSurface( source, clip, destination, &offset );}

    bool check_collision( SDL_Rect A, SDL_Rect B ){
        int leftA, leftB;
        int rightA, rightB;
        int topA, topB;
        int bottomA, bottomB;

        leftA = A.x;
        rightA = A.x + A.w;
        topA = A.y;
        bottomA = A.y + A.h;

        leftB = B.x;
        rightB = B.x + B.w;
        topB = B.y;
        bottomB = B.y + B.h;

        if( bottomA <= topB ){return false;}
        if( topA >= bottomB ){return false;}
        if( rightA <= leftB ){return false;}
        if( leftA >= rightB ){return false;}
        return true;}

    bool init(){
        if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 ){return false;}

        screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );
        if( screen == NULL ){return false;}

        SDL_WM_SetCaption( "Move the Square", NULL );
        return true;}

    bool load_files(){
        square = load_image( "square.bmp" );
        if( square == NULL ){return false;}
        return true;}

    void clean_up(){
        SDL_FreeSurface( square );
        SDL_Quit();}

    Square::Square(){
        box.x = 50;
        box.y = 360;
        box.w = SQUARE_WIDTH;
        box.h = SQUARE_HEIGHT;
        xVel = 0;
        yVel = 0;}

    void Square::handle_input(){
        if( event.type == SDL_KEYDOWN ){
            switch( event.key.keysym.sym ){
                case SDLK_UP: yVel -= SQUARE_HEIGHT / 2; break;
                //case SDLK_DOWN: yVel += SQUARE_HEIGHT / 2; break;
                case SDLK_LEFT: xVel -= SQUARE_WIDTH / 2; break;
                case SDLK_RIGHT: xVel += SQUARE_WIDTH / 2; break;}}
        else if( event.type == SDL_KEYUP ){
            switch( event.key.keysym.sym ){
                case SDLK_UP: yVel += SQUARE_HEIGHT / 2; break;
                //case SDLK_DOWN: yVel -= SQUARE_HEIGHT / 2; break;
                case SDLK_LEFT: xVel += SQUARE_WIDTH / 2; break;
                case SDLK_RIGHT: xVel -= SQUARE_WIDTH / 2; break;}}}

    void Square::move(){
        box.x += xVel;
        if( ( box.x < 0 ) || ( box.x + SQUARE_WIDTH > SCREEN_WIDTH ) || ( check_collision( box, wall ) ) ){
            box.x -= xVel;}

        box.y += yVel;

        if( ( box.y < 0 ) || ( box.y + SQUARE_HEIGHT > SCREEN_HEIGHT ) || ( check_collision( box, wall ) ) ){
            box.y -= yVel;}}

    void Square::show(){
        apply_surface( box.x, box.y, square, screen );}

    Timer::Timer(){
        startTicks = 0;
        pausedTicks = 0;
        paused = false;
        started = false;}

    void Timer::start(){
        started = true;
        paused = false;
        startTicks = SDL_GetTicks();}

    void Timer::stop(){
        started = false;
        paused = false;}

    void Timer::pause(){
        if( ( started == true ) && ( paused == false ) ){
            paused = true;
            pausedTicks = SDL_GetTicks() - startTicks;}}

    void Timer::unpause(){
        if( paused == true ){
            paused = false;
            startTicks = SDL_GetTicks() - pausedTicks;
            pausedTicks = 0;}}

    int Timer::get_ticks(){
        if( started == true ){
            if( paused == true ){
                return pausedTicks;}
            else{return SDL_GetTicks() - startTicks;}
        }return 0;}

    bool Timer::is_started(){return started;}
    bool Timer::is_paused(){return paused;}

    int main( int argc, char* args[] ){
        bool quit = false;
        Square mySquare;
        Timer fps;
        if( init() == false ){return 1;}
        if( load_files() == false ){return 1;}

        wall.x = 130;
        wall.y = 300;
        wall.w = 220;
        wall.h = 20;

        while( quit == false ){
            fps.start();
            while( SDL_PollEvent( &event ) ){
                mySquare.handle_input();
                if( event.type == SDL_QUIT ){
                    quit = true;}}

            mySquare.move();
            SDL_FillRect( screen, &screen->clip_rect, SDL_MapRGB( screen->format, 1, 1, 1 ) );
            SDL_FillRect( screen, &wall, SDL_MapRGB( screen->format, 237, 145, 33 ) );
            mySquare.show();

            if( SDL_Flip( screen ) == -1 ){return 1;}
            if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND ){
                SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );}}

        clean_up();
        return 0;}
4

4 に答える 4

3

昔ながらのマリオは、ジャンプに手作りの状態ベースの関数を使用していると確信しています。純粋な意味で、実際の物理学や正弦/放物線の曲線にリモートで似ているものはありません(ただし、ジャンプの頂点にそのような数学を使用する場合があります) )。

検討する入力と状態は、STATE_PRESSED、STATE_DECELERATE、STATE_DESCEND、STATE_FREEFALLのようなものです。

STATE_PRESSEDは、ユーザーがボタンを押している間、またはタイムアウトが発生するまで、直線的に上向きに移動します。これにより、ユーザーは最大ジャンプ高さを制限しながらジャンプ高さを制御できます。STATE_DECELERATEは通常、放物線関数が機能する場所です。ただし、多くのプラットフォーマーは、ユーザーがジャンプボタンを押し続けた場合、この状態の間、減速の速度をわずかに遅くすることに注意してください。最大の高さを得るためにジャンプボタンをジャンプの頂点まで押し続けていることに暗黙のうちに気付いた場合、どのゲームでもこれがいつ行われるかを知ることができます。

STATE_DESCENDは非常に明白であり、放物線関数でおそらく十分です。特定の速度に達すると、状態はSTATE_FREEFALLに移行する必要があります。この時点で、速度は増加しなくなります。これらの状態はどちらも、ユーザーのボタンを押しても影響を受けません。

必要に応じて、最後の2つの状態をまとめることができますが、ステートマシンを作成した時点では、状態を明確に定義しておくことをお勧めします。たとえば、プレーヤーの現在のジャンプまたは落下のステータスを確認したいゲームの他の部分にも役立ちます。

雑学:NESの元のマリオゲームは、古いNES CPUが基本的な加算/減算以外のことを実行するための設備がかなり整っていないため、実際には手作りのルックアップテーブルを使用していましたが、指数曲線/放物線曲線に十分に似ています。

于 2013-01-20T17:45:20.200 に答える
3

非常に基本的な物理学を適用する必要があります。利用可能なライブラリはありますが、このような単純なケースでは最小限のコードしか必要ありません。

すでに xVel 変数と yVel 変数があります。UPキーを押すとボックスが「ジャンプ」し、yVelに値を加算します。重力は、ボックスの速度に影響を与える一定の力です。ボックスは下向きに加速します。実際には、move() 関数ですべてのフレームの速度に重力を適用します。

yVel -= GRAVITY;    // Zero in space, small value on Moon, big value on Jupiter
box.y += yVel;

ボックスが床にぶつかると、y 速度が反転することに注意してください。これは問題ありませんが、重力が適用されると、重力が蓄積され、ボックスがとんでもない速度で跳ね返ります。ボックスはバウンドするときに「エネルギー」を失う必要があるため、たとえば速度が半分になる可能性があります。

コードの問題は、整数値を使用していることです。重力は弱い力なので、リアルに見せるためには小さい値にする必要があります。整数値では、この値を見つけることができない場合があります。(固定小数点値をいじることはありませんが、それは別のトピックです。)

したがって、xVel と yVel を float に変更することを検討してください。もちろん、ボックスの位置もフロートを使用する必要があります。

より正確な物理学とフレームレートを考慮した非常に精巧なソリューションについては、https ://gamedev.stackexchange.com/questions/15708/how-can-i-implement-gravity を確認してください。

于 2013-01-20T17:59:55.020 に答える
1

C++で開発されたオープンソースのbox2d2D物理エンジンをチェックアウトする必要があります。 BOX2D

于 2013-01-20T17:03:15.477 に答える
1

ジャンプ速度には正弦曲線を使用できます。最初は速いですが、ジャンプの上部で速度がゼロになるまで遅くなり、その後、モーションは同じ曲線に従います(ただし、反対なので、最初は遅くなります)。キーだけUpが押された場合はY位置のみを変更し、そうでない場合は左またはRight同様に押された場合はX位置も変更します。

スプライトが途中でオブジェクトに衝突した場合、ジャンプの頂点に到達する前に、すぐに上下の方向を逆にします。スピードダウンは、スプライトが衝突したときと同じ速度で始まり、「地面」に戻るまで同じ正弦曲線に沿って加速します。

実験できる加速度と速度の正確な方程式。

于 2013-01-20T17:03:57.553 に答える