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

22.09.2012, 03:13

Tile-Map Grundgerüst startet zeichnet aber nichts

[Erledigt]
Hat sich erledigt! Ich Nabnös hab vergessen bei .load() die Dateiendung mit anzugeben! Jetzt funktioniert alles!!!
Es werden die Texturen geladen, die Tile-Map wird definiert und alles wird gezeichnet!
Vielen Dank noch an Tobiking der mir den Tritt verpasst hat noch mal alles zu kontrollieren :D
[Erledigt/closed]


Hallo Zusammen
Bin ganz frisch (ca.1Monat) im Programmieren und hier im Forum (bis jetzt nur viel gelesen) unterwegs! Ich lerne C++ und SFML. C++ lerne ich mit Hr.Kalista`s Buch und SFML mit Hilfe des Internets :) .
Zu meinem Problem:
Ich möchte ein Tile-Map Grundgerüst aufbauen mit Hilfe von diesem Tutorial:
Link 1
Link 2
Das ganze soll erstmal eine .txt Datei laden, diverse Variablen (Maphöhe,Texture Pfade...etc) einlesen, ein 2D Vector füllen und dann einfach nur zeichnen!
Das Problem dabei ist das die Tut´s DirectX benutzen und ich aber das ganze in SFML umformatieren will! Bin jetzt so weit das es startet, ein Fenster öffnet sich, aber es bleibt komplett schwarz!
Ich denke mal es wird warscheinlich an Tilemap.load und .draw liegen. Ich steige nur nicht hinter wo das Problem liegt ;( Hoffe ihr könnt mir helfen den Fehler zu finden!
Zum Code:

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
//Tilemap und Tile Header//
#include <SFML\Graphics.hpp>
#include <vector>
#include <iostream>
class Tile
{
public:
// Attribute
    int GroundLayer;
    bool Collision;
       // Konstruktor
    Tile()
    {
        GroundLayer = 0;
        Collision = false;
    }
};

class Tilemap
{
public:

    // Konstruktor und Destruktor
    Tilemap();
    ~Tilemap();

    // map laden
    bool Load(const char* path);

    // map zeichnen
    void Draw(sf::RenderWindow &window);

    // die map größe in tiles-anzahl zurückgeben
    unsigned int GetWidth() const { return m_map_width; }
    unsigned int GetHeight() const { return m_map_height; }

    // Gibt Anzahl der Sprites zurück, die in der Map verwendet werden
    size_t GetSpriteCount() const { return vSprite.size(); }

    // Gibt ein Tile zurück
    Tile GetTile(unsigned int x, unsigned int y) const { return vMap.at(y).at(x); }

private:

    //  map größe in tiles anzahl
    unsigned int m_map_width;
    unsigned int m_map_height;

    // tile größe
    unsigned int m_tile_width;
    unsigned int m_tile_height;

    // 2-Dimensionales Feld mit Tiles
    std::vector< std::vector< Tile > > vMap;

    // Vector für Texturen
    std::vector<sf::Texture* > vTexture;

    // Feld mit Sprite-Zeigern
    std::vector< sf::Sprite* > vSprite;
};


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
//Tilemap Source//
#include "Tile.h"
#include <fstream>
#include <iostream>


// Konstruktor
Tilemap::Tilemap()
{
    m_map_width = 0;
    m_map_height = 0;

    m_tile_width = 0;
    m_tile_height = 0;

    vMap.clear();
}


// Destruktor - alle Sprites löschen, da diese dynamisch erstellt wurden
Tilemap::~Tilemap()
{
    std::vector< sf::Sprite* >::iterator it;
    for(it = vSprite.begin(); it != vSprite.end(); it++)
    {
        delete *it;
        it = vSprite.erase(it);
    }
}

// Map laden
bool Tilemap::Load(const char* path)
{   
    std::ifstream in;
    
    in.open(path);
    
    if(in.is_open()){
        char buffer[256];
        
         // Höhe einlesen
        in.getline(buffer, 8, '\n');
        m_map_height = atoi(buffer);

        // Breite einlesen
        in.getline(buffer, 8, '\n');
        m_map_width = atoi(buffer);

        // Tile-Höhe einlesen
        in.getline(buffer, 8, '\n');
        m_tile_height = atoi(buffer);

        // Tile-Breite einlesen
        in.getline(buffer, 8, '\n');
        m_tile_width = atoi(buffer);

        // Anzahl der Sprites einlesen
        in.getline(buffer, 8, '\n');
        unsigned int sprite_anzahl = atoi(buffer);

        // Alle Pfade zu den Sprites einlesen und die Sprites erstellen
        for(unsigned int i = 0; i < sprite_anzahl; i++)
        {
            // Pfad einlesen
            in.getline(buffer, 255, '\n');
            std::cout << buffer << std::endl;
            // Sprite erstellen
            sf::Sprite *sp   = new sf::Sprite;
            //Texture erstellen
            sf::Texture *tex = new sf::Texture;
            // Textur laden
            if(!tex->loadFromFile(buffer))
                return EXIT_FAILURE;
            //Sprite laden
            sp->setTexture(*tex);
        
            vSprite.push_back(sp);
        }

        // Alle SpriteIDs und Collisionwerte einlesen
        // Dazu alle Spalten und alle Zeilen der Map durchlaufen
        for(unsigned int i = 0; i < m_map_height; i++)
        {
            // Neue Zeile erstellen
            std::vector< Tile > zeile;

            // Alle Werte für eine Zeile einlesen
            for(unsigned int j = 0; j < m_map_width; j++)
            {
                Tile tile;

                // erster Wert bedeutet spriteID
                in.getline(buffer, 8, ',');
                tile.GroundLayer = atoi(buffer);
                std::cout << buffer << std::endl;
                // zweiter Wert sagt aus ob das Tile begehbar ist
                in.getline(buffer, 8, '|');
                tile.Collision = (atoi(buffer)==1) ? true : false;
                std::cout << buffer << std::endl;
                // Tile in die Zeile einfügen
                zeile.push_back(tile);
                }

            // Die Zeile in Vector einfügen
            vMap.push_back(zeile);
        }
    }
    else // Datei wurde nicht geöffnet
        return false;

    // Datei schließen
    in.close();

    return true;
    
}


// Map zeichnen
void Tilemap::Draw(sf::RenderWindow &window)
{
    // map zeichnen  
    // alle spalten durchlaufen
    for( unsigned int i = 0; i < m_map_height; i++)
    {
        // alle zeilen durchlaufen
        for( unsigned int j = 0; j < m_map_width; j++)
        {

            // zuerst das aktuelle Tile holen
            Tile tile = vMap.at(i).at(j);
            // und dann die Sprite-Nr aus dem Tile auslesen
            int ground_sprite_nr = tile.GroundLayer;
            
            // wenn kleiner null (-1), dann wird kein Sprite gezeichnet
            if(ground_sprite_nr < 0 && ground_sprite_nr < GetSpriteCount())
                continue;
            
            // Grundlayer zeichnen
            vSprite[ ground_sprite_nr ]->setPosition(j*m_tile_width, i*m_tile_height);
            window.draw(*vSprite[ground_sprite_nr]);                        
            
        }
    }
}


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
//Und die unvollständige Main//
#include <SFML\Graphics.hpp>
#include <SFML\Window.hpp>
#include <iostream>
#include <conio.h>
#include <vector>
#include <fstream>
#include <string>
#include <sstream>
#include <stdio.h>
#include "Tile.h"

int main()
{
    sf::RenderWindow window(sf::VideoMode(800,600,32),"MonkeyStyleGames");  
    window.setFramerateLimit(60);
    sf::Clock deltaClock;
    Tilemap map;    

    while (window.isOpen())
    {       
        window.clear();

        sf::Event event;
        sf::Time dt = deltaClock.restart();

        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed||sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
                window.close();
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
            ;
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
            ;
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
            ;
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
            ;
        
        map.Load("Level\Level 1 - Map");
        
        map.Draw(window);
        
        window.display();
    }

    return 0;
}


Ahso die .txt Datei sieht folgendermassen aus:

10
10
40
40
9
Texturen/wand.png
Texturen/bode.png
Texturen/grass.png
Texturen/water.png
Texturen/zellentuer.png
Texturen/kerkertuer.png
Texturen/monster.png
Texturen/held.png
Texturen/schatz.png
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|
0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|0,0|

So das zum Code.
Wenn es noch irgendwelche Anregungen gibt zwecks anders machen, Stil verbessern etc nur her damit :D

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Pappet« (23.09.2012, 02:03)


Tobiking

1x Rätselkönig

  • Private Nachricht senden

2

22.09.2012, 05:08

Erstmal ein Tipp für die Zukunft. Versuch den Fehler selber erstmal einzukreisen. Mit dem Debugger oder ein paar Ausgaben wäre dir direkt aufgefallen wo das Problem liegt. Über 200 Zeilen Quellcode ohne konkrete Fehlerbeschreibung liest sich wohl kaum einer durch. Ich schreib auch nur weil ich den Fehler beim durchscrollen direkt gesehen habe.

Nun zum Fehler. Das Problem dürfte das map.Load("Level\Level 1 - Map"); sein. Der Backslash wird, wie du schon wissen solltest, in C++ als Escape Zeichen verwendet. Willst du ihn in einem String als Zeichen haben, musst du \\ schreiben. So wie du das hast ist der Pfad ungültig, die Datei wird nicht geöffnet und deine Load funktion gibt false zurück. Eine einfache Ausgabe hier hätte dir das gesagt.

Und übrigens lädst du die Map in jedem Durchlauf des Gameloop neu.

3

23.09.2012, 01:27

Ok Ja das stimmt. War etwas voreilig mit dem Post. Sorry.
Um das ganze jetzt weiter einzugrenzen hab ich deine Tipps mal befolgt.
Habe die map.load Methode verändert und in ner cout Ausgabe gibt er mir nun den richtigen String an.
Jetzt taucht aber ein neues Problem auf :(
und zwar

C-/C++-Quelltext

1
2
in.getline(buffer,8,'\n');
m_map_height = atoi(buffer);

in der load Methode, liest keine korrekten Werte ein sondern nur ne 0.
Habe das ganze mit cout überprüft.
Das gleiche in der for Schleife, um die Texturen über den Pfad in der .txt-Datei zu laden.

C-/C++-Quelltext

1
2
if(!tex->loadFromFile(buffer))
   return EXIT_FAILURE;

Da sagt er als Reason: Unable to open file.
aber die

C-/C++-Quelltext

1
if(in.is_open())
läuft ja???
Kann es sein das die ganze Sache mit dem char buffer nicht korrekt funktioniert?
Oder seh ich etwas nicht?

[Edit]
Siehe Anfang erster Post!

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Pappet« (23.09.2012, 02:05)


David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

4

23.09.2012, 10:00

Heißt deine Textur wirklich "Texturen/bode.png" oder nicht eher "Texturen/boden.png"?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

5

23.09.2012, 10:13

Abgesehen davon: Wenn du eh schon einen C++ Stream verwendest, kannst du deine Zahlen doch einfach per >> Operator einlesen, statt mit atoi()!? ;)

6

25.09.2012, 19:11

So komm jetzt mal wieder zum schreiben!



Heißt deine Textur wirklich "Texturen/bode.png" oder nicht eher "Texturen/boden.png"?

Ja, das ist eine Texture des Flusses! *g* Nein hab mich beim posten vertippt heißt eigentlich "Texturen/boden.png".

Abgesehen davon: Wenn du eh schon einen C++ Stream verwendest, kannst du deine Zahlen doch einfach per >> Operator einlesen, statt mit atoi()!? ;)

mhh also du meinst das ich zB:

C-/C++-Quelltext

1
in >> m_map_height;


anstatt

C-/C++-Quelltext

1
2
in.getline(buffer,8,'\n');
m_map_height = atoi(buffer);


benutze? mhh scheint mir zu einfach und führt auch zu einem Fehler. Wie meinst du das genau dot?

Ahso es hat sich noch ein Fehler ergeben! Und zwar hängt er im Destruktor der Klasse Tilemap.
Da habe ich bis jetzt diese Methode:

C-/C++-Quelltext

1
2
3
4
5
6
 std::vector< sf::Sprite* >::iterator it;
    for(it = vSprite.begin(); it != vSprite.end(); it++)
    {
        delete *it;
        it = vSprite.erase(it);
    }


Ich muss ja die Kopien, die ich beim laden der Texturen und im Vector gespeichert habe, wieder löschen. Sonst entstehen ja diese "Memory-Leaks" oder?
Aber bei der Methode ernte ich einen Fehler beim Schliessen des Programms! Klar, denn da wird ja der Destruktor eingesetzt.
Der Fehler besagt das der "vector iterator not incrementable" ist. Was genau bedeutet das und könnt ihr mir helfen?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

7

25.09.2012, 20:17

Abgesehen davon: Wenn du eh schon einen C++ Stream verwendest, kannst du deine Zahlen doch einfach per >> Operator einlesen, statt mit atoi()!? ;)

mhh also du meinst das ich zB:

C-/C++-Quelltext

1
in >> m_map_height;


anstatt

C-/C++-Quelltext

1
2
in.getline(buffer,8,'\n');
m_map_height = atoi(buffer);


benutze? mhh scheint mir zu einfach und führt auch zu einem Fehler. Wie meinst du das genau dot?

Ganz genau das meinte ich. Zu was genau für einem Fehler führt das denn?

Ahso es hat sich noch ein Fehler ergeben! Und zwar hängt er im Destruktor der Klasse Tilemap.
Da habe ich bis jetzt diese Methode:

C-/C++-Quelltext

1
2
3
4
5
6
 std::vector< sf::Sprite* >::iterator it;
    for(it = vSprite.begin(); it != vSprite.end(); it++)
    {
        delete *it;
        it = vSprite.erase(it);
    }


Ich muss ja die Kopien, die ich beim laden der Texturen und im Vector gespeichert habe, wieder löschen. Sonst entstehen ja diese "Memory-Leaks" oder?
Aber bei der Methode ernte ich einen Fehler beim Schliessen des Programms! Klar, denn da wird ja der Destruktor eingesetzt.
Der Fehler besagt das der "vector iterator not incrementable" ist. Was genau bedeutet das und könnt ihr mir helfen?

Richtig, wenn du die Objekte, auf die die Zeiger in diesem vector zeigen, per new angelegt hast, dann musst du sie natürlich auch deleten. Das erase() ist aber wohl unnötig, da der vector wohl sowieso gleich darauf zerstört wird!? Gibt es einen besonderen Grund, wieso du it außerhalb der Schleife deklarierst? Die Fehlermeldung sagt eh schon alles: Du versuchst dort einen iterator zu inkrementieren, der nicht inkrementierbar (weil ungültig) ist...

8

25.09.2012, 21:04

Zitat

Ganz genau das meinte ich. Zu was genau für einem Fehler führt das denn?
Unbehandelte Ausnahme bei 0x773b15de in 2D-Spiel.exe: Microsoft C++-Ausnahme: std::out_of_range an Speicherposition 0x0041f3d4..

Spuckt er mir aus???
kann es sein das es in verbindung mit .getline nicht funktioniert? also zB so:

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
std::fstream in;
    
in.open(path);
if(in.is_open()){
        char buffer[256];

        // Höhe einlesen
        in >> m_map_height;
      
        // Breite einlesen
        in >> m_map_width;

        // Tile-Höhe einlesen
        in >> m_tile_height;

        // Tile-Breite einlesen
        in >> m_tile_width;

        // Anzahl der Sprites einlesen
        in >> sprite_anzahl;

        // Alle Pfade zu den Sprites einlesen und die Sprites erstellen
        for(unsigned int i = 0; i < sprite_anzahl; i++)
        {
            // Pfad einlesen
            in.getline(buffer, 255, '\n');

            // Sprite erstellen
            sf::Sprite *sp   = new sf::Sprite;

            //Texture erstellen
            sf::Texture *tex = new sf::Texture;

            // Textur laden
            if(!tex->loadFromFile(buffer))
                return EXIT_FAILURE;

            //Texture-Vector füllen
            vTexture.push_back(tex);

            //Sprite textuieren
            sp->setTexture(*tex);

            //Sprite-Vector mit texturierten Sprites füllen
            vSprite.push_back(sp);
        }


Richtig, wenn du die Objekte, auf die die Zeiger in diesem vector zeigen, per new angelegt hast, dann musst du sie natürlich auch deleten. Das erase() ist aber wohl unnötig, da der vector wohl sowieso gleich darauf zerstört wird!? Gibt es einen besonderen Grund, wieso du it außerhalb der Schleife deklarierst? Die Fehlermeldung sagt eh schon alles: Du versuchst dort einen iterator zu inkrementieren, der nicht inkrementierbar (weil ungültig) ist...
mhh ok also liegt es daran das es ein sf::Sprite iterator ist? aber was sollte ich dann nehmen?

Werbeanzeige