How do I move a rectangle across a screen with SDL2.0 in C++?
I’m looking to move a square around another square using SDL. I’m looping through a series of values and rendering using the values as a position for one of my rectangles. I have another rectangle that is stationary, I’m currently re-rendering the stationary rectangle every time I loop, but I want to eliminate that as I know it’s not efficient.
#include "render.h" #include "SDL2/SDL.h" #include #include using namespace std; const string FILE_NAME = "Orbit.txt"; const int WINDOW_WIDTH = 1280; const int WINDOW_HEIGHT = 800; const int SUN_LENGTH = 40; const int EARTH_LENGTH = 20; int main() < //Initialize SDL SDL_Init(SDL_INIT_VIDEO); SDL_Window *window; SDL_Renderer *renderer; window = SDL_CreateWindow( "test", //title SDL_WINDOWPOS_CENTERED, //initial x position SDL_WINDOWPOS_CENTERED, //initial y position WINDOW_WIDTH, //width WINDOW_HEIGHT, //height 0 //flags ); if (window == NULL) < // In the case that the window could not be made. printf("Could not create window: %s\n", SDL_GetError()); return 1; >renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); ifstream file; file.open(FILE_NAME); //Prepare render loop string trash; string time; double xPos; double yPos; int shorterEdge; if (WINDOW_HEIGHT < WINDOW_WIDTH) < shorterEdge = WINDOW_HEIGHT; >else < shorterEdge = WINDOW_WIDTH; >int numPixelsAU = (shorterEdge/2) - (EARTH_LENGTH/2) - 5; SDL_Rect sun; sun.x = ((WINDOW_WIDTH/2) - (SUN_LENGTH/2)); sun.y = ((WINDOW_HEIGHT/2) - (SUN_LENGTH/2)); sun.w = SUN_LENGTH; sun.h = SUN_LENGTH; SDL_Rect earth; //Render loop while (file >> trash >> time >> trash >> xPos >> trash >> yPos) < //Clear previous render SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); //Render Sun SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); SDL_RenderDrawRect(renderer, &sun); SDL_RenderFillRect(renderer, &sun); //Render Earth earth.x = ((sun.x + (SUN_LENGTH/2)) + (numPixelsAU*xPos) - (EARTH_LENGTH/2)); earth.y = ((sun.y + (SUN_LENGTH/2)) - (numPixelsAU*yPos) - (EARTH_LENGTH/2)); earth.w = EARTH_LENGTH; earth.h = EARTH_LENGTH; SDL_SetRenderDrawColor(renderer, 30, 144, 255, 255); SDL_RenderDrawRect(renderer, &earth); SDL_RenderFillRect(renderer, &earth); SDL_RenderPresent(renderer); SDL_Delay(50); >SDL_Delay(3000); // Close and destroy the window SDL_DestroyWindow(window); // Clean up SDL_Quit(); return 0; >
Введение в программирование: заготовка игры-платформера на SDL в 300 строк C++
Этот текст предназначен для тех, кто только осваивает программирование. Я читаю лекции по C++ на первом курсе местного университета, и в качестве практикума предлагаю запрограммировать любую игру (не выношу проектов типа «софт бронирования книг в местной библиотеке»). Соответственно, чтобы помочь начинающим, я сделал некоторое количество заготовок, с которых можно стартовать свой проект. Например, заготовку олдскульного 3д шутера в 486 строк C++ я уже описывал, а вот тут можно посмотреть, что из неё сделали первокурсники.
В этот раз всё будет ещё проще, я хочу сделать заготовку под простейший платформер, вот так выглядит результат:
На данный момент проект содержит менее трёхсот строчек цпп:
ssloy@khronos:~/sdl2-demo/src$ cat *.cpp *.h | wc -l 296
Мой опыт показывает, что просто выложить код заготовки недостаточно. Нужно детально описать, как именно я пришёл к такому коду, ведь самый главный навык программиста — это суметь разбить сложную задачу на некоторое количество более простых подзадач, каждая из которых решается легко.
Шаг первый: компилируем проект и открываем окно
Я ничего не знаю про оконные библиотеки, поэтому выбрал первую попавшуюся, а именно SDL. Первый шаг — самый сложный. Нужно суметь скомпилировать пустой проект, и слинковаться с выбранной библиотекой. Самая первая задача — открыть пустое окно, и отработать событие его закрытия. Вот тут можно найти соответствующий коммит:
Изначально я хотел сделать черновой репозиторий, и потом почистить историю, убрать детские баги, сделать красивый «один коммит на одну фичу», но тут случился анекдот: только я создал репозиторий, как он был немедленно форкнут парой человек, один из которых мне к тому же прислал пулл реквест . Соответственно, чистить историю, не сломав их репы, я уже не могу. Издержки двух с половиной тысяч фолловеров на гитхабе
. Таким образом, у меня честный репозиторий без прикрас.
Сборочный файл CMakeLists.txt лучше взять из последней версии, он линкуется с SDL2, если найдёт его в системе, а в противном случае подтягивает его исходники, и компилирует его сам. Должно работать без сучка-задоринки как под линухом, так и под виндой.
Вот так выглядит код, открывающий пустое окно:
#include #define SDL_MAIN_HANDLED #include void main_loop(SDL_Renderer *renderer) < while (1) < // main game loop SDL_Event event; // handle window closing if (SDL_PollEvent(&event) && (SDL_QUIT==event.type || (SDL_KEYDOWN==event.type && SDLK_ESCAPE==event.key.keysym.sym))) break; // quit SDL_RenderClear(renderer); // re-draw the window SDL_RenderPresent(renderer); >> int main() < SDL_SetMainReady(); // tell SDL that we handle main() function ourselves, comes with the SDL_MAIN_HANDLED macro if (SDL_Init(SDL_INIT_VIDEO)) < std::cerr SDL_Window *window = nullptr; SDL_Renderer *renderer = nullptr; if (SDL_CreateWindowAndRenderer(1024, 768, SDL_WINDOW_SHOWN | SDL_WINDOW_INPUT_FOCUS, &window, &renderer)) < std::cerr SDL_SetWindowTitle(window, "SDL2 game blank"); SDL_SetRenderDrawColor(renderer, 210, 255, 179, 255); main_loop(renderer); // all interesting things happen here SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; >
Собственно, там ничего сверхъестественного: мы инициализируем SDL, создаём окно с зоной рендера и запускаем основной цикл игры. По событию закрытия окна или по нажатию эскейпа выходим из цикла и чистим память. Piece of cake.
Шаг второй: счётчик fps
На втором этапе я решил отобразить количество перерисовок экрана в секунду, хочу увидеть вот такой результат (не пугайтесь сорока тысяч fps, всё же мы ничего не делаем в основном цикле!):
Для этого нам нужно две вещи:
- научиться считать количество перерисовок экрана в секунду
- научиться его отображать на экране
Давайте начнём с отрисовки счётчика. Сперва я хотел рендерить текст при помощи библиотеки SDL_ttf, но потом выяснилось, что она тянет за собой ещё и другие зависимости, и мне стало лень автоматически собирать ещё и их, если они не установлены в системе. Поэтому я решил сделать существенно тупее: я нарисовал десять цифр размера 24×30 пикселей, и упаковал их в один .bmp файл размера 240 x 30 пикселей:
Всё едино мне нужно будет уметь работать со спрайтами, так почему бы не использовать эту же технику для отрисовки счётчика? Итак, вот структура для работы со спрайтами:
struct Sprite < Sprite(SDL_Renderer *renderer, const std::string filename, const int width) : width(width) < SDL_Surface *surface = SDL_LoadBMP((std::string(RESOURCES_DIR) + filename).c_str()); if (!surface) < std::cerr if (!(surface->w%width) && surface->w/width) < // image width must be a multiple of sprite width height = surface->h; nframes = surface->w/width; texture = SDL_CreateTextureFromSurface(renderer, surface); > else std::cerr SDL_Rect rect(const int idx) const < // choose the sprite number idx from the texture return < idx*width, 0, width, height >; > ~Sprite() < // do not forget to free the memory! if (texture) SDL_DestroyTexture(texture); >SDL_Texture *texture = nullptr; // the image is to be stored here int width = 0; // single sprite width (texture width = width * nframes) int height = 0; // sprite height int nframes = 0; // number of frames in the animation sequence >;
В переменных состояния объекта у нас указатель на непосредственно текстуру, ширина одного спрайта, высота спрайта, и количество спрайтов в текстуре. Конструктор просто подтягивает .bmp файл и проверяет, что его размеры совпадают с ожидаемым. Ну а метод rect(idx) позволяет выбрать спрайт с индексом idx для последующей его отрисовке в зоне рендера.
А теперь давайте поговорим про счётчик. Я создал структуру под названием FPS_Counter , и просто вызываю её метод .draw() внутри основного цикла:
void main_loop(SDL_Renderer *renderer) < FPS_Counter fps_counter(renderer); while (1) < // main game loop [. ] SDL_RenderClear(renderer); // re-draw the window fps_counter.draw(); SDL_RenderPresent(renderer); >>
Метод .draw() ведёт подсчёт вызовов, и отрисовывает счётчик, используя подгруженные спрайты с цифрами. Давайте внимательно посмотрим на эту структуру. Основая идея — измерять количество вызовов .draw() раз в некоторое время (у меня триста миллисекунд). Соответственно, у меня есть два инта — fps_prev хранит последнее измеренное значение fps, а fps_cur это текущий счётчик. Ещё нужно хранить временную метку timestamp для отслеживания этих самых трёхсот миллисекунд. Вот так выглядит полный код структуры:
struct FPS_Counter < FPS_Counter(SDL_Renderer *renderer) : renderer(renderer), numbers(renderer, "numbers.bmp", 24) <>void draw() < fps_cur++; double dt = std::chrono::duration(Clock::now() - timestamp).count(); if (dt>=.3) < // every 300 ms update current FPS reading fps_prev = fps_cur/dt; fps_cur = 0; timestamp = Clock::now(); >SDL_Rect dst = ; // first character will be drawn here for (const char c : std::to_string(fps_prev)) < // extract individual digits of fps_prev SDL_Rect src = numbers.rect(c-'0'); // crude conversion of numeric characters to int: '7'-'0'=7 SDL_RenderCopy(renderer, numbers.texture, &src, &dst); // draw current digit dst.x += numbers.width + 4; // draw characters left-to-right, +4 for letter spacing (TODO: add padding directly to the .bmp file) >> int fps_cur = 0; // the FPS readings are updated once in a while; fps_cur is the number of draw() calls since the last reading int fps_prev = 0; // and here is the last fps reading TimeStamp timestamp = Clock::now(); // last time fps_prev was updated SDL_Renderer *renderer; // draw here const Sprite numbers; // "font" file >;
fps_counter.draw(); inside main loop while(1) < . >.
Вот тут можно посмотреть коммит с рабочим кодом.
Шаг третий: сорок тысяч fps это многовато, давайте поменьше
На данный момент у меня на ноуте вентиляторы крутятся так, что он порывается улететь. Давайте-ка снизим нагрузку на проц. Как заставить основной цикл исполняться не больше 50 раз в секунду? Самый наивный вариант — это что-то вроде такого кода:
while (1) < // main game loop do_something(); sleep(20); >>
Мы можем тупо вставить задержку на 20 миллисекунд в тело цикла, получив максимум 50 fps. Такой подход имеет право на жизнь, но он предполагает, что время работы do_nothing() пренебрежимо. А если вдруг оно будет исполняться, скажем, за 12мс? Тогда нам задержку нужно не 20, а 8, иначе сильно проседает FSP. А ведь это ещё зависит от компа… Поэтому я предлагаю следующий подход:
TimeStamp timestamp = Clock::now(); while (1) < // main game loop double dt = std::chrono::duration(Clock::now() - timestamp).count(); if (dt <.02) < // 50 FPS regulation std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; >timestamp = Clock::now(); do_something(); > >
Мы просто храним временную метку timestamp , соответствующую последней отрисовке экрана, и не даём пройти внутрь цикла до тех пор, пока не истекут 20 миллисекунд. Задержка на 1мс вставлена для того, чтобы не грузить CPU на 100% пустыми проверками времени. Разумеется, в реальной игре за это время лучше делать что-нибудь полезное, считать физику, например.
Итак, вот результат:
Шаг четвёртый: отрисовываем уровень
Теперь давайте отрисуем карту уровня, вот тут соответствующий коммит. Я хочу видеть вот такой результат:
Для этого я сначала нарисовал текстуру 768 x 128 пикслей, в которую у меня упаковано шесть спрайтов каменюк размером 128×128:
Мой экран разбит на 192 клетки (16 по горизонтали и 12 по вертикали), и каждой клетке соответствует какая-то текстура.
Я создал структуру Map , которая используется следующим образом в основом цикле игры:
Map map(renderer); while (1) < // main game loop [. ] SDL_RenderClear(renderer); // re-draw the window map.draw(); SDL_RenderPresent(renderer); >
Сама структура определена следующим образом:
struct Map < Map(SDL_Renderer *renderer) : renderer(renderer), textures(renderer, "ground.bmp", 128) < assert(sizeof(level) == w*h+1); // +1 for the null terminated string int window_w, window_h; if (!SDL_GetRendererOutputSize(renderer, &window_w, &window_h)) < tile_w = window_w/w; tile_h = window_h/h; >else std::cerr void draw() < // draw the level in the renderer window for (int j=0; j; SDL_Rect src = textures.rect(get(i,j)); SDL_RenderCopy(renderer, textures.texture, &src, &dst); > > int get(const int i, const int j) const < // retreive the cell, transform character to texture index assert(i>=0 && j>=0 && i bool is_empty(const int i, const int j) const < assert(i>=0 && j>=0 && i SDL_Renderer *renderer; // draw here int tile_w = 0, tile_h = 0; // tile size in the renderer window const Sprite textures; // textures to be drawn static constexpr int w = 16; // overall map dimensions, the array level[] has the length w*h+1 (+1 for the null character) static constexpr int h = 12; // space character for empty tiles, digits indicate the texture index to be used per tile static constexpr char level[w*h+1] = " 123451234012340"\ "5 5"\ "0 0"\ "5 5 5"\ "0 0 0"\ "512340 12345 5"\ "0 0"\ "5 51"\ "0 50 12"\ "5 51234"\ "0 12345"\ "1234012345052500"; >;
Самое главное тут — массив level , который определяет, какой клетке соответствует какая текстура. В методе .draw() я прохожу по всем клеткам уровня, и для каждой незанятой отрисовываю соответствующий спрайт. Вспомогательные методы is_empty(i, j) и get(i, j) позволяют определить, пуста ли клетка с индексами i, j , и понять номер спрайта. Ну а в конструкторе я просто подтягиваю соответствующий .bmp файл и определяю размер клетки в пикселях экрана.
Шаг пятый: персонаж и его анимация
Осталось совсем немного: непосредственно персонаж. Давайте для начала научимся показывать анимации. Я взял карандаш и нарисовал последовательность кадров, которая показывает идущего человечка:
Я хочу получить вот такой результат (коммит брать тут):
Не бейте меня больно за кривые рисунки, я программист, а не художник! Как же нам их показать на экране? Для начала давайте опишем структуру, которая будет ответственна за анимации:
struct Animation : public Sprite < Animation(SDL_Renderer *renderer, const std::string filename, const int width, const double duration, const bool repeat) : Sprite(renderer, filename, width), duration(duration), repeat(repeat) <>bool animation_ended(const TimeStamp timestamp) const < // is the animation sequence still playing? double elapsed = std::chrono::duration(Clock::now() - timestamp).count(); // seconds from timestamp to now return !repeat && elapsed >= duration; > int frame(const TimeStamp timestamp) const < // compute the frame number at current time for the the animation started at timestamp double elapsed = std::chrono::duration(Clock::now() - timestamp).count(); // seconds from timestamp to now int idx = static_cast(nframes*elapsed/duration); return repeat ? idx % nframes : std::min(idx, nframes-1); > SDL_Rect rect(const TimeStamp timestamp) const < // choose the right frame from the texture return < frame(timestamp)*width, 0, width, height >; > const double duration = 1; // duration of the animation sequence in seconds const bool repeat = false; // should we repeat the animation? >;
Анимация не особо отличается от простых спрайтов, поэтому я её и унаследовал от структуры Sprite . У неё два дополнительных члена: время проигрывания анимации и булевская переменная, которая говорит, нужно ли играть анимацию в цикле. Конструктор просто наследуется от конструктора Sprite , а дополнительные методы позволяют узнать, закончилось ли проигрывание ( animation_ended(timestamp) ), и получить текущий кадр анимации ( frame(timestamp) + rect(timestamp) ).
Теперь осталось описать персонажа:
struct Player < enum States < REST=0, TAKEOFF=1, FLIGHT=2, LANDING=3, WALK=4, FALL=5 >; Player(SDL_Renderer *renderer) : renderer(renderer), sprites < >void draw() < SDL_Rect src = sprites[state].rect(timestamp); SDL_Rect dest = < int(x)-sprite_w/2, int(y)-sprite_h, sprite_w, sprite_h >; SDL_RenderCopyEx(renderer, sprites[state].texture, &src, &dest, 0, nullptr, backwards ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE); > double x = 150, y = 200; // coordinates of the player bool backwards = false; // left or right int state = WALK; TimeStamp timestamp = Clock::now(); const int sprite_w = 256; // size of the sprite on the screen const int sprite_h = 128; SDL_Renderer *renderer; // draw here std::array sprites; // sprite sequences to be drawn >;
Я сразу сделал заготовку под то, что у персонажа будет несколько характерных состояний (ходьба, прыжок, падение). Положение игрока на экране я задаю переменными x и y , они соответствют середине подошвы. Направление лево/право задаётся булевской переменной backwards , а переменная timestamp задаёт метку времени начала проигрывания анимации.
Использование этой структуры пока что идентично использованию карты и счётчика fps:
Player player(renderer); while (1) < // main game loop [. ] SDL_RenderClear(renderer); // re-draw the window [. ] player.draw(); SDL_RenderPresent(renderer); >
Шаг шестой: опрос клавиатуры и обработка столкновений
А теперь давайте научимся опрашивать клавиатуру и обрабатывать столкновения с картой (вот коммит). Я хочу получить вот такой результат:
Для обработки клавиатуры я добавил функцию handle_keyboard() , которая вызывает функцию смены состояния set_state() в зависимости от того, какие курсорные стрелки нажаты:
void handle_keyboard() < const Uint8 *kbstate = SDL_GetKeyboardState(NULL); if (state==WALK && !kbstate[SDL_SCANCODE_RIGHT] && !kbstate[SDL_SCANCODE_LEFT]) set_state(REST); if (state==REST && (kbstate[SDL_SCANCODE_LEFT] || kbstate[SDL_SCANCODE_RIGHT])) < backwards = kbstate[SDL_SCANCODE_LEFT]; set_state(WALK); >> void set_state(int s)
Для изменения положения на экране я вызываю функцию update_state , которая и занимается тем, что изменяет переменную состояния x :
void update_state(const double dt, const Map &map) < x += dt*vx; // candidate coordinates prior to collision detection if (!map.is_empty(x/map.tile_w, y/map.tile_h)) < // horizontal collision detection int snap = std::round(x/map.tile_w)*map.tile_w; // snap the coorinate to the boundary of last free tile x = snap + (snap>x ? 1 : -1); // be careful to snap to the left or to the right side of the free tile vx = 0; // stop > >
Для начала я считаю координату на следующем шаге: x = x + dt*vx , а затем проверяю, не попадает ли эта координата в заполненную клетку карты. Если такое случается, то я останавливаю персонажа, и обновляю x таким образом, чтобы она оказалась на границе заполненной клетки.
Шаг седьмой: сила тяжести!
Сила тяжести добавляется элементарно, мы выписываем абсолютно такое же поведение и для вертикальной координаты, лишь добавив ещё и увеличение вертикальной скорости vy += dt*300 , где 300 — это ускорение свободного падения в 300 пикселей в секунду за секунду.
void update_state(const double dt, const Map &map) < [. ] y += dt*vy; // prior to collision detection vy += dt*300; // gravity [. ] >
Обработку столкновения по вертикали даже описывать не буду, она ничем не отличается от горизонтали, коммит брать тут, ну а вот и результат:
Последние штрихи
Единственное, что осталось добавить нашу заготовку — это обработку прыжков. Это делается заданием начальных скоростей vx и vy в зависимости от комбинации курсорных клавиш. Чисто по приколу у меня есть прыжки в высоту и прыжки в длину:
Заключение
Ну вот, собственно, и всё. Как я и обещал, игры как таковой у меня нет, но есть играбельная демка всего из 296 строк кода.
Моей задачей было лишь объяснить основные принципы построения примитивного платформера, и мне очень любопытно будет посмотреть, что выдадут мои студенты в этом году. Любые комментарии и идеи приветствуются, как по улучшению этого кода, так и по поводу того, что можно сделать ещё.
SDL_PointInRect
Use this function to check if a point resides inside a rectangle.
Syntax
SDL_bool SDL_PointInRect(const SDL_Point* p, const SDL_Rect* r)
Function Parameters
p
an SDL_Point which is the point
r
an SDL_Rect which is the rectangle
Return Value
Returns SDL_TRUE if point resides inside rectangle or SDL_FALSE if not.
Remarks
Both p and r must not be NULL.
Version
This function is available since SDL 2.0.4.
[ − ] [src] Struct sdl2:: rect:: Rect
The width and height of a Rect must always be strictly positive (never zero). In cases where empty rects may need to represented, it is recommended to use Option , with None representing an empty rectangle (see, for example, the output of the intersection method).
Methods
impl Rect [src]
pub fn new(x: i32, y: i32, width: u32, height: u32) -> Rect [src]
Creates a new rectangle from the given values.
The width and height are clamped to ensure that the right and bottom sides of the rectangle does not exceed i32::max_value() (the value 2147483647, the maximal positive size of an i32). This means that the rect size will behave oddly if you move it very far to the right or downwards on the screen.
Rect s must always be non-empty, so a width and/or height argument of 0 will be replaced with 1.
pub fn from_center(center: P, width: u32, height: u32) -> Rect where
P: Into, [src]
Creates a new rectangle centered on the given position.
The width and height are clamped to ensure that the right and bottom sides of the rectangle does not exceed i32::max_value() (the value 2147483647, the maximal positive size of an i32). This means that the rect size will behave oddly if you move it very far to the right or downwards on the screen.
Rect s must always be non-empty, so a width and/or height argument of 0 will be replaced with 1.
pub fn x(&self) -> i32 [src]
The horizontal position of this rectangle.
pub fn y(&self) -> i32 [src]
The vertical position of this rectangle.
pub fn width(&self) -> u32 [src]
The width of this rectangle.
pub fn height(&self) -> u32 [src]
The height of this rectangle.
pub fn size(&self) -> (u32, u32) [src]
Returns the width and height of this rectangle.
pub fn set_x(&mut self, x: i32) [src]
Sets the horizontal position of this rectangle to the given value, clamped to be less than or equal to i32::max_value() / 2.
pub fn set_y(&mut self, y: i32) [src]
Sets the vertical position of this rectangle to the given value, clamped to be less than or equal to i32::max_value() / 2.
pub fn set_width(&mut self, width: u32) [src]
Sets the width of this rectangle to the given value, clamped to be less than or equal to i32::max_value() / 2.
Rect s must always be non-empty, so a width argument of 0 will be replaced with 1.
pub fn set_height(&mut self, height: u32) [src]
Sets the height of this rectangle to the given value, clamped to be less than or equal to i32::max_value() / 2.
Rect s must always be non-empty, so a height argument of 0 will be replaced with 1.
pub fn left(&self) -> i32 [src]
Returns the x-position of the left side of this rectangle.
pub fn right(&self) -> i32 [src]
Returns the x-position of the right side of this rectangle.
pub fn top(&self) -> i32 [src]
Returns the y-position of the top side of this rectangle.
pub fn bottom(&self) -> i32 [src]
Returns the y-position of the bottom side of this rectangle.
pub fn center(&self) -> Point [src]
Returns the center position of this rectangle.
Note that if the width or height is not a multiple of two, the center will be rounded down.
Example
use sdl2::rect::Rect,Point>; let rect = Rect::new(1,0,2,3); assert_eq!(Point::new(2,1),rect.center());
pub fn top_left(&self) -> Point [src]
Returns the top-left corner of this rectangle.
Example
use sdl2::rect::Rect, Point>; let rect = Rect::new(1, 0, 2, 3); assert_eq!(Point::new(1, 0), rect.top_left());
pub fn top_right(&self) -> Point [src]
Returns the top-right corner of this rectangle.
Example
use sdl2::rect::Rect, Point>; let rect = Rect::new(1, 0, 2, 3); assert_eq!(Point::new(3, 0), rect.top_right());
pub fn bottom_left(&self) -> Point [src]
Returns the bottom-left corner of this rectangle.
Example
use sdl2::rect::Rect, Point>; let rect = Rect::new(1, 0, 2, 3); assert_eq!(Point::new(1, 3), rect.bottom_left());
pub fn bottom_right(&self) -> Point [src]
Returns the bottom-right corner of this rectangle.
Example
use sdl2::rect::Rect, Point>; let rect = Rect::new(1, 0, 2, 3); assert_eq!(Point::new(3, 3), rect.bottom_right());
pub fn set_right(&mut self, right: i32) [src]
Sets the position of the right side of this rectangle to the given value, clamped to be less than or equal to i32::max_value() / 2.
pub fn set_bottom(&mut self, bottom: i32) [src]
Sets the position of the bottom side of this rectangle to the given value, clamped to be less than or equal to i32::max_value() / 2.
pub fn center_on(&mut self, point: P) where
P: Into<(i32, i32)>, [src]
Centers the rectangle on the given point.
pub fn offset(&mut self, x: i32, y: i32) [src]
Move this rect and clamp the positions to prevent over/underflow. This also clamps the size to prevent overflow.
pub fn reposition(&mut self, point: P) where
P: Into<(i32, i32)>, [src]
Moves this rect to the given position after clamping the values.
pub fn resize(&mut self, width: u32, height: u32) [src]
Resizes this rect to the given size after clamping the values.
pub fn contains_point(&self, point: P) -> bool where
P: Into<(i32, i32)>, [src]
Checks whether this rectangle contains a given point.
Points along the right and bottom edges are not considered to be inside the rectangle; this way, a 1-by-1 rectangle contains only a single point. Another way to look at it is that this method returns true if and only if the given point would be painted by a call to Renderer::fill_rect .
Examples
use sdl2::rect::Rect, Point>; let rect = Rect::new(1, 2, 3, 4); assert!(rect.contains_point(Point::new(1, 2))); assert!(!rect.contains_point(Point::new(0, 1))); assert!(rect.contains_point(Point::new(3, 5))); assert!(!rect.contains_point(Point::new(4, 6)));
pub fn contains_rect(&self, other: Rect) -> bool [src]
Checks whether this rectangle completely contains another rectangle.
This method returns true if and only if every point contained by other is also contained by self ; in other words, if the intersection of self and other is equal to other .
Examples
use sdl2::rect::Rect; let rect = Rect::new(1, 2, 3, 4); assert!(rect.contains_rect(rect)); assert!(rect.contains_rect(Rect::new(3, 3, 1, 1))); assert!(!rect.contains_rect(Rect::new(2, 1, 1, 1))); assert!(!rect.contains_rect(Rect::new(3, 3, 2, 1)));
pub fn raw(&self) -> *const SDL_Rect [src]
Returns the underlying C Rect.
pub fn raw_mut(&mut self) -> *mut SDL_Rect [src]
pub fn raw_slice(slice: &[Rect]) -> *const SDL_Rect [src]
pub fn from_ll(raw: SDL_Rect) -> Rect [src]
pub fn from_enclose_points>>(
points: &[Point],
clipping_rect: R
) -> Option where
R: Into
Calculate a minimal rectangle enclosing a set of points. If a clipping rectangle is given, only points that are within it will be considered.
pub fn has_intersection(&self, other: Rect) -> bool [src]
Determines whether two rectangles intersect.
Rectangles that share an edge but don’t actually overlap are not considered to intersect.
Examples
use sdl2::rect::Rect; let rect = Rect::new(0, 0, 5, 5); assert!(rect.has_intersection(rect)); assert!(rect.has_intersection(Rect::new(2, 2, 5, 5))); assert!(!rect.has_intersection(Rect::new(5, 0, 5, 5)));
pub fn intersection(&self, other: Rect) -> Option [src]
Calculates the intersection of two rectangles.
Returns None if the two rectangles don’t intersect. Rectangles that share an edge but don’t actually overlap are not considered to intersect.
The bitwise AND operator & can also be used.
Examples
use sdl2::rect::Rect; let rect = Rect::new(0, 0, 5, 5); assert_eq!(rect.intersection(rect), Some(rect)); assert_eq!(rect.intersection(Rect::new(2, 2, 5, 5)), Some(Rect::new(2, 2, 3, 3))); assert_eq!(rect.intersection(Rect::new(5, 0, 5, 5)), None);
pub fn union(&self, other: Rect) -> Rect [src]
Calculates the union of two rectangles (i.e. the smallest rectangle that contains both).
The bitwise OR operator | can also be used.
Examples
use sdl2::rect::Rect; let rect = Rect::new(0, 0, 5, 5); assert_eq!(rect.union(rect), rect); assert_eq!(rect.union(Rect::new(2, 2, 5, 5)), Rect::new(0, 0, 7, 7)); assert_eq!(rect.union(Rect::new(5, 0, 5, 5)), Rect::new(0, 0, 10, 5));
pub fn intersect_line(&self, start: Point, end: Point) -> Option<(Point, Point)> [src]
Calculates the intersection of a rectangle and a line segment and returns the points of their intersection.
Trait Implementations
impl AsMut for Rect [src]
fn as_mut(&mut self) -> &mut SDL_Rect [src]
Performs the conversion.
impl AsRef for Rect [src]
fn as_ref(&self) -> &SDL_Rect [src]
Performs the conversion.
impl BitAnd for Rect [src]
type Output = Option
The resulting type after applying the & operator.
fn bitand(self, rhs: Rect) -> Option [src]
Performs the & operation.
impl BitOr for Rect [src]
type Output = Rect
The resulting type after applying the | operator.
fn bitor(self, rhs: Rect) -> Rect [src]
Performs the | operation.
impl Clone for Rect [src]
fn clone(&self) -> Rect [src]
Returns a copy of the value. Read more
fn clone_from(&mut self, source: &Self) 1.0.0 [src]
Performs copy-assignment from source . Read more
impl Copy for Rect [src]
impl Debug for Rect [src]
fn fmt(&self, fmt: &mut Formatter) -> Result [src]
Formats the value using the given formatter. Read more
impl Deref for Rect [src]
type Target = SDL_Rect
The resulting type after dereferencing.
fn deref(&self) -> &SDL_Rect [src]
Example
use sdl2::rect::Rect; let rect = Rect::new(2, 3, 4, 5); assert_eq!(2, rect.x);
impl DerefMut for Rect [src]
fn deref_mut(&mut self) -> &mut SDL_Rect [src]
Example
use sdl2::rect::Rect; let mut rect = Rect::new(2, 3, 4, 5); rect.x = 60; assert_eq!(60, rect.x);
impl Eq for Rect [src]
impl From<(i32, i32, u32, u32)> for Rect [src]
fn from((x, y, width, height): (i32, i32, u32, u32)) -> Rect [src]
Performs the conversion.
impl From for Rect [src]
fn from(raw: SDL_Rect) -> Rect [src]
Performs the conversion.
impl Hash for Rect [src]
fn hash(&self, state: &mut H) [src]
Feeds this value into the given [ Hasher ]. Read more
fn hash_slice(data: &[Self], state: &mut H) where
H: Hasher, 1.3.0 [src]
Feeds a slice of this type into the given [ Hasher ]. Read more
impl Into<(i32, i32, u32, u32)> for Rect [src]
fn into(self) -> (i32, i32, u32, u32) [src]
Performs the conversion.
impl Into for Rect [src]
fn into(self) -> SDL_Rect [src]
Performs the conversion.
impl PartialEq for Rect [src]
fn eq(&self, other: &Rect) -> bool [src]
This method tests for self and other values to be equal, and is used by == . Read more
#[must_use] fn ne(&self, other: &Rhs) -> bool 1.0.0 [src]
This method tests for != .