拖动和移动位置不正确 C++ 和 SDL2

Drag and move position is incorrect C++ and SDL2

我正在用 SDL2 和 C++ 创建游戏。虽然我在其他编程语言方面有很多经验,但我已经多年没有用 C++ 编写代码了,想再试一次。无论如何,我发现了一个名为 SDL2 的 C++ 库,并决定创建一个游戏。游戏的想法仍未选择,但我正在尝试创建一个“虚拟 window”,它基本上只是一个矩形,您可以四处拖动并在其中显示内容。除了拖动部分,我几乎完成了所有操作。我想拖动 window 的工具栏,并根据我的鼠标位置移动整个 window。基本上,我想把鼠标放在工具栏上,当点击和拖动时,我希望整个 window 随鼠标移动,但我几乎所有的尝试都是整个 window 传送到我鼠标的末端,所以我不能将它向左移动,只能向右和向下移动。我找到了一种潜在的方法,它包含在源代码中,但是 window 总是会切换位置。我真的不知道如何解释这个,但它会切换,例如在一帧,X 是 68,在下一帧是 271,在下一帧是 67 (68-1),在下一帧是 270 ( 271-1) 等。我在 post.

中包含了源代码

“Window” class:

class Window {
public:
    int x, y, w, h;
    SDL_Color winc, wintc;
    bool draggable;
    int titleh;
    Window(int wx, int wy, int ww, int wh, SDL_Color window_color = {255, 255, 255, 255}, SDL_Color window_title_color = {200, 200, 200, 255}) {
        x = wx;
        y = wy;
        w = ww;
        h = wh;
        winc = window_color;
        wintc = window_title_color;
        draggable = true;
        titleh = 50;
    }
    void Render(SDL_Renderer* renderer) {
        SDL_Rect _t;
        _t.x = x;
        _t.y = y;
        _t.w = w;
        _t.h = h;
        SDL_SetRenderDrawColor(renderer, winc.r, winc.g, winc.b, winc.a);
        SDL_RenderFillRect(renderer, &_t);
        SDL_Rect title;
        title.x = x;
        title.y = y;
        title.w = w;
        title.h = titleh;
        SDL_SetRenderDrawColor(renderer, wintc.r, wintc.g, wintc.b, wintc.a);
        SDL_RenderFillRect(renderer, &title);
        int tx, ty;
        if (draggable == true and isdown == true and mx > x and mx < (x + w) and my > y and my < (y + titleh)) {
            tx = x;
            ty = y;
            x = mx - tx;
            y = my - ty;
        }
    }
};

整个文件:

// Library includes
#include <SDL2/SDL.h>
#include <stdio.h>

bool isdown = false;
int mx, my;

// Screen rendering helper
void on_render(SDL_Window* window, SDL_Renderer* renderer);

// Concatenation (probably not spelt correctly but idrc) for easier use
const char * concat(const char * one, const char * two) {
    char * buffer = new char[strlen(one) + strlen(two) + 1];
    strcpy(buffer, one);
    strcat(buffer, two);
    return buffer;
}

// Main method, required for performing application run
int main(int argc, const char * argv[]) {
    SDL_Renderer    *renderer = NULL;   // Initialize the renderer
    SDL_Event       event = { 0 };      // Create a null event
    SDL_Window      *win = NULL;        // Initialize a window
    int             exit = 0;           // If exit is 1, win closes
    
    // Window pre-modifiers
    const char * appName = "test";

    // SDL VIDEO mode initialization and error check
    if(SDL_Init(SDL_INIT_VIDEO) == -1) {
        printf("SDL_Init() failed with \"%s.\"", SDL_GetError());
        return 1;
    }
    
    // Create the window and load it into a previously defined variable
    win = SDL_CreateWindow(concat(appName, " - Initialization in progress"), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
    
    // Window creation was unsuccessfull
    if(!win) {
        printf("SDL_CreateWindow() failed with \"%s.\"", SDL_GetError());
        return -1;
    }

    // Creating renderer
    renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
    
    // If renderer failed to load...
    if(!renderer) {
        printf("SDL_CreateRenderer() failed with \"%s.\"", SDL_GetError());
        return -1;
    }
    
    // Everything has gone OK, thus the window can be renamed
    SDL_SetWindowTitle(win, appName);

    // Game loop, as said previously, false = 0, true = 1.
    // while !exit                |
    // while not exit            <- |
    // while exit is 0 (false)     <-
    while (!exit) {
        // Event loop
        if (SDL_WaitEvent(&event)) {
            // Event types
            switch(event.type) {
            case SDL_QUIT:
                exit = 1; // Exit = 1, thus app is being exitted
                break;
            case SDL_KEYDOWN:
                if(event.key.keysym.sym == SDLK_ESCAPE) exit = 1; // If ESC is pressed
                break;
            case SDL_MOUSEBUTTONUP:
                isdown = false;
                break;
            case SDL_MOUSEBUTTONDOWN:
                isdown = true;
                break;
            case SDL_MOUSEMOTION:
                mx = event.motion.x;
                my = event.motion.y;
                break;
            case SDL_WINDOWEVENT:
                switch(event.window.event) {
                case SDL_WINDOWEVENT_CLOSE: // macOS and/or other OSes rely on right click + Quit to fully exit out of an application. This makes it easier by just hitting the close button.
                    exit = 1;
                    break;
                }
                break;
            default: break;
            }
        }
        // Render the screen
        on_render(win, renderer);

        // Swap buffers to display
        SDL_RenderPresent(renderer);
    }

    // Cleanup
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(win);
    SDL_Quit();

    return 0;
}

class Window {
public:
    int x, y, w, h;
    SDL_Color winc, wintc;
    bool draggable;
    int titleh;
    Window(int wx, int wy, int ww, int wh, SDL_Color window_color = {255, 255, 255, 255}, SDL_Color window_title_color = {200, 200, 200, 255}) {
        x = wx;
        y = wy;
        w = ww;
        h = wh;
        winc = window_color;
        wintc = window_title_color;
        draggable = true;
        titleh = 50;
    }
    void Render(SDL_Renderer* renderer) {
        SDL_Rect _t;
        _t.x = x;
        _t.y = y;
        _t.w = w;
        _t.h = h;
        SDL_SetRenderDrawColor(renderer, winc.r, winc.g, winc.b, winc.a);
        SDL_RenderFillRect(renderer, &_t);
        SDL_Rect title;
        title.x = x;
        title.y = y;
        title.w = w;
        title.h = titleh;
        SDL_SetRenderDrawColor(renderer, wintc.r, wintc.g, wintc.b, wintc.a);
        SDL_RenderFillRect(renderer, &title);
        int tx, ty;
        if (draggable == true and isdown == true and mx > x and mx < (x + w) and my > y and my < (y + titleh)) {
            tx = x;
            ty = y;
            x = mx - tx;
            y = my - ty;
        }
    }
};

Window test1 = Window(300, 200, 300, 200);

void on_render(SDL_Window* window, SDL_Renderer* renderer) {
    SDL_Rect wind = { 0 };
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
    SDL_GetWindowSize(window, &wind.w, &wind.h);
    test1.Render(renderer);
}

问题是 event.motion.x (mx) 和 event.motion.y (my) 是您的鼠标坐标相对于您的应用程序 window。 由于您没有在新 Window-object 位置的计算中添加任何偏移量,因此渲染将从这些坐标开始。

例如,您可以做这样的事情,将鼠标指针置于 Window 对象的中心:

//Mouse-cursor will stay in the middle of the Window-object
x = mx - w / 2;
y = my - h / 2;

或者您可以将鼠标光标的位置保持在 Window-对象 内,如下所示:

class Window 
{
    private:
    //Are the offsets already calculated?
    bool offset_calculated = false;
    //...
}

在您的 Window-对象渲染函数中:

if (draggable == true and isdown == true and mx > x and mx < (x + w) and my > y and my < (y + titleh))
{
    if(!offset_calculated)
    {
        //Calculate x- and y-offset once per click
        offsetx = mx - x;
        offsety = my - y;
        offset_calculated = true;
    }

    //Keep mouse-cursor position inside of the Window-object
    x = mx - offsetx;
    y = my - offsety;
}
else
{
    offset_calculated = false;
}

文档: https://wiki.libsdl.org/SDL_MouseMotionEvent