C ++ и SDL Почему обработка моих объектов напрямую, а не с помощью указателей иногда вызывает визуальные проблемы? - PullRequest
0 голосов
/ 30 апреля 2018

В настоящее время я разрабатываю приложение с графическим интерфейсом на C ++ и SDL2. Я столкнулся с очень странным поведением при попытке рендеринга некоторых моих текстур. Я использую пользовательскую текстурную обёртку, основанную на уроках Lazy Foo (http://lazyfoo.net/tutorials/SDL/43_render_to_texture/index.php), которая называется Texture, построенная поверх класса SDL текстуры SDL_Texture. Обычно, когда я создаю текстуру с помощью моего конструктора по умолчанию "Texture ()", а затем попробуйте отобразить его на экране, все работает как задумано. Однако в трех отдельных случаях у меня получилось очень странное поведение. В проблемных случаях он часто рисует текстуру из другого объекта и получается искаженным, иногда просто не рисует вообще, и часто беспорядочно мигает, как будто средство визуализации рисует его только каждые несколько кадров. При многократном запуске текстуры всегда испорчены, но какие симптомы появляются, например, мигает ли он или даже отображается при всех изменениях без перекомпиляции кода. Нет ошибок или предупреждений компилятора, и я не получаю вывод stderr во время работы. Что странно, если я устанавливаю объект как указатель и создаю его экземпляр с помощью "new Texture ()", все работает точно так, как должно .

К счастью, я нашел решение своей проблемы с помощью указателей, но я могу предположить, что причиной этого является только одна из трех: есть ошибка в библиотеке SDL2 (маловероятно), есть ошибка в моем классе текстур, или Мне не хватает какой-то очень фундаментальной концепции того, как объекты обрабатываются в C ++. В любом из этих случаев он вернется, чтобы укусить меня, если я попытаюсь игнорировать это.

Я занимаюсь разработкой под Linux (Ubuntu 16.04) и компилирую с G ++.

Я включу некоторый код для одного из проблемных случаев ниже. В этом случае я создавал класс «HistoryEntry», который мог бы взять некоторые атрибуты и нарисовать себя в произвольных X и Y. Все объекты текстуры атрибута HistoryEntry, кроме timeText, перепутались, и когда они действительно рисуют, они, кажется, рисуют искаженную версию. текстуры timeText. Похоже, они сохранили свои собственные значения ширины и высоты, но их текстура перезаписывалась, когда я загружал timeText. Однако, насколько я понимаю, конструктор всегда будет генерировать совершенно новый объект, и особенно странно, если копируются только некоторые его атрибуты. Все идет гладко, если я изменю все атрибутные объекты текстуры на указатели, которые указывают на указанные объекты текстуры.

Конструктор текстур и функция загрузки:

Texture::Texture()
{
    //Initialize
    mTexture = NULL;
    mWidth = 0;
    mHeight = 0;
}

bool Texture::loadText( std::string textureText, SDL_Color textColor, TTF_Font* gFont)
{
    //Get rid of preexisting texture
    free();

    //Render text surface
    SDL_Surface* textSurface = TTF_RenderText_Blended( gFont, textureText.c_str(), textColor ); 
    if( textSurface == NULL )
    {
        printf( "Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() );
    }
    else
    {
        //Create texture from surface pixels
        mTexture = SDL_CreateTextureFromSurface( getRenderer(), textSurface );
        if( mTexture == NULL )
        {
            printf( "Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError() );
        }
        else
        {
            //Get image dimensions
            mWidth = textSurface->w;
            mHeight = textSurface->h;
        }

        //Get rid of old surface
        SDL_FreeSurface( textSurface );
    }

    //Return success
    return mTexture != NULL;
}

Метод рендеринга текстур:

void Texture::render( int x, int y, int anchor, SDL_Rect* clip )
{
    SDL_Rect renderQuad;
    //Set rendering space and render to screen
    if(anchor == MIDDLE_CENTER){
        renderQuad = { x - mWidth/2, y - mHeight/2, mWidth, mHeight };
    }else if(anchor == MIDDLE_LEFT){
        renderQuad = { x, y - mHeight/2, mWidth, mHeight };
    }else if(anchor == MIDDLE_RIGHT){
        renderQuad = { x - mWidth, y - mHeight/2, mWidth, mHeight };
    }else{
        renderQuad = { x, y, mWidth, mHeight };
    }

    //Set clip rendering dimensions
    if( clip != NULL )
    {
        renderQuad.w = clip->w;
        renderQuad.h = clip->h;
    }

    //Render to screen
    SDL_RenderCopy( getRenderer(), mTexture, clip, &renderQuad );
}

HistoryEntry класс:

#include <SDL.h>
#include <SDL_image.h>
#include <string>
#include "auxx.h"
#include "texture.h"
#include "history_entry.h"

HistoryEntry::HistoryEntry(){

}

// Constructor for notifications
HistoryEntry::HistoryEntry(int t, std::string n, std::string v)
{
    //Atributes
    type = t;
    eventTime = GetTimer();
    name = Texture();
    name.loadText(n, defaultColor, defaultFont);
    value = Texture();
    value.loadText(v, defaultColor, defaultFont);
    //code = Texture();
    //code.loadText(c, defaultColor, defaultFontSmall);
    //icon = i;
    timeText = Texture();

}

// Constuctor for error and warnings
HistoryEntry::HistoryEntry(int t, std::string n, std::string c, Texture i)
{
    //Atributes
    type = t;
    eventTime = GetTimer();
    name = Texture();
    name.loadText(n, defaultColor, defaultFont);
    //value = Texture();
    //value.loadText(v, defaultColor, defaultFont);
    code = Texture();
    code.loadText(c, defaultColor, defaultFontSmall);
    icon = i;
    timeText = Texture();

}

void HistoryEntry::draw(SDL_Renderer* renderer, int x, int y){

    if(type == NOTIFICATION_ENTRY){
        name.render(x + 7, y + 15);
        value.render(x + 620, y + 15);

        // Set up time text
        // Seconds
        if(timeSince() < 60){
            timeBuff = std::to_string(timeSince()) + " sec ago";
            timeText.loadText(timeBuff , defaultColor, defaultFont);
        }
        // Minutes
        else{
            timeBuff = std::to_string(timeSince()/60) + " min ago";
            timeText.loadText(timeBuff , defaultColor, defaultFont);
        }

        timeText.render(x + 440, y + 15);
    }else{
        icon.render(x + 3, y + 13);
        name.render(x + 44, y + 15);
        code.render(x + 44, y + 45);
        //value.render(x + 620, y + 15);

        // Set up time text
        // Seconds
        if(timeSince() < 60){
            timeBuff = std::to_string(timeSince()) + " sec ago";
            timeText.loadText(timeBuff , defaultColor, defaultFont);
        }
        // Minutes
        else{
            timeBuff = std::to_string(timeSince()/60) + " min ago";
            timeText.loadText(timeBuff , defaultColor, defaultFont);
        }

        timeText.render(x + 440, y + 15);
    }
}

int HistoryEntry::getHeight(){
    if(type = NOTIFICATION_ENTRY){
        return 63;
    }else{
        return 89;
    }
}

int HistoryEntry::timeSince(){
    return GetTimePassedSeconds(eventTime);
}

Часть функции отображения, которая вызывает чертеж:

void DrawGraphics()
{
    SDL_Renderer *renderer = getRenderer();

    //Clear screen
    SDL_SetRenderDrawColor( renderer, 0xFF, 0xFF, 0xFF, 0xFF );
    SDL_RenderClear( renderer );

    ...

    // Draw history if it is active
    if(history_active){
        historyBackground.render(480, 79);
    }

    // Y position of first history entry
    int rowY = 134;
    // Draw history entries
    if(history_active){
        // Set color for history lines
        SDL_SetRenderDrawColor(renderer, 52, 64, 79, 0);

        for(int i = 0; i < history.size(); i++){
            history[i].draw(renderer, 495, rowY);
            // Increment the y value based on entry size
            if(history[i].type == NOTIFICATION_ENTRY){
                rowY += 63;
            }else{
                rowY += 89;
            }
            // Draw lines
            SDL_RenderDrawLine(renderer, 495, rowY - 1, 1287, rowY - 1);
        }
    }

    //Update screen
    SDL_RenderPresent( renderer );

}
...