多线程 SDL 应用程序出现 InvalidCursor 错误
我重写了 Lode 的光线投射教程代码,使其在单独的线程中处理事件。我发现任何调用xlib函数的SDL调用都需要在主线程中,因此在这段代码中所有依赖xlib的函数都在主线程中。
这是我仍然从应用程序中随机得到的错误:
X Error of failed request: BadCursor (invalid Cursor parameter)
Major opcode of failed request: 95 (X_FreeCursor)
Resource id in failed request: 0x4a0000b
Serial number of failed request: 108
Current serial number in output stream: 107
有时当我运行它时,我会得到这个错误,但如果我再次运行它,它就会工作。
我不确定还需要如何更改代码,因为所有图形处理都在主线程中,单独的线程仅处理事件处理。有人知道我做错了什么吗?
raycaster.cpp
#include <iostream>
#include <cmath>
#include "SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include "game.hpp"
using std::cout;
static int SCREENW = 500;
static int SCREENH = 500;
static int BPP = 32;
int events_loop(void* data);
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_EVERYTHING | SDL_INIT_EVENTTHREAD);
TTF_Init();
SDL_Thread* events;
Game_state* gs = new Game_state();
events = SDL_CreateThread(events_loop, (void*)gs);
SDL_Surface* screen = SDL_SetVideoMode(SCREENW, SCREENH, BPP, SDL_SWSURFACE);
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
SDL_WM_SetCaption("Raycaster (non-textured)", NULL);
Game* game = new Game(screen, SCREENW, SCREENH, BPP);
//BEGIN GAME VARIABLES
//game map
int map[Game::MAP_WIDTH][Game::MAP_WIDTH] = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 4, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
//direction variables
double pos_x = Game::PLAYER_START_X;
double pos_y = Game::PLAYER_START_Y;
double dir_x = -1; double old_dir_x;
double dir_y = 0;
int map_x, map_y;
//timing variables
double start_ticks = 0;
double end_ticks = 0;
double frame_time = 0;
//camera varibales
double camera_x;
double ray_pos_x, ray_pos_y;
double ray_dir_x, ray_dir_y;
double plane_x = 0; double plane_y = Game::FOV; double old_plane_x;
int line_height;
//DDA variables
double side_dist_x, side_dist_y;
double delta_dist_x, delta_dist_y;
double perpen_wall_dist;
int step_x, step_y;
bool EW_side; //east west side hit, negative implies north south side
bool hit = false;
//drawing variables
int draw_low_y, draw_high_y;
int r, g, b;
//movement variables
double move_speed, rotation_speed;
//BEGIN RENDERING LOGIC
while(gs->over == false) {
start_ticks = SDL_GetTicks();
//lock screen to modify its pixels
/*if(SDL_MUSTLOCK(screen)) {
SDL_LockSurface(screen);
}*/
game->clear_screen();
//BEGIN DRAWING PIXELS
for(int x = 0; x < SCREENW; x++) {
//set up camera
camera_x = 2 * x / (double(SCREENW) - 1);
ray_pos_x = pos_x; ray_pos_y = pos_y;
ray_dir_x = dir_x + plane_x * camera_x;
ray_dir_y = dir_y + plane_y * camera_x;
delta_dist_x = sqrt(1 + (ray_dir_y * ray_dir_y) / (ray_dir_x * ray_dir_x));
delta_dist_y = sqrt(1 + (ray_dir_x * ray_dir_x) / (ray_dir_y * ray_dir_y));
//what box are we in?
map_x = int(ray_pos_x); map_y = int(ray_pos_y);
//calculate step and side_dist
if(ray_dir_x < 0) {
step_x = -1;
side_dist_x = (ray_pos_x - map_x) * delta_dist_x;
}
else {
step_x = 1;
side_dist_x = (map_x + 1.0 - ray_pos_x) * delta_dist_x;
}
if(ray_dir_y < 0) {
step_y = -1;
side_dist_y = (ray_pos_y - map_y) * delta_dist_y;
}
else {
step_y = 1;
side_dist_y = (map_y + 1.0 - ray_pos_y) * delta_dist_y;
}
//step using DDA until a wall is hit
hit = false;
while(hit == false) {
if(side_dist_x < side_dist_y) {
side_dist_x += delta_dist_x;
map_x += step_x;
EW_side = false;
}
else {
side_dist_y += delta_dist_y;
map_y += step_y;
EW_side = true;
}
if(map[map_x][map_y] > 0) { hit = true; }
}
//calculate dist from camera to wall that was hit
if(EW_side == false) {
perpen_wall_dist = fabs((map_x - ray_pos_x + (1 - step_x) / 2) / ray_dir_x);
}
else {
perpen_wall_dist = fabs((map_y - ray_pos_y + (1 - step_y) / 2) / ray_dir_y);
}
//calculate line height from perpendicular wall distance
line_height = abs(int(SCREENH / perpen_wall_dist));
//calculate how high to draw the line
draw_high_y = -line_height / 2 + SCREENH / 2;
if(draw_high_y < 0) { draw_high_y = 0; }
draw_low_y = line_height / 2 + SCREENH / 2;
if(draw_low_y >= SCREENH) { draw_low_y = SCREENH - 1; }
if(draw_low_y < 0) { draw_low_y = 0; } //added (shouldn't need to be here)
//finally draw the line
game->draw_line(x, draw_low_y, draw_high_y, map[map_x][map_y], EW_side);
}
//unlock screen for blitting
/*if(SDL_MUSTLOCK(screen)) {
SDL_UnlockSurface(screen);
}*/
//calculate timing and print the FPS
end_ticks = SDL_GetTicks();
frame_time = (end_ticks - start_ticks) / 1000.0;
game->blit_fps(frame_time);
game->blit_location(map_x, map_y);
if(SDL_Flip(screen) != 0) {
cout << "ERROR: couldn't draw to the screen <" << SDL_GetError() << ">\n";
}
//BEGIN CALCULATING NEXT STEP
//calculate new direction based on frames drawn
move_speed = frame_time * Game_state::MOVEMENT_MULTIPLIER;
rotation_speed = frame_time * Game_state::ROTATION_MULTIPLIER;
//process movement for next frame
if(gs->movement_forward == Game_state::MOVE_UP) {
if(map[int(pos_x + dir_x * move_speed)][int(pos_y)] == 0) { pos_x += dir_x * move_speed; }
if(map[int(pos_x)][int(pos_y + dir_y * move_speed)] == 0) { pos_y += dir_y * move_speed; }
}
else if(gs->movement_forward == Game_state::MOVE_DOWN) {
if(map[int(pos_x - dir_x * move_speed)][int(pos_y)] == 0) { pos_x -= dir_x * move_speed; }
if(map[int(pos_x)][int(pos_y - dir_y * move_speed)] == 0) { pos_y -= dir_y * move_speed; }
}
if(gs->movement_side == Game_state::MOVE_RIGHT) {
old_dir_x = dir_x;
dir_x = dir_x * cos(-rotation_speed) - dir_y * sin(-rotation_speed);
dir_y = old_dir_x * sin(-rotation_speed) + dir_y * cos(-rotation_speed);
old_plane_x = plane_x;
plane_x = plane_x * cos(-rotation_speed) - plane_y * sin(-rotation_speed);
plane_y = old_plane_x * sin(-rotation_speed) + plane_y * cos(-rotation_speed);
}
else if(gs->movement_side == Game_state::MOVE_LEFT) {
old_dir_x = dir_x;
dir_x = dir_x * cos(rotation_speed) - dir_y * sin(rotation_speed);
dir_y = old_dir_x * sin(rotation_speed) + dir_y * cos(rotation_speed);
old_plane_x = plane_x;
plane_x = plane_x * cos(rotation_speed) - plane_y * sin(rotation_speed);
plane_y = old_plane_x * sin(rotation_speed) + plane_y * cos(rotation_speed);
}
}
delete gs;
delete game;
return 0;
}
int events_loop(void* data) {
Game_state* gs = (Game_state*)data;
SDL_Event evt;
while(1) {
while(SDL_PollEvent(&evt)) {
if(evt.type == SDL_QUIT) { gs->over = true; cout << "quit\n"; return 0; }
else if(evt.type == SDL_KEYDOWN) {
if(evt.key.keysym.sym == SDLK_w) {
gs->move(Game_state::MOVE_UP);
}
else if(evt.key.keysym.sym == SDLK_s) {
gs->move(Game_state::MOVE_DOWN);
}
else if(evt.key.keysym.sym == SDLK_a) {
gs->move(Game_state::MOVE_LEFT);
}
else if(evt.key.keysym.sym == SDLK_d) {
gs->move(Game_state::MOVE_RIGHT);
}
else if(evt.key.keysym.sym == SDLK_ESCAPE) { gs->over = true; cout << "escape\n"; return 0; }
}
else if(evt.type == SDL_KEYUP) {
if(evt.key.keysym.sym == SDLK_w) {
gs->stop_move(Game_state::MOVE_UP);
}
else if(evt.key.keysym.sym == SDLK_s) {
gs->stop_move(Game_state::MOVE_DOWN);
}
else if(evt.key.keysym.sym == SDLK_a) {
gs->stop_move(Game_state::MOVE_LEFT);
}
else if(evt.key.keysym.sym == SDLK_d) {
gs->stop_move(Game_state::MOVE_LEFT);
}
}
else { /* ignore */ }
}
}
}
game.cpp
#include <iostream>
#include "game.hpp"
using std::cout; using std::endl;
Game_state::Game_state() {
movement_forward = NO_MOVE;
movement_side = NO_MOVE;
over = false;
}
void Game_state::move(int direction) {
switch(direction) {
case NO_MOVE:
break;
case MOVE_UP:
if(movement_forward == MOVE_DOWN) { movement_forward = NO_MOVE; }
else { movement_forward = MOVE_UP; }
break;
case MOVE_DOWN:
if(movement_forward == MOVE_UP) { movement_forward = NO_MOVE; }
else { movement_forward = MOVE_DOWN; }
break;
case MOVE_LEFT:
if(movement_side == MOVE_RIGHT) { movement_side = NO_MOVE; }
else { movement_side = MOVE_LEFT; }
break;
case MOVE_RIGHT:
if(movement_side == MOVE_LEFT) { movement_side = NO_MOVE; }
else { movement_side = MOVE_RIGHT; }
break;
default:
cout << "ERROR: invalid movement in Game_state::move() at time " << SDL_GetTicks() << endl;
break;
}
}
void Game_state::stop_move(int direction) {
switch(direction) {
case NO_MOVE:
break;
case MOVE_UP:
case MOVE_DOWN:
movement_forward = NO_MOVE;
break;
case MOVE_RIGHT:
case MOVE_LEFT:
movement_side = NO_MOVE;
break;
default:
cout << "ERROR: invalid movement in Game_state::stop_move() at time " << SDL_GetTicks() << endl;
break;
}
}
Game::Game(SDL_Surface* scr, int w, int h, int b) {
screen = scr;
scr_w = w; scr_h = h; bpp = b;
//fps printing vars
fps_location.x = 0; fps_location.y = 0;
fps_font = TTF_OpenFont("/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf", 24);
fps_color.r = 0; fps_color.g = 0; fps_color.b = 255; //blue
//location printing vars
location_font = TTF_OpenFont("/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf", 18);
//determine how high the font surface should be from the bottom
location_color.r = 0; location_color.g = 90; location_color.b = 240;
location_surface = TTF_RenderText_Solid(location_font, location_buffer, location_color);
location_location.x = 0; location_location.y = scr_h - location_surface->clip_rect.h;
//set up the wall colors
wall_color[OUTSIDE_WALL].r = 255; wall_color[OUTSIDE_WALL].g = 255; wall_color[OUTSIDE_WALL].b = 255;
wall_color[RED_WALL].r = 255; wall_color[RED_WALL].g = 0; wall_color[RED_WALL].b = 0;
wall_color[GRAY_WALL].r = 160; wall_color[GRAY_WALL].g = 160; wall_color[GRAY_WALL].b = 160;
wall_color[GOLD_WALL].r = 232; wall_color[GOLD_WALL].g = 211; wall_color[GOLD_WALL].g = 34;
}
void Game::clear_screen() {
SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0, 0, 0));
}
void Game::blit_fps(double frame_time) {
sprintf(fps_buffer, "FPS: %3.3f", 1.0 / frame_time);
fps_surface = TTF_RenderText_Solid(fps_font, fps_buffer, fps_color);
if(SDL_BlitSurface(fps_surface, NULL, screen, &fps_location) != 0) {
cout << "ERROR: couldn't blit the FPS surface <" << SDL_GetError() << ">\n";
}
}
void Game::blit_location(int x, int y) {
sprintf(location_buffer, "location: %d, %d", x, y);
location_surface = TTF_RenderText_Solid(location_font, location_buffer, location_color);
if(SDL_BlitSurface(location_surface, NULL, screen, &location_location) != 0) {
cout << "ERROR: couldn't blit the location surface <" << SDL_GetError() << ">\n";
}
}
//high_y means the y coord closest to the top of the screen
void Game::draw_line(int x, int low_y, int high_y, int wall_type, bool EW_side) {
//cout << "high_y = " << high_y << " low_y = " << low_y << endl;
int r = wall_color[wall_type].r;
int g = wall_color[wall_type].g;
int b = wall_color[wall_type].b;
if(EW_side == true) { r /= 2; g /= 2; b /= 2; }
//cout << "r = " << r << " g = " << g << " b = " << b << "\n";
//draw ceiling
/*for(int y = 0; y < high_y - 1; y++) {
put_pixel(x, y, 0, 255, 90);
}*/
for(int y = high_y; y <= low_y; y++) {
put_pixel(x, y, r, g, b);
}
//draw floor (checkered)
/*for(int y = low_y + 1; y <= scr_h; y++) {
if(x % 20 > 10 && y % 20 > 10) {
put_pixel(x, y, 255, 255, 255);
}
}*/
}
void Game::put_pixel(int x, int y, int r, int g, int b) {
int bpp = screen->format->BytesPerPixel;
Uint8* p = (Uint8*)screen->pixels + y * screen->pitch + x * bpp;
Uint32 pixel = SDL_MapRGB(screen->format, r, g, b);
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16*)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
}
else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32*)p = pixel;
break;
}
}
game.hpp
#include "SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
class Game_state {
public:
//movement statics
static const int NO_MOVE = 0;
static const int MOVE_UP = 1;
static const int MOVE_DOWN = 2;
static const int MOVE_LEFT = 3;
static const int MOVE_RIGHT = 4;
static const double MOVEMENT_MULTIPLIER = 5.0;
static const double ROTATION_MULTIPLIER = 3.0;
int movement_forward;
int movement_side;
bool over;
Game_state();
void move(int direction);
void stop_move(int direction);
};
class Game {
private:
//fps vars
char fps_buffer[50];
TTF_Font* fps_font;
SDL_Surface* fps_surface;
SDL_Rect fps_location;
SDL_Color fps_color;
//location vars
char location_buffer[24];
TTF_Font* location_font;
SDL_Surface* location_surface;
SDL_Rect location_location;
SDL_Color location_color;
void put_pixel(int x, int y, int r, int g, int b);
public:
//game statics
static const int MAP_WIDTH = 20;
static const int MAP_HEIGHT = 20;
static const double FOV = 0.66;
static const int PLAYER_START_X = 1;
static const int PLAYER_START_Y = 1;
//wall options
static const int FLOOR = 0;
static const int OUTSIDE_WALL = 1;
static const int RED_WALL = 2;
static const int GRAY_WALL = 3;
static const int GOLD_WALL = 4;
//game variables
SDL_Surface* screen;
int scr_w;
int scr_h;
int bpp;
SDL_Color wall_color[5];
Game(SDL_Surface* scr, int w, int h, int b);
void clear_screen();
void blit_fps(double frame_time);
void blit_location(int x, int y);
void draw_line(int x, int low_y, int high_y, int wall_type, bool EW_side);
};
I rewrote Lode's raycasting tutorial code to make it process events in a separate thread. I found out that any SDL calls that call xlib functions need to be the main thread, so in this code all functions that rely on xlib are in the main thread.
This is the error I still get randomly from the application:
X Error of failed request: BadCursor (invalid Cursor parameter)
Major opcode of failed request: 95 (X_FreeCursor)
Resource id in failed request: 0x4a0000b
Serial number of failed request: 108
Current serial number in output stream: 107
Sometimes when I run it I'll get this error, but if I run it again it will work.
I'm not for sure how else I need to change the code because all of the graphics processing is in the main thread, the separate thread only deals with events processing. Does anybody know what I'm doing wrong?
raycaster.cpp
#include <iostream>
#include <cmath>
#include "SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include "game.hpp"
using std::cout;
static int SCREENW = 500;
static int SCREENH = 500;
static int BPP = 32;
int events_loop(void* data);
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_EVERYTHING | SDL_INIT_EVENTTHREAD);
TTF_Init();
SDL_Thread* events;
Game_state* gs = new Game_state();
events = SDL_CreateThread(events_loop, (void*)gs);
SDL_Surface* screen = SDL_SetVideoMode(SCREENW, SCREENH, BPP, SDL_SWSURFACE);
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
SDL_WM_SetCaption("Raycaster (non-textured)", NULL);
Game* game = new Game(screen, SCREENW, SCREENH, BPP);
//BEGIN GAME VARIABLES
//game map
int map[Game::MAP_WIDTH][Game::MAP_WIDTH] = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 4, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
//direction variables
double pos_x = Game::PLAYER_START_X;
double pos_y = Game::PLAYER_START_Y;
double dir_x = -1; double old_dir_x;
double dir_y = 0;
int map_x, map_y;
//timing variables
double start_ticks = 0;
double end_ticks = 0;
double frame_time = 0;
//camera varibales
double camera_x;
double ray_pos_x, ray_pos_y;
double ray_dir_x, ray_dir_y;
double plane_x = 0; double plane_y = Game::FOV; double old_plane_x;
int line_height;
//DDA variables
double side_dist_x, side_dist_y;
double delta_dist_x, delta_dist_y;
double perpen_wall_dist;
int step_x, step_y;
bool EW_side; //east west side hit, negative implies north south side
bool hit = false;
//drawing variables
int draw_low_y, draw_high_y;
int r, g, b;
//movement variables
double move_speed, rotation_speed;
//BEGIN RENDERING LOGIC
while(gs->over == false) {
start_ticks = SDL_GetTicks();
//lock screen to modify its pixels
/*if(SDL_MUSTLOCK(screen)) {
SDL_LockSurface(screen);
}*/
game->clear_screen();
//BEGIN DRAWING PIXELS
for(int x = 0; x < SCREENW; x++) {
//set up camera
camera_x = 2 * x / (double(SCREENW) - 1);
ray_pos_x = pos_x; ray_pos_y = pos_y;
ray_dir_x = dir_x + plane_x * camera_x;
ray_dir_y = dir_y + plane_y * camera_x;
delta_dist_x = sqrt(1 + (ray_dir_y * ray_dir_y) / (ray_dir_x * ray_dir_x));
delta_dist_y = sqrt(1 + (ray_dir_x * ray_dir_x) / (ray_dir_y * ray_dir_y));
//what box are we in?
map_x = int(ray_pos_x); map_y = int(ray_pos_y);
//calculate step and side_dist
if(ray_dir_x < 0) {
step_x = -1;
side_dist_x = (ray_pos_x - map_x) * delta_dist_x;
}
else {
step_x = 1;
side_dist_x = (map_x + 1.0 - ray_pos_x) * delta_dist_x;
}
if(ray_dir_y < 0) {
step_y = -1;
side_dist_y = (ray_pos_y - map_y) * delta_dist_y;
}
else {
step_y = 1;
side_dist_y = (map_y + 1.0 - ray_pos_y) * delta_dist_y;
}
//step using DDA until a wall is hit
hit = false;
while(hit == false) {
if(side_dist_x < side_dist_y) {
side_dist_x += delta_dist_x;
map_x += step_x;
EW_side = false;
}
else {
side_dist_y += delta_dist_y;
map_y += step_y;
EW_side = true;
}
if(map[map_x][map_y] > 0) { hit = true; }
}
//calculate dist from camera to wall that was hit
if(EW_side == false) {
perpen_wall_dist = fabs((map_x - ray_pos_x + (1 - step_x) / 2) / ray_dir_x);
}
else {
perpen_wall_dist = fabs((map_y - ray_pos_y + (1 - step_y) / 2) / ray_dir_y);
}
//calculate line height from perpendicular wall distance
line_height = abs(int(SCREENH / perpen_wall_dist));
//calculate how high to draw the line
draw_high_y = -line_height / 2 + SCREENH / 2;
if(draw_high_y < 0) { draw_high_y = 0; }
draw_low_y = line_height / 2 + SCREENH / 2;
if(draw_low_y >= SCREENH) { draw_low_y = SCREENH - 1; }
if(draw_low_y < 0) { draw_low_y = 0; } //added (shouldn't need to be here)
//finally draw the line
game->draw_line(x, draw_low_y, draw_high_y, map[map_x][map_y], EW_side);
}
//unlock screen for blitting
/*if(SDL_MUSTLOCK(screen)) {
SDL_UnlockSurface(screen);
}*/
//calculate timing and print the FPS
end_ticks = SDL_GetTicks();
frame_time = (end_ticks - start_ticks) / 1000.0;
game->blit_fps(frame_time);
game->blit_location(map_x, map_y);
if(SDL_Flip(screen) != 0) {
cout << "ERROR: couldn't draw to the screen <" << SDL_GetError() << ">\n";
}
//BEGIN CALCULATING NEXT STEP
//calculate new direction based on frames drawn
move_speed = frame_time * Game_state::MOVEMENT_MULTIPLIER;
rotation_speed = frame_time * Game_state::ROTATION_MULTIPLIER;
//process movement for next frame
if(gs->movement_forward == Game_state::MOVE_UP) {
if(map[int(pos_x + dir_x * move_speed)][int(pos_y)] == 0) { pos_x += dir_x * move_speed; }
if(map[int(pos_x)][int(pos_y + dir_y * move_speed)] == 0) { pos_y += dir_y * move_speed; }
}
else if(gs->movement_forward == Game_state::MOVE_DOWN) {
if(map[int(pos_x - dir_x * move_speed)][int(pos_y)] == 0) { pos_x -= dir_x * move_speed; }
if(map[int(pos_x)][int(pos_y - dir_y * move_speed)] == 0) { pos_y -= dir_y * move_speed; }
}
if(gs->movement_side == Game_state::MOVE_RIGHT) {
old_dir_x = dir_x;
dir_x = dir_x * cos(-rotation_speed) - dir_y * sin(-rotation_speed);
dir_y = old_dir_x * sin(-rotation_speed) + dir_y * cos(-rotation_speed);
old_plane_x = plane_x;
plane_x = plane_x * cos(-rotation_speed) - plane_y * sin(-rotation_speed);
plane_y = old_plane_x * sin(-rotation_speed) + plane_y * cos(-rotation_speed);
}
else if(gs->movement_side == Game_state::MOVE_LEFT) {
old_dir_x = dir_x;
dir_x = dir_x * cos(rotation_speed) - dir_y * sin(rotation_speed);
dir_y = old_dir_x * sin(rotation_speed) + dir_y * cos(rotation_speed);
old_plane_x = plane_x;
plane_x = plane_x * cos(rotation_speed) - plane_y * sin(rotation_speed);
plane_y = old_plane_x * sin(rotation_speed) + plane_y * cos(rotation_speed);
}
}
delete gs;
delete game;
return 0;
}
int events_loop(void* data) {
Game_state* gs = (Game_state*)data;
SDL_Event evt;
while(1) {
while(SDL_PollEvent(&evt)) {
if(evt.type == SDL_QUIT) { gs->over = true; cout << "quit\n"; return 0; }
else if(evt.type == SDL_KEYDOWN) {
if(evt.key.keysym.sym == SDLK_w) {
gs->move(Game_state::MOVE_UP);
}
else if(evt.key.keysym.sym == SDLK_s) {
gs->move(Game_state::MOVE_DOWN);
}
else if(evt.key.keysym.sym == SDLK_a) {
gs->move(Game_state::MOVE_LEFT);
}
else if(evt.key.keysym.sym == SDLK_d) {
gs->move(Game_state::MOVE_RIGHT);
}
else if(evt.key.keysym.sym == SDLK_ESCAPE) { gs->over = true; cout << "escape\n"; return 0; }
}
else if(evt.type == SDL_KEYUP) {
if(evt.key.keysym.sym == SDLK_w) {
gs->stop_move(Game_state::MOVE_UP);
}
else if(evt.key.keysym.sym == SDLK_s) {
gs->stop_move(Game_state::MOVE_DOWN);
}
else if(evt.key.keysym.sym == SDLK_a) {
gs->stop_move(Game_state::MOVE_LEFT);
}
else if(evt.key.keysym.sym == SDLK_d) {
gs->stop_move(Game_state::MOVE_LEFT);
}
}
else { /* ignore */ }
}
}
}
game.cpp
#include <iostream>
#include "game.hpp"
using std::cout; using std::endl;
Game_state::Game_state() {
movement_forward = NO_MOVE;
movement_side = NO_MOVE;
over = false;
}
void Game_state::move(int direction) {
switch(direction) {
case NO_MOVE:
break;
case MOVE_UP:
if(movement_forward == MOVE_DOWN) { movement_forward = NO_MOVE; }
else { movement_forward = MOVE_UP; }
break;
case MOVE_DOWN:
if(movement_forward == MOVE_UP) { movement_forward = NO_MOVE; }
else { movement_forward = MOVE_DOWN; }
break;
case MOVE_LEFT:
if(movement_side == MOVE_RIGHT) { movement_side = NO_MOVE; }
else { movement_side = MOVE_LEFT; }
break;
case MOVE_RIGHT:
if(movement_side == MOVE_LEFT) { movement_side = NO_MOVE; }
else { movement_side = MOVE_RIGHT; }
break;
default:
cout << "ERROR: invalid movement in Game_state::move() at time " << SDL_GetTicks() << endl;
break;
}
}
void Game_state::stop_move(int direction) {
switch(direction) {
case NO_MOVE:
break;
case MOVE_UP:
case MOVE_DOWN:
movement_forward = NO_MOVE;
break;
case MOVE_RIGHT:
case MOVE_LEFT:
movement_side = NO_MOVE;
break;
default:
cout << "ERROR: invalid movement in Game_state::stop_move() at time " << SDL_GetTicks() << endl;
break;
}
}
Game::Game(SDL_Surface* scr, int w, int h, int b) {
screen = scr;
scr_w = w; scr_h = h; bpp = b;
//fps printing vars
fps_location.x = 0; fps_location.y = 0;
fps_font = TTF_OpenFont("/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf", 24);
fps_color.r = 0; fps_color.g = 0; fps_color.b = 255; //blue
//location printing vars
location_font = TTF_OpenFont("/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf", 18);
//determine how high the font surface should be from the bottom
location_color.r = 0; location_color.g = 90; location_color.b = 240;
location_surface = TTF_RenderText_Solid(location_font, location_buffer, location_color);
location_location.x = 0; location_location.y = scr_h - location_surface->clip_rect.h;
//set up the wall colors
wall_color[OUTSIDE_WALL].r = 255; wall_color[OUTSIDE_WALL].g = 255; wall_color[OUTSIDE_WALL].b = 255;
wall_color[RED_WALL].r = 255; wall_color[RED_WALL].g = 0; wall_color[RED_WALL].b = 0;
wall_color[GRAY_WALL].r = 160; wall_color[GRAY_WALL].g = 160; wall_color[GRAY_WALL].b = 160;
wall_color[GOLD_WALL].r = 232; wall_color[GOLD_WALL].g = 211; wall_color[GOLD_WALL].g = 34;
}
void Game::clear_screen() {
SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0, 0, 0));
}
void Game::blit_fps(double frame_time) {
sprintf(fps_buffer, "FPS: %3.3f", 1.0 / frame_time);
fps_surface = TTF_RenderText_Solid(fps_font, fps_buffer, fps_color);
if(SDL_BlitSurface(fps_surface, NULL, screen, &fps_location) != 0) {
cout << "ERROR: couldn't blit the FPS surface <" << SDL_GetError() << ">\n";
}
}
void Game::blit_location(int x, int y) {
sprintf(location_buffer, "location: %d, %d", x, y);
location_surface = TTF_RenderText_Solid(location_font, location_buffer, location_color);
if(SDL_BlitSurface(location_surface, NULL, screen, &location_location) != 0) {
cout << "ERROR: couldn't blit the location surface <" << SDL_GetError() << ">\n";
}
}
//high_y means the y coord closest to the top of the screen
void Game::draw_line(int x, int low_y, int high_y, int wall_type, bool EW_side) {
//cout << "high_y = " << high_y << " low_y = " << low_y << endl;
int r = wall_color[wall_type].r;
int g = wall_color[wall_type].g;
int b = wall_color[wall_type].b;
if(EW_side == true) { r /= 2; g /= 2; b /= 2; }
//cout << "r = " << r << " g = " << g << " b = " << b << "\n";
//draw ceiling
/*for(int y = 0; y < high_y - 1; y++) {
put_pixel(x, y, 0, 255, 90);
}*/
for(int y = high_y; y <= low_y; y++) {
put_pixel(x, y, r, g, b);
}
//draw floor (checkered)
/*for(int y = low_y + 1; y <= scr_h; y++) {
if(x % 20 > 10 && y % 20 > 10) {
put_pixel(x, y, 255, 255, 255);
}
}*/
}
void Game::put_pixel(int x, int y, int r, int g, int b) {
int bpp = screen->format->BytesPerPixel;
Uint8* p = (Uint8*)screen->pixels + y * screen->pitch + x * bpp;
Uint32 pixel = SDL_MapRGB(screen->format, r, g, b);
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16*)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
}
else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32*)p = pixel;
break;
}
}
game.hpp
#include "SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
class Game_state {
public:
//movement statics
static const int NO_MOVE = 0;
static const int MOVE_UP = 1;
static const int MOVE_DOWN = 2;
static const int MOVE_LEFT = 3;
static const int MOVE_RIGHT = 4;
static const double MOVEMENT_MULTIPLIER = 5.0;
static const double ROTATION_MULTIPLIER = 3.0;
int movement_forward;
int movement_side;
bool over;
Game_state();
void move(int direction);
void stop_move(int direction);
};
class Game {
private:
//fps vars
char fps_buffer[50];
TTF_Font* fps_font;
SDL_Surface* fps_surface;
SDL_Rect fps_location;
SDL_Color fps_color;
//location vars
char location_buffer[24];
TTF_Font* location_font;
SDL_Surface* location_surface;
SDL_Rect location_location;
SDL_Color location_color;
void put_pixel(int x, int y, int r, int g, int b);
public:
//game statics
static const int MAP_WIDTH = 20;
static const int MAP_HEIGHT = 20;
static const double FOV = 0.66;
static const int PLAYER_START_X = 1;
static const int PLAYER_START_Y = 1;
//wall options
static const int FLOOR = 0;
static const int OUTSIDE_WALL = 1;
static const int RED_WALL = 2;
static const int GRAY_WALL = 3;
static const int GOLD_WALL = 4;
//game variables
SDL_Surface* screen;
int scr_w;
int scr_h;
int bpp;
SDL_Color wall_color[5];
Game(SDL_Surface* scr, int w, int h, int b);
void clear_screen();
void blit_fps(double frame_time);
void blit_location(int x, int y);
void draw_line(int x, int low_y, int high_y, int wall_type, bool EW_side);
};
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
SDL_PollEvent
文档指出(至少对于 SDL 1.2 来说是这样)它只能从设置视频模式的同一线程调用。SDL_PollEvent
documentation states (clearly for SDL 1.2 at least) that it should only be called from the same thread that set the video mode.