使用c++ Win32实现重力翻转机制

Implementing gravity flipping mechanics using C++ Win32

本文关键字:翻转 机制 实现 c++ Win32 使用      更新时间:2023-10-16

我正在想一种有效的方法将重力翻转机制植入我的c++ Win32游戏中。虽然我不是很流利的语言,这就是为什么我在这里问。

我有:

    一个存储播放器属性的类(例如位置和大小,以及一些函数)。
  • 接收来自用户(和其他来源)的消息的消息路由器。
  • 这些消息包括玩家输入。我处理消息路由器的输入,然后调用播放器类的函数。

我纠结于如何执行重力翻转机制。这是一款免费奔跑游戏。玩家跑向窗口的右侧,点击一个键后,玩家角色将在正常(向下)重力或倒转(向上)重力之间交替。

我所考虑的方法是使用do-while。这样做的问题是,用户必须等到do-while完成。也就是说,如果玩家还没有到达屏幕顶部,那就继续向上移动。

我想到了其他方法,但我认为它们不值得一试。它只会导致一些其他的麻烦。

有什么建议吗?

Thanks in advance:)

这是我一直在做的代码,我现在的问题是,一旦玩家到达屏幕的底部或顶部。我似乎无法改变重力。但这让我能够在玩家触及顶部或底部窗口边缘之前做到这一点。这是为什么?

请随意批评我的代码,并告诉我如何改进。

Main.cpp

#include "BaseWindow.h"
#include "GameWindow.h"
int APIENTRY WinMain ( HINSTANCE h_instance, HINSTANCE h_prev_instance, LPSTR lp_cmd_line, int n_cmd_show ) {
    // Create the Game Window
    GameWindow game_window ( h_instance, TEXT ( "GameWindow" ) );
    game_window.Register ();
    // Create the Base Window
    BaseWindow base_window ( TEXT ( "BaseWindow" ), game_window.ClassName () );
    base_window.Create ();
    base_window.Show ();
    // Pump Messages
    MSG messages;
    int status;
    while ( ( status = GetMessage ( &messages, 0, 0, 0 ) ) != 0 ) {
        if ( status == -1 ) {
            // Break;
            break;
        }
        TranslateMessage ( &messages );
        DispatchMessage ( &messages );
    }
    return messages.wParam;
}

AbstractWindow.h

#pragma once
#ifndef __ABSTRACT_WINDOW_H__
#define __ABSTRACT_WINDOW_H__
#include <Windows.h>
class AbstractWindow {
    public:
        AbstractWindow ();
        ~AbstractWindow ();
        virtual bool Create ();
        static LRESULT CALLBACK MessageRouter ( HWND, UINT, WPARAM, LPARAM );
    protected:
        HWND hwnd_;
        DWORD style_ex_;
        LPCTSTR class_name_;
        LPCTSTR window_name_;
        DWORD style_;
        int x_;
        int y_;
        int width_;
        int height_;
        HWND parent_;
        HMENU menu_;
        HINSTANCE h_instance_;
        LPVOID param_;
        // Bitmap Variables - Start
        // Stucture for thw Window width and height
        RECT rect_;
        // Handle to Device Context
        HDC hdc_;
        // Handle to Device Context - Back Buffer
        HDC back_buffer_;
        // Bitmap - Front
        HBITMAP bitmap_; // Bitmap
        // Bitmap Variables - End

        // Default Handlers
        virtual bool OnCreate ( HWND ) = 0;
        virtual bool OnCommand ( int, int ) = 0;
        virtual bool OnDestroy () = 0;
        virtual bool OnPaint ( HWND, WPARAM ) = 0;
        // Player Handlers
        virtual bool UpdatePlayerPosition( HWND, WPARAM ) = 0;
};
#endif // !__ABSTRACT_WINDOW_H__

AbstractWindow.cpp

#include "AbstractWindow.h"
AbstractWindow::AbstractWindow () {}
AbstractWindow::~AbstractWindow () {}
bool AbstractWindow::Create () {
    hwnd_ = CreateWindowEx (
        style_ex_,
        class_name_,
        window_name_,
        style_,
        x_,
        y_,
        width_,
        height_,
        parent_,
        menu_,
        h_instance_,
        this                    // Pointer to this class instance
        );
    if ( hwnd_ ) {
        OnCreate(hwnd_);
        return true;
    }
    return false;
}
LRESULT CALLBACK AbstractWindow::MessageRouter ( HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param ) {
    AbstractWindow* abstract_window = 0;
    static bool move = false;
    if ( message == WM_NCCREATE ) {
        abstract_window = ( AbstractWindow* ) ( ( LPCREATESTRUCT ( l_param ) )->lpCreateParams );
        SetWindowLong ( hwnd, GWL_USERDATA, long ( abstract_window ) );
        return DefWindowProc ( hwnd, message, w_param, l_param );
    }
    else {
        abstract_window = ( AbstractWindow* ) ( GetWindowLong ( hwnd, GWL_USERDATA ) );
        if ( abstract_window ) {
            switch ( message ) {
                case WM_COMMAND:
                    return abstract_window->OnCommand ( LOWORD ( w_param ), HIWORD ( w_param ) );
                case WM_DESTROY:
                    return abstract_window->OnDestroy ();
                case WM_PAINT:
                    return abstract_window->OnPaint ( hwnd, w_param);
                case WM_KEYDOWN:
                    return abstract_window->UpdatePlayerPosition( hwnd, w_param );
                default:
                    return DefWindowProc ( hwnd, message, w_param, l_param );
            }
        }
        else {
            return DefWindowProc ( hwnd, message, w_param, l_param );
        }
    }
}
**BaseWindow.h**
#pragma once
#ifndef __BASE_WINDOW_H__
#define __BASE_WINDOW_H__
#include <Windows.h>
#include "AbstractWindow.h"
#include "Player.h"
class BaseWindow : public AbstractWindow {
    public:
        BaseWindow ();
        ~BaseWindow ();
        BaseWindow ( const TCHAR*, const TCHAR* );
        // Display the window onto the screen while updating the client area
        void Show ();

    private:
        // Player Object
        Player player;  
        // Handlers
        virtual bool OnCreate ( HWND );
        virtual bool OnCommand ( int, int );
        virtual bool OnDestroy ();
        virtual bool OnPaint( HWND, WPARAM );
        // Player Handlers
        virtual bool UpdatePlayerPosition( HWND, WPARAM );

};
#endif // !__BASE_WINDOW_H__

BaseWindow.cpp

#include "BaseWindow.h"
#pragma region Constructor and Destructor
    BaseWindow::BaseWindow() {}
    BaseWindow::~BaseWindow() {}
    BaseWindow::BaseWindow( const TCHAR* window_name, const TCHAR* class_name ) : AbstractWindow() {
        // Member variables are declaried in AbstractWindow as Protected
        window_name_ = window_name;
        class_name_ = class_name;
        // Get the module handle for the window currently running
        h_instance_ = GetModuleHandle( NULL );
        style_ = ( WS_OVERLAPPED| WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX );
        x_ = CW_USEDEFAULT;
        y_ = CW_USEDEFAULT;
        width_ = CW_USEDEFAULT;
        height_ = CW_USEDEFAULT;
        parent_ = NULL;
        menu_ = NULL;
    }
#pragma endregion
#pragma region Functions
    void BaseWindow::Show() {
        // hwnd_ is from AbstractWindow Class
        ShowWindow( hwnd_, SW_SHOW );
        UpdateWindow( hwnd_ );
    }
#pragma endregion
#pragma region Basic Handlers
    bool BaseWindow::OnCreate( HWND hwnd ) {
        MessageBox( hwnd, TEXT( "[BaseWindow] Window has been successfully created!" ), TEXT( "SUCCESS" ), MB_OK );
        GetClientRect( hwnd, &rect_ );
        width_ = rect_.right;
        height_ = rect_.bottom;
        // Create the back buffer
        back_buffer_ = CreateCompatibleDC( NULL );
        // Get the Device Context
        hdc_ = GetDC( hwnd );
        // Create the Bitmap
        bitmap_ = CreateCompatibleBitmap( hdc_, width_, height_ );
        // Select the Bitmap
        SelectObject( back_buffer_, bitmap_ );
        // Release
        ReleaseDC( hwnd, hdc_ );
        return true;
    }
    bool BaseWindow::OnCommand( int ctrl_id, int notify_code ) {
        // Lower 16 bits specifies the element the mouse was used on
        // Upper 16 bits specifies the mouse properties
        // ctrl_id = Lower
        // notify_code = Upper
        return true;
    }
    bool BaseWindow::OnDestroy() {
        PostQuitMessage( 0 );
        return true;
    }
#pragma endregion
#pragma region Other Handlers
    bool BaseWindow::OnPaint( HWND hwnd, WPARAM w_param ) {
        PAINTSTRUCT paint_struct;
        // Get the Device Context
        hdc_ = BeginPaint( hwnd, &paint_struct );
        BitBlt( back_buffer_, 0, 0, width_, height_, NULL, NULL, NULL, WHITENESS );
        if ( player.WithinTopBounds() && player.WithinBottomBounds(height_) ) {
            player.GravityMovement();
        }

        // Select the Rectangle Brush
        SelectObject( back_buffer_, CreateSolidBrush( RGB( player.SpecializerRed(), player.SpecializerGreen(), player.SpecializerBlue() ) ) );
        // Draw the Rectangle on the back buffer
        Rectangle( back_buffer_,
                   player.X() - player.Width() / 2,
                   player.Y() - player.Height() / 2,
                   player.X() + player.Width() / 2,
                   player.Y() + player.Height() / 2);
        // Display the back buffer
        BitBlt( hdc_, 0, 0, width_, height_, back_buffer_, 0, 0, SRCCOPY );
        // Repaint the screen
        InvalidateRect( hwnd, NULL, false );
        EndPaint( hwnd, &paint_struct );
        return true;
    }
    #pragma region Player
        bool BaseWindow::UpdatePlayerPosition(HWND hwnd, WPARAM w_param ) {
            if ( w_param == VK_RIGHT ) {
                if ( player.withinRightBounds( width_ ) ) {
                    // Movement to the right
                    player.RightMovement();
                }
            }
            if ( w_param == VK_LEFT ) {
                if ( player.WithinLeftBounds() ) {
                    // Movement to right left
                    player.LeftMovement();
                }           
            }
            if ( w_param == VK_UP ) {               
                // Movement upwards
                if ( !player.WithinTopBounds() ) {
                    player.SetInverted( false );
                }
                player.SetInverted( true );
            }
            if ( w_param == VK_DOWN ) {
                if ( !player.WithinBottomBounds( height_ ) ) {
                    player.SetInverted( true );
                }
                // Movement downwards
                player.SetInverted( false );
            }
            return true;
        }
    #pragma endregion

#pragma endregions

GameWindow.h

#pragma once
#ifndef __GAME_WINDOW_H__
#define __GAME_WINDOW_H__
#include <Windows.h>
#include "AbstractWindow.h"
class GameWindow : protected WNDCLASSEX {
    public:
        GameWindow ();
        ~GameWindow ();
        GameWindow ( HINSTANCE, const TCHAR* );
        virtual bool Register ();
        virtual const TCHAR* ClassName () const;
    protected:
        UINT cb_size_;
        UINT style_;
        WNDPROC lpfn_wnd_proc_;
        int cb_cls_extra_;
        int cb_wnd_extra_;
        HINSTANCE h_instance_;
        HICON h_icon_;
        HCURSOR h_cursor_;
        HBRUSH hbr_background_;
        LPCTSTR lpsz_menu_name_;
        LPCTSTR lpsz_class_name_;
        HICON h_icon_sm_;

};
#endif // !__GAME_WINDOW_H__

**GameWindow.cpp**
#include "GameWindow.h"
GameWindow::GameWindow () {}
GameWindow::~GameWindow () {}
GameWindow::GameWindow ( HINSTANCE h_instance, const TCHAR* class_name ) {
    // All messages belonging to this Window Class will get sent to MsgRouter
    hInstance = h_instance;
    lpszClassName = class_name;
    lpfnWndProc = AbstractWindow::MessageRouter;
    lpszMenuName = NULL;
    cbSize = sizeof ( WNDCLASSEX );
    cbClsExtra = NULL;
    cbWndExtra = NULL;
    // Prevent Window from Redrawing
    style = 0;
    hIcon = LoadIcon ( NULL, IDI_APPLICATION );
    hIconSm = LoadIcon ( NULL, IDI_APPLICATION );
    hCursor = LoadCursor ( NULL, IDC_HAND );
    hbrBackground = CreateSolidBrush ( RGB ( 125, 255, 255 ) );
}
const TCHAR* GameWindow::ClassName () const {
    return lpszClassName;
}
bool GameWindow::Register () {
    return ( ( RegisterClassEx ( this ) ) ? true : false );
}

Player.h

#pragma once
#ifndef __PLAYER_H__
#define __PLAYER_H__
#include <Windows.h>
#include <algorithm>
#include <string>
using namespace std;
class Player {
    public:
        Player ();
        ~Player ();
        // Mutator
        void SetX ( float );
        void SetY ( float );
        void SetPlayerSpeed ( float );
        void SetSpecializerState ( string );
        void SetInverted( bool );
        // Accessor
        float X () const;
        float Y () const;
        int Width () const;
        int Height () const;
        int SpecializerRed () const;
        int SpecializerGreen () const;
        int SpecializerBlue () const;
        string SpecializerState () const;
        bool Inverted() const;

        // Player Functionality
        void RightMovement ();
        void LeftMovement ();
        void GravityMovement();
        // Within Bounds Detection
        bool WithinLeftBounds();
        bool withinRightBounds(int);
        bool WithinTopBounds();
        bool WithinBottomBounds(int);

    private:
        // General Information
        float x_;
        float y_;
        int width_;
        int height_;
        float speed_;
        float gravity_;
        bool inverted_;
        // Player States
        int specializer_state_;
        // RGB
        int specializer_red_;
        int specializer_green_;
        int specializer_blue_;
        void SetSpecializerColor (int);

};
#endif // !__PLAYER_H__

Player.cpp

#include "Player.h"

#pragma region Constructor and Destructor
Player::Player () {
    x_ = 200;
    y_ = 200;
    width_ = 50;
    height_ = 50;
    speed_ = 5.0f;
    gravity_ = 0.2f;
    inverted_ = false;
    // Specializer
    // Set to Default Specializer State
    specializer_state_ = 0;
    specializer_red_ = 0;
    specializer_green_ = 0;
    specializer_blue_ = 0;
    SetSpecializerColor ( specializer_state_ );
}
Player::~Player () {}
#pragma endregion
#pragma region Mutator
    #pragma region Position Properties
    void Player::SetX ( float x ) {
        x_ = x;
    }
    void Player::SetY ( float y ) {
        y_ = y;
    }
    #pragma endregion
    void Player::SetInverted( bool inverted ) {
        inverted_ = inverted;
    }
    #pragma region Specializer Properties
    void Player::SetSpecializerState ( string input_state ) {
        if ( input_state == "NORMAL" ) {
            specializer_state_ = 0;
        }
        else if ( input_state == "SHIELD" ) {
            specializer_state_ = 1;
        }
        else if ( input_state == "SPEED" ) {
            specializer_state_ = 2;
        }
        else if ( input_state == "PROJECTILE" ) {
            specializer_state_ = 3;
        }
        else if ( input_state == "MULTIPLIER" ) {
            specializer_state_ = 4;
        }
        SetSpecializerColor ( specializer_state_ );
    }
    void Player::SetSpecializerColor ( int state ) {
        if ( specializer_state_ == 0 ) {
            // NORMAL - White
            specializer_red_ = 255;
            specializer_green_ = 255;
            specializer_blue_ = 255;
        }
        else if ( specializer_state_ == 1 ) {
            // SHIELD - Green
            specializer_red_ = 255;
            specializer_green_ = 153;
            specializer_blue_ = 76;
        }
        else if ( specializer_state_ == 2 ) {
            // SPEED - Blue
            specializer_red_ = 0;
            specializer_green_ = 0;
            specializer_blue_ = 102;
        }
        else if ( specializer_state_ == 3 ) {
            // PROJECTILE - Yellow
            specializer_red_ = 255;
            specializer_green_ = 255;
            specializer_blue_ = 0;
        }
        else if ( specializer_state_ == 4 ) {
            // MULTIPLIER - Red
            specializer_red_ = 204;
            specializer_green_ = 0;
            specializer_blue_ = 0;
        }
    }
    #pragma endregion

#pragma endregion
#pragma region Accessor
    #pragma region Position Properties
    float Player::X () const {
        return x_;
    }
    float Player::Y () const {
        return y_;
    }
    int Player::Width () const {
        return width_;
    }
    int Player::Height () const {
        return height_;
    }
    bool Player::Inverted() const {
        return inverted_;
    }
    #pragma endregion
    #pragma region Specializer Properties
    int Player::SpecializerRed () const {
        return specializer_red_;
    }
    int Player::SpecializerGreen () const {
        return specializer_green_;
    }
    int Player::SpecializerBlue () const {
        return specializer_blue_;
    }
    string Player::SpecializerState () const {
        string specializer_state = "";
        if ( specializer_state_ == 0 ) {
            specializer_state = "NORMAL";
        }
        else if ( specializer_state_ == 1 ) {
            specializer_state = "SHIELD";
        }
        else if ( specializer_state_ == 2 ) {
            specializer_state = "SPEED";
        }
        else if ( specializer_state_ == 3 ) {
            specializer_state = "PROJECTILE";
        }
        else if ( specializer_state_ == 4 ) {
            specializer_state = "MULTIPLIER";
        }
        return specializer_state;
    }
    #pragma endregion
#pragma endregion
#pragma region Functionality
    #pragma region Movement
        void Player::RightMovement() {
            x_ += speed_;
        }
        void Player::LeftMovement() {
            x_ -= speed_;
        }
        void Player::GravityMovement() {
            if ( inverted_ ) {
                // Upwards
                if ( min( 0.0f, gravity_ ) == 0.0f ) {
                    gravity_ = -gravity_;
                }
                y_ += gravity_;
            }
            else {
                // Downwards
                if ( max( 0.0f, gravity_ ) == 0.0f ) {
                    gravity_ = -gravity_;
                }
                y_ += gravity_;
            }
        }
    #pragma endregion
    #pragma region Within Window Bounds

        bool Player::WithinLeftBounds() {
            if ( x_ - ( width_ / 2 ) > 0 ) {
                return true;
            }
            return false;
        }
        bool Player::withinRightBounds( int width ) {
            if ( x_ + (width_ / 2) < width ) {
                return true;
            }
            return false;
        }
        bool Player::WithinTopBounds() {
            if ( y_ - ( height_ / 2 ) > 0 ) {
                return true;
            }
            return false;
        }
        bool Player::WithinBottomBounds( int height ) {
            if ( y_ + ( height_ / 2 ) < height ) {
                return true;
            }
            return false;
        }
    #pragma endregion

#pragma endregion

你的游戏应该有一个游戏循环。

一般来说,一个(非常基本的)游戏循环是这样的:
while (playing) {
    accept_input()
    update_game_logic()
    render()
}

在你的update_game_logic()函数中,你应该有一个更新玩家位置的部分。玩家的位置更新步骤通常看起来像是:

// 1. sum up forces on the player (i.e. running to the right)
// 2. add current_gravity (normal or inverted)
// 3. check for any collision and add an opposing force 
    // (if touching the ground add a force directly opposite the ground, equal to the force the object is pushing down with)
// 4. resolve position using some form of integration
    // e.g. add force (* deltatime) to acceleration, add acceleration (* deltatime) to velocity, add velocity (* deltatime) to position.
因为你是根据每一秒的力量调整玩家的位置,你可以继续接受输入并在更新游戏逻辑的同时渲染屏幕。