I have rectangle object (sf::IntRect) , with attributes: left, top, width and height on a 2D plane. I want to rotate it by a multiple of 90 degrees (that is 90, 180 or 270), around point (0,0). Thus I am writing a function like this:
void rotateRect(sf::IntRect& rect, int rot)
{
//code I need
}
the rotation is either 0 (0), 1 (90), 2 (180) or 3 (270).
How can I achieve this as simply as possible.
Here is a basic solution using sf::Tranform on sf::FloatRect:
constexpr auto rotationAngle(int rot) { return rot * 90.f; }
void rotateRect(sf::IntRect& rect, int rot)
{
auto deg = rotationAngle(rot);
auto transform = sf::Transform();
transform.rotate(deg);
// Would be better if rect was a FloatRect...
auto rectf = sf::FloatRect(rect);
rectf = transform.transformRect(rectf);
rect = static_cast<sf::IntRect>(rectf);
}
However, I personally would slightly change the signature of your function to use float rects and a more compact notation:
sf::FloatRect rotateRect(sf::FloatRect const& rect, int rot)
{
return sf::Transform().rotate(rotationAngle(rot)).transformRect(rect);
}
And below a full example showing how it behaves.
#include <SFML/Graphics.hpp>
constexpr auto rotationAngle(int rot) { return rot * 90.f; }
sf::FloatRect rotateRect(sf::FloatRect const& rect, int rot)
{
return sf::Transform().rotate(rotationAngle(rot)).transformRect(rect);
}
void updateShape(sf::RectangleShape& shape, sf::FloatRect const& rect)
{
shape.setPosition(rect.left, rect.top);
shape.setSize({ static_cast<float>(rect.width), static_cast<float>(rect.height) });
}
int main(int, char const**)
{
sf::RenderWindow window(sf::VideoMode(500, 500), "rotate");
auto rect = sf::FloatRect(0, 0, 100, 50);
auto shape = sf::RectangleShape();
shape.setFillColor(sf::Color::Red);
updateShape(shape, rect);
auto view = window.getView();
view.move({ -250, -250 });
window.setView(view);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::R)
{
rect = rotateRect(rect, 1);
updateShape(shape, rect);
}
if (event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::N)
{
rect = sf::FloatRect(50, 50, 100, 50);
updateShape(shape, rect);
}
if (event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::M)
{
rect = sf::FloatRect(0, 0, 100, 50);
updateShape(shape, rect);
}
}
window.clear();
window.draw(shape);
window.display();
}
return EXIT_SUCCESS;
}
NB: I used a few trick from C++14 but I'm sure you can convert that code to C++11/C++98 if you need to.
Related
I am making a simples possible jigsaw game using SDL.
Currently i am having trouble applying image texutes to Rects that serve as a puzzle pieces.
The way I applied texture to background doesnt work for pieces(rect1-9).
All pieces are 100x100 and picture is 300x300.
Tell me what is wrong, how to do it right and also explain how to Clip render(apply only part of the texture) with SDL_RenderCopy
#include <list>
#include <SDL.h>
int main(int argc, char ** argv)
{
// variables
bool quit = false;
SDL_Event event;
bool leftMouseButtonDown = false;
SDL_Point mousePos;
SDL_Point clickOffset;
// init SDL
SDL_Init(SDL_INIT_VIDEO);
SDL_Window * window = SDL_CreateWindow("SDL2 Drag and Drop",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 600, 400, 0);
SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
SDL_Surface* bgSurface = SDL_LoadBMP("background.bmp");
SDL_Texture* bgTexture = SDL_CreateTextureFromSurface (renderer,bgSurface);
SDL_Surface* picSurface = SDL_LoadBMP("picture.bmp");
SDL_Texture* picTexture = SDL_CreateTextureFromSurface (renderer,bgSurface);
SDL_Rect rect1 = { 52, 51, 100, 100 };
SDL_Rect rect2 = { 152, 51, 100, 100 };
SDL_Rect rect3 = { 250, 51, 100, 100 };
SDL_Rect rect4 = { 52, 151, 100, 100 };
SDL_Rect rect5 = { 152, 151, 100, 100 };
SDL_Rect rect6 = { 250, 151, 100, 100 };
SDL_Rect rect7 = { 52, 249, 100, 100 };
SDL_Rect rect8 = { 152, 249, 100, 100 };
SDL_Rect rect9 = { 250, 249, 100, 100 };
SDL_Rect background = { 0, 0, 600, 400 };
SDL_Rect * selectedRect = NULL;
std::list<SDL_Rect *> rectangles;
rectangles.push_back(&rect1);
rectangles.push_back(&rect2);
rectangles.push_back(&rect3);
rectangles.push_back(&rect4);
rectangles.push_back(&rect5);
rectangles.push_back(&rect6);
rectangles.push_back(&rect7);
rectangles.push_back(&rect8);
rectangles.push_back(&rect9);
// handle events
while (!quit)
{
SDL_Delay(10);
SDL_PollEvent(&event);
switch (event.type)
{
case SDL_QUIT:
quit = true;
break;
case SDL_MOUSEBUTTONUP:
if (leftMouseButtonDown && event.button.button == SDL_BUTTON_LEFT)
{
leftMouseButtonDown = false;
selectedRect = NULL;
}
break;
case SDL_MOUSEBUTTONDOWN:
if (!leftMouseButtonDown && event.button.button == SDL_BUTTON_LEFT)
{
leftMouseButtonDown = true;
for (auto rect : rectangles)
{
if (SDL_PointInRect(&mousePos, rect))
{
selectedRect = rect;
clickOffset.x = mousePos.x - rect->x;
clickOffset.y = mousePos.y - rect->y;
break;
}
}
}
break;
case SDL_MOUSEMOTION:
{
mousePos = { event.motion.x, event.motion.y };
if (leftMouseButtonDown && selectedRect != NULL)
{
selectedRect->x = mousePos.x - clickOffset.x;
selectedRect->y = mousePos.y - clickOffset.y;
}
}
break;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer,bgTexture,NULL,&background );
SDL_RenderCopy(renderer,bgTexture,NULL,&rect1 );
for (auto const& rect : rectangles)
{
if (rect == selectedRect)
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 100);
else
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 0);
SDL_RenderFillRect(renderer, rect);
}
SDL_RenderPresent(renderer);
}
// cleanup SDL
SDL_DestroyTexture (bgTexture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
I'm drawing a convex shape figure using SFML library:
#include <SFML/Graphics.hpp>
using namespace sf;
int main()
{
RenderWindow window(VideoMode(400, 400), "SFML window");
ConvexShape convex;
convex.setPointCount(8);
convex.setPoint(0, sf::Vector2f(0, 0));
convex.setPoint(1, sf::Vector2f(180, 0));
convex.setPoint(2, sf::Vector2f(180, 90));
convex.setPoint(3, sf::Vector2f(100, 90));
convex.setPoint(4, sf::Vector2f(100, 180));
convex.setPoint(5, sf::Vector2f(30, 180));
convex.setPoint(6, sf::Vector2f(30, 90));
convex.setPoint(7, sf::Vector2f(0, 90));
convex.setPosition(100, 100);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(convex);
window.display();
}
return EXIT_SUCCESS;
}
The points defined in clockwise order and everything is ok!
convex1
But when I change two coordinates a little bit I'm getting something that I really don't expect:
#include <SFML/Graphics.hpp>
using namespace sf;
int main()
{
RenderWindow window(VideoMode(400, 400), "SFML window");
ConvexShape convex;
convex.setPointCount(8);
convex.setPoint(0, sf::Vector2f(0, 0));
convex.setPoint(1, sf::Vector2f(180, 0));
convex.setPoint(2, sf::Vector2f(180, 90));
convex.setPoint(3, sf::Vector2f(100, 90));
convex.setPoint(4, sf::Vector2f(100, 200)); // CHANGED 180 to 200
convex.setPoint(5, sf::Vector2f(30, 200)); // CHANGED 180 to 200
convex.setPoint(6, sf::Vector2f(30, 90));
convex.setPoint(7, sf::Vector2f(0, 90));
convex.setPosition(50, 50);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(convex);
window.display();
}
return EXIT_SUCCESS;
}
I'm getting the following window:
convex2
Why this situation take place?
These coordinates are related to the points in your Convex shape, you should use those only to change the format of the object. In order to move your entire object you should use this:
convex.move(x Position, y Position);
I have a file that contains textures. I load it to sf::Texture and split into sprites with setTextureRect.
Now lets say one sprite contains part of texture that is 20 pixels wide. How can I render it to fit width of e.g. 213 pixels. The only way I can think about is to render it to sf::RenderTexture and crop it with another sprite.
Is there a better way to do this?
You can use sf::Texture::setRepeated to do that.
However, you'll need to copy that part of your bigger image into an independant texture.
Here is an example:
#include <SFML/Graphics.hpp>
int main(int, char const**)
{
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window");
sf::Image img;
img.create(20, 20);
for (auto i = 0; i < 20; ++i) {
for (auto j = 0; j < 20; ++j) {
img.setPixel(i, j, sf::Color(255 * i / 20, 255 * j / 20, 255 * i / 20 * j / 20));
}
}
sf::Texture texture;
texture.loadFromImage(img);
texture.setRepeated(true);
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setTextureRect({ 0, 0, 800, 600 });
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) {
window.close();
}
}
window.clear();
window.draw(sprite);
window.display();
}
return EXIT_SUCCESS;
}
I'm a beginner in SDL and I am simply experimenting with movement of sprites in SDL. I have a rectangle at the bottom of the screen that you can move left and right. However, when you move the sprite left and right, it "duplicates", creating a sort of trace/snake-game-like effect when you move the player left and right. Any ideas on how to fix this?
///the headers
#include "stdafx.h"
#include <SDL.h>
#include <SDL_image.h>
#include <string>
using namespace std;
//screen attributes
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 480;
const int SPRITE_SIZE = 32;
//the surfaces
SDL_Surface *image = NULL;
SDL_Surface *screen = NULL;
int main( int argc, char* argv[] )
{
SDL_Surface *screen, *temp,/* *temp2*/ *sprite/*, *sprite2*/;
SDL_Rect rcSprite/*, rcSprite2*/;
SDL_Event event;
Uint8 *keystate;
int gameover;
//initiailise sdl
SDL_Init(SDL_INIT_VIDEO);
SDL_WM_SetCaption("Pong Move Test", "Pong Move Test");
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
//load the sprite (paddle)
temp = SDL_LoadBMP("pongpaddle1.bmp");
sprite = SDL_DisplayFormat(temp);
SDL_FreeSurface(temp);
//set positions of sprite
rcSprite.x = 400;
rcSprite.y = 450;
gameover = 0;
//message pump
while (!gameover)
{
//look for an event
if (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
gameover = 1;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_ESCAPE:
case SDLK_q:
gameover = 1;
break;
}
break;
}
}
//handle sprite movement
keystate = SDL_GetKeyState(NULL);
if (keystate[SDLK_LEFT])
{
rcSprite.x -= 1;
}
if (keystate[SDLK_RIGHT])
{
rcSprite.x += 1;
}
//colide with edges of screen
if (rcSprite.x < 0)
{
rcSprite.x =0;
}
else if (rcSprite.x > (SCREEN_WIDTH - SPRITE_SIZE))
{
rcSprite.x = (SCREEN_WIDTH - SPRITE_SIZE);
}
//draw the sprite
SDL_BlitSurface(sprite, NULL, screen, &rcSprite);
//update the screen
SDL_UpdateRect(screen, 0, 0, 0, 0);
}
//clean up area
SDL_FreeSurface(sprite);
SDL_Quit();
return 0;
}
You aren't clearing the screen before drawing each frame, so you're actually drawing over the previous frame, which is why you get a 'trailing' effect.
You can solve this by using SDL_FillRect to fill the entire screen with a background color before drawing each frame:
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
This is how I've made the rectangle to move by pressing buttons:
#include <stdio.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_primitives.h>
#define ScreenWidth 1440
#define ScreenHeight 790
int main()
{
if (!al_init())
{
al_show_native_message_box(NULL, NULL, NULL, "Could not initialize Allegro 5", NULL, NULL);
return -1;
}
al_set_new_display_flags(ALLEGRO_WINDOWED);
ALLEGRO_DISPLAY *display = al_create_display(ScreenWidth, ScreenHeight);
al_set_window_position(display, 0, 0);
al_set_window_title(display, "Lab 14");
if (!display)
{
al_show_native_message_box(display, "Error", NULL, "Display window was not created succesfully", NULL, ALLEGRO_MESSAGEBOX_ERROR);
return -1;
}
al_init_primitives_addon();
al_install_keyboard();
ALLEGRO_COLOR orange = al_map_rgb(255, 160, 0);
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
al_register_event_source(event_queue, al_get_keyboard_event_source());
bool done = false;
int x = 10, y = 10;
int moveSpeed = 5;
int state = NULL;
while (!done)
{
ALLEGRO_EVENT events;
al_wait_for_event(event_queue, &events);
if(events.type = ALLEGRO_EVENT_KEY_DOWN)
{
switch(events.keyboard.keycode)
{
case ALLEGRO_KEY_DOWN:
y += moveSpeed;
break;
case ALLEGRO_KEY_UP:
y -= moveSpeed;
break;
case ALLEGRO_KEY_RIGHT:
x += moveSpeed;
break;
case ALLEGRO_KEY_LEFT:
x -= moveSpeed;
break;
case ALLEGRO_KEY_ESCAPE:
done = true;
break;
}
}
al_draw_rectangle(x, y, x + 20, y + 20, orange, 2.0);
al_flip_display();
al_clear_to_color(al_map_rgb(0, 0, 0));
}
al_draw_rectangle(300, 600, 500, 200, orange, 2.0);
al_destroy_display(display);
al_destroy_event_queue(event_queue);
return 0;
}
How do I modify the code to make the rectangle smoothly cross the display border while appearing on the opposite side? Smoothly means that it shouldn't fully appear on the opposite side.
You have the screen dimensions and position and size of your rectangle, thus you know how much it will be cut off screen.
You have to check every rectangle before you blit it to the screen.
If you detect a cut simply calculate how much and make another blit call for the same rectangle on displaced coordinates on the other side of the screen so just a part of the rect. is visible.
In your code
al_draw_rectangle(x, y, x + 20, y + 20, orange, 2.0);
add another call with different coordinates if the rectangle is (partially) offscreen.