2D Raylib Texture Collisions - c

I was wondering how to properly handle collisions in Raylib. After setting up the basics, like a window, I would first load in the player texture, like so:
Texture2D playerTexture = LoadTexture("Player.png");
After that, I would draw the texture, with the Raylib function DrawTextureEx which allows me to adjust the rotation and scale of the texture. Then, I would add a simple movement script for the player, and that would be pretty much it.
As for collisions, I would just be testing the player collision on a rectangle. To get the area for the rectangle, I would just create a simple Rectangle struct like this:
Rectangle rect1 = {100, 100, 50, 50};
As for getting the player area, I would make something similar:
Rectangle playerArea = {player.x, player.y, playerTexture.width*player.scale, playerTexture.height*player.scale} //Values are stored in Player struct
However, when I try to check the collisons like this, nothing happens:
if (CheckCollisionRecs(playerArea, rect1)) {
DrawText("Collided", 5, 5, 25, BLACK);
}
Here's my full code, if needed:
#include "raylib.h"
#include <stdio.h>
#include <stdlib.h>
#define WIDTH 1200
#define HEIGHT 800
typedef struct {
const int width;
const int height;
int FPS;
} Window;
Window window = {
.FPS = 60,
};
typedef struct {
Vector2 pos;
Vector2 acc;
Vector2 vel;
int width;
int height;
double accSpeed;
int maxVel;
double friction;
double rotation;
double scale;
} Player;
Player player = {
.pos = {WIDTH/2, HEIGHT/2},
.acc = {0, 0},
.vel = {0, 0},
.width = 25,
.height = 25,
.accSpeed = 0.15,
.maxVel = 7,
.friction = 0.2,
.rotation = 0,
.scale = 0.5,
};
void movePlayer();
int rectCollide();
int main() {
InitWindow(WIDTH, HEIGHT, "Window");
SetTargetFPS(window.FPS);
Texture2D playerImg = LoadTexture("Player.png");
Rectangle playerArea = {player.pos.x, player.pos.y, playerImg.width*player.scale, playerImg.height*player.scale};
Rectangle rect1 = {100, 100, 50, 50};
Camera2D camera = { 0 };
camera.target = (Vector2){ player.pos.x + 20.0f, player.pos.y + 20.0f };
camera.offset = (Vector2){WIDTH/2.0f, HEIGHT/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;
if (CheckCollisionRecs(playerArea, rect1)) {
DrawText("Collided", 5, 5, 25, BLACK);
}
while (!WindowShouldClose()) {
BeginDrawing();
ClearBackground(SKYBLUE);
movePlayer();
camera.target = (Vector2){ player.pos.x + 20, player.pos.y + 20 };
BeginMode2D(camera);
DrawRectangle(100, 100, 50, 50, BLACK);
DrawTextureEx(playerImg, player.pos, player.rotation, player.scale, RAYWHITE);
EndMode2D();
EndDrawing();
}
UnloadTexture(playerImg);
CloseWindow();
return 0;
}
void movePlayer() {
if (IsKeyDown(KEY_LEFT) && player.vel.x > -player.maxVel) {
player.acc.x = -player.accSpeed;
} else if (IsKeyDown(KEY_RIGHT) && player.vel.x < player.maxVel) {
player.acc.x = player.accSpeed;
} else if (abs(player.vel.x) > 0.2) {
if (player.vel.x < 0) {
player.acc.x = player.friction;
} else {
player.acc.x = -player.friction;
}
} else {
player.vel.x = 0;
player.acc.x = 0;
}
player.vel.x += player.acc.x;
player.pos.x += player.vel.x;
if (IsKeyDown(KEY_UP) && player.vel.y > -player.maxVel) {
player.acc.y = -player.accSpeed;
} else if (IsKeyDown(KEY_DOWN) && player.vel.x < player.maxVel) {
player.acc.y = player.accSpeed;
} else if (abs(player.vel.y) > 0.2) {
if (player.vel.y < 0) {
player.acc.y = player.friction;
} else {
player.acc.y = -player.friction;
}
} else {
player.vel.y = 0;
player.acc.y = 0;
}
player.vel.y += player.acc.y;
player.pos.y += player.vel.y;
}
int rectCollide(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
return x1 + w1 > x2 && x1 < x2 + w2 && y1 + h1 > y2 && y1 < y2 + h2;
}
And so, I was wondering: first of all, is there a better way to get the area of the texture, without having to create a Rectangle struct for each texture, and multiply the scale by the texture width and height? Secondly, I was wondering why my collisions aren't being detected. What is wrong with my code?

The collision check
if (CheckCollisionRecs(playerArea, rect1))
precedes the event loop.
Even moving it inside the loop will not have any effect, as playerArea is never updated in your event loop.
A quick fix would be to update the collision box after moving the player.
(example snippet of main, without images)
Rectangle playerArea;
Rectangle rect1 = {100, 100, 50, 50};
while (!WindowShouldClose()) {
movePlayer();
playerArea = (Rectangle) {
player.pos.x,
player.pos.y,
player.width,
player.height,
};
camera.target = (Vector2){ player.pos.x + 20, player.pos.y + 20 };
BeginDrawing();
ClearBackground(SKYBLUE);
BeginMode2D(camera);
DrawRectangleRec(rect1, RED);
DrawRectangle(player.pos.x, player.pos.y, player.width, player.height, RAYWHITE);
EndMode2D();
if (CheckCollisionRecs(playerArea, rect1))
DrawText("Collided", 5, 5, 25, BLACK);
EndDrawing();
}

Related

SDL2 Camera Cutting Off Screen Edges

I'm trying to create a simple camera for my SDL2 platformer with C. However, whenever the player reaches the screen edges, it seems to be cut off by the background. Here is what the player looks like normally:
And here is what the player looks like when it reaches the screen edges:
To make the camera follow the player, I'm just creating a SDL_Rect called camera, setting it to the player x and y positions, and setting the viewport with SDL_RenderSetViewport to the camera rectangle.
Here's the code for that:
void handle_camera() {
SDL_Rect camera = {
.x = WIDTH/2 - player.x - BLOCK_SIZE/2,
.y = HEIGHT/2 - player.y - BLOCK_SIZE/2,
.w = WIDTH,
.h = HEIGHT
};
SDL_RenderSetViewport(game.renderer, &camera);
}
Therefore, I was wondering: what's wrong with my camera function and why is the player being cut off when it gets near the screen edges?
Here is the full code if needed (I organized in functions so I hope it's not too hard to understand):
#include <stdio.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#define WIDTH 1200
#define HEIGHT 800
#define BLOCK_SIZE 50
#define PLATFORM_AMOUNT 11 //This makes sure there are enough iterations of the loop, and also allocates enough memory for the 2D array of platforms.
#define LAVA_AMOUNT 2
#define TRAMPOLINE_AMOUNT 1
//Prototyping Functions
int initialize();
void handle_input();
void draw_player();
void player_moveX();
void player_moveY();
void checkCollisionsX();
void checkCollisionsY();
int rectCollide();
void drawLevel();
void resetPlayer();
void handle_camera();
typedef struct {
SDL_Renderer *renderer;
SDL_Window *window;
SDL_Surface *surface;
bool running;
int FPS;
bool close_requested;
int input[256];
} Game;
Game game = {
.running = true,
.FPS = 80,
.close_requested = false,
.input = {},
};
typedef struct {
int x;
int y;
double x_vel;
double y_vel;
double x_acc;
double y_acc;
int width;
int height;
double accSpeed;
int maxVel;
double gravity;
double friction;
double jumpForce;
double canJump;
} Player;
Player player = {
.y = HEIGHT-(BLOCK_SIZE*2),
.x = BLOCK_SIZE,
.x_vel = 0,
.y_vel = 0,
.x_acc = 0,
.y_acc = 0,
.width = BLOCK_SIZE,
.height = BLOCK_SIZE,
.accSpeed = 0.15,
.maxVel = 7,
.gravity = 0.5,
.friction = 0.15,
.jumpForce = 15,
.canJump = true,
};
int platforms[PLATFORM_AMOUNT][4] = {
{0, 0, BLOCK_SIZE, HEIGHT}, //WALLS
{0, HEIGHT-BLOCK_SIZE, WIDTH, BLOCK_SIZE},
{400-BLOCK_SIZE, HEIGHT-(BLOCK_SIZE*2), BLOCK_SIZE, BLOCK_SIZE}, //RAMP TO LAVA
{400-BLOCK_SIZE, HEIGHT-(BLOCK_SIZE*3), BLOCK_SIZE, BLOCK_SIZE},
{300, HEIGHT-(BLOCK_SIZE*2), BLOCK_SIZE, BLOCK_SIZE},
{800, HEIGHT-(BLOCK_SIZE*2), BLOCK_SIZE, BLOCK_SIZE},
{800, HEIGHT-(BLOCK_SIZE*3), BLOCK_SIZE, BLOCK_SIZE},
{800+BLOCK_SIZE, HEIGHT-(BLOCK_SIZE*2), BLOCK_SIZE, BLOCK_SIZE},
{WIDTH-BLOCK_SIZE*10, HEIGHT-(BLOCK_SIZE*8), BLOCK_SIZE, BLOCK_SIZE}, //Blocks above lava
{WIDTH-BLOCK_SIZE*8, HEIGHT-(BLOCK_SIZE*10), BLOCK_SIZE, BLOCK_SIZE},
{BLOCK_SIZE, BLOCK_SIZE*3, BLOCK_SIZE*9, BLOCK_SIZE}, //Top platform
};
int lava[LAVA_AMOUNT][4] = {
{400, HEIGHT-(BLOCK_SIZE*3), 400, BLOCK_SIZE*2},
{BLOCK_SIZE*4, BLOCK_SIZE*2, BLOCK_SIZE*3, BLOCK_SIZE},
};
int trampoline[TRAMPOLINE_AMOUNT][4] = {
{WIDTH/2-(BLOCK_SIZE/2), HEIGHT-(BLOCK_SIZE*5), BLOCK_SIZE, BLOCK_SIZE}
};
int main() {
initialize();
while(game.running && !game.close_requested) { //Game loop
SDL_SetRenderDrawColor(game.renderer, 181, 247, 255, 255);
SDL_RenderClear(game.renderer);
handle_input();
handle_camera();
//Collisions only work in this order: playerMoveX, checkCollisionsX, playerMoveY, checkCollisionsY. Then you can draw the platforms and the player.
player_moveX();
checkCollisionsX();
player_moveY();
checkCollisionsY();
drawLevel();
draw_player();
SDL_RenderPresent(game.renderer);
SDL_Delay(1000/game.FPS);
} //End of game loop
SDL_DestroyRenderer(game.renderer);
SDL_DestroyWindow(game.window);
SDL_Quit();
return 0;
}
int initialize() {
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) != 0) { //return 0 on success
printf("error initializing SDL: %s\n", SDL_GetError());
return 1;
}
game.window = SDL_CreateWindow("Sam's Platformer", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE); //creates window
if (!game.window) {
printf("error creating window: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
Uint32 render_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC; //creates a renderer
game.renderer = SDL_CreateRenderer(game.window, -1, render_flags);
if (!game.renderer) {
printf("error creating renderer: %s\n", SDL_GetError());
SDL_DestroyWindow(game.window);
SDL_Quit();
return 1;
}
return 0;
}
void handle_input() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
game.close_requested = true;
game.running = false;
}
//printf("input: %p code: %i\n", game.input, event.key.keysym.scancode);
if (event.type == SDL_KEYDOWN) {
game.input[event.key.keysym.scancode] = true;
//printf("True");
}
if (event.type == SDL_KEYUP) {
game.input[event.key.keysym.scancode] = false;
//printf("False");
}
}
if (game.input[SDL_SCANCODE_R]) {
resetPlayer();
}
}
void draw_player() {
SDL_Rect playerRect = {
.x = player.x,
.y = player.y,
.w = player.width,
.h = player.height
};
SDL_SetRenderDrawColor(game.renderer, 0, 200, 50, 255);
SDL_RenderFillRect(game.renderer, &playerRect);
}
void resetPlayer() {
player.y = HEIGHT-(BLOCK_SIZE*2);
player.x = BLOCK_SIZE;
player.x_vel = 0;
player.y_vel = 0;
}
void player_moveX() {
if (game.input[SDL_SCANCODE_LEFT] && player.x_vel > -player.maxVel) {
player.x_acc = -player.accSpeed;
} else if (game.input[SDL_SCANCODE_RIGHT] && player.x_vel < player.maxVel) {
player.x_acc = player.accSpeed;
} else if (abs(player.x_vel) > 0.2) {
if (player.x_vel < 0) {
player.x_acc = player.friction;
} else {
player.x_acc = -player.friction;
}
} else {
player.x_vel = 0;
player.x_acc = 0;
}
player.x_vel += player.x_acc;
player.x += player.x_vel;
}
void player_moveY() {
if (game.input[SDL_SCANCODE_UP] && player.y_vel == 0 && player.y_acc == 0 && player.canJump) {
player.canJump = false;
player.y_vel = -player.jumpForce;
}
player.y_acc += player.gravity;
player.y_vel += player.y_acc;
player.y += player.y_vel;
player.y_acc = 0;
}
void checkCollisionsX() {
for (int i = 0; i < PLATFORM_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, platforms[i][0], platforms[i][1], platforms[i][2], platforms[i][3])) {
if (player.x_vel < 0) { // If the player moved left and collided with the right side of block
player.x = platforms[i][0] + platforms[i][2];
} else { // If the player moved right and collided with the left side of block
player.x = platforms[i][0] - player.width;
}
player.x_vel = 0;
}
}
/*if (player.x >= WIDTH - player.width) {
player.x = WIDTH - player.width;
player.x_vel = 0;
}*/
for (int i = 0; i < LAVA_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, lava[i][0], lava[i][1], lava[i][2], lava[i][3])) {
resetPlayer();
}
}
for (int i = 0; i < TRAMPOLINE_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, trampoline[i][0], trampoline[i][1], trampoline[i][2], trampoline[i][3])) {
if (player.x_vel < 0) { // If the player moved left and collided with the right side of block
player.x = trampoline[i][0] + trampoline[i][2];
} else { // If the player moved right and collided with the left side of block
player.x = trampoline[i][0] - player.width;
}
player.x_vel = 0;
}
}
}
void checkCollisionsY() {
for (int i = 0; i < PLATFORM_AMOUNT; i++) {
if (rectCollide(player.x, player.y, player.width, player.height, platforms[i][0], platforms[i][1], platforms[i][2], platforms[i][3])) {
if (player.y_vel < 0) { // If the player hit their head
player.y = platforms[i][1] + platforms[i][3];
player.y_vel *= -0.5; // Not -1 because collisions are not perfectly elastic
} else {
player.y = platforms[i][1] - player.height;
player.y_vel = 0;
player.y_acc = 0;
player.canJump = true;
}
}
if (player.y >= HEIGHT - player.height) {
player.y_vel = 0;
player.y = HEIGHT - player.height;
if (!game.input[SDL_SCANCODE_UP]) {
player.canJump = true;
}
}
}
for (int i = 0; i < LAVA_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, lava[i][0], lava[i][1], lava[i][2], lava[i][3])) {
resetPlayer();
}
}
for (int i = 0; i < TRAMPOLINE_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, trampoline[i][0], trampoline[i][1], trampoline[i][2], trampoline[i][3])) {
if (player.y_vel < 0) { // If the player hit their head
player.y = trampoline[i][1] + trampoline[i][3];
player.y_vel *= -0.5; // Not -1 because collisions are not perfectly elastic
} else {
player.y = trampoline[i][1] - trampoline[i][3];
player.y_vel = -player.y_vel;
}
}
}
}
int rectCollide(int x1, int y1, int w1, int h1, int x2,int y2, int w2, int h2) {
return x1 + w1 > x2 && x1 < x2 + w2 && y1 + h1 > y2 && y1 < y2 + h2;
}
void drawLevel() {
for (int i = 0; i < PLATFORM_AMOUNT; i++) {
SDL_Rect platform = {platforms[i][0], platforms[i][1], platforms[i][2], platforms[i][3]};
SDL_SetRenderDrawColor(game.renderer, 156, 104, 0, 255);
SDL_RenderFillRect(game.renderer, &platform);
}
for (int i = 0; i < LAVA_AMOUNT; i++) {
int lavaRedColor = 255;
SDL_Rect lavaBlock = {lava[i][0], lava[i][1], lava[i][2], lava[i][3]};
SDL_SetRenderDrawColor(game.renderer, lavaRedColor, 0, 0, 255);
SDL_RenderFillRect(game.renderer, &lavaBlock);
}
for (int i = 0; i < TRAMPOLINE_AMOUNT; i++) {
SDL_Rect trampolineBlock = {trampoline[i][0], trampoline[i][1], trampoline[i][2], trampoline[i][3]};
SDL_SetRenderDrawColor(game.renderer, 235, 52, 229, 255);
SDL_RenderFillRect(game.renderer, &trampolineBlock);
}
}
void handle_camera() {
SDL_Rect camera = {
.x = WIDTH/2 - player.x - BLOCK_SIZE/2,
.y = HEIGHT/2 - player.y - BLOCK_SIZE/2,
.w = WIDTH, //Screen width
.h = HEIGHT //Screen height
};
SDL_RenderSetViewport(game.renderer, &camera);
}
Thanks for any help.
https://gamedev.stackexchange.com/questions/121421/how-to-use-the-sdl-viewport-properly
See this related post. Essentially changing the SDL viewport is not the way you'd typically handle an in game camera. You need to consider drawing your in game entities (level objects etc.) relative to the camera.

Rotate Player in Relation to Rotated Rectangle

I'm trying to build a game where the player can stand on rotated rectangles. Take the example if a rectangular platform is rotated 20 degrees, then the player would be standing on the platform at a 20 degree angle as well, and could move along that rotated axis. Perhaps this image will clear this up:
Therefore, I need to:
Make the platform solid so the player can stand on it
Make the player rotate to the same angle as the platform
Make the player move on the rotated axis
I've tried to do this, but I've failed. Here's my code that I tried:
#include "raylib.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define WIDTH 1200
#define HEIGHT 800
typedef struct {
const int width;
const int height;
int FPS;
} Window;
Window window = {
.FPS = 60,
};
typedef struct {
Vector2 pos;
Vector2 acc;
Vector2 vel;
int width;
int height;
double accSpeed;
int maxVel;
double friction;
double rotation;
double scale;
float jumpForce;
float gravity;
bool canJump;
} Player;
Player player = {
.pos = {WIDTH/2, HEIGHT/2},
.acc = {0, 0},
.vel = {0, 0},
.width = 50,
.height = 100,
.accSpeed = 0.15,
.maxVel = 7,
.friction = 0.2,
.rotation = 0,
.scale = 0.5,
.jumpForce = 15,
.gravity = 0.5,
.canJump = true,
};
void movePlayerX();
void movePlayerY();
int rectCollide();
int main() {
InitWindow(WIDTH, HEIGHT, "Window");
SetTargetFPS(window.FPS);
//Rectangle playerArea = {player.pos.x, player.pos.y, playerImg.width*player.scale, playerImg.height*player.scale};
Rectangle playerArea;
Rectangle rect1 = {500, 600, 100, 250};
while (!WindowShouldClose()) {
playerArea = (Rectangle) {
player.pos.x,
player.pos.y,
player.width,
player.height,
};
BeginDrawing();
ClearBackground(RAYWHITE);
DrawRectanglePro(rect1, (Vector2) {0, 0}, 20, BLUE);
DrawRectanglePro(playerArea, (Vector2) {0, 0}, player.rotation, RED);
movePlayerX();
/*if (CheckCollisionRecs(playerArea, rect1)) {
if (player.vel.x < 0) { // If the player moved left and collided with the right side of block
player.pos.x = rect1.x + rect1.width;
} else { // If the player moved right and collided with the left side of block
player.pos.x = rect1.x - playerArea.width;
}
player.vel.x = 0;
}*/
movePlayerY();
if (CheckCollisionRecs(playerArea, rect1)) {
if (player.vel.y < 0) { // If the player hit their head
player.pos.y = rect1.y + rect1.height;
player.vel.y *= -0.5; // Not -1 because collisions are not perfectly elastic
} else {
player.pos.y = rect1.y - player.height;
player.rotation = 20;
player.vel.y = 0;
player.acc.y = 0;
player.canJump = true;
}
}
if (player.pos.y >= HEIGHT - player.height) {
player.canJump = true;
player.pos.y = HEIGHT - player.height;
player.vel.y = 0;
}
EndDrawing();
}
}
void movePlayerX() {
if (IsKeyDown(KEY_LEFT) && player.vel.x > -player.maxVel) {
player.acc.x = -player.accSpeed;
} else if (IsKeyDown(KEY_RIGHT) && player.vel.x < player.maxVel) {
player.acc.x = player.accSpeed;
} else if (abs(player.vel.x) > 0.2) {
if (player.vel.x < 0) {
player.acc.x = player.friction;
} else {
player.acc.x = -player.friction;
}
} else {
player.vel.x = 0;
player.acc.x = 0;
}
player.vel.x += player.acc.x;
player.pos.x += player.vel.x;
}
void movePlayerY() {
if (IsKeyPressed(KEY_UP) && player.vel.y == 0 && player.acc.y == 0 && player.canJump) {
player.canJump = false;
player.vel.y = -player.jumpForce;
}
player.acc.y += player.gravity;
player.vel.y += player.acc.y;
player.pos.y += player.vel.y;
player.acc.y = 0;
}
As can be seen from the code, after I call the player movement functions, movePlayerX() and movePlayerY(), I'm trying to detect if the player is colliding with the rectangle. However, I'm just using the regular built in CheckCollisionRecs() function, which detects collision on a non-rotated rectangle, which won't work.
Therefore, I was wondering: how can I get a function to detect the player's collison on a rotated rectangle, then rotate and move the player upon that rotated angle?
Thanks.

What is a smart way to recognize what struct contains the value you're working on?

EDIT: Full code has now been posted so u can compile.
So we're a few first semester software students kind of stuck on a problem regarding structs in c. Stack overflow has helped me so many times already, so figured I'd actually try asking this time, as i couldn't seem to find what im looking for.
Our logic (see code below):
Main calls empty_trash
empty_trash (in its parameter) calls compare_trash
Compare trash parses through 4 areas (structs)
Compare trash checks if the average of our data is over a margin, if so
checks if that average is higher than firstly our empty struct,
which presumably has 0 average, and then if a higher is found that
one. This leaves us with the index of the struct with the highest
average
Compare trash returns this struct to empty trash, and an int
pointer of the index to main.
empty trash then goes through the subareas and checks their average of data and resets the ones over margin2
we return the reset area to main
in main we have an array of our structs 1-4. We assign the area returned from empty trash to arrayofstructs[index pointed back]
In our minds the logic makes sense, but it seems it's not working as our program crashes at the line in main. We think it's because we don't assign the struct correctly in main, but not 100% sure.
We're kinda baffled as both of these functions work fine on their own when we tested them seperately, but together they do not.
Any help would be much appreciated <3
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define MARGIN 70
#define MARGIN2 30
#define SIZE 5
struct subarea
{
int co2_cost, time;
double average, sensorData[SIZE];
};
struct area
{
struct subarea sub_area1, sub_area2, sub_area3, sub_area4;
double average;
};
struct area simmulated_days(struct area area, int lower_random, int upper_random);
struct area average_trash(struct area area);
struct area sensor_data_start(struct area area1);
struct area compare_trash_to_empty(struct area area1, struct area area2, struct area area3, struct area area4, int *);
struct area empty_trash(struct area chosen_area, int *co2_counter_p, int *time_counter_p);
int main(void)
{
int co2_counter, time_counter;
int day_counter = 0;
int *co2_counter_p = &co2_counter;
int *time_counter_p = &time_counter;
int area_number;
srand(time(NULL));
struct subarea subarea1_1 = {50, 50, 0, {}};
struct subarea subarea1_2 = {50, 50, 0, {}};
struct subarea subarea1_3 = {50, 50, 0, {}};
struct subarea subarea1_4 = {50, 50, 0, {}};
struct area area1 = {subarea1_1, subarea1_2, subarea1_3, subarea1_4, 0};
struct subarea subarea2_1 = {50, 50, 0, {}};
struct subarea subarea2_2 = {50, 50, 0, {}};
struct subarea subarea2_3 = {50, 50, 0, {}};
struct subarea subarea2_4 = {50, 50, 0, {}};
struct area area2 = {subarea2_1, subarea2_2, subarea2_3, subarea2_4, 0};
struct subarea subarea3_1 = {50, 50, 0, {}};
struct subarea subarea3_2 = {50, 50, 0, {}};
struct subarea subarea3_3 = {50, 50, 0, {}};
struct subarea subarea3_4 = {50, 50, 0, {}};
struct area area3 = {subarea3_1, subarea3_2, subarea3_3, subarea3_4, 0};
struct subarea subarea4_1 = {50, 50, 0, {}};
struct subarea subarea4_2 = {50, 50, 0, {}};
struct subarea subarea4_3 = {50, 50, 0, {}};
struct subarea subarea4_4 = {50, 50, 0, {}};
struct area area4 = {subarea4_1, subarea4_2, subarea4_3, subarea4_4, 0};
struct area useless_area = {};
struct area all_areas[5] = {useless_area, area1, area2, area3, area4};
struct area local_area = {};
area1 = sensor_data_start(area1);
area2 = sensor_data_start(area2);
area3 = sensor_data_start(area3);
area4 = sensor_data_start(area4);
int running = 1;
while (running)
{
area1 = simmulated_days(area1, 7, 10);
area2 = simmulated_days(area2, 4, 7);
area3 = simmulated_days(area3, 9, 12);
area4 = simmulated_days(area4, 6, 9);
for (int i = 0; i < SIZE; i++)
{
printf("%lf | %lf | %lf | %lf |\n", area1.sub_area1.sensorData[i], area2.sub_area1.sensorData[i], area3.sub_area1.sensorData[i], area4.sub_area1.sensorData[i]);
}
day_counter++;
printf("Day %d\n", day_counter);
area1 = average_trash(area1);
area2 = average_trash(area2);
area3 = average_trash(area3);
area4 = average_trash(area4);
printf("hihi\n");
all_areas[area_number] = empty_trash(compare_trash_to_empty(area1, area2, area3, area4, &area_number), co2_counter_p, time_counter_p);
printf("titi\n");
for (int i = 0; i < SIZE; i++)
{
printf("Local area %lf\t", local_area.sub_area1.sensorData[i]);
}
printf("\n");
if (day_counter == 2)
{
running = 0;
}
}
}
struct area simmulated_days(struct area area, int lower_random, int upper_random)
{
for (int i = 0; i < SIZE; i++)
{
area.sub_area1.sensorData[i] += ((rand() % (upper_random - lower_random + 1)) + lower_random);
area.sub_area2.sensorData[i] += ((rand() % (upper_random - lower_random + 1)) + lower_random);
area.sub_area3.sensorData[i] += ((rand() % (upper_random - lower_random + 1)) + lower_random);
area.sub_area4.sensorData[i] += ((rand() % (upper_random - lower_random + 1)) + lower_random);
}
return area;
}
//Average Trash Function
struct area average_trash(struct area area)
{
double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;
for (int i = 0; i < SIZE; i++)
{
sum1 += area.sub_area1.sensorData[i];
sum2 += area.sub_area2.sensorData[i];
sum3 += area.sub_area3.sensorData[i];
sum4 += area.sub_area4.sensorData[i];
}
area.sub_area1.average = sum1 / SIZE;
area.sub_area2.average = sum2 / SIZE;
area.sub_area3.average = sum3 / SIZE;
area.sub_area4.average = sum4 / SIZE;
area.average = (area.sub_area1.average + area.sub_area2.average + area.sub_area3.average + area.sub_area4.average) / 4;
return area;
}
struct area sensor_data_start(struct area area1)
{
double x = 75;
for (int i = 0; i < SIZE; i++)
{
area1.sub_area1.sensorData[i] = x;
area1.sub_area2.sensorData[i] = x;
area1.sub_area3.sensorData[i] = x;
area1.sub_area4.sensorData[i] = x;
}
return area1;
}
struct area compare_trash_to_empty(struct area area1, struct area area2, struct area area3, struct area area4, int *area_number_p)
{
struct area local_area = {};
int i, highBlock = 0;
struct area block[5] = {local_area, area1, area2, area3, area4};
for (i = 1; i <= 4; i++)
{
if (block[i].average >= MARGIN)
{
if (block[i].average > block[highBlock].average)
{
highBlock = i;
}
}
}
*area_number_p = highBlock;
return block[highBlock];
}
struct area empty_trash(struct area chosen_area, int *co2_counter_p, int *time_counter_p)
{
int co2_counter = 0;
int time_counter = 0;
if (chosen_area.sub_area1.average > MARGIN2)
{
co2_counter += chosen_area.sub_area1.co2_cost;
time_counter += chosen_area.sub_area1.time;
chosen_area.sub_area1.average = 0;
for (int i = 0; i < SIZE; i++)
{
chosen_area.sub_area1.sensorData[i] = 0;
printf("ET %lf\t", chosen_area.sub_area1.sensorData[i]);
}
printf("\n");
}
if (chosen_area.sub_area2.average > MARGIN2)
{
co2_counter += chosen_area.sub_area2.co2_cost;
time_counter += chosen_area.sub_area2.time;
chosen_area.sub_area2.average = 0;
for (int i = 0; i < SIZE; i++)
{
chosen_area.sub_area2.sensorData[i] = 0;
}
}
if (chosen_area.sub_area3.average > MARGIN2)
{
co2_counter += chosen_area.sub_area3.co2_cost;
time_counter += chosen_area.sub_area3.time;
chosen_area.sub_area3.average = 0;
for (int i = 0; i < SIZE; i++)
{
chosen_area.sub_area3.sensorData[i] = 0;
}
}
if (chosen_area.sub_area4.average > MARGIN2)
{
co2_counter += chosen_area.sub_area4.co2_cost;
time_counter += chosen_area.sub_area4.time;
chosen_area.sub_area4.average = 0;
for (int i = 0; i < SIZE; i++)
{
chosen_area.sub_area4.sensorData[i] = 0;
}
}
*co2_counter_p = co2_counter;
*time_counter_p = time_counter;
return chosen_area;
}
My original comment:
Side note: Whenever I see (e.g.) v1 v2 v3 v4 v5 I want to replace it with an array v[5]. So, in struct area, you want: struct subarea sub_area[5]; This will greatly simplify your code. You already do this in main –
Craig Estey
Your response:
#Craig Estey Ye don't have a huge experience with debuggers yet, usually just use printfs, but sounds very helpful so ill youtube a guide side note: very helpful we didn't foresee this at first but would simplify so much, ty4input –
Thybo
I've done better(?) I've done the preliminary restructuring of your code. I believe this will be helpful to others in addition to yourselves.
I'm sorry, but (and I'm saying this with kindness), the code needs a lot of restructuring. The sheer [needless] complexity could be masking numerous bugs.
Things to note:
Passing a struct by value is hazardous. It is legal, but it makes the code complicated. Passing by pointer is 99.44% of the time better. You can just modify the given struct "in-place" rather than having to return it [again, by value].
Learn about iterating through an array by using a pointer to the "current" element instead of an index. You'll see this in the FORALL loop below.
Don't "replicate" code. Notice what happens when using arrays. Instead of (e.g) four copies of the code, we have one
Instead of 4 areas (e.g. area1, area2, area3, area4), if we had 10,000 areas. Would you replicate the code 10,000 times as was done in (e.g.) empty_trash?
I added typedef for your two struct to simplify things a bit.
I've added a few advanced techniques, such as the use of the FORALL macro to traverse arrays.
I've used preprocessor conditionals to denote old code vs new:
#if 0
// old code
#else
// new code
#endif
I've done some/most of the editing. I've compiled it. I could have made a zillion mistakes. But, it should give you the idea:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define MARGIN 70
#define MARGIN2 30
#define SIZE 5
#define NAREA 4
typedef struct subarea {
int co2_cost, time;
double average, sensorData[SIZE];
} subarea_t;
typedef struct area {
#if 0
struct subarea sub_area1, sub_area2, sub_area3, sub_area4;
#else
subarea_t sub_area[NAREA];
#endif
double average;
} area_t;
#define COUNTOF(_arr) \
(sizeof(_arr) / sizeof(_arr[0]))
#define FORALL(_ptr,_arr) \
__typeof__(_arr[0]) *_ptr = &_arr[0]; _ptr < &_arr[COUNTOF(_arr)]; ++_ptr
void
simulated_days(area_t *area, int lower_random, int upper_random)
{
for (FORALL(sub,area->sub_area)) {
for (FORALL(data,sub->sensorData)) {
*data += ((rand() % (upper_random - lower_random + 1)) +
lower_random);
}
}
}
//Average Trash Function
void
average_trash(area_t *area)
{
double sum[NAREA] = { 0 };
int i;
#if 0
for (int i = 0; i < SIZE; i++) {
sub[i] = a
sum1 += area.sub_area1.sensorData[i];
sum2 += area.sub_area2.sensorData[i];
sum3 += area.sub_area3.sensorData[i];
sum4 += area.sub_area4.sensorData[i];
}
#else
for (FORALL(sub,area->sub_area), ++i) {
i = 0;
for (FORALL(data,sub->sensorData), ++i)
sum[i] += *data;
}
#endif
#if 0
area.sub_area1.average = sum1 / SIZE;
area.sub_area2.average = sum2 / SIZE;
area.sub_area3.average = sum3 / SIZE;
area.sub_area4.average = sum4 / SIZE;
#else
i = 0;
for (FORALL(sub,area->sub_area), ++i)
sub->average = sum[i] / SIZE;
#endif
#if 0
area.average = (area.sub_area1.average +
area.sub_area2.average +
area.sub_area3.average +
area.sub_area4.average) / 4;
#else
area->average = 0;
for (FORALL(sub,area->sub_area))
area->average += sub->average;
area->average /= NAREA;
#endif
}
#if 0
area_t
sensor_data_start(area_t area1)
{
double x = 75;
for (int i = 0; i < SIZE; i++) {
area1.sub_area1.sensorData[i] = x;
area1.sub_area2.sensorData[i] = x;
area1.sub_area3.sensorData[i] = x;
area1.sub_area4.sensorData[i] = x;
}
return area1;
}
#else
void
sensor_data_start(area_t *area)
{
double x = 75;
for (FORALL(sub,area->sub_area)) {
for (FORALL(data,sub->sensorData))
*data = x;
}
}
#endif
#if 0
area_t
compare_trash_to_empty(area_t area1, area_t area2, area_t area3, area_t area4, int *area_number_p)
{
area_t local_area = { };
int i,
highBlock = 0;
area_t block[5] = { local_area, area1, area2, area3, area4 };
for (i = 1; i <= 4; i++) {
if (block[i].average >= MARGIN) {
if (block[i].average > block[highBlock].average) {
highBlock = i;
}
}
}
*area_number_p = highBlock;
return block[highBlock];
}
#else
area_t *
compare_trash_to_empty(const area_t *blocks, area_t *all_areas)
{
area_t local_area = { };
int i, highBlock = 0;
#if 0
area_t block[5] = { local_area, area1, area2, area3, area4 };
#else
const area_t *block;
#endif
for (i = 1; i < NAREA; ++i) {
block = &blocks[i];
if (block->average >= MARGIN) {
if (block->average > blocks[highBlock].average)
highBlock = i;
}
}
all_areas[highBlock] = blocks[highBlock];
return &all_areas[highBlock];
}
#endif
#if 0
area_t
empty_trash(area_t chosen_area, int *co2_counter_p, int *time_counter_p)
{
int co2_counter = 0;
int time_counter = 0;
if (chosen_area.sub_area1.average > MARGIN2) {
co2_counter += chosen_area.sub_area1.co2_cost;
time_counter += chosen_area.sub_area1.time;
chosen_area.sub_area1.average = 0;
for (int i = 0; i < SIZE; i++) {
chosen_area.sub_area1.sensorData[i] = 0;
printf("ET %lf\t", chosen_area.sub_area1.sensorData[i]);
}
printf("\n");
}
if (chosen_area.sub_area2.average > MARGIN2) {
co2_counter += chosen_area.sub_area2.co2_cost;
time_counter += chosen_area.sub_area2.time;
chosen_area.sub_area2.average = 0;
for (int i = 0; i < SIZE; i++) {
chosen_area.sub_area2.sensorData[i] = 0;
}
}
if (chosen_area.sub_area3.average > MARGIN2) {
co2_counter += chosen_area.sub_area3.co2_cost;
time_counter += chosen_area.sub_area3.time;
chosen_area.sub_area3.average = 0;
for (int i = 0; i < SIZE; i++) {
chosen_area.sub_area3.sensorData[i] = 0;
}
}
if (chosen_area.sub_area4.average > MARGIN2) {
co2_counter += chosen_area.sub_area4.co2_cost;
time_counter += chosen_area.sub_area4.time;
chosen_area.sub_area4.average = 0;
for (int i = 0; i < SIZE; i++) {
chosen_area.sub_area4.sensorData[i] = 0;
}
}
*co2_counter_p = co2_counter;
*time_counter_p = time_counter;
return chosen_area;
}
#else
void
empty_trash(area_t *chosen_area, int *co2_counter_p, int *time_counter_p)
{
int co2_counter = 0;
int time_counter = 0;
for (FORALL(sub,chosen_area->sub_area)) {
if (sub->average > MARGIN2) {
co2_counter += sub->co2_cost;
time_counter += sub->time;
for (FORALL(data,sub->sensorData))
*data = 0;
}
}
*co2_counter_p = co2_counter;
*time_counter_p = time_counter;
}
#endif
int
main(void)
{
int co2_counter, time_counter;
int day_counter = 0;
int *co2_counter_p = &co2_counter;
int *time_counter_p = &time_counter;
int area_number;
srand(time(NULL));
#if 0
subarea_t subarea1_1 = { 50, 50, 0, {} };
subarea_t subarea1_2 = { 50, 50, 0, {} };
subarea_t subarea1_3 = { 50, 50, 0, {} };
subarea_t subarea1_4 = { 50, 50, 0, {} };
area_t area1 = { subarea1_1, subarea1_2, subarea1_3, subarea1_4, 0 };
subarea_t subarea2_1 = { 50, 50, 0, {} };
subarea_t subarea2_2 = { 50, 50, 0, {} };
subarea_t subarea2_3 = { 50, 50, 0, {} };
subarea_t subarea2_4 = { 50, 50, 0, {} };
area_t area2 = { subarea2_1, subarea2_2, subarea2_3, subarea2_4, 0 };
subarea_t subarea3_1 = { 50, 50, 0, {} };
subarea_t subarea3_2 = { 50, 50, 0, {} };
subarea_t subarea3_3 = { 50, 50, 0, {} };
subarea_t subarea3_4 = { 50, 50, 0, {} };
area_t area3 = { subarea3_1, subarea3_2, subarea3_3, subarea3_4, 0 };
subarea_t subarea4_1 = { 50, 50, 0, {} };
subarea_t subarea4_2 = { 50, 50, 0, {} };
subarea_t subarea4_3 = { 50, 50, 0, {} };
subarea_t subarea4_4 = { 50, 50, 0, {} };
area_t area4 = { subarea4_1, subarea4_2, subarea4_3, subarea4_4, 0 };
#else
area_t areamain[NAREA];
for (FORALL(area,areamain)) {
for (FORALL(sub,area->sub_area)) {
sub->co2_cost = 50;
sub->time = 50;
sub->average = 50;
for (FORALL(data,sub->sensorData))
*data = 0.0;
}
}
#endif
#if 0
area_t useless_area = { };
area_t all_areas[5] = { useless_area, area1, area2, area3, area4 };
#else
area_t *all_areas = areamain;
#endif
area_t local_area = { };
#if 0
area1 = sensor_data_start(area1);
area2 = sensor_data_start(area2);
area3 = sensor_data_start(area3);
area4 = sensor_data_start(area4);
#else
for (FORALL(area,areamain))
sensor_data_start(area);
#endif
int running = 1;
while (running) {
simulated_days(&areamain[0], 7, 10);
simulated_days(&areamain[1], 4, 7);
simulated_days(&areamain[2], 9, 12);
simulated_days(&areamain[3], 6, 9);
#if 0
for (int i = 0; i < SIZE; i++) {
printf("%lf | %lf | %lf | %lf |\n",
area1.sub_area1.sensorData[i],
area2.sub_area1.sensorData[i],
area3.sub_area1.sensorData[i],
area4.sub_area1.sensorData[i]);
}
#else
for (int i = 0; i < SIZE; i++) {
for (FORALL(area,areamain))
printf("%lf |",area->sub_area[i]);
printf("\n");
}
#endif
day_counter++;
printf("Day %d\n", day_counter);
#if 0
area1 = average_trash(area1);
area2 = average_trash(area2);
area3 = average_trash(area3);
area4 = average_trash(area4);
#else
for (FORALL(area,areamain))
average_trash(area);
#endif
printf("hihi\n");
#if 0
all_areas[area_number] =
empty_trash(compare_trash_to_empty(area1, area2, area3, area4,
&area_number), co2_counter_p, time_counter_p);
#else
area_t *area_out = compare_trash_to_empty(areamain,all_areas);
empty_trash(area_out, co2_counter_p, time_counter_p);
#endif
printf("titi\n");
#if 0
for (int i = 0; i < SIZE; i++) {
printf("Local area %lf\t", local_area.sub_area1.sensorData[i]);
}
printf("\n");
#else
do {
subarea_t *sub = area_out->sub_area;
printf("Local area ");
for (FORALL(data,sub->sensorData))
printf("%f\t", *data);
printf("\n");
} while (0);
#endif
if (day_counter == 2) {
running = 0;
}
}
}

Why IsKeyDown always returns true?

I was working in a University C project (first sem), in which our group wanted to make a simple pong game, we decided to use raylib, as it seemed easy. But here is the problem, in the following code:
void UpdatePad(Pad* pad)
{
int height = GetScreenHeight();
if (IsKeyDown(pad->Scheme.DownButton)) {
printf("Down = %d\n", pad->Scheme.DownButton);
pad->Position.y += GetFrameTime() * pad->Speed;
if ( pad->Position.y + pad->Size.y/2 > height ) {
pad->Position.y = height - pad->Size.y /2;
}
}
if (IsKeyDown(pad->Scheme.UpButton)) {
printf("Up = %d\n", pad->Scheme.UpButton);
pad->Position.y -= GetFrameTime() * pad -> Speed;
if (pad->Position.y - pad->Size.y/2 < 0 ) {
pad->Position.y = pad->Size.y /2;
}
}
}
The function IsKeyDown of raylib always returns true, whatever I do. Even if I replace pad->Scheme.DownButton to KEY_UP or something, it always returns true and executes the code block, Is there any solution for this?
Full script is:
#include <raylib.h>
#include <stdio.h>
#include "pad.h"
#include "main.h"
void DrawPad(Pad* pad);
void UpdatePad(Pad* pad);
void Update();
void DrawUpdate();
void Loop();
int main()
{
int screenWidth = 800;
int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "Table Tennis");
SetTargetFPS(12);
Loop();
return 0;
}
void Loop()
{
while (!WindowShouldClose()) {
DrawUpdate();
}
}
void UpdatePad(Pad* pad)
{
int height = GetScreenHeight();
if (IsKeyDown(pad->Scheme.DownButton)) {
printf("Down = %d\n", pad->Scheme.DownButton);
pad->Position.y += GetFrameTime() * pad->Speed;
if ( pad->Position.y + pad->Size.y/2 > height ) {
pad->Position.y = height - pad->Size.y /2;
}
}
if (IsKeyDown(pad->Scheme.UpButton)) {
printf("Up = %d\n", pad->Scheme.UpButton);
pad->Position.y -= GetFrameTime() * pad -> Speed;
if (pad->Position.y - pad->Size.y/2 < 0 ) {
pad->Position.y = pad->Size.y /2;
}
}
}
void DrawPad(Pad* pad)
{
DrawRectangle(pad->Position.x, pad->Position.y - (pad->Size.y /2), pad->Size.x, pad->Size.y, WHITE);
}
void DrawUpdate()
{
const char* scoreLeft = TextFormat("%d", 10);
int scoreSizeLeft = MeasureText(scoreLeft, 20);
InputScheme Input = { .DownButton = KEY_S, .UpButton = KEY_W };
Vector2 paddySize = { .x = 5, .y = 50 };
Vector2 paddyPos = { .x = GetScreenWidth() - paddySize.x , .y = GetScreenHeight() - paddySize.y };
Pad pad = { .Size = paddySize, .Speed = 50, .Scheme = Input , .Position = paddyPos };
Vector2 from = {.x = (GetScreenWidth() / (float) 2), .y = 5};
Vector2 to = { .x = (GetScreenWidth() / (float) 2), .y = ( GetScreenHeight() - (float) 5 ) };
UpdatePad(&pad);
BeginDrawing();
ClearBackground(BLACK);
DrawLineEx(from, to, 2, LIGHTGRAY);
DrawText(scoreLeft, (GetScreenWidth()/2) - 10 -scoreSizeLeft, 10, 20, LIGHTGRAY);
DrawPad(&pad);
EndDrawing();
}
The pad is:-
#include <raylib.h>
typedef struct {
int UpButton;
int DownButton;
} InputScheme;
typedef struct {
InputScheme Scheme;
int Score;
float Speed;
Vector2 Position;
Vector2 Size;
} Pad;

How to change direction smoothly using SDL2 and how to keep moving when I press shift after holding a key?

It's my Code, it's a very simple tank games, I have 2 problems:
use WSAD to move with speed 4, when I pressing and holding shift, the speed change to 8, when I release shift, speed back to 4. But, with my code, when I press shift after holding WSAD, the tank stop, I must repress the key to move it.
When I press WSAD, the tank just change the direction, but not move,so when I holding d, the tank first change it's direction to right for about 0.5 second, then start to move. How to get it move smoothly?
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdio.h>
const int WINDOW_WIDTH = 960; //窗体宽度
const int WINDOW_HEIGHT = 640; //窗体高度
//使用旋转角度定义方向
const int UP = 0;
const int DOWN = 180;
const int LEFT = 270;
const int RIGHT = 90;
//-------------------
typedef struct {
int x;
int y;
int width;
int height;
int direction;
SDL_Texture *texture;
} Player;
void RenderPlayer(Player player, SDL_Renderer *renderer) {
SDL_Rect srcRect = {.x = 0, .y = 0, .w = player.width, .h = player.height};
SDL_Rect destRect = {.x = player.x, .y = player.y, .w = player.width / 2, .h = player.height / 2};
SDL_RenderCopyEx(renderer, player.texture, &srcRect, &destRect, player.direction, NULL, SDL_FLIP_NONE);
}
int main(int argc, char *argv[]) {
SDL_Window *window;
SDL_Renderer *renderer;
if (SDL_Init(SDL_INIT_EVERYTHING)) {
printf("SDL对象初始化失败!\n");
return -1;
}
if (!(window = SDL_CreateWindow("坦克大战", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL))) {
printf("窗体创建失败!\n");
return -1;
}
if (!(renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE))) {
printf("渲染器创建失败!\n");
return -1;
}
SDL_SetRenderDrawColor(renderer, 76, 110, 150, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Surface *surface = IMG_Load("Tank01.png");
if (!surface) {
printf("像素集创建失败:%s\n", IMG_GetError());
return -1;
}
printf("宽度:%d\n高度:%d\n", surface->w, surface->h);
Player player1 = {(WINDOW_WIDTH - surface->w) / 2, WINDOW_HEIGHT - surface->h, surface->w, surface->h, UP, SDL_CreateTextureFromSurface(renderer, surface)};
int speed;
const Uint8 *state = SDL_GetKeyboardState(NULL);
SDL_Event sdlEvent;
while (1) {
int flag1 = 0;
while (SDL_PollEvent(&sdlEvent)) {
speed = 4;
switch (sdlEvent.type) {
case SDL_KEYDOWN:
if (state[SDL_SCANCODE_LSHIFT] && (state[SDL_SCANCODE_W] || state[SDL_SCANCODE_S] || state[SDL_SCANCODE_A] || state[SDL_SCANCODE_D])) {
speed = 8;
}
switch (sdlEvent.key.keysym.sym) {
case SDLK_w:
if (player1.y > 0) {
player1.y -= speed;
}
player1.direction = UP;
break;
case SDLK_s:
if (player1.y < WINDOW_HEIGHT - player1.height / 2) {
player1.y += speed;
}
player1.direction = DOWN;
break;
case SDLK_a:
if (player1.x > 0) {
player1.x -= speed;
}
player1.direction = LEFT;
break;
case SDLK_d:
if (player1.x < WINDOW_WIDTH - player1.width / 2) {
player1.x += speed;
}
player1.direction = RIGHT;
break;
case SDLK_ESCAPE:
SDL_Quit();
flag1 = 1;
break;
}
break;
case SDL_QUIT:
SDL_Quit();
flag1 = 1;
break;
}
}
SDL_RenderClear(renderer);
RenderPlayer(player1, renderer);
SDL_RenderPresent(renderer); //使用渲染器更新窗体
if (flag1) {
break;
}
}
return 0;
}
I couldn't reproduce your first problem with the code you provided, if I hold any of wasd and press shift the tank keeps moving.
The problem here is a bit hard to understand, but you're basically moving the tank only on SDL_Pressed events, meaning that the only reason your tank is moving at all is because SDL repeats the same events over and over, and because it doesn't do this very fast (polling events is kinda slow), your movement is choppy/laggy/steppy whatever you want to call it.
A fix for this is to remove the actual movement step outside your SDL_Event loop, also you should filter out repeat events since you've already checked them.
Meaning your code should look should be something like this
while (1) {
while (SDL_Event(e)) {
if (event is repeated)
skip event
if (UP Key is pressed)
up_state = 1;
if (UP key is released)
up_state = 0;
// etc..
}
// Outside the event loop
if (up_state == 1) // if its 0 we don't add anything
player.x -= speed;
// and so on..
}
For this to work properly you'll also need to read about how to implement a delta time in your loop, because this will run as fast as your processor can meaning that your speed wont be regular, it might add more or less speed depending on how fast is running, which is bad.
Here is your code with these problems fixed, except the delta time, since I actually don't which platform you're on and I was too lazy to do it in C, I capped the framerate at 60fps with a sleep at the end, which is a very bad idea.
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
// I don't know if you're on windows or linux
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
const int WINDOW_WIDTH = 960; //窗体宽度
const int WINDOW_HEIGHT = 640; //窗体高度
//使用旋转角度定义方向
const int UP = 0;
const int DOWN = 180;
const int LEFT = 270;
const int RIGHT = 90;
//-------------------
typedef struct {
int x;
int y;
int width;
int height;
int direction;
SDL_Texture *texture;
} Player;
int m_up = 0;
int m_down = 0;
int m_left = 0;
int m_right = 0;
void RenderPlayer(Player player, SDL_Renderer *renderer) {
SDL_Rect srcRect = {.x = 0, .y = 0, .w = player.width, .h = player.height};
SDL_Rect destRect = {.x = player.x,
.y = player.y,
.w = player.width / 2,
.h = player.height / 2};
SDL_RenderCopyEx(renderer, player.texture, &srcRect, &destRect,
player.direction, NULL, SDL_FLIP_NONE);
}
int main(int argc, char *argv[]) {
SDL_Window *window;
SDL_Renderer *renderer;
if (SDL_Init(SDL_INIT_EVERYTHING)) {
return -1;
}
if (!(window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH,
WINDOW_HEIGHT, SDL_WINDOW_OPENGL))) {
return -1;
}
if (!(renderer = SDL_CreateRenderer(window, -1,
SDL_RENDERER_ACCELERATED |
SDL_RENDERER_TARGETTEXTURE))) {
return -1;
}
SDL_SetRenderDrawColor(renderer, 76, 110, 150, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Surface *surface = IMG_Load("tank.png");
if (!surface) {
return -1;
}
Player player1 = {(WINDOW_WIDTH - surface->w) / 2,
WINDOW_HEIGHT - surface->h,
surface->w,
surface->h,
UP,
SDL_CreateTextureFromSurface(renderer, surface)};
int speed = 4;
SDL_Event sdlEvent;
int run = 1;
while (run) {
while (SDL_PollEvent(&sdlEvent)) {
// Now we skip over repeat events
if ((sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP) &&
sdlEvent.key.repeat == 0) {
SDL_Keycode key = sdlEvent.key.keysym.sym;
int is_pressed = sdlEvent.key.state == SDL_PRESSED;
if (key == SDLK_LSHIFT && is_pressed) {
speed = 8;
} else if (key == SDLK_LSHIFT && !is_pressed) {
speed = 4;
}
if (key == SDLK_w && is_pressed) {
m_up = 1;
} else if (key == SDLK_w) {
m_up = 0;
}
if (key == SDLK_s && is_pressed) {
m_down = 1;
} else if (key == SDLK_s) {
m_down = 0;
}
if (key == SDLK_a && is_pressed) {
m_left = 1;
} else if (key == SDLK_a) {
m_left = 0;
}
if (key == SDLK_d && is_pressed) {
m_right = 1;
} else if (key == SDLK_d) {
m_right = 0;
}
if (key == SDLK_ESCAPE) {
run = 0;
}
if (sdlEvent.type == SDL_QUIT) {
run = 0;
}
}
}
// Handle the movement outside the event loop
if (m_up && player1.y > 0)
player1.y -= speed;
if (m_down && player1.y < WINDOW_HEIGHT - player1.height / 2)
player1.y += speed;
if (m_left && player1.x > 0)
player1.x -= speed;
if (m_right && player1.x < WINDOW_WIDTH - player1.width / 2)
player1.x += speed;
SDL_RenderClear(renderer);
RenderPlayer(player1, renderer);
SDL_RenderPresent(renderer);
#ifdef _WIN32
sleep(0.016); // asume 60 fps, horrible idea
#else
usleep(16000);
#endif
}
SDL_Quit();
return 0;
}
the test image I was using as 'tank.png' https://i.imgur.com/qlUagLp.png

Resources