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

1

09.10.2010, 16:07

Iterator übernimmt Wert nicht richtig

Hallo,

Ich hab n problem mit nem iterator ...
Aus irgendeinen grund ist der wert, der mir per debugger ausgegeben wird, falsch.
Am Ende meiner Ladefunktion (der Klasse KI) definiere ich den Iterator über die Funktion .begin() einer verketteten liste.
Die Ladefunktion (in der Klasse Game) sollte funktionieren, da tritt bei mir kein fehler auf.

Hier mal n Codeausschnitt von meinem Problem:

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
Game.hpp:

#include <sstream>
#include <windows.h>
#include <list>
#pragma comment (lib, "winmm.lib") 

class CGame
{
public:
...
    void Init();
    void LoadMap(bool bReset);
    void Update_and_RenderKI();
...

private:
...
    void LoadKI();
    list<CKI> listKI; //List, in der die aktuellen KI Einstellungen gelden werden
...
}

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
Game.cpp:

void Init() //Sollte kein Prolem geben
{
...
        //KI Konfiguration löschen
    listKI.clear();
...
}

void CGame::LoadMap(bool bReset) //Sollte auch funktionieren
{
    static int Level = 100; //Überprüft, ob die Karte schon geladen wurde
    if(bReset)
        Level = 100;

    //Karte laden
    if(Level != m_Level)
    {
        ostringstream ss;
        ss << "Data/Map/LVL" << m_Level << ".map";
        m_Map.LoadMap(ss.str());
        Level = m_Level;

        //KI laden
        LoadKI();
    }
}

void CGame::LoadKI() //Sollte ebenfalls kein Problem darstellen
{
    //Alte Konfiguration löschen
    listKI.clear();

    //Überprüfe, ob es für das aktuelle Level eine KI Einstellung gibt
    stringstream ssPath;
    ssPath << "Data/KI/Conf/" << m_Level << "/*ki"; //Nach Files suchen

    WIN32_FIND_DATA fData; //Such - "Datenbank"
    HANDLE hwndSearch = FindFirstFile(ssPath.str().c_str(),&fData); //Suche das File
    if(hwndSearch != INVALID_HANDLE_VALUE)
    {
        ssPath.str("");
        ssPath << "Data/KI/Conf/" << m_Level << "/" << fData.cFileName; //Pfad der Datei

        CKI tempKI; //Temporäre KI - Konfiguration
        tempKI.Init();
        tempKI.Load(ssPath.str());
        listKI.push_back(tempKI);
        cout << "KI \"" << fData.cFileName << "\" (Data/KI/Conf/" << m_Level << "/" << fData.cFileName << ") geladen\n";

        //Auf weitere Files überprüfen
        while(FindNextFile(hwndSearch,&fData))
        {
            ssPath.str("");
            ssPath << "Data/KI/Conf/" << m_Level << "/" << fData.cFileName;
            tempKI.Load(ssPath.str());
            listKI.push_back(tempKI);
            cout << "KI \"" << fData.cFileName << "\" (Data/KI/Conf/" << m_Level << "/" << fData.cFileName << ") geladen\n";
        }
        tempKI.Quit();
    }
    else
        cout << "Keine KI fuer dieses Level gefunden" << endl;
}

//Funktion zum Updaten und rendern der KI
void CGame::Update_and_RenderKI() //In der Funktion habe ich den Debugger angeschaut
{
    list<CKI>::iterator iter; //Iterator für die KI

    //KI´s updaten und rendern
    for(iter = listKI.begin(); iter != listKI.end(); iter++)
    {
        iter -> Update(); // An dieser Stelle habe ich mir den Debugger angeschaut
        iter -> Render();
    }
}



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
KI.hpp:

#include <iostream>
#include <list>
#include <vector>
#include <fstream>
#include <string>
#include "Sprite.hpp"

using namespace std;

class CKI
{

public:
...
    void Load(const string &sPath);

    void Update();
    void Render();
...

private:
...
    list<int> listKIConfig; //Konfiguration der KI
    list<int>::iterator ConfigIterator; //Iterator für die Konfiguration
...

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
KI.cpp:

void CKI::Load(const string &sPath)
{
    //Datei öffnen
    ifstream input(sPath.c_str(),ios::binary);
    if(input == NULL)
        cout << "Fehler beim oeffnen der Datei " << sPath.c_str() << endl;
    else
    {
        listKIConfig.clear(); //Konfiguration löschen
        while(!input.eof()) //Datei lesen
        {
            //Datei in die Konfiguration schreiben
            string sZwischenspeicher = "";
            input >> sZwischenspeicher;
            listKIConfig.push_back(atoi(sZwischenspeicher.c_str()));
        }
        input.close();
        
        //Da die letzte Zeile NIE einen Inhalt besitzt, muss der letzte Eintrag gelöscht werden
        listKIConfig.pop_back();
    }

    //Positionen und Vektoren setzten
    ConfigIterator = listKIConfig.begin();
    if(*ConfigIterator % 16 == 0)
    {
        vCurPos.at(0) = 0;
        vCurPos.at(1) = (*ConfigIterator + 15) / 16;
    }
    else
    {
        vCurPos.at(0) = (*ConfigIterator) % 16;
        vCurPos.at(1) = (*ConfigIterator + 15) / 16 - 1;
    }

    vCurPos.at(0) *= 50; //Mit Frame - Weite der Map multiplizieren
    vCurPos.at(1) *= 50; //Mit Frame - Weite der Map multiplizieren    

    //Winkel für die Pos bestimmen
    ConfigIterator++;
    if(*ConfigIterator == *ConfigIterator + 1)
        vCurPos.at(2) = 0;
    else if(*ConfigIterator == *ConfigIterator - 1)
        vCurPos.at(2) = 180;
    else if(*ConfigIterator == *ConfigIterator - 16)
        vCurPos.at(2) = 90;
    else if(*ConfigIterator == *ConfigIterator + 16)
        vCurPos.at(2) = 270;

    //Nächste Pos bestimmen
    if(*ConfigIterator % 16 == 0)
    {
        vNextPos.at(0) = 0;
        vNextPos.at(1) = (*ConfigIterator + 15) / 16;
    }
    else
    {
        vNextPos.at(0) = (*ConfigIterator) % 16;
        vNextPos.at(1) = (*ConfigIterator + 15) / 16 - 1;
    }

    vNextPos.at(0) *= 50; //Mit Frame - Weite der Map multiplizieren
    vNextPos.at(1) *= 50; //Mit Frame - Weite der Map multiplizieren    

    ConfigIterator = listKIConfig.begin(); //Iterator auf die richtige Stelle resetten
}

void CKI::Update()
{
    float fSpeed = 2.0f; //Geschwindigkeit der Bots
    
    //Timer für die Bewegung
    static float fTimer = 0.0f; 
    float fTimerOut = 0.01f;

    //Bewege die Figur in die jeweilige Richtung
    if(vCurPos.at(2) == 0 && fTimer >= fTimerOut)
    {
            vCurPos.at(0) += static_cast<int> (fSpeed); 
            fTimer -= fTimerOut;
    }
    else if(vCurPos.at(2) == 90 && fTimer >= fTimerOut)
    {
        vCurPos.at(1) -= static_cast<int> (fSpeed); 
        fTimer -= fTimerOut;
    }
    else if(vCurPos.at(2) == 180 && fTimer >= fTimerOut)
    {
        vCurPos.at(0) -= static_cast<int> (fSpeed); 
        fTimer -= fTimerOut;
    }
    else if(vCurPos.at(2) == 270 && fTimer >= fTimerOut)
    {
        vCurPos.at(1) += static_cast<int> (fSpeed); 
        fTimer -= fTimerOut;
    }
    else 
        fTimer += g_pTimer -> GetElapsed();

    //Überprüfe, ob man sich schon in der nächsten Konfiguration befindet
    if((vCurPos.at(0) >= vNextPos.at(0) && vCurPos.at(2) == 0) || (vCurPos.at(0) <= vNextPos.at(0) && vCurPos.at(2) == 180) //if(rechts || links|| oben || unten)
        || (vCurPos.at(1) >= vNextPos.at(1) && vCurPos.at(2) == 270) || (vCurPos.at(1) <= vNextPos.at(1) && vCurPos.at(2) == 90))
    {
        //Nächste Konfiguration abfragen und für nächtse Position berechnen
        ConfigIterator++;
        
        //Berechnung
        if(*ConfigIterator % 16 == 0)
        {
            vNextPos.at(0) = 0;
            vNextPos.at(1) = (*ConfigIterator + 15) / 16;
        }
        else
        {
            vNextPos.at(0) = (*ConfigIterator) % 16;
            vNextPos.at(1) = (*ConfigIterator + 15) / 16 - 1;
        }

        vNextPos.at(0) *= 50; //Mit Frame - Weite der Map multiplizieren
        vNextPos.at(1) *= 50; //Mit Frame - Weite der Map multiplizieren    

        //Winkel berechnen
        if(*ConfigIterator == *(ConfigIterator++) + 1)
        {
            vCurPos.at(2) = 0;
            ConfigIterator--;
        }
        else if(*(ConfigIterator--) == *(ConfigIterator++) - 1)
        {
            vCurPos.at(2) = 180;
            ConfigIterator--;
        }
        else if(*(ConfigIterator--) == *ConfigIterator++ - 16)
        {
            vCurPos.at(2) = 90;
            ConfigIterator--;
        }
        else if(*(ConfigIterator--) == *ConfigIterator++ + 16)
        {
            vCurPos.at(2) = 270;
            ConfigIterator--;
        }
        else
            ConfigIterator--;
    }
    //cout << "Cur: " << vCurPos.at(0) << " " << vCurPos.at(1) << " " << vCurPos.at(2) << " " << *ConfigIterator << endl;

    //Neu starten
    ConfigIterator++;
    if(*ConfigIterator++ == 999) //Kontrollieren, ob neu begonnen werden muss
    {
        ConfigIterator = listKIConfig.begin(); //Neu konfigurieren

        vCurPos.at(2) = 0;

        ConfigIterator = listKIConfig.begin();
    }
    else
    {
        ConfigIterator--;
        ConfigIterator--;
    }
}

//Funktion zum rendern der KI
void CKI::Render()
{
    //KI - Rendern
    p_SpriteKI -> SetPos(static_cast<float> (vCurPos.at(0)), static_cast<float> (vCurPos.at(1)));
    p_SpriteKI -> Render();
}


Bei der Render - Funktion der KI hat der ConfigIterator den Wert -17891602, sollte aber den Wert 85 haben, da dass der erste wert in der liste listKIConfig ist.
Bei meinem Testfall befindet sich übrigens nur ein Element in der Liste der Game Klasse (listKI).

Ich hoffe, ihr könnt mir weiterhelfen,
Liebe Grüße,
Ombalat

PS.: Bin vom 10. - 13.10. (SO - MI) nich da und kann voraussichtlich erst am donnerstag (und heute ^^) spätestens wieder ins Forum schaun

2

09.10.2010, 16:30

Wieo gehst Du nicht einfach mit dem Debugger Zeile für Zeile durch, dann siehst Du ja, wo's in die falsche Richtung geht?

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

3

09.10.2010, 16:41

hab drei kleine tipps.
benutze vorzugsweise den präinkrementoperator also
++It und nicht It++.
das postinkrement sollte nur benutzt werden man sich voll im klaren ist was man tut.
der wahrscheinlich schneller und wird dir früher oder später einen schwer zu findenden fehler ersparen.

in dieser zeile:

C-/C++-Quelltext

1
if(*ConfigIterator++ == 999) //Kontrollieren, ob neu begonnen werden muss
ist auf dem ersten blick nicht verständlich was du machen willst(den iterator erhöhen oder den wert). da solltest du mit klammern arbeiten.

ausserdem ist es hilfreich die member der klasse mit "_" oder "m_" zu markieren(ich muss ständig nachschauen ob es member sind oder nicht)

wenn du das beachtet hast wird die fehlersuche schonmal etwas einfacher...

edit: statische variablen solltest du auch vermeiden.
das sind alles mögliche fehlerquellen.
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

4

09.10.2010, 17:49

Vielen Dank für deine Tipps, werde ich am donnerstag mal ändern und ich hoffe mal, dass ich den fehler dann finde.

Was mich vor allem irritiert: Wenn ich nur eine KI verwenden würde und keine liste, in der ich die KI speichere, funktioniert alles fehlerfrei.
(Also wenn ich nur eine Instanz von der KI verwende und die liste weglasse)
Daher ist es mir auch nicht klar, wieso der iterator in meiner KI Klasse nicht stimmt ...

Sorry wegen den m_ ... normalerweise verwende ich diese schreibweise, weiß auch nicht, warum ich das bei den zwei klassen vernachlässigt habe.

Liebe Grüße,
Ombalat
werde mich voraussichtlich Ende nächste Woche oder in zwei wochen dann wieder melden.

5

11.10.2010, 18:48

Also der Code is leider mit Fehlern bestückt.

C-/C++-Quelltext

1
if(*ConfigIterator == *(ConfigIterator++) + 1)
macht außerdem glaub ich nicht das was du wilst :D

(1) (Fehler-)Ausgabe und Datenverarbeitung drennen!
(2) Post/Pre-Inkrement angucken (s. o.)
(3) const-correctness
(4) da fehlt oft nen std:: ...
(5)

C-/C++-Quelltext

1
2
3
4
CKI tempKI; //Temporäre KI - Konfiguration
        tempKI.Init();
        tempKI.Load(ssPath.str());
        listKI.push_back(tempKI);
c-tor ist für init da! schöner sähe doch sowas aus:

C-/C++-Quelltext

1
listKI.push_back(CKI(ssPath.str()));

(6) Was sagt dir ss über die Variable aus? Na nicht viel was dir IntelliSens nicht sagen könnte. Benutz aussagekräftige bezeichner!
(7)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
 list<CKI>::iterator iter; //Iterator für die KI

    //KI´s updaten und rendern
    for(iter = listKI.begin(); iter != listKI.end(); iter++)
    {
        iter -> Update(); // An dieser Stelle habe ich mir den Debugger angeschaut
        iter -> Render();
    }

->

C-/C++-Quelltext

1
2
3
4
5
for (std::list<CKI>::iterator it(listKI.begin()), end(listKI.end()); it != end; ++it)
{
    it->Update();
    it->Render();
}


C-/C++-Quelltext

1
2
3
4
5
6
7
8
while(!input.eof()) //Datei lesen
        {
            //Datei in die Konfiguration schreiben
            string sZwischenspeicher = "";
            input >> sZwischenspeicher;
            listKIConfig.push_back(atoi(sZwischenspeicher.c_str()));
        }
        input.close();
Ah und was soll das darstellen? Wenn du wirklich Zahlen auslesen willst, machst es so:

C-/C++-Quelltext

1
std::copy(std::istream_iterator<unsigned int>(input), std::istream_iterator<unsigned int>(), std::back_inserte(listKIConfig));
na geht doch :)
(8) Hör auf so nen Quatsch mit den Iteratoren zu machen. Hol dir den Wert und von mir aus den vom nächsten, arbeite mit den Werten und weiß die am Ende wieder zu.
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

6

13.10.2010, 20:10

Hallo, da bin ich wieder ;)
Bin gerade nach Hause gekommen und habe deshalb noch keine Zeit gehabt, mein Programm zu ändern.

Danke schon mal, für deine Hilfestellung !

Wollte mal nachfragen, ob ich

Zitat

(4) da fehlt oft nen std:: ...

auch dann schreiben muss, wenn ich in meiner Klasse schon mit "using namespace std" arbeite (was ich jz noch nicht in allen klassen drinnen stehen habe), oder kann ich dass dann weg - lassen?

(6) Was sagt dir ss über die Variable aus? Na nicht viel was dir IntelliSens nicht sagen könnte. Benutz aussagekräftige bezeichner!

ss bedeutet für mich, dass es sich um einen string stream handelt, und nich um einen "normalen" string. Im normalfall hab ich hinter der bezeichnung auch immer eine nähere bezeichnung [wie (ss)Path], hab ich bei miener LoadMap Funktion wohl vergessen :S

Vielen Dank jedenfalls schon mal für die Hilfe, die ich bekommen habe!
Liebe Grüße,
Ombalat

7

14.10.2010, 00:04

(...) wenn ich in meiner Klasse schon mit "using namespace std" arbeite (...)
Wenn du den Namespace angibst, dann brauchst du kein std mehr, kannst es aber trotzdem benutzen. Übrigens kannst du den Namespace "std" auch global bekannt machen, statt in jeder Klasse extra, denn in der Regel greift man auf diesen Namespace relativ häufig zu und so spart man sich es, den bei jeder Klasse wieder bekannt zu machen.

Gruß
SaRu_

8

14.10.2010, 10:06

Hallo

Aufpassen sollte man allerdings in headerfiles.

chrische

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

9

14.10.2010, 10:52

Übrigens kannst du den Namespace "std" auch global bekannt machen, statt in jeder Klasse extra, denn in der Regel greift man auf diesen Namespace relativ häufig zu und so spart man sich es, den bei jeder Klasse wieder bekannt zu machen.

Aufpassen sollte man allerdings in headerfiles.

um es global zu machen muss man das in eine header schreiben oder nicht? man brauch std nur in ganz wenigen datein sehr oft. deshalb bringt es global nicht viel und man hat davon mehr probleme als vorteile.
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

10

14.10.2010, 13:00

So und jetzt ließt bitte mal jeder von euch nach, wofür es Namespaces gibt und was ihr damit anstellt, wenn ihr die using namespace im Header nutzt ;)

Wenn man das wirklich soo häufig braucht, das die 5 Zeichen einem zu viel Schreibaufwand darstellen kann man das using namespace am Anfang der Funktion o. der Quelldatei schreiben ;)
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

Werbeanzeige