В этом уроке научимся показывать таймер, фпс, и ускорять/замедлять frame per second.

Код:

SDL_Texture* titleInscription(SDL_Renderer* gRenderer,TTF_Font* timerFont,SDL_Color timerColor,int*x,int* k,SDL_Rect yourNameRect,char*writeYourName)
{
  
  SDL_Surface* image = TTF_RenderText_Solid(timerFont,writeYourName,timerColor);
  *x = image->w,*k=image->h;
  yourNameRect.w = *x; yourNameRect.h = *k; 
  SDL_Texture* groundTexture = SDL_CreateTextureFromSurface(gRenderer,image);
  SDL_RenderCopy(gRenderer,groundTexture,NULL,&yourNameRect);
  SDL_RenderPresent(gRenderer);
  return groundTexture;
}

Эта функция выводит надписи на экран(gRenderer) , и корректирует размер отображения(x,y)
исходя из длины символов.

void pasteNumberToChar(char **bufferString,int minNumber) {
   char* bufferString2 =  (char*)malloc(25);
   int BufferCounter = 0;
  while(minNumber >= 1) {
      *bufferString2 = (minNumber%10) + '0';
      bufferString2++;
      minNumber = minNumber/10;
  }
  bufferString2--;
  while(*bufferString2){
      **bufferString = *bufferString2;
      bufferString2--; (*bufferString)++;
      BufferCounter++;
  }
  **bufferString =' ';
  (*bufferString)++;
  (**bufferString) = '\0';
  (*bufferString) -=BufferCounter +1;
  return; 
}

Функция, которая меняет число на ее отображение строку.

void changeFPS (int *quit, SDL_Event e,int *delayTime,int *timerZero, int *framesInt){

  while(SDL_PollEvent(&e) != 0)
  {
    if (e.type == SDL_QUIT) {
      *quit =1 ;

    }else if (e.type == SDL_KEYDOWN || e.type == SDL_MOUSEBUTTONDOWN) {
      if ( e.key.keysym.sym ==SDLK_UP ) { 
       if ( *delayTime >5 ){

         (*delayTime) += 5;  
         *framesInt = 1;  
         *timerZero = time(NULL);  
       }   

      }else if ( e.key.keysym.sym ==SDLK_DOWN) {

       if( *delayTime <300 ){
         *framesInt = 1;  
         (*delayTime) -= 5;
         *timerZero = time(NULL);
        }   
       
      }else {
          *quit =1; 
       }   
    }   
 }
}

Функция, которая изменяет фпс в зависимости от нажатия вверх или вниз, и выходит из программы при нажатии любой другой кнопки.

int main ( void )
{

int mWidth = 800, mHeight = 600;
  SDL_Init(SDL_INIT_VIDEO);
  TTF_Init();
  SDL_Window* window = SDL_CreateWindow("Read_Write", SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,mWidth,mHeight,SDL_WINDOW_SHOWN);
  SDL_Renderer* gRenderer = SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);
  SDL_Color timerColor = {0,255,0};

 TTF_Font* timerFont = TTF_OpenFont("v_DigitalStrip_v1.5.ttf",25);

 SDL_Rect timerPosition = {700,50,100,20};

 SDL_Rect titleRect = { 580,50, 200,50};
 SDL_Surface* timerSurface = NULL;
 SDL_Texture* timerTexture = NULL;

 SDL_Rect frameRect = { 580,100, 200,50};
 SDL_Rect framePosition = { 700,100, 200,50};
 SDL_Surface* frameSurface = NULL;
 SDL_Texture* frameTexture = NULL;

  SDL_Event e;
  int quit = 0, checkerTime= 0;
  int timerZero,timerNow;
  timerZero = time(NULL);
 SDL_Delay(1000); 
  char* timerText = "TIME:";
  char* frameText = "FRAME:";
  char* timerChar =(char*)malloc(12);
  char* frameChar =(char*)malloc(12);
  SDL_Texture* titleTime = titleInscription( gRenderer, timerFont, timerColor, &titleRect.w, &titleRect.h,titleRect,timerText);
  SDL_Texture* titleFrame = titleInscription( gRenderer, timerFont, timerColor, &frameRect.w, &frameRect.h,frameRect,frameText);

C++

int main ( void )
{
​
int mWidth = 800, mHeight = 600;
  SDL_Init(SDL_INIT_VIDEO);
  TTF_Init();
  SDL_Window* window = SDL_CreateWindow("Read_Write", SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,mWidth,mHeight,SDL_WINDOW_SHOWN);
  SDL_Renderer* gRenderer = SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);
  SDL_Color timerColor = {0,255,0};
​
 TTF_Font* timerFont = TTF_OpenFont("v_DigitalStrip_v1.5.ttf",25);
​
 SDL_Rect timerPosition = {700,50,100,20};
​
 SDL_Rect titleRect = { 580,50, 200,50};
 SDL_Surface* timerSurface = NULL;
 SDL_Texture* timerTexture = NULL;
​
 SDL_Rect frameRect = { 580,100, 200,50};
 SDL_Rect framePosition = { 700,100, 200,50};
 SDL_Surface* frameSurface = NULL;
 SDL_Texture* frameTexture = NULL;
​
  SDL_Event e;
  int quit = 0, checkerTime= 0;
  int timerZero,timerNow;
  
  char* timerText = "TIME:";
  char* frameText = "FRAME:";
  char* timerChar =(char*)malloc(12);
  char* frameChar =(char*)malloc(12);
  SDL_Texture* titleTime = titleInscription( gRenderer, timerFont, timerColor, &titleRect.w, &titleRect.h,titleRect,timerText);
  SDL_Texture* titleint framesInt = 0, delayTime = 15;Frame = titleInscription( gRenderer, timerFont, timerColor, &frameRect.w, &frameRect.h,frameRect,frameText);
 int framesInt = 0, delayTime = 15;
 
  timerZero = time(NULL);

Стандартно задаем все переменные, так же задаем начальное время(timerZero).

while (!quit)
  {
    changeFPS(&quit, e, &delayTime,&timerZero,&framesInt);

    SDL_SetRenderDrawColor(gRenderer,0,0,0,0);
    SDL_RenderClear(gRenderer);
  
		framesInt++;
  
    timerNow = time(NULL)+1;
    pasteNumberToChar( &timerChar, (timerNow - timerZero));
    pasteNumberToChar( &frameChar, (framesInt)/(timerNow - timerZero));

Создаем бесконечный цикл, пока не нажата кнопка, проверяем функцией, проверяем нажата ли кнопка и если нажата меняем фпс(changeFPS),

устанавливаем черный цвет(SDL_SetRenderDrawColor),очищаем экран(SDL_RenderClear),
считываем фрейм(framesInt)

Считываем время сейчас(timerNow) и изменяем числа на текст для отображения.

    timerSurface = TTF_RenderText_Solid(timerFont,timerChar,timerColor);
    timerTexture = SDL_CreateTextureFromSurface(gRenderer,timerSurface);
    frameSurface = TTF_RenderText_Solid(timerFont,frameChar,timerColor);
    frameTexture = SDL_CreateTextureFromSurface(gRenderer,frameSurface);

    timerPosition.w = timerSurface->w;
    timerPosition.h = timerSurface->h;
    framePosition.w = frameSurface->w;
    framePosition.h = frameSurface->h;


    SDL_RenderCopy(gRenderer,timerTexture,NULL,&timerPosition);
    SDL_RenderCopy(gRenderer,frameTexture,NULL,&framePosition);
    SDL_RenderCopy(gRenderer,titleTime,NULL,&titleRect);
    SDL_RenderCopy(gRenderer,titleFrame,NULL,&frameRect);
    SDL_RenderPresent(gRenderer);
    
    SDL_FreeSurface(timerSurface);
    SDL_FreeSurface(frameSurface);
    SDL_DestroyTexture(timerTexture);
    SDL_DestroyTexture(frameTexture);
		
    SDL_Delay( delayTime ); 

Создаем текстуры времени и фпс, и выводим их на экран

после этого очищаем текстуры, для экономии памяти
и ставим задержку на delayTime, который регулируется в функции changeFPS

   SDL_DestroyWindow( window );
   TTF_Quit();
   SDL_Quit();

}

Ссылка на файлы

<< предыдущий урок

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


  1. CJay
    22.10.2021 21:46

    В первом методе

    SDL_Texture* titleInscription(..)
    {
    ...
    }

    не нужно ли удалять SDL_Surface* image после конвертации его в текстуру?


    1. scorpka Автор
      23.10.2021 12:21

      (gdb) printf "%i\n",titleTime
      1437434912
      (gdb) printf "%i\n",groundTexture
      No symbol "groundTexture" in current context.
      (gdb) printf "%i\n",image
      No symbol "image" in current context.
      _________________________________
      как видно из отладчика, локальные переменные внешней функции после использования автоматически уничтожаются


      1. CJay
        28.10.2021 21:41

        Вот только SDL_Surface* image -- это не локальная переменная, а указатель на область памяти, выделенный внутри функции TTF_RenderText_Solid.
        И чтобы не было утечки памяти, необходимо не забывать вызывать метод SDL_FreeSurface(image).


        1. scorpka Автор
          02.11.2021 06:26

          Спасибо за ваше недоверие, image это локальная переменная, содержащая в себе указатель на структуру, а не на динамическую память. Ну и так как никакой динамической памяти не выделяется, то можно и не освобождать surface


          1. CJay
            02.11.2021 22:28

            Так, попробую с другого бока.
            Посмотрите, вот у вас имеется строка:
            SDL_Surface* image = TTF_RenderText_Solid(timerFont,writeYourName,timerColor);
            В ней вы создали локальную переменную, содержащую в себе указатель на структуру SDL_Surface. Структура эта создана в куче (то есть, в области динамической памяти), а не на стеке. Если бы она была создана на стеке, то строка имела бы вид:
            SDL_Surface image = TTF_RenderText_Solid(timerFont,writeYourName,timerColor);

            Когда функция titleInscription(...) завершит своё выполнение, то локальная переменная, содержащая в себе указатель на струкруту SDL_Surface уничтожится, а вот память с этой структурой, выделенная в динамической области памяти, никуда не денется.
            А функция titleInscription(...) вызывается на каждом кадре, выделяя память под стркутуру SDL_Surface размером 200x50 пикселей, что должно занимать память не менее чем 200*50*3=30000 байт за кадр. А если кадров 60 в секунду, то в секунду утечка памяти на 30 килобайт. За час работы программы утекает не менее 105 мегабайт памяти только на выводе FPS на экран.
            Чтобы этого безобразия не было, нужно в функции titleInscription(...) после строки

            SDL_Texture* groundTexture = SDL_CreateTextureFromSurface(gRenderer,image);

            вызвать метод SDL_FreeSurface(image);


            1. scorpka Автор
              08.11.2021 11:40

              titleInscription(...) вызывается только два раза за программу, и больше не вызывается.