响应按键移动矩形?

问题描述 投票:0回答:1

有没有办法让 if 语句检查是否按下“d”键起作用?

#include <iostream>
#include <SDL.h>
#include <conio.h>
using namespace std;

void player(SDL_Renderer* renderer) {
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_Rect rect;
    rect.w = 50;
    rect.h = 50;
    rect.x = 600 - (rect.w / 2);
    rect.y = 325 - (rect.h / 2);
    SDL_RenderDrawRect(renderer, &rect);
    SDL_RenderPresent(renderer);

    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_KEYDOWN) {
            if (event.key.keysym.sym == 'd') {
                rect.x += 10;
            }
        }
    }
}

void runwindow(int width, int height) {
    SDL_Init(SDL_INIT_EVERYTHING);
    SDL_Window* window = SDL_CreateWindow("Top Down Shooter", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);

    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);
    
    //initialize stuff
    player(renderer);

    //quit
    bool quit = false;
    SDL_Event e;

    while (!quit) {
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
        }
        
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}



int main(int argc, char* argv[]) {
    runwindow(1200, 650);
    
    return 0;
}

附加信息:

  1. 使用 Visual Studio 2022
  2. 使用SDL2
c++ sdl-2
1个回答
1
投票

首先,整个程序中通常必须有一个主循环。这个循环:

while (!quit) {
    while (SDL_PollEvent(&e)) {
        if (e.type == SDL_QUIT) {
            quit = true;
        }
    }

    // <---
}

所有逻辑和渲染都应该发生在这个循环中的某个位置,即

// <--

player()
中删除另一个循环(我现在刚刚删除了整个函数)。

这样的事情可以在循环内部工作:

// Update.

if (SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_D])
    player_x += 3;

// Draw.

SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);

SDL_Rect rect;
rect.w = 50;
rect.h = 50;
rect.x = width / 2 - rect.w / 2 + player_x;
rect.y = height / 2 - rect.h;
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
SDL_RenderDrawRect(renderer, &rect);
SDL_RenderPresent(renderer);

其中

player_x
是在循环迭代之间持续存在的变量(
int player_x = 0;
while (!quit) {
之前声明)。


您还需要限制每秒发生

player_x += 3;
的次数,否则方块将立即离开屏幕。关于这样做的著名文章是修复你的时间步长!。最小的理智示例如下所示:

循环外:

std::uint64_t tick_duration = SDL_GetPerformanceFrequency() / 60;
std::uint64_t starting_time = SDL_GetPerformanceCounter();
std::uint64_t accumulated_time = 0;

循环内:

// Update.

std::uint64_t current_time = SDL_GetPerformanceCounter();
if (current_time > starting_time)
    accumulated_time += current_time - starting_time;
starting_time = current_time;

while (accumulated_time > tick_duration)
{
    accumulated_time -= tick_duration;
    if (SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_D])
        player_x += 3;
}

SDL_GetPerformanceCounter()
返回一个连续递增的数字。递增的频率为每秒
SDL_GetPerformanceFrequency()
次。

在这里,

while (accumulated_time > tick_duration)
最终将平均每秒执行 60 次(因为我们执行了
tick_duration = SDL_GetPerformanceFrequency() / 60
)。


您还应该限制您的 FPS 以避免无缘无故地升温您的机器。将

SDL_RENDERER_PRESENTVSYNC
作为最后一个参数传递给
SDL_CreateRenderer()
,而不是
0


还要摆脱

<conio.h>
。你不会使用它,它只存在于 Windows 上。我必须删除它才能在 Linux 上编译。


我使用

SDL_GetKeyboardState()
来确定是否按下了某个键。如果您愿意,您可以在事件循环中手动执行此操作(就像您尝试做的那样)。像这样的东西:

循环外:

bool d_pressed = false;

SDL_PollEvent()
循环替换为:

while (SDL_PollEvent(&e)) {
    switch (e.type)
    {
      case SDL_QUIT:
        quit = true;
        break;
      case SDL_KEYDOWN:
        if (e.key.keysym.scancode == SDL_SCANCODE_D)
            d_pressed = true;
        break;
      case SDL_KEYUP:
        if (e.key.keysym.scancode == SDL_SCANCODE_D)
            d_pressed = false;
        break;
    }
}

并将

if (SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_D])
替换为
if (d_pressed)

请注意,我使用的是

e.key.keysym.scancode == SDL_SCANCODE_D
而不是
e.key.keysym.sym == 'd'
。这两种方法表面上看起来都有效,但游戏控制应该始终使用前者,否则具有不寻常键盘布局(AZERTY)的玩家会讨厌你(扫描码代表物理键位置,因此 WASD 扫描码将自动重新映射到 AZERTY 键盘上的 ZQSD,这是一件好事;键码不会被重新映射)。


检查各种初始化函数的返回值是否有错误也是一个好主意。


这是我最终得到的完整代码:

#include <iostream>
#include <SDL.h>
#include <cstdint>

using namespace std;

void runwindow(int width, int height) {
    SDL_Init(SDL_INIT_EVERYTHING);
    SDL_Window* window = SDL_CreateWindow("Top Down Shooter", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);

    //quit
    bool quit = false;

    int player_x = 0;

    std::uint64_t tick_duration = SDL_GetPerformanceFrequency() / 60;
    std::uint64_t starting_time = SDL_GetPerformanceCounter();
    std::uint64_t accumulated_time = 0;
    bool d_pressed = false;

    while (!quit) {
        SDL_Event e;
        while (SDL_PollEvent(&e)) {
            switch (e.type)
            {
              case SDL_QUIT:
                quit = true;
                break;
              case SDL_KEYDOWN:
                if (e.key.keysym.scancode == SDL_SCANCODE_D)
                    d_pressed = true;
                break;
              case SDL_KEYUP:
                if (e.key.keysym.scancode == SDL_SCANCODE_D)
                    d_pressed = false;
                break;
            }
        }

        // Update.

        std::uint64_t current_time = SDL_GetPerformanceCounter();
        if (current_time > starting_time)
            accumulated_time += current_time - starting_time;
        starting_time = current_time;

        while (accumulated_time > tick_duration)
        {
            accumulated_time -= tick_duration;
            if (d_pressed)
                player_x += 3;
        }

        // Draw.

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        SDL_Rect rect;
        rect.w = 50;
        rect.h = 50;
        rect.x = width / 2 - rect.w / 2 + player_x;
        rect.y = height / 2 - rect.h;
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        SDL_RenderDrawRect(renderer, &rect);
        SDL_RenderPresent(renderer);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

int main(int, char**) {
    runwindow(1200, 650);
    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.