Как известно, написание игры на SDL без каких-либо надстроек — очень муторное дело.


Есть несколько решений этого:


  1. Написание библиотеки, облегчающей жизнь.
  2. Бросаем SDL и переходим на какую-нибудь другую библиотеку.

Мы выберем более трудный путь — №1.


Наша библиотека будет включать:


  • Создание Core-структуры, которая включает в себя работу с окном, рендером, и.т.д.
  • Облегчённая работа со спрайтами — текстура, поворот, обрез текстуры.
  • Рисование текста.

Итак, начнём! Создаём header и source файлы:


  • ITopping.h
  • ITopping.cpp

(Нашу библиотеку мы назовём ITopping)


Инициализируем файл заголовка и создаём Core-структуру (окно+рендер):


ITopping.h


#ifndef ITOPPING_H_
#define ITOPPING_H_

#include <iostream>
#include <fstream>
#include <vector>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include <SDL2/SDL_ttf.h>

typedef struct{

    SDL_Window* window;
    SDL_Renderer* render;

} ITCore;

//...

#endif /* ITOPPING_H_ */

Соответственно, создаём функцию инициализации Core-структуры:


ITopping.h


ITCore initCore(char* title,int screenx,int screeny,bool isResizable,int w,int h,bool isAcceleratedRender);

ITopping.cpp


ITCore initCore(char* title,int screenx,int screeny,bool isResizable,int w,int h,bool isAcceleratedRender)
{
ITCore core;
core.window = SDL_CreateWindow(title,screenx,screeny,w,h,SDL_WINDOW_SHOWN | (isResizable ? SDL_WINDOW_RESIZABLE
: 0x0));
core.render = SDL_CreateRenderer(core.window,-1, isAcceleratedRender ? SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE);
return(core);
}

Теперь к спрайтам:


Спрайт — это объект, имеющий изображение, координаты и (иногда) угол.


ITopping.h


ITSprite initSprite(ITCore* core,char* texturepath,int imagew,int imageh,int initx,int inity,bool isCropped,SDL_Rect crop);

ITopping.cpp


ITSprite initSprite(ITCore* core,char* texturepath,int imagew,int imageh,int initx,int inity,bool isCropped,SDL_Rect crop)
{
    ITSprite spr;
    spr.texture = IMG_LoadTexture(core->render,texturepath);
    spr.rect.x = initx;
    spr.rect.y = inity;
    spr.rect.w = imagew;
    spr.rect.h = imageh;
    if(isCropped == true)
    {
        spr.crop = crop;
    }
    return spr;
}

Поворот спрайта:


Наш спрайт будет иметь значение угла, соответственно поворот — это сложение введёных градусов с углом спрайта по модулю 360:


ITopping.h


void turnSprite(ITSprite* spr,double angle);

ITopping.cpp


void turnSprite(ITSprite* spr,double angle)
{
    spr->angle = ((spr->angle + angle) % 360);
}

Наконец, отрисовка спрайта:


ITopping.h


void drawSprite(ITCore* core,ITSprite* spr,bool isCropped);

ITopping.cpp


void drawSprite(ITCore* core,ITSprite* spr,bool isCropped)
{
    if(isCropped) SDL_RenderCopyEx(core->render,spr->texture,&(spr->crop),&(spr->rect),spr->angle,NULL,SDL_FLIP_NONE);
    else SDL_RenderCopyEx(core->render,spr->texture,NULL,&(spr->rect),spr->angle,NULL,SDL_FLIP_NONE);
}

Рендер текста:


Рендерим текст в поверхность, конвертируем поверхность в текстуру, выводим на экран с поворотом/без поворота.


ITopping.h


void drawText(ITCore* core,const char* text,const char* fontpath,int basesize,SDL_Color color,int x,int y,bool isRotated,int angle);

ITopping.cpp


void drawText(ITCore* core,const char* text,const char* fontpath,int basesize,SDL_Color color,int x,int y,bool isRotated,int angle)
{
    TTF_Font* font = TTF_OpenFont(fontpath,basesize);
    SDL_Surface* textSurface = TTF_RenderText_Solid(font, text, color);
    SDL_Texture* textTexture = SDL_CreateTextureFromSurface(core->render,textSurface);
    SDL_Rect textrect;
    textrect.w = textSurface->w;
    textrect.h = textSurface->h;
    textrect.x = x;
    textrect.y = y;
    if(isRotated == false) SDL_RenderCopy(core->render,textTexture,NULL,&textrect);
    else SDL_RenderCopyEx(core->render,textTexture,NULL,&textrect,angle,NULL,SDL_FLIP_NONE);
}

Код на GitHub'е: https://github.com/Fedorsturov/itopping


Спасибо за внимание!

Комментарии (9)


  1. lnroma
    11.04.2016 17:50
    -1

    sorry конечно но вы специально игнорируете `else` и скобки в if

    if(isResizable == true) core.window = SDL_CreateWindow(title,screenx,screeny,w,h,SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    if(isResizable == false) core.window = SDL_CreateWindow(title,screenx,screeny,w,h,SDL_WINDOW_SHOWN);


    это же тяжело читаеться.
    почему бы просто не использовать `else` и человеческие скобки, или что C++ этого неумеет?

    if(isResizable == true) {
    core.window = SDL_CreateWindow(title,screenx,screeny,w,h,SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    } else {
    core.window = SDL_CreateWindow(title,screenx,screeny,w,h,SDL_WINDOW_SHOWN);
    }


    1. bfDeveloper
      11.04.2016 18:00
      +8

      C++ всё умеет. Конструкция if(isResizable == true) вызывает много вопросов зачем? Можно же просто if(isResizable). В статье описана очень простая школьная поделка, которую автор зачем-то опубликовал не хабре. То есть код откровенно слабый, но все когда-то что-то подобное писали.
      Чтобы не быть голословным и оставить хоть какую-то пользу от комментария предложу рефакторинг:

      if(isAcceleratedRender == true) core.render = SDL_CreateRenderer(core.window,-1,SDL_RENDERER_ACCELERATED);
      if(isAcceleratedRender == false) core.render = SDL_CreateRenderer(core.window,-1,SDL_RENDERER_SOFTWARE);
      

      Заменить на
      core.render = SDL_CreateRenderer(core.window,-1, isAcceleratedRender ? SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE);
      


      1. fedor2612
        12.04.2016 11:06
        +1

        Исправляю код…


  1. lnroma
    11.04.2016 18:21
    -1

    `core.render = SDL_CreateRenderer(core.window,-1, isAcceleratedRender? SDL_RENDERER_ACCELERATED: SDL_RENDERER_SOFTWARE);` написать можно но так как строка выйдет за пределы окна редактора, вы сами в ней запутаетесь. Как бы спорный вариант…


    1. ComradeAndrew
      11.04.2016 23:36

      У вас окно редактора такое узенькое? Всего-то на 15 символов строка длиннее. Но в принципе для читабельности можно сделать и так.


      core.render = SDL_CreateRenderer(core.window, -1, isAcceleratedRender ?
                                                        SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE);

      Вариант короче первоначального варианта на 6 символов и тоже занимает те же 2 строки.


      1. 6opoDuJIo
        12.04.2016 02:13

        На самом деле, он использует крупный шрифт.


      1. lnroma
        12.04.2016 10:01
        -1

        Ну у меня ide дерево проэкта, терминал, дебагер и т.д. Второй вариант ещё не читабельней. Есть специальные лекции по кодинг стайлу и стандарту кодирование, люди же их придумали не зря, вообще лучше развернуть все `if` и представить в конструкции, которые видно сразу, которые очевидны. Простой пример

        core.window = SDL_CreateWindow(title,screenx,screeny,w,h,isResizable? SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE: SDL_WINDOW_SHOWN);
        core.render = SDL_CreateRenderer(core.window,-1, isAcceleratedRender? SDL_RENDERER_ACCELERATED: SDL_RENDERER_SOFTWARE);


        Всего две строки а уже сложнее читать и очевидность условия пропадает. А представьте программу в сотни строк, десятков подключаемых файлов. И написаных в током стиле? вы сможете разобраться что там происходит и почему так, и за какое время сможете?


  1. 6opoDuJIo
    11.04.2016 19:18
    +2

    Сравнение boolean'ов, лишние условия:

    if(isResizable == true) core.window = SDL_CreateWindow(title,screenx,screeny,w,h,SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    if(isResizable == false) core.window = SDL_CreateWindow(title,screenx,screeny,w,h,SDL_WINDOW_SHOWN);
    


    Облегчите жизнь себе и другим: потренируйтесь на кошках, а потом пишите гайды.


  1. Lertmind
    11.04.2016 21:06

    Слабовато получилось, тот кто начал использовать SDL сможет сам написать такие обёртки. Замечу, что код в стиле C, так что тег C++ излишен.
    У SDL_RenderCopyEx параметр angle имеет тип double, так что в написанных структурах и функциях использование int только ограничивает точность поворота.