i want to make interpolation for my mini game. There are 2 SDL_Textures, when i click on first and then on second, they positions are swapped, but without move animation.
So how to make animated place swapping for SDL_Textures ?
void MainLoop() {
SDL_Event Event;
float t = 0.0f;
float dt = 0.1f;
float currentTime = 0.0f;
float accumulator = 0.0f;
while (Running)
{
while(SDL_PollEvent(&Event)) {
OnEvent(&Event);
}
const float newTime = SDL_GetTicks();
float frameTime = newTime - currentTime;
const Uint32 maxFrameTime = 1000; // 1 sec per frame is the slowest we allow
if( frameTime > maxFrameTime) {
frameTime = maxFrameTime;
}
currentTime = newTime;
accumulator += frameTime;
while( accumulator >= dt )
{
this->UpdatePositions(); // simulate a "frame" of logic at a different FPS than we simulate a frame of rendering
accumulator -= dt;
t += dt;
}
Render();
}
Main Render method:
void Puzzle::Render() const {
SDL_RenderClear(renderer);
for (int i = 0; i < blocksNum; ++i)
{
Rectangle texRect = normalizeTexCoords(blockRect(blocks[i]));
Rectangle scrRectOrigin = blockRect(i);
DrawRect(scrRectOrigin, i, texRect);
}
SDL_RenderPresent(renderer);
}
void DrawRect(const Rectangle& screenCoords, int textureId, const Rectangle& textureCoord) {
SDL_Texture *texture = blockTexture(blocks[textureId]);
renderTexture(texture, renderer, (int)screenCoords.left, (int)screenCoords.top, (int)blockWidth, (int)blockHeight);
//SDL_DestroyTexture(texture2);
}
void renderTexture(SDL_Texture *tex, SDL_Renderer *ren, int x, int y, int w, int h) {
// Destination rectangle position
SDL_Rect dst;
dst.x = x; dst.y = y; dst.w = w; dst.h = h;
SDL_RenderCopy(ren, tex, NULL, &dst);
}
And in my UpdatePosition method i want to make my SDL_Textures moving, but it still swapping without animation.
void Puzzle::UpdatePositions()
{
if (secondSwappedBlock != -1) {
Rectangle firstRect = blockRect(blocks[firstSwappedBlock]);
firstRect.left += speed * deltaTime;
firstRect.right += speed * deltaTime;
SDL_Texture *texture = blockTexture(blocks[firstSwappedBlock]);
renderTexture(texture, renderer, (int)firstRect.left, (int)firstRect.top, (int)blockWidth, (int)blockHeight);
}
}
void Puzzle::SwapBlocks(const int first, const int second) const
{
if (first != second)
{
const int t = blocks[first];
blocks[first] = blocks[second];
blocks[second] = t;
}
}
Here is a minimal (yet fully working) example:
#include "SDL.h"
#include <assert.h>
/* linear interpolation between {start.x; start.y} and {end.x; end.y} */
static void rect_lerp(SDL_Rect *out, const SDL_Rect *start, const SDL_Rect *end, float f) {
float t = 1.0f - f;
out->x = (float)start->x * t + (float)end->x * f;
out->y = (float)start->y * t + (float)end->y * f;
}
int main(int argc, char **argv) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
SDL_Window *window = SDL_CreateWindow("example",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
640,
480,
SDL_WINDOW_SHOWN);
assert(window);
SDL_Renderer *renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED);
assert(renderer);
int quit = 0;
int animate = 0;
Uint32 animation_start_time;
/* whole animation will play in e.g. one seconds */
const Uint32 animation_time_total = 1000;
SDL_Rect rect0 = {
.x = 0, .y = 0, .w = 128, .h = 128
};
SDL_Rect rect1 = {
.x = 256, .y = 256, .w = 128, .h = 128
};
while(!quit) {
/* time of current frame */
Uint32 tcurrent = SDL_GetTicks();
SDL_Event event;
while(SDL_PollEvent(&event)) {
if(event.type == SDL_KEYUP) {
if(event.key.keysym.sym == SDLK_ESCAPE) {
quit = 1;
break;
} else if(event.key.keysym.sym == SDLK_SPACE) {
animate = 1;
animation_start_time = tcurrent;
}
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_Rect r0 = rect0, r1 = rect1;
/* if not within animation - leave coordinates as they are */
if(animate) {
if(tcurrent > animation_start_time + animation_time_total) {
/* animation ends by now */
/* swap rect0 and rect1 and stop animation */
SDL_Rect t = rect0;
rect0 = rect1;
rect1 = t;
animate = 0;
/* need to update r0 and r1 too */
r0 = rect0;
r1 = rect1;
} else {
/* animation is incomplete - interpolate coordinates */
/* calculate current animation percentage - in range [0; 1] */
float factor = ((float)(tcurrent - animation_start_time)) / animation_time_total;
rect_lerp(&r0, &rect0, &rect1, factor);
rect_lerp(&r1, &rect1, &rect0, factor);
}
}
/* r0 and r1 now have correct coordinates, draw */
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDrawRect(renderer, &r0);
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
SDL_RenderDrawRect(renderer, &r1);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
For sake of simplicity it draws rectangles instead of textures, but that could be easily changed.
As you can see, in that example I'm keeping old coordinates for as long as animation incomplete, and fixating it only when it is fully gone. This easily allows to replay it back and don't have problems with precision; but - if you'll try to press space when animation already playing, it will just restart.
Another way could be updating coordinates on each animation step (may have different update frequency then redraw); in that case, you'll need to keep current coordinates, destination coordinates (the ones that you'll reach once animation is complete) and, probably, start coordinates (to eliminate precision problems).
Related
Im following this (https://www.udemy.com/course/free-learn-c-tutorial-beginners/learn/lecture/1838486#announcements ) tutorial and im stuck.
It seem that windows throws that exception and it cant be handled.
I think I try write something to some wrong place but cant see it.
Heres the line that causes the problem.
m_buffer[(y * SCREEN_WIDTH) + x] = color;
And here is everything else
//====================================main.cpp=======================================
#include<iostream>
#include "Screen.h"
#include<SDL.h>
using namespace std;
int main(int argc, char* argv[]) {
Screen screen;
if (!screen.init()) {
cout << "Error initialising SDL." << endl;
}
while (true) {
// update particles
// draw particles
for (int y = 0; y < Screen::SCREEN_HEIGHT; y++) {
for (int x = 0; x < Screen::SCREEN_WIDTH; x++ ){
screen.setPixel(x, y, 128, 0, 255);
}
}
//Draw the screen
screen.update();
// see for messages/events
if (!screen.processEvents()) {
break;
}
}
screen.close();
return 0;
}
//==========================================================================================
screen.h
#ifndef SCREEN_H_
#define SCREEN_H_
#include<SDL.h>
#include<iostream>
class Screen
{
public:
const static int SCREEN_WIDTH = 800;
const static int SCREEN_HEIGHT = 600;
private:
SDL_Window* m_window;
SDL_Renderer* m_renderer;
SDL_Texture* m_texture;
Uint32* m_buffer;
public:
Screen();
bool init();
void update();
void setPixel(int x, int y, Uint8 red, Uint8 green, Uint8 blue);
bool processEvents();
void close();
};
#endif // !SCREEN_H_
//=======================screen.cpp======================================================
#include "Screen.h"
Screen::Screen() : m_window(NULL), m_renderer(NULL), m_texture(NULL), m_buffer(NULL)
{
}
bool Screen::init() {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
return false;
}
m_window = SDL_CreateWindow("Particle Fire Explosion",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); //-----------------SDL_WINDOW_HIDDEN SDL_WINDOW_SHOWN
if (m_window == 0) {
SDL_Quit();
return false;
}
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_PRESENTVSYNC);
m_texture = SDL_CreateTexture(m_renderer,
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, SCREEN_WIDTH, SCREEN_HEIGHT);
if (m_renderer == NULL) {
SDL_DestroyWindow(m_window);
SDL_Quit();
return false;
}
if (m_texture == NULL) {
SDL_DestroyWindow(m_window);
SDL_DestroyRenderer(m_renderer);
SDL_Quit();
return false;
}
Uint32* buffer = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
memset(buffer, 0xFF, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(Uint32));
return true;
}
bool Screen::processEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT)
{
return false;
}
}
return true;
}
void Screen::setPixel(int x, int y, Uint8 red, Uint8 green, Uint8 blue) {
Uint32 color = 0;
color += red;
color <<= 8;
color += green;
color <<= 8;
color += blue;
color <<= 8;
color += 0xFF;
m_buffer[(y * SCREEN_WIDTH) + x] = color;
}
void Screen::update() {
SDL_UpdateTexture(m_texture, NULL, m_buffer, SCREEN_WIDTH * sizeof(Uint32));
SDL_RenderClear(m_renderer);
SDL_RenderCopy(m_renderer, m_texture, NULL, NULL);
SDL_RenderPresent(m_renderer);
}
void Screen::close() {
delete[] m_buffer;
SDL_DestroyRenderer(m_renderer);
SDL_DestroyTexture(m_texture);
SDL_DestroyWindow(m_window);
SDL_Quit();
}
//==========================================================================================
Maybe someone can see what is the broblem?
Your Screen::init() method never sets m_buffer, so when setPixel() tries to dereference it, it is dereferencing a NULL pointer, which invokes undefined behavior.
this line is wrong
Uint32* buffer = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
you mean
m_buffer = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
When I ran the following code, I expected a result where pressing one of three buttons would each trigger a matching state that would render a matching image adjacent to those buttons, allowing me to rotate through fresh images at leisure with each successive button press.
Instead of that happy scenario, any initial button press skips right to the last image...or does nothing...or renders a blank white screen depending on how I fiddle with my placement of the initial state declaration and whether I place the state switch within or without the event handling switch. The whole system breaks and stops dead in its tracks. What is going on, and how do I fix it?
#include <string>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include "cleanup.h"
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Rect* clip = NULL;
const int LONGBUTTON_HEIGHT = 128;
const int LONGBUTTON_WIDTH = 256;
int Screen_Width = 640;
int Screen_Height = 480;
int mouse_x;
int mouse_y;
int alphabutton_x = 0;
int alphabutton_y = 5;
int alphabutton_h = Screen_Width / 10;
int alphabutton_w = Screen_Width / 5;
int betabutton_x = 0;
int betabutton_y = 0.5 * (Screen_Width / 5) + 5;
int betabutton_h = Screen_Width / 10;
int betabutton_w = Screen_Width / 5;
int gammabutton_x = 0;
int gammabutton_y = 1 * (Screen_Width / 5) + 5;
int gammabutton_h = Screen_Width / 10;
int gammabutton_w = Screen_Width / 5;
int alpha_x = Screen_Width / 5;
int alpha_y = 0;
int alpha_h = Screen_Height;
int alpha_w = (4* Screen_Width)/5;
int beta_x = Screen_Width / 5;
int beta_y = 0;
int beta_h = Screen_Height;
int beta_w = (4* Screen_Width)/5;
int gamma_x = Screen_Width / 5;
int gamma_y = 0;
int gamma_h = Screen_Height;
int gamma_w = (4* Screen_Width)/5;
enum alphaButtonSprite {ALPHA_DEFAULT, ALPHA_HOVER, ALPHA_INACTIVE, ALPHA_PRESSED, ALPHA_TOTAL};
enum betaButtonSprite {BETA_DEFAULT, BETA_HOVER, BETA_INACTIVE, BETA_PRESSED, BETA_TOTAL};
enum gammaButtonSprite {GAMMA_DEFAULT, GAMMA_HOVER, GAMMA_INACTIVE, GAMMA_PRESSED, GAMMA_TOTAL};
enum State {ALPHA_STATE, BETA_STATE, GAMMA_STATE};
State state;
SDL_Texture* loadTexture(const std::string& file, SDL_Renderer* renderer)
{
SDL_Texture* texture = IMG_LoadTexture(renderer, file.c_str());
return texture;
}
void renderTexture(SDL_Texture* texture, SDL_Renderer* renderer, SDL_Rect dest,
SDL_Rect* clip = nullptr)
{
SDL_RenderCopy(renderer, texture, clip, &dest);
}
void renderTexture(SDL_Texture* texture, SDL_Renderer* renderer, int x, int y, int h, int w,
SDL_Rect* clip = nullptr)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
dest.h = h;
dest.w = w;
renderTexture(texture, renderer, dest, clip);
}
// Main Function
int main(int, char**)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("New Window", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, Screen_Width, Screen_Height, SDL_WINDOW_RESIZABLE);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_Texture* longbutton_image = loadTexture("longbuttonSpriteSheet.png", renderer);
SDL_Texture* alpha_image = loadTexture("alphaImage.png", renderer);
SDL_Texture* beta_image = loadTexture("betaImage.png", renderer);
SDL_Texture* gamma_image = loadTexture("gammaImage.png", renderer);
state = BETA_STATE;
SDL_Rect alpha_clips[alphaButtonSprite::ALPHA_TOTAL];
for (int i = 0; i < alphaButtonSprite::ALPHA_TOTAL; i++)
{
alpha_clips[i].x = i * LONGBUTTON_WIDTH;
alpha_clips[i].y = 0;
alpha_clips[i].w = LONGBUTTON_WIDTH;
alpha_clips[i].h = LONGBUTTON_HEIGHT;
}
int usealpha_Clip = ALPHA_DEFAULT;
SDL_Rect beta_clips[betaButtonSprite::BETA_TOTAL];
for (int i = 0; i < betaButtonSprite::BETA_TOTAL; i++)
{
beta_clips[i].x = i * LONGBUTTON_WIDTH;
beta_clips[i].y = 4 * LONGBUTTON_HEIGHT;
beta_clips[i].w = LONGBUTTON_WIDTH;
beta_clips[i].h = LONGBUTTON_HEIGHT;
}
int usebeta_Clip = BETA_DEFAULT;
SDL_Rect gamma_clips[gammaButtonSprite::GAMMA_TOTAL];
for (int i = 0; i < gammaButtonSprite::GAMMA_TOTAL; i++)
{
gamma_clips[i].x = i * LONGBUTTON_WIDTH;
gamma_clips[i].y = 5 * LONGBUTTON_HEIGHT;
gamma_clips[i].w = LONGBUTTON_WIDTH;
gamma_clips[i].h = LONGBUTTON_HEIGHT;
}
int usegamma_Clip = GAMMA_DEFAULT;
SDL_Event e;
bool quit = false;
while (!quit)
{
while (SDL_PollEvent(&e))
{
switch (e.type)
{
case SDL_MOUSEBUTTONDOWN:
mouse_x = e.button.x;
mouse_y = e.button.y;
if ((mouse_x <= (alphabutton_x + alphabutton_w)) && (mouse_x > alphabutton_x) &&
(mouse_y <= (alphabutton_y + alphabutton_h)) && (mouse_y > alphabutton_y))
usealpha_Clip = ALPHA_PRESSED;
state = ALPHA_STATE;
if ((mouse_x <= (betabutton_x + betabutton_w)) && (mouse_x > betabutton_x) &&
(mouse_y <= (betabutton_y + betabutton_h)) && (mouse_y > betabutton_y))
usebeta_Clip = BETA_PRESSED;
state = BETA_STATE;
if ((mouse_x <= (gammabutton_x + gammabutton_w)) && (mouse_x > gammabutton_x) &&
(mouse_y <= (gammabutton_y + gammabutton_h)) && (mouse_y > gammabutton_y))
usegamma_Clip = GAMMA_PRESSED;
state = GAMMA_STATE;
break;
}
}
switch (state)
{
case ALPHA_STATE:
SDL_RenderClear(renderer);
renderTexture(longbutton_image, renderer, alphabutton_x, alphabutton_y, alphabutton_h, alphabutton_w, &alpha_clips[usealpha_Clip]);
renderTexture(longbutton_image, renderer, betabutton_x, betabutton_y, betabutton_h, betabutton_w, &beta_clips[usebeta_Clip]);
renderTexture(longbutton_image, renderer, gammabutton_x, gammabutton_y, gammabutton_h, gammabutton_w, &gamma_clips[usegamma_Clip]);
renderTexture(alpha_image, renderer, alpha_x, alpha_y, alpha_h, alpha_w, nullptr);
SDL_RenderPresent(renderer);
break;
case BETA_STATE:
SDL_RenderClear(renderer);
renderTexture(longbutton_image, renderer, alphabutton_x, alphabutton_y, alphabutton_h, alphabutton_w, &alpha_clips[usealpha_Clip]);
renderTexture(longbutton_image, renderer, betabutton_x, betabutton_y, betabutton_h, betabutton_w, &beta_clips[usebeta_Clip]);
renderTexture(longbutton_image, renderer, gammabutton_x, gammabutton_y, gammabutton_h, gammabutton_w, &gamma_clips[usegamma_Clip]);
renderTexture(beta_image, renderer, beta_x, beta_y, beta_h, beta_w, nullptr);
SDL_RenderPresent(renderer);
break;
case GAMMA_STATE:
SDL_RenderClear(renderer);
renderTexture(longbutton_image, renderer, alphabutton_x, alphabutton_y, alphabutton_h, alphabutton_w, &alpha_clips[usealpha_Clip]);
renderTexture(longbutton_image, renderer, betabutton_x, betabutton_y, betabutton_h, betabutton_w, &beta_clips[usebeta_Clip]);
renderTexture(longbutton_image, renderer, gammabutton_x, gammabutton_y, gammabutton_h, gammabutton_w, &gamma_clips[usegamma_Clip]);
renderTexture(gamma_image, renderer, gamma_x, gamma_y, gamma_h, gamma_w, nullptr);
SDL_RenderPresent(renderer);
break;
}
}
//Destroy the various items
cleanup(longbutton_image, alpha_image, beta_image, gamma_image, renderer, window);
IMG_Quit();
SDL_Quit();
return 0;
}
Found the answer to this one. In the mousebuttondown event switch cases, just put the state code before the useclip code. Why it makes a difference, I don't know, but it works. Cough...Also, doing it in the mousebuttonup switch cases instead of mousebuttondown works even better.
Question:
First off, I'm very new to C++ so I'm just starting out. One of the exercises I'm currently on is having me create a "Particle Explosion" as you can see in the window title then have it flash different colors as you can see in the main.cpp.
My problem:
Whenever I run it in VS2015 I get my console to open, the window opens, but it doesn't flash any colors, it just stays white. Everything seems to be working just fine and it doesn't freeze or have any errors. Just. No. Colors.
Does anyone see an error I'm making? Once I created it I compared it to the exercise, but everything seems to be 100% exactly the same.
I'm on W7, VS2015 and using SDL2 lib.
main.cpp
#include <iostream>
#include <SDL.h>
#include "Screen.h"
#include <math.h>
#undef main
using namespace std;
using namespace caveofprogramming;
int main() {
Screen screen;
if (screen.init() == false) {
cout << "Error initialzing SDL." << endl;
}
while (true) {
// Update particles
// Draw Particles
int elapsed = SDL_GetTicks();
unsigned char green = (1 + sin(elapsed * 0.001)) * 128;
unsigned char red = (1 + sin(elapsed * 0.002)) * 128;
unsigned char blue = (1 + sin(elapsed * 0.003)) * 128;
for (int y = 0; y < Screen::SCREEN_HEIGHT; y++) {
for (int x = 0; x < Screen::SCREEN_WIDTH; x++) {
screen.setPixel(x, y, red, green, blue);
}
}
//Draw the screen
// Check for messages/events
if (screen.processEvents() == false) {
break;
}
}
screen.close();
return 0; // usually when your program runs ok it returns 0
}
Screen.cpp
#include "Screen.h"
#include <iostream>
namespace caveofprogramming {
Screen::Screen() :
m_window(NULL), m_renderer(NULL), m_texture(NULL), m_buffer(NULL) {
}
bool Screen::init() {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
return false;
}
m_window = SDL_CreateWindow("Particle Fire Explosion",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (m_window == NULL) {
SDL_Quit();
return false;
}
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_PRESENTVSYNC);
m_texture = SDL_CreateTexture(m_renderer, SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_STATIC, SCREEN_WIDTH, SCREEN_HEIGHT);
if (m_renderer == NULL) {
SDL_DestroyWindow(m_window);
SDL_Quit();
return false;
}
if (m_texture == NULL) {
SDL_DestroyRenderer(m_renderer);
SDL_DestroyWindow(m_window);
SDL_Quit();
return false;
}
m_buffer = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
memset(m_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(Uint32));
return true;
}
void Screen::clear() {
memset(m_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(Uint32));
}
void Screen::setPixel(int x, int y, Uint8 red, Uint8 green, Uint8 blue) {
if (x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= SCREEN_HEIGHT) {
return;
}
Uint32 color = 0;
color += red;
color <<= 8;
color += green;
color <<= 8;
color += blue;
color <<= 8;
color += 0xFF;
m_buffer[(y * SCREEN_WIDTH) + x] = color;
}
void Screen::update() {
SDL_UpdateTexture(m_texture, NULL, m_buffer, SCREEN_WIDTH * sizeof(Uint32));
SDL_RenderClear(m_renderer);
SDL_RenderCopy(m_renderer, m_texture, NULL, NULL);
SDL_RenderPresent(m_renderer);
}
bool Screen::processEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
return false;
}
}
return true;
}
void Screen::close() {
delete[] m_buffer;
SDL_DestroyRenderer(m_renderer);
SDL_DestroyTexture(m_texture);
SDL_DestroyWindow(m_window);
SDL_Quit();
}
}
Screen.h
#pragma once
#include <SDL.h>
namespace caveofprogramming {
class Screen {
public:
const static int SCREEN_WIDTH = 800;
const static int SCREEN_HEIGHT = 600;
private:
SDL_Window *m_window;
SDL_Renderer *m_renderer;
SDL_Texture *m_texture;
Uint32 *m_buffer;
public:
Screen();
bool init();
bool processEvents();
void update();
void clear();
void setPixel(int x, int y, Uint8 red, Uint8 green, Uint8 blue);
void close();
};
}
As Elderbug said, I just had to add the function to my object to render.
//Draw the screen
screen.update();
thanks!
I'am new there. I've been learning classes and tried to make a very simple platform game. But I have problem now. I've wanted to set Class "Player" to collide with 2 objects of Class "Block" But Collision do not work for one of them.
Here is my code:
#include <iostream>
#include <SDL.h>
#include <SDL_image.h>
#undef main
class Block
{
private:
SDL_Texture *BlockTexture;
public:
Block(SDL_Renderer *renderTarget, std::string filePath, int xPos, int yPos, int Width, int Height);
~Block();
void Draw(SDL_Renderer *renderTarget);
SDL_Rect BlockPos;
};
Block::Block(SDL_Renderer *renderTarget, std::string filePath, int xPos, int yPos, int Width, int Height)
{
SDL_Surface *surface = IMG_Load(filePath.c_str());
{
BlockTexture = SDL_CreateTextureFromSurface(renderTarget, surface);
}
SDL_FreeSurface(surface);
BlockPos.x = xPos;
BlockPos.y = yPos;
BlockPos.w = Width;
BlockPos.h = Height;
}
Block::~Block()
{
SDL_DestroyTexture(BlockTexture);
}
void Block::Draw(SDL_Renderer *renderTarget)
{
SDL_RenderCopy(renderTarget, BlockTexture, NULL, &BlockPos);
}
class Player
{
private:
SDL_Texture *Texture;
float moveSpeed;
float jumpSpeed;
int falling = 0;
SDL_Scancode keys [3];
public:
SDL_Rect PlayerPos;
Player(SDL_Renderer *renderTarget, std::string filePath, int PosX, int PosY, int Width, int Height);
~Player();
void Update(float delta, const Uint8 *Keystate);
void Draw(SDL_Renderer *renderTarget);
bool Collision(Block &p);
};
Player::Player(SDL_Renderer *renderTarget, std::string filePath, int PosX, int PosY, int Width, int Height)
{
SDL_Surface *surface = IMG_Load(filePath.c_str());
{
Texture = SDL_CreateTextureFromSurface(renderTarget, surface);
}
SDL_FreeSurface(surface);
PlayerPos.x = PosX;
PlayerPos.y = PosY;
PlayerPos.w = Width;
PlayerPos.h = Height;
keys[0] = SDL_SCANCODE_UP;
keys[1] = SDL_SCANCODE_LEFT;
keys[2] = SDL_SCANCODE_RIGHT;
moveSpeed = 200.f;
jumpSpeed = 100.f;
}
Player::~Player()
{
SDL_DestroyTexture(Texture);
}
void Player::Update(float delta, const Uint8 *KeyState)
{
if(KeyState[keys[0]])
{
PlayerPos.y -= moveSpeed * delta;
}
if(KeyState[keys[1]])
{
PlayerPos.x -= (moveSpeed / 2) * delta;
}
if(KeyState[keys[2]])
{
PlayerPos.x += moveSpeed * delta;
}
if(falling == 0)
{
PlayerPos.y += jumpSpeed * delta;
}
}
void Player::Draw(SDL_Renderer *renderTarget)
{
SDL_RenderCopy(renderTarget, Texture, NULL, &PlayerPos);
}
bool Player::Collision(Block &p)
{
if(PlayerPos.x + PlayerPos.w <= p.BlockPos.x || PlayerPos.x >= p.BlockPos.x + p.BlockPos.w ||
PlayerPos.y + PlayerPos.h <= p.BlockPos.y || PlayerPos.y >= p.BlockPos.y + p.BlockPos.h)
{
falling = 0;
return true;
}
else
falling = 1;
return false;
}
SDL_Texture *LoadTexture(std::string filePath, SDL_Renderer *Renderer)
{
SDL_Texture *texture = NULL;
SDL_Surface *surface = IMG_Load(filePath.c_str());
{
texture = SDL_CreateTextureFromSurface(Renderer, surface);
}
SDL_FreeSurface(surface);
return texture;
}
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *window = SDL_CreateWindow("Platform", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
SDL_Renderer *renderTarget = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
int imgFlags = IMG_INIT_PNG;
int currentTime = 0;
int previousTime = 0;
float delta = 0;
const Uint8 *Keystate;
Player Player(renderTarget, "BlockP.png", 100, 100, 50, 50);
Block Block1(renderTarget, "Block.png", 0, 500, 800, 100);
Block Block2(renderTarget, "Block.png", 100, 300, 300, 50);
bool isRunning = true;
SDL_Event ev;
while(isRunning)
{
Keystate = SDL_GetKeyboardState(NULL);
Player.Collision(Block1);
Player.Collision(Block2);
previousTime = currentTime;
currentTime = SDL_GetTicks();
delta = (currentTime - previousTime) / 1000.0f;
Player.Update(delta, Keystate);
while(SDL_PollEvent(&ev) != 0)
{
if(ev.type == SDL_QUIT)
isRunning = false;
}
SDL_RenderClear(renderTarget);
Player.Draw(renderTarget);
Block1.Draw(renderTarget);
Block2.Draw(renderTarget);
SDL_RenderPresent(renderTarget);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderTarget);
window = NULL;
renderTarget = NULL;
SDL_Quit();
return 0;
}
The problem with your code is that each call to Player.Collision overwrites the "falling" variable.
Player.Collision(Block1); //this call calculates a falling value
Player.Collision(Block2); //...then this call overwrites falling with a new value
So effectively your code is only testing if the player is colliding with Block2, so collisions with Block1 are ignored.
Currently your Collision function is:
bool Player::Collision(Block &p)
{
if(PlayerPos.x + PlayerPos.w <= p.BlockPos.x || PlayerPos.x >= p.BlockPos.x + p.BlockPos.w ||
PlayerPos.y + PlayerPos.h <= p.BlockPos.y || PlayerPos.y >= p.BlockPos.y + p.BlockPos.h)
{
falling = 0;
return true;
}
else
falling = 1;
return false;
}
Firstly, your "return false;" is not actually part of the else, as you don't have {}. In this particular case it makes no difference as the else is exited and then the return happens but your indentation would suggest you expect the "return false;" line to be executed as part of the else block so you should put:
else
{
falling = 1;
return false;
}
Next you want to say if you have already detected a collision (eg, with Block1) then don't set falling to 1, to do this add an if statement.
else
{
if(falling != 0) //if we haven't already detected a collision this frame
{
falling = 1;
}
return false;
}
You will however need to set falling back to 1 at the start of each frame, otherwise if a collision is detected on one frame then the player will never fall on subsequent frames, even if they are not colliding with a block.
As a side note, your Player.Update code modifies the player's y position if falling == 0, this seems counter intuitive as usually 0 is false and 1 is true, hence you seem to be saying if not falling then update y, where as it should be if falling update y. Personally I would use a bool rather than an int to hold the value of falling, and then say if(falling) update y, this would make your code clearer.
I have been struggling with this for a while and was wondering if anyone could help. I am trying to make a particle sample using C++ and SDL1.3 and I have had great success up until this point. The program compiles and the screen opens and nothing happens. When I run the debugger I get this error:
Unhandled exception at 0x0102414a in SDL 1.3 Space.exe: 0xC0000005: Access violation reading location 0x00000008.
The program '[7272] SDL 1.3 Space.exe: Native' has exited with code -1073741819 (0xc0000005).
and it points to this piece of the code:
bool particle::isDead()
{
return (SDL_GetTicks() >= endTime || x == 0 || y == 0 || x == SDL_GetVideoSurface()->w -1 || y == SDL_GetVideoSurface()->h - 1);
}
It would be greatly appreciated if someone would be so kind as to help me and /or point me in the right direction.
This is the entire program:
#include "SDL.h"
#include "common.h"
#include <stdio.h>
#include <string>
#include <cstdlib>
#include <vector>
#include <ctime>
/*static SDL_Texture *background = 0; //background
SDL_Renderer *renderer;
void render()
{
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, background, NULL, NULL); //Display background
SDL_RenderPresent(renderer);
endFrame = SDL_GetTicks();
};*/
class particle
{
float x, y, xvel, yvel;
Uint32 endTime;
Uint8 color;
public:
particle( float X, float Y, float Xvel, float Yvel, int life, Uint8 Color );
void move();
void show();
bool isDead();
};
particle::particle( float X, float Y, float Xvel, float Yvel, int life, Uint8 Color )
{
x = X;
y = Y;
xvel = Xvel;
yvel = Yvel;
endTime = SDL_GetTicks() + life;
color = Color;
}
void particle::move()
{
x += xvel;
y += yvel;
if ( x < 0 )
x = 0;
if ( y < 0 )
y = 0;
if ( x > SDL_GetVideoSurface() -> w)
x = SDL_GetVideoSurface() -> w - 1;
if ( y > SDL_GetVideoSurface() -> h)
y = SDL_GetVideoSurface() -> h -1;
}
void particle::show()
{
Uint8* pixels = (Uint8*) SDL_GetVideoSurface()->pixels;
Uint8* pixel = pixels + (int) y * SDL_GetVideoSurface()->pitch + (int) x;
*pixel = color;
}
bool particle::isDead()
{
return (SDL_GetTicks() >= endTime || x == 0 || y == 0 || x == SDL_GetVideoSurface()->w -1 || y == SDL_GetVideoSurface()->h - 1);
}
class particleEngine
{
std::vector <particle*> particles;
int x, y, maxparticle;
public:
particleEngine( int maxpart, int X, int Y );
~particleEngine();
void refresh();
};
particleEngine::particleEngine( int maxpart, int X, int Y )
{
x = X;
y = Y;
maxparticle = maxpart;
for ( int i = 0; i < maxparticle; i++ )
particles.push_back (new particle( x + rand()%6-3, y + rand()%6-3, rand()%10 + (float)rand()/(float)RAND_MAX - 5, rand()%10 + (float)rand()/(float)RAND_MAX - 5, 500 + rand()%1000, 0));
}
particleEngine::~particleEngine()
{
for ( int i = 0; i < maxparticle; i++)
delete particles[i];
}
void particleEngine::refresh()
{
for ( int i = 0; i < maxparticle; i++)
{
if ( particles[i]->isDead())
{
delete particles[i];
particles[i] = new particle( x + rand()%6-3, y + rand()%6-3, rand()%10 + (float)rand()/(float)RAND_MAX - 5, rand()%10 + (float)rand()/(float)RAND_MAX - 5, 500 + rand()%1000, 0);
}
else
{
particles[i]->move();
particles[i]->show();
}
}
}
int main( int argc, char* argv[] )
{
bool running = true;
const int FPS = 30;
Uint32 startFrame;
srand (time(0));
particleEngine ps(1000, SCREEN_WIDTH/2, SCREEN_HEIGHT/2);
SDL_Window *window; /* main window */
SDL_Renderer *renderer;
if (SDL_Init(SDL_INIT_EVERYTHING)!= 0)
{
printf("Could not initialize SDL");
}
window = SDL_CreateWindow("SDL 1.3 Particles", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN );
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
// Body of the program goes here.
while (running)
{
startFrame = SDL_GetTicks();
SDL_Event event;
//render();
//While there's events to handle
while( SDL_PollEvent( &event ) )
{
//If the user has Xed out the window
if (event.type == SDL_QUIT)
{
running = false;
}
}
//SDL_FillRect(renderer, &renderer->clip_rect, SDL_MapRGB(renderer->format, 0x00,0x00, 0x00));
ps.refresh();
//SDL_RenderCopy(renderer, 0, 0, 0);
SDL_RenderPresent(renderer);
//SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
//SDL_RenderClear(renderer);
if (1000/FPS>SDL_GetTicks()-startFrame)
SDL_Delay(1000/FPS-(SDL_GetTicks()-startFrame));
}
SDL_Quit();
return 0;
}
Access violations mean you are dereferencing null pointers(or pointers to memory you don't have access to), so this would mean (assuming the debugger synced with the source correctly) that SDL_GetVideoSurface is returning null, so you'll probably wanna throw a few checks in isDead. Secondly, its probably a good idea to store the w/h of the surface minus 1 in your class at during the creation of the surface, should mean a little less computational overhead along with shorter code.