Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

31

30.08.2010, 14:24

Guten Tag,

Eins schon mal vorweg, is mir nämlich gerade aufgefallen. Bei deinem Operator kannst du dir das if-Statement sparen. SDL_FreeSurface() einen NULL-Zeiger zu übergeben ist sicher - in diesem Fall passiert einfach gar nichts.

Vielen Dank für die Info :)

Dein Quellcode sollte eigentlich funktionieren, wo taucht denn die Zugriffsverletzung auf? Immer noch beim SetColourKey()?
Prüf doch mal nach LoadStory() ob SpriteIntro_Text NULL ist.

Die Zugriffsverletzung taucht immer noch nach der Lade funktion auf (also in diesem fall bei SetColourKey()).
Das Sprite ist nicht NULL.
Mir ist jedoch aufgefallen, dass im Sprite die Breite, Hohe und das Format scheinbar nicht richtig übernommen wurden (Im m_pImage des CSprites).
Im Dateianhang habe ich nen Ausschnitt vom Debugger gescreenshottet, dass du siehst, was ich meine.

Was machst du denn mit dem Sprite im weiteren Verlauf deines Prorgamms? Du renderst das Sprite doch mit Sicherheit oder? Und das machst du dann auch in einer anderen Funktion von CGame oder? Dann "musst" du das Objekt eh auf dem Heap anlegen.

Ich habe in meiner Game Klasse keine eigene Render funktion.
In der Game klasse Rendere ich die Objekte in der Hauptschleife des Spiels (CGame::Run()), wo die einzelnen render funktionen der Objekte dann aufgerufen werden.
Die Storry wird jedoch nciht jedes Frage gerendert, sondern nur ca jedes 5. Level die entsprechende geschichte.
Daher lade ich in der Render Funtkion der Geschichte (CGame::RenderStory(), in der ich mich beim funktionsaufruf befinde) den richtigen Teil der Geschichte (die jeweils in einer eigenen datei abgespeichert sind), kurz bevor ich die Geschichte rendere.
Daher brauche ich die Variable nicht umbedingt auf dem heap erzeugen.

Ich überlege mir zZ aber, ob es nicht über den Heap einfacher währe.
Wenn ich in der Lade funktion das Sprite erst erzeuge, würde ich auch keinen überladenen operator mehr brauchen.
Ich hätte mir das so vorgestellt:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Funktionsaufruf:

    CSprite *pSpriteIntro_Text = NULL; 
    pSpriteIntro_Text = LoadStory(ID_Story); //Storry Datei laden
    //pSpriteIntro_Text -> Load("Data/Emphasis/Storry/Storry_2.Emphasis");
    pSpriteIntro_Text -> SetColourKey(205,179,139);
   //dann rendern
   //und nach dem rendern per delete freigeben

Funktion:

CSprite* CGame::LoadStory(const int ID_Story)
{
    CSprite *Sprite = new CSprite; //CSprite für den Text
    if(ID_Story == 1)
        Sprite.Load("Data/Emphasis/Storry/Storry_1.Emphasis");
    else if(ID_Story == 2)
        Sprite.Load("Data/Emphasis/Storry/Storry_2.Emphasis");
    
    return Sprite;
}


Weil der Heap wird doch nicht automatisch frei gegeben?
So könnte das doch sogar gehen, ohne einen operator überladen zu müssen?

Liebe Grüße,
Ombalat
»Ombalat« hat folgende Bilder angehängt:
  • Debuggerscreen1.JPG
  • Debuggerscreen2.JPG

jokester

Treue Seele

Beiträge: 125

Wohnort: Mainz

  • Private Nachricht senden

32

30.08.2010, 16:40

Wenn du den operator= und Kopierkonstruktor nicht implementierst, solltest du in diesem Fall beide wenigstens verbieten. Also im private-Teil deiner Klasse deklarieren.
"There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory which states that this has already happened" -- Douglas Adams.

33

30.08.2010, 18:08

(...) im Sprite die Breite, Hohe und das Format scheinbar nicht richtig übernommen wurden (...)
Hallo Ombalat,

das hast du richtig erkannt. Vermutlich löst auch das dein Problem aus, denn SDL_SetColorKey() benötigt nunmal ein gültiges Format.
Ich denke mal ich habe den Fehler gefunden, auch wenn es mir auf den ersten Blick gar nicht aufgefallen ist, aber wenn man sich die SDL Funktionen mal etwas genauer anschaut...

In deinem Operator rufst du SDL_FreeSurface() auf. Lass es weg! Die Surface deines Sprites soll ja weiter bestehen, lediglich soll eine neue Grafik geladen werden und das übernimmt ja deine Funktion Load().

Hab es selbst mal in einer SDL Applikation getestet. SDL_FreeSurface() und ein darauf folgendes, erneutes Laden einer Grafik führt auch bei mir zu einer Zugriffsverletzung.

Gruß
SaRu_

34

30.08.2010, 18:33

Hallo SaRu,

Bei mir löst das weglassen der SDL_FreeSurface() aber auch nicht das problem (hatte ich früher auch schon probiert.).
Ich habe auch ohne dem noch die selbe zugriffsverletzung und die selben probleme wie vorher :(

Was sagst du zu meiner 2. idee (siehe meinen vorherigen post)?

LG
Ombalat

35

30.08.2010, 18:40

Hallo Ombalat,

na du (oder viel mehr dein Problem) bist ja hartnäckig. ;)
Daher brauche ich die Variable nicht umbedingt auf dem heap erzeugen.

Ich überlege mir zZ aber, ob es nicht über den Heap einfacher währe.

Also davon halte ich nicht so sehr viel. Wenn du es vermeiden kannst eine Variable auf dem Heap zu erzeugen, dann solltest du es, sofern es keinen großen Mehraufwand bedeutet, auch tun!

Zu deinem Problem... öhm... also... ich geh jetzt erstma nen Kaffee trinken, dann schau ichs mir noch mal an... ^^

EDIT:
In deinem ersten Post sieht deine Sprite Klasse unter anderem so aus:

C-/C++-Quelltext

1
2
3
private:
    string sPath; //Der Pfad, wo die Bilddatei liegt
    string GetPath() { return (sPath); }


Warum? Die Funktion GetPath() lieft den Pfad zur Bilddatei, ok. Aber was macht sie im private Bereich der Klasse? Innerhalb der Klasse und ihrer Funktionen kannst du ja auf private Variablen sowieso zugreifen und von außen solltest du dann GetPath() benutzen, dafür muss die Funktion allerdings public sein. Schau mal bitte (mit einer zwischengeschobenen Ausgabe oder so) nach, was der Funktionsaufruf in deinem Operator zurückliefert.

Gruß
SaRu_

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »SaRu« (30.08.2010, 19:06)


36

30.08.2010, 19:19

Hallo SaRu,

Vielen Dank, dass du dir so viel Zeit für mein Problem nimmst !
Find ich echt super :D

Warum? Die Funktion GetPath() lieft den Pfad zur Bilddatei, ok. Aber was macht sie im private Bereich der Klasse? Innerhalb der Klasse und ihrer Funktionen kannst du ja auf private Variablen sowieso zugreifen und von außen solltest du dann GetPath() benutzen, dafür muss die Funktion allerdings public sein. Schau mal bitte (mit einer zwischengeschobenen Ausgabe oder so) nach, was der Funktionsaufruf in deinem Operator zurückliefert.

Stimmt, da hast du recht... da hab ich mich vertan.
Habe die Funktion jetzt gelöscht und mache es direkt über die Variable.
Habe aber beide Varianten überprüft, und ich bekomme in beiden fällen den richtigen pfad.

Hier der aktualisierte Code:

C-/C++-Quelltext

1
2
3
4
Sprite.hpp:

private:
    string sPath; //Der Pfad, wo die Bilddatei liegt

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
Sprite.cpp:

CSprite& CSprite::operator =(CSprite& rhs)
{
    cout << rhs.sPath.c_str();
    Load(rhs.sPath);

    return *this;
}


Schönen Abend,
Ombalat

37

30.08.2010, 19:53

Soo, neuer Versuch, neues Glück. :D

Also ich hab es jetzt mal mit dem Standard-Kopier-Konstruktor der SDL_Surface Klasse probiert. So sparst du dir das speichern des Dateipfads, was in deinem Fall wohl auch nicht viel Sinn macht und es müsste eigentlich auch etwas flotter gehn, da keine Datei geöffnet und eingelesen wird.

Hier meine CSprite Klasse (aufs notwendigste reduziert):

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Sprite.hpp

class CSprite
{
public:
    SDL_Surface*            GetSurface      () {return m_pSurface;}
    CSprite&                operator =      (CSprite& rhs);
private:
    SDL_Surface*            m_pSurface;
};


// Sprite.cpp

CSprite& CSprite::operator = (CSprite& rhs)
{
m_pSurface = new SDL_Surface(*rhs.GetSurface());

return *this; 
}


// Sollte nun funktionieren...

CSprite EinSprite;

EinSprite = LoadStory(ID_Story);
EinSprite.SetColourKey(...);


Hoffe es funktioniert für dich.

Gruß
SaRu_

38

30.08.2010, 22:10

Hallo!

Du wirst es nicht für möglich hallten, aber die Zugriffsverletztung ist jetzt behoben ...
Jedoch erreicht mein Programm nun die Zeile mit dem SetColourKey erst gar nicht, da es einfach davor hängen bleibt.
Laut Debugger besteht das selbe problem wie vorher.

Ich habe den Standard - Konstruktor überladen, könnte es daran liegen?
Hier der Code (Mit standardkonstruktor)

C-/C++-Quelltext

1
2
3
4
5
Funktionsaufruf (unverändert):

        SpriteIntro_Text = LoadStory(ID_Story); //Storry Datei laden
    //pSpriteIntro_Text -> Load("Data/Emphasis/Storry/Storry_2.Emphasis");
    SpriteIntro_Text.SetColourKey(205,179,139);

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
Ladefunktion (unverändert):

CSprite CGame::LoadStory(const int ID_Storry)
{
    CSprite TempSprite; //Temporäres CSprite für den Text
    if(ID_Storry == 1)
        TempSprite.Load("Data/Emphasis/Story/Story_1.Emphasis");
    else if(ID_Storry == 2)
        TempSprite.Load("Data/Emphasis/Story/Story_2.Emphasis");
    
    return TempSprite;
}

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
Sprite.hpp:

public:
    CSprite();
    SDL_Surface *GetSurface() { return(m_pImage); }
    CSprite &operator = (CSprite& rhs); //Überladen des = Operators

private:
    SDL_Surface *m_pScreen; //Das Surface für den Screen, in den Gerendert wird
    SDL_Surface *m_pImage; // Das Surface für das Bild, in dem sich das zu rendernde Bild befindet

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Sprite.cpp:

//Standard - Konstruktor des Sprites
//Holt den Screen für das Ziel - Surface vom Framework
CSprite::CSprite()
{
    m_pImage = NULL;
    m_pScreen = g_pFramework -> GetSurface();
}

CSprite& CSprite::operator =(CSprite& rhs)
{
    m_pImage = new SDL_Surface(*rhs.GetSurface());
    return *this;
}

Die SetColourKey funktion hab ich jetzt mal weggelassen, die kennst du ja schon ;)
Sollte man beim operator nicht noch am anfang die m_pImage variable freigeben (über delete oder SDL_FreeSurface)?
Sonst hätte man doch wieder einen memmory - leak?

Schönen Abend noch,
Ombalat

EDIT:
Ich mach´s jetzt einfach mal über den heap - so funktioniert es wenigstens :)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Funktionsaufruf:

        CSprite *SpriteIntroText = NULL;
        SpriteIntro_Text = LoadStory(ID_Story); //Storry Datei laden
    //pSpriteIntro_Text -> Load("Data/Emphasis/Storry/Storry_2.Emphasis");
    SpriteIntro_Text -> SetColourKey(205,179,139); 
        //Hier noch rendern
        delete (SpriteIntro_Text);

Ladefunktion:

CSprite *CGame::LoadStory(const int ID_Storry)
{
    CSprite *Sprite = new CSprite; //CSprite für den Text
    if(ID_Storry == 1)
        Sprite -> Load("Data/Emphasis/Story/Story_1.Emphasis");
    else if(ID_Storry == 2)
        Sprite -> Load("Data/Emphasis/Story/Story_2.Emphasis");
    
    return Sprite;
}

Liebe Grüße
Ombalat

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Ombalat« (01.09.2010, 21:59)


39

02.09.2010, 16:07

Absolutes NO-go ...lad mal deinen kompletten Source für die Klasse hoch dann korrigier ich den dir mal ;)
Devil Entertainment :: Your education is our inspiration
Der Spieleprogrammierer :: Community Magazin
Merlin - A Legend awakes :: You are a dedicated C++ (DirectX) programmer and you have ability to work in a team? Contact us!
Siedler II.5 RttR :: The old settlers-style is comming back!

Also known as (D)Evil

40

03.09.2010, 00:30

Hallo Deviloper,

Ich weiß nicht, wie sinnvoll es ist, wenn ich dir die ganze Klasse hochlade, weil ich keine Funktionen der Klassen mehr verwende.
Ich poste nochmal den code, den ich verwende.

Hier noch mal zusammengefasst, worum es mir geht:
In meinem Spiel soll nach einer bestemmten anzahl an leveln die Geschichte gerendert werden.
Dazu will ich in meiner Game Klasse eine Funktion haben, in der ich die Geschichte je nach stelle laden kann (über die Story_ID, weilche den Abschnitt darstellt). Man könnte das auch direkt in der Funktion (RenderStory()) machen, doch zwecks übersichtlichkeit und leichterer änderbarkeit der pfadangaben (zum hinzufügen, umbenennen etc.) habe ich mich für eine eigene funktion entschieden ;)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Game.cpp:

void CGame::RenderStory(const int &ID_Story)
{
    //Story rendern
    bool bIntro = true; //Abbruchbedningung für das Intro
    float fYPos = 600.0f;
    SDL_Event Event;

    //Sprites für den Text initialisieren
    CSprite* SpriteIntro_Text = NULL; 

    SpriteIntro_Text = LoadStory(ID_Story); //Storry Datei laden
    //pSpriteIntro_Text -> Load("Data/Emphasis/Storry/Storry_2.Emphasis");
    SpriteIntro_Text -> SetColourKey(205,179,139); 

    do
    {
        g_pFramework -> Update();
        g_pFramework -> Clear();

        //Text des Intros rendern
        //RenderText("Escape - Taste drücken",40,85,85,85,280,250);
        SpriteIntro_Text -> SetPos(0.0f,fYPos);
        SpriteIntro_Text -> Render();
        
        g_pFramework -> Flip();

        fYPos -= 25 * g_pTimer -> GetElapsed();

        //Abbruchbedingung
        if(SDL_PollEvent(&Event))
        {
            switch(Event.type)
            {
                //Wurde eine Taste gedrückt?
                case(SDL_KEYDOWN):
                    {
                        switch(Event.key.keysym.sym)
                        {
                            case (SDLK_ESCAPE):
                                {
                                    bIntro = false;
                                }
                        }
                    }break;
            }
        }
    }while(bIntro && fYPos > -1247.0f);
    delete (SpriteIntro_Text);
}

//Funktion zum Laden der Storry
CSprite* CGame::LoadStory(const int ID_Storry)
{
    CSprite* Sprite = new CSprite; //CSprite für den Text
    if(ID_Storry == 1)
        Sprite -> Load("Data/Emphasis/Story/Story_1.Emphasis");
    else if(ID_Storry == 2)
        Sprite -> Load("Data/Emphasis/Story/Story_2.Emphasis");
    
    return Sprite;
}


Und das ist meine Sprite - Klasse:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Sprite.hpp:

#ifndef SPRITE_HPP
#define SPRITE_HPP

#include "Framework.hpp"

class CSprite
{
public:
    CSprite();
    ~CSprite();
    void Load(const string sFilename);
    void Load(const string sFilename, int NumFrames, int FrameWidth, int FrameHeight);
    void Render();
    void Render(float fFrameNumber);
    void SetColourKey(int R, int G, int B);
    void SetAlphaKey(Uint8 AlphaValue);
    void SetPos(float fXPos, float fYPos);
    SDL_Rect GetRect() { return (m_Rect); };

private:
    SDL_Surface *m_pScreen; //Das Surface für den Screen, in den Gerendert wird
    SDL_Surface *m_pImage; // Das Surface für das Bild, in dem sich das zu rendernde Bild befindet
    SDL_Rect m_Rect; //Das Rect (Rechteck) für das Bild
    SDL_Rect m_FrameRect; //Das Rect für die Animationsphase
    int m_NumFrames; //Anzahl der Animationsphasen
    int m_FrameWidth; //Breite der Animationsphase 
    int m_FrameHeight; //Höhe der Animationsphase
    int m_NumFramesX; //Anzahl der Animationsphasen in X - Richtung
};


#endif

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
Site - Klasse:

#include "Sprite.hpp"

//Standard - Konstruktor des Sprites
//Holt den Screen für das Ziel - Surface vom Framework
CSprite::CSprite()
{
    m_pImage = NULL;
    m_pScreen = g_pFramework -> GetSurface();
}

//Destruktor
//Gibt das Surface vom Image frei
CSprite::~CSprite()
{
    if(m_pImage != NULL)
    {
        SDL_FreeSurface(m_pImage);
        m_pImage = NULL;
    }
}

//Load - Funktion.
//Ermöglicht das Laden eines nicht animierten Sprites
void CSprite::Load(const string sFilename)
{
    m_pImage = SDL_LoadBMP(sFilename.c_str()); //BMP laden

    //Fehlerabfrage
    if (m_pImage == NULL)
    {
        cout << "Fehler beim Laden von: " << sFilename.c_str() << endl;
        cout << "Fehlermeldung: " << SDL_GetError() << endl;

        //Framework herunterfahren
        g_pFramework -> Quit();
        //Gesammtes Programm herunterfahren
        exit(1);
    }

    //Rect initialisieren
    m_Rect.x = 0; //X - Pos für die linke obere Ecke im Rect
    m_Rect.y = 0; //Y - Pos für die linke obere Ecke im Rect
    m_Rect.w = m_pImage -> w; //Breite des Rect
    m_Rect.h = m_pImage -> h; //Höhe des Rect
}

//Load - Funktion
//Ermöglicht das laden von animierten Sprites
void CSprite::Load(const string sFilename, int NumFrames, int FrameWidth, int FrameHeight)
{
    //Laden der Bitemap
    Load(sFilename.c_str());

    //Rect für die Animationsphase initialisieren
    m_NumFrames = NumFrames;
    m_FrameWidth = FrameWidth;
    m_FrameHeight = FrameHeight;
    m_FrameRect.w = FrameWidth;
    m_FrameRect.h = FrameHeight;
    m_NumFramesX = (m_pImage -> w) / FrameWidth;
}

//Funktion zum Setzen der Poition des Sprites
void CSprite::SetPos(float fXPos, float fYPos)
{
    m_Rect.x = static_cast<int> (fXPos);
    m_Rect.y = static_cast<int> (fYPos);
}

//Funktion zum definieren des Colour - Keys
void CSprite::SetColourKey(int R, int G, int B)
{
    if(SDL_SetColorKey(m_pImage,SDL_SRCCOLORKEY,SDL_MapRGB(m_pImage->format,R,G,B)) < 0)
        cout << "Fehler beim setzten des Colour - Keys!\nFehler: " << SDL_GetError() << endl;
}

//Funktion zum definieren der Transparenz des Sprites
void CSprite::SetAlphaKey(Uint8 AlphaValue)
{
    SDL_SetAlpha(m_pImage,SDL_SRCALPHA, static_cast<Uint8> (AlphaValue));
}

//Funktion zum Rendern eines Nicht animierten Sprites
void CSprite::Render()
{
    SDL_BlitSurface(m_pImage,NULL,m_pScreen,&m_Rect);
}

//Funktion zum Rendern eines animierten Sprites
void CSprite::Render(float fFrameNumber)
{
    // Spalte berechnen
    int Column = static_cast<int>(fFrameNumber)%m_NumFramesX;

    // Zeile berechnen
    int Row = static_cast<int>(fFrameNumber)/m_NumFramesX;

    // Rect berechnen
    m_FrameRect.x = Column * m_FrameWidth;
    m_FrameRect.y = Row * m_FrameHeight;

    //Rendern
    SDL_BlitSurface(m_pImage,&m_FrameRect,m_pScreen,&m_Rect);
}


Liebe Grüße,
Ombalat

Werbeanzeige