看,我可能会因为我的代码而彻底被毁掉(我三天前才开始,好吧),但是有什么方法可以让 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;
}
首先,整个程序中通常必须有一个事件循环。这个循环:
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>
//#include <conio.h>
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);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
//quit
bool quit = false;
SDL_Event e;
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) {
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;
}