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

Anonymous

unregistriert

1

30.09.2006, 20:37

[Tutorial] 2D Leveleditor

Okay, einige haben sich ja gefragt, wie man einen Leveleditor und ein Levelformat schreibt. Dazu habe ich jetzt endlich den ersten Teil fertig.

Fangen wir doch gleich mit der WinMain an, die sollte ja jedem bekannt
sein und jeder sollte diese selber schreiben können:

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
int __stdcall WinMain (::HINSTANCE__* hInst, ::HINSTANCE__* hPrevInst, char* cmdLine, int cmdShow)
{
    // Variablen

    ::HWND__*     hWnd;
    ::MSG         message;
    ::WNDCLASSEXW winclass;

    // Initialisieren vor der Verwendung nicht vergessen!

    ::ZeroMemory (&hWnd,     sizeof (::HWND__*));
    ::ZeroMemory (&message,  sizeof (::MSG));
    ::ZeroMemory (&winclass, sizeof (::WNDCLASSEXW));

    // Eigenschaften setzen

    winclass.cbSize        = sizeof (winclass);
    winclass.style         = CS_VREDRAW | CS_HREDRAW;
    winclass.lpfnWndProc   = WndProc;
    winclass.hInstance     = hInst;
    winclass.hIcon         = ::LoadIcon   (hInst, MAKEINTRESOURCEW (25)); // Eigenes Icon laden

    winclass.hCursor       = ::LoadCursor (NULL, IDC_ARROW);
    winclass.hbrBackground = static_cast <::HBRUSH__*> (::GetStockObject(1)); // Grauer Hintergrund

    winclass.lpszClassName = L"EditClass";
    winclass.hIconSm       = ::LoadIcon (hInst, MAKEINTRESOURCEW (25));

    // Klasse registrieren

    if (!::RegisterClassExW (&winclass))
    {
        ::MessageBoxW (NULL, L"Fenster Klasse konnte nicht registriert werden. (Kein Windows NT vorhanden?)", L"Error", MB_ICONERROR); 
        return 0;
    }

    // Fenster erzeugen (mit Menü, dazu kommen wir später) + Scrollbar

    hWnd = ::CreateWindowEx (WS_EX_OVERLAPPEDWINDOW, L"EditClass",  L"Super Mario Leveleditor", 
                             WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
                             CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
                             NULL, Menu, hInst, NULL);

    // Berechnung von "unsigned long"

    ::RECT rc = {0, 0, 800, 600};

    ::AdjustWindowRectEx (&rc, WS_OVERLAPPEDWINDOW, false, 0);

    int centerX = GetSystemMetrics (SM_CXFULLSCREEN) / 2 - (abs (rc.left) + rc.right) / 2;
    int centerY = GetSystemMetrics (SM_CYFULLSCREEN) / 2 - (abs (rc.top) + rc.bottom) / 2;

    ::MoveWindow (hWnd, centerX, centerY, rc.right - rc.left, rc.bottom - rc.top, true);
    // Berechnung von "unsigned long" (gefällt mir sehr gut! ;) )


    // Fenster zeigen

    ::ShowWindow   (hWnd, cmdShow);
    ::UpdateWindow (hWnd);

    // Nachrichtenschleife mit Überprüfung, ob das Fenster noch aktiv ist

    while (!::IsIconic (hWnd) && ::GetMessage (&message, NULL, 0, 0))
    { 
        if (message.message == WM_QUIT) 
            break;

        ::TranslateMessage (&message);
        ::DispatchMessageW (&message);
    }

    return (static_cast <int> (message.wParam));
}


Soweit sollte alles klar sein. Jetzt machen wir uns erstmal an das Menü für den Leveleditor. Dazu wird momentan leider eine globale Variable verwendet, da ich im Moment keine andere Lösung gefunden habe, dies wird aber spätestens im nächsten Teil berichtigt. Dazu laden wir schonmal 2 Bitmaps zum testen der Funktionalität des Menü's. Also weiter mit der WndProc:

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
::HMENU__* Menu = ::CreateMenu ();

_w64 long __stdcall WndProc (::HWND__* hWnd, unsigned int message, 
                             unsigned int wParam, _w64 long lParam)
{
    // ID's für das Menü

    const unsigned short ID_NEW    = 0;
    const unsigned short ID_OPEN   = 1;
    const unsigned short ID_SAVE   = 2;
    const unsigned short ID_SAVEAS = 3;
    const unsigned short ID_QUIT   = 4;
    const unsigned short ID_ABOUT  = 5;

    static io_file       dialogs; // Eine Klasse der Dialog Funktionen für das Laden und Speichern, usw.

    static unsigned char map [42][60]; // Map Array


    static ::PAINTSTRUCT ps;
    static ::RECT        rc; // Rect zum zeichnen


    // Die beiden Tiles zum testen

    static ::HBITMAP__* normal = static_cast <::HBITMAP__*> (::LoadImageW (NULL, L"default.bmp", IMAGE_BITMAP, 16, 16, LR_LOADFROMFILE));
    static ::HBITMAP__* tile1  = static_cast <::HBITMAP__*> (::LoadImageW (NULL, L"tile1.bmp", IMAGE_BITMAP, 16, 16, LR_LOADFROMFILE));

    static ::HMENU__*   MenuTemp = ::CreateMenu ();
    static ::HDC__*     hdc;
    static ::HDC__*     hdc2;

    ::ZeroMemory (&hdc,  sizeof (::HDC__*));
    ::ZeroMemory (&hdc2, sizeof (::HDC__*));

    switch (message)
    {
    case WM_CREATE:
        // Menü erstellen

        ::AppendMenuW (MenuTemp, MF_STRING, ID_NEW, L"Neu");
        ::AppendMenuW (MenuTemp, MF_STRING, ID_OPEN, L"Öffnen");
        ::AppendMenuW (MenuTemp, MF_STRING, ID_SAVE, L"Speichern");
        ::AppendMenuW (MenuTemp, MF_STRING, ID_SAVEAS, L"Speichern unter ...");
        ::AppendMenuW (MenuTemp, MF_SEPARATOR, 0, NULL);
        ::AppendMenuW (MenuTemp, MF_STRING, ID_QUIT, L"Beenden");
        ::AppendMenuW (Menu, MF_POPUP, reinterpret_cast <UINT_PTR> (MenuTemp), L"Datei");

        MenuTemp = ::CreateMenu();

        ::AppendMenuW (MenuTemp, MF_STRING, ID_ABOUT, L"About");
        ::AppendMenuW (Menu, MF_POPUP, reinterpret_cast <UINT_PTR> (MenuTemp), L"Help");

        MenuTemp = CreateMenu();

        // Map mit dem ersten Tile initialisieren

        for (int x = 0; x < 60; ++x)
        for (int y = 0; y < 42; ++y)
            map[y][x] = 1;

        break;

    case WM_PAINT:
        // Map zeichnen, sollte soweit klar sein

        hdc  = ::BeginPaint (hWnd, &ps);
        hdc2 = ::CreateCompatibleDC (hdc);
        ::GetClientRect(hWnd, &rc); 

        for (int x = 0; x < 60; ++x)
        for (int y = 0; y < 42; ++y)
        {
            if (map[y][x] == 1)
            {
                ::SelectObject (hdc2, normal);
                ::BitBlt (hdc, x*16, y*16, rc.right, rc.bottom, hdc2, 0, 0, SRCCOPY);
            }
            else if (map[y][x] == 2)
            {
                ::SelectObject (hdc2, tile1);
                ::BitBlt (hdc, x*16, y*16, rc.right, rc.bottom, hdc2, 0, 0, SRCCOPY);
            }
        } 

        ::DeleteDC(hdc);
        ::DeleteDC(hdc2);
        ::EndPaint (hWnd, &ps);
        break;

        // Hier werden die ID's abgefragt und dementsprechend mit den Funktionen der Klasse gehandelt

    case WM_COMMAND: 
        switch (LOWORD(wParam))
        {
        case ID_NEW:
            dialogs.create_new (hWnd, rc, map);
            break;

        case ID_OPEN:
            dialogs.open_file (hWnd, map);
            break;

        case ID_QUIT:
            return dialogs.quit (hWnd, map);

        case ID_SAVE:
            dialogs.save_as (hWnd, map);
            break;

        case ID_SAVEAS:
            dialogs.save_as (hWnd, map);
            break;

        case ID_ABOUT: // Komischer cpp Tag Fehler Oo ich hoffe es stört euch nicht weiter, ihr wisst ja, wie es eigentlich aussehen soll

            ::MessageBoxW (NULL, L"Written by Riddick.\neMail: Riddick@infekt-software.de", L"About", MB_OK);
            break;

        } break;

    case WM_CLOSE:
        return dialogs.quit (hWnd, map);
    }

    return (static_cast<_w64 long>(::DefWindowProc (hWnd, message, wParam, lParam)));
}


Das sollte jetzt auch soweit klar sein, also machen wir uns doch gleich
an die angesprochene Klasse:

MENU.H

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
#include <windows.h>
#include "file.h" // File Klasse, falls ihr keine habt, nehmt einfach die aus nix da's/unsigned long's File Klassen Tutorial www.spieleprogrammierer.de/phpBB2/viewtopic.php?t=6212


class io_file
{
public:
    io_file  (void);
    ~io_file (void);

    // Vorerst nehmen wir statische Map's, dies wird in späteren Teilen geändert!

    void create_new (::HWND__* hWnd, ::RECT rc, unsigned char map [42][60]);
    void open_file  (::HWND__* hWnd, unsigned char map [42][60]);
    void save_as    (::HWND__* hWnd, unsigned char map [42][60]);
    int  quit       (::HWND__* hWnd, unsigned char map [42][60]);

private:
    ::OPENFILENAMEW save_file;

    wchar_t         filename[200];
    wchar_t         title[100];
    wchar_t         file_save[200];
    file            map_file;
};


MENU.CPP

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
#include "menu.h"

io_file::io_file (void)
{
    ::wsprintf (filename, L"*.swp");
}
io_file::~io_file (void)
{
}

void io_file::create_new (::HWND__* hWnd, ::RECT rc, unsigned char map [42][60])
{
    if (::MessageBoxW (NULL, L"Sind Sie sicher, dass Sie eine neue Datei anlegen möchten?", L"Neue Datei?", MB_YESNO | MB_ICONQUESTION) == IDYES)
    {
        ::wsprintf (filename, L"Untitled1.swp");
        ::wsprintf (title, L"Super Mario Leveleditor - %s", filename);

        // Neuen Fenstertitel setzen.

        ::SendMessageW (hWnd, WM_SETTEXT, ::wcslen (title) + 1, (LPARAM)(LPCSTR)title);

        // Map mit den neuen Daten initialisieren. Testweise wird hier die ID 2 verwendet für das 2. Teil, damit ihr sehen könnt, dass es funktioniert.

        for (int x = 0; x < 60; ++x)
            for (int y = 0; y < 42; ++y)
                map[y][x] = 2;

        // Neue Datei anlegen

        map_file.close ();
        map_file.open (filename);
        map_file.write (map, sizeof (map));

        // Fenster neu zeichnen

        ::InvalidateRect (hWnd, &rc, true);
    }
}

void io_file::open_file (::HWND__* hWnd, unsigned char map [42][60])
{
    ::wsprintfW (filename, L"*.swp");

    ::OPENFILENAMEW open_file;
    ::ZeroMemory (&open_file, sizeof (::OPENFILENAMEW));

    // initialisieren

    open_file.lStructSize     = sizeof (::OPENFILENAMEW);
    open_file.hwndOwner       = hWnd;
    open_file.lpstrFile       = filename;
    open_file.nMaxFile        = sizeof (filename);
    open_file.lpstrFilter     = L"Super Mario\0*.swp\0All\0*.*\0";
    open_file.nFilterIndex    = 1;
    open_file.lpstrFileTitle  = NULL;
    open_file.nMaxFileTitle   = 0;
    open_file.lpstrInitialDir = NULL;
    open_file.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

    // Wenn eine neue Datei geöffnet wird

    if (::GetOpenFileNameW (&open_file))
    {
        // Titel ändern

        ::wsprintfW (title, L"Super Mario Leveleditor - %s", filename);
        ::SendMessageW (hWnd, WM_SETTEXT, ::wcslen (title) + 1, (LPARAM)(LPCSTR)title);
    } // ansonsten halt nicht ;)


    // Neue Datei anlegen

    map_file.close ();
    map_file.open (filename);
    map_file.read (map, sizeof (map));
}

void io_file::save_as (::HWND__* hWnd, unsigned char map [42][60])
{
    // Sollte alles klar sein

    ::wsprintfW (file_save, filename);

    ::ZeroMemory (&save_file, sizeof (::OPENFILENAMEW));

    save_file.lStructSize     = sizeof (::OPENFILENAMEW);
    save_file.hwndOwner       = hWnd;
    save_file.lpstrFile       = filename;
    save_file.nMaxFile        = sizeof (filename);
    save_file.lpstrFilter     = L"Super Mario\0*.swp\0All\0*.*\0";
    save_file.nFilterIndex    = 1;
    save_file.lpstrFileTitle  = NULL;
    save_file.nMaxFileTitle   = 0;
    save_file.lpstrInitialDir = NULL;
    save_file.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

    ::GetSaveFileNameW (&save_file);

    map_file.open (filename);
    map_file.write (map, sizeof (map));
}

int io_file::quit (::HWND__* hWnd, unsigned char map [42][60])
{
    switch (::MessageBoxW (NULL, L"Möchten Sie vor dem Beenden noch speichern?", L"Beenden?",  MB_YESNOCANCEL | MB_ICONQUESTION))
    {
    case IDYES: // Wenn Ja gedrückt wurde:

            save_as (hWnd, map); // Speicher Dialog aufrufen

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


            // Beenden

            ::PostQuitMessage (0);
            return 0;

    case IDNO:
            map_file.close ();
            ::PostQuitMessage (0);
            return 0;

    case IDCANCEL: // Abbrechen gedrückt? Dann nichts machen und zum Programm zurückkehren

        return 0;

    default: return 0;
    }
}


So das wars auch erstmal, ich poste gleich nochmal den kompletten Code und die Fileklasse müsst ihr wie gesagt selber schreiben oder unsigned long's nehmen. Der Grund? Das ist kein Tutorial über File Klassen! ;) Ich finde das ist ein Thema für sich. Ich bin jetzt davon ausgegangen, dass ihr über gewisse Grundlagen der WinApi informiert seid. Falls es nicht so ist, fragt einfach nach, ich werde euch dann alle Fragen zu diesem Tutorial beantworten.

eMail: Riddick@infekt-software.de

So im nächsten Tutorial werden wir dann ein wenig umstrukturieren und
Maps bauen können und im 3. Teil werden wir dann alles noch einmal
ordentlich optimieren und eventuell ein paar weiter Features einbauen und eventuell sogar ein paar Funktionen für Direct Draw schreiben zum zeichnen der Map. Also hier kommt dann jetzt der Code:

MAIN.CPP

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
#include "menu.h"

::HMENU__* Menu = ::CreateMenu ();

_w64 long __stdcall WndProc (::HWND__* hWnd, unsigned int message, 
                             unsigned int wParam, _w64 long lParam)
{
    const unsigned short ID_NEW    = 0;
    const unsigned short ID_OPEN   = 1;
    const unsigned short ID_SAVE   = 2;
    const unsigned short ID_SAVEAS = 3;
    const unsigned short ID_QUIT   = 4;
    const unsigned short ID_ABOUT  = 5;

    static io_file       dialogs;
    static unsigned char map [42][60];

    static ::PAINTSTRUCT ps;
    static ::RECT        rc;

    static ::HBITMAP__* normal = static_cast <::HBITMAP__*> (::LoadImageW (NULL, L"default.bmp", IMAGE_BITMAP, 16, 16, LR_LOADFROMFILE));
    static ::HBITMAP__* tile1  = static_cast <::HBITMAP__*> (::LoadImageW (NULL, L"tile1.bmp", IMAGE_BITMAP, 16, 16, LR_LOADFROMFILE));

    static ::HMENU__*   MenuTemp = ::CreateMenu ();
    static ::HDC__*     hdc;
    static ::HDC__*     hdc2;

    ::ZeroMemory (&hdc,  sizeof (::HDC__*));
    ::ZeroMemory (&hdc2, sizeof (::HDC__*));

    switch (message)
    {
    case WM_CREATE:
        ::AppendMenuW (MenuTemp, MF_STRING, ID_NEW, L"Neu");
        ::AppendMenuW (MenuTemp, MF_STRING, ID_OPEN, L"Öffnen");
        ::AppendMenuW (MenuTemp, MF_STRING, ID_SAVE, L"Speichern");
        ::AppendMenuW (MenuTemp, MF_STRING, ID_SAVEAS, L"Speichern unter ...");
        ::AppendMenuW (MenuTemp, MF_SEPARATOR, 0, NULL);
        ::AppendMenuW (MenuTemp, MF_STRING, ID_QUIT, L"Beenden");
        ::AppendMenuW (Menu, MF_POPUP, reinterpret_cast <UINT_PTR> (MenuTemp), L"Datei");

        MenuTemp = ::CreateMenu();

        ::AppendMenuW (MenuTemp, MF_STRING, ID_ABOUT, L"About");
        ::AppendMenuW (Menu, MF_POPUP, reinterpret_cast <UINT_PTR> (MenuTemp), L"Help");

        MenuTemp = CreateMenu();

        for (int x = 0; x < 60; ++x)
        for (int y = 0; y < 42; ++y)
            map[y][x] = 1;

        break;

    case WM_PAINT:
        hdc  = ::BeginPaint (hWnd, &ps);
        hdc2 = ::CreateCompatibleDC (hdc);
        ::GetClientRect(hWnd, &rc); 

        for (int x = 0; x < 60; ++x)
        for (int y = 0; y < 42; ++y)
        {
            if (map[y][x] == 1)
            {
                ::SelectObject (hdc2, normal);
                ::BitBlt (hdc, x*16, y*16, rc.right, rc.bottom, hdc2, 0, 0, SRCCOPY);
            }
            else if (map[y][x] == 2)
            {
                ::SelectObject (hdc2, tile1);
                ::BitBlt (hdc, x*16, y*16, rc.right, rc.bottom, hdc2, 0, 0, SRCCOPY);
            }
        } 

        ::DeleteDC(hdc);
        ::DeleteDC(hdc2);
        ::EndPaint (hWnd, &ps);
        break;

    case WM_COMMAND: 
        switch (LOWORD(wParam))
        {
        case ID_NEW:
            dialogs.create_new (hWnd, rc, map);
            break;

        case ID_OPEN:
            dialogs.open_file (hWnd, map);
            break;

        case ID_QUIT:
            return dialogs.quit (hWnd, map);

        case ID_SAVE:
            dialogs.save_as (hWnd, map);
            break;

        case ID_SAVEAS:
            dialogs.save_as (hWnd, map);
            break;

        case ID_ABOUT:
            ::MessageBoxW (NULL, L"Written by Riddick.\neMail: Riddick@infekt-software.de", L"About", MB_OK);
            break;

        } break;

    case WM_CLOSE:
        return dialogs.quit (hWnd, map);
    }

    return (static_cast<_w64 long>(::DefWindowProc (hWnd, message, wParam, lParam)));
}

int __stdcall WinMain (::HINSTANCE__* hInst, ::HINSTANCE__* hPrevInst, char* cmdLine, int cmdShow)
{
    ::HWND__*     hWnd;
    ::MSG         message;
    ::WNDCLASSEXW winclass;

    ::ZeroMemory (&hWnd,     sizeof (::HWND__*));
    ::ZeroMemory (&message,  sizeof (::MSG));
    ::ZeroMemory (&winclass, sizeof (::WNDCLASSEXW));

    winclass.cbSize        = sizeof (winclass);
    winclass.style         = CS_VREDRAW | CS_HREDRAW;
    winclass.lpfnWndProc   = WndProc;
    winclass.hInstance     = hInst;
    winclass.hIcon         = ::LoadIcon   (hInst, MAKEINTRESOURCEW (25));
    winclass.hCursor       = ::LoadCursor (NULL, IDC_ARROW);
    winclass.hbrBackground = static_cast <::HBRUSH__*> (::GetStockObject(1));
    winclass.lpszClassName = L"EditClass";
    winclass.hIconSm       = ::LoadIcon (hInst, MAKEINTRESOURCEW (25));

    if (!::RegisterClassExW (&winclass))
    {
        ::MessageBoxW (NULL, L"Fenster Klasse konnte nicht registriert werden. (Kein Windows NT vorhanden?)", L"Error", MB_ICONERROR); 
        return 0;
    }

    hWnd = ::CreateWindowEx (WS_EX_OVERLAPPEDWINDOW, L"EditClass",  L"Super Mario Leveleditor", 
                             WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
                             CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
                             NULL, Menu, hInst, NULL);

    ::RECT rc = {0, 0, 800, 600};

    ::AdjustWindowRectEx (&rc, WS_OVERLAPPEDWINDOW, false, 0);

    int centerX = GetSystemMetrics (SM_CXFULLSCREEN) / 2 - (abs (rc.left) + rc.right) / 2;
    int centerY = GetSystemMetrics (SM_CYFULLSCREEN) / 2 - (abs (rc.top) + rc.bottom) / 2;

    ::MoveWindow (hWnd, centerX, centerY, rc.right - rc.left, rc.bottom - rc.top, true);

    ::ShowWindow   (hWnd, cmdShow);
    ::UpdateWindow (hWnd);

    while (!::IsIconic (hWnd) && ::GetMessage (&message, NULL, 0, 0))
    { 
        if (message.message == WM_QUIT) 
            break;

        ::TranslateMessage (&message);
        ::DispatchMessageW (&message);
    }

    return (static_cast <int> (message.wParam));
}


MENU.H

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
#include <windows.h>
#include "file.h"

class io_file
{
public:
    io_file  (void);
    ~io_file (void);

    void create_new (::HWND__* hWnd, ::RECT rc, unsigned char map [42][60]);
    void open_file  (::HWND__* hWnd, unsigned char map [42][60]);
    void save_as    (::HWND__* hWnd, unsigned char map [42][60]);
    int  quit       (::HWND__* hWnd, unsigned char map [42][60]);

private:
    ::OPENFILENAMEW save_file;

    wchar_t         filename[200];
    wchar_t         title[100];
    wchar_t         file_save[200];
    file            map_file;
};


MENU.CPP

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
#include "menu.h"

io_file::io_file (void)
{
    ::wsprintf (filename, L"*.swp");
}
io_file::~io_file (void)
{
}

void io_file::create_new (::HWND__* hWnd, ::RECT rc, unsigned char map [42][60])
{
    if (::MessageBoxW (NULL, L"Sind Sie sicher, dass Sie eine neue Datei anlegen möchten?", L"Neue Datei?", MB_YESNO | MB_ICONQUESTION) == IDYES)
    {
        ::wsprintf (filename, L"Untitled1.swp");
        ::wsprintf (title, L"Super Mario Leveleditor - %s", filename);

        ::SendMessageW (hWnd, WM_SETTEXT, ::wcslen (title) + 1, (LPARAM)(LPCSTR)title);

        for (int x = 0; x < 60; ++x)
            for (int y = 0; y < 42; ++y)
                map[y][x] = 2;

        map_file.close ();
        map_file.open (filename);
        map_file.write (map, sizeof (map));

        ::InvalidateRect (hWnd, &rc, true);
    }
}

void io_file::open_file (::HWND__* hWnd, unsigned char map [42][60])
{
    ::wsprintfW (filename, L"*.swp");

    ::OPENFILENAMEW open_file;
    ::ZeroMemory (&open_file, sizeof (::OPENFILENAMEW));

    // initialisieren

    open_file.lStructSize     = sizeof (::OPENFILENAMEW);
    open_file.hwndOwner       = hWnd;
    open_file.lpstrFile       = filename;
    open_file.nMaxFile        = sizeof (filename);
    open_file.lpstrFilter     = L"Super Mario\0*.swp\0All\0*.*\0";
    open_file.nFilterIndex    = 1;
    open_file.lpstrFileTitle  = NULL;
    open_file.nMaxFileTitle   = 0;
    open_file.lpstrInitialDir = NULL;
    open_file.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

    if (::GetOpenFileNameW (&open_file))
    {
        ::wsprintfW (title, L"Super Mario Leveleditor - %s", filename);
        ::SendMessageW (hWnd, WM_SETTEXT, ::wcslen (title) + 1, (LPARAM)(LPCSTR)title);
    }

    map_file.close ();
    map_file.open (filename);
    map_file.read (map, sizeof (map));
}

void io_file::save_as (::HWND__* hWnd, unsigned char map [42][60])
{
    ::wsprintfW (file_save, filename);

    ::ZeroMemory (&save_file, sizeof (::OPENFILENAMEW));

    save_file.lStructSize     = sizeof (::OPENFILENAMEW);
    save_file.hwndOwner       = hWnd;
    save_file.lpstrFile       = filename;
    save_file.nMaxFile        = sizeof (filename);
    save_file.lpstrFilter     = L"Super Mario\0*.swp\0All\0*.*\0";
    save_file.nFilterIndex    = 1;
    save_file.lpstrFileTitle  = NULL;
    save_file.nMaxFileTitle   = 0;
    save_file.lpstrInitialDir = NULL;
    save_file.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

    ::GetSaveFileNameW (&save_file);

    map_file.open (filename);
    map_file.write (map, sizeof (map));
}

int io_file::quit (::HWND__* hWnd, unsigned char map [42][60])
{
    switch (::MessageBoxW (NULL, L"Möchten Sie vor dem Beenden noch speichern?", L"Beenden?",  MB_YESNOCANCEL | MB_ICONQUESTION))
    {
    case IDYES:
            save_as (hWnd, map);
            map_file.close ();

            ::PostQuitMessage (0);
            return 0;

    case IDNO:
            map_file.close ();
            ::PostQuitMessage (0);
            return 0;

    case IDCANCEL:
        return 0;

    default: return 0;
    }
}


So noch ein allerletztes Wort: Feedback erwünscht, aber bitte konstruktiv!

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

30.09.2006, 20:42

Das Tutorial enthält so gut wie gar keinen erklärenden Text, sondern der Leser bekommt einfach seitenweise Code um die Ohren gehauen. Das ist doch nicht der Sinn der Sache, denn man erhält gar keinen Durchblick.
Das solltest du auf jeden Fall ändern.

Ein Tutorial sollte den Leser durch Erklärungen in die Lage versetzen, die beschriebene Sache selbst zu implementieren.

Anonymous

unregistriert

3

30.09.2006, 20:48

Stimmt, werde ich bei der nächsten Gelegenheit ändern.

EDIT: Bin übelst im Stress, kann etwas dauern, bis ich das überarbeiten
kann, ich meld mich dann.

john

Alter Hase

Beiträge: 786

Beruf: Schüler

  • Private Nachricht senden

4

01.10.2006, 23:39

Hm ich hab zu dem Thema auch vor kurzem ein Tutorial geschrieben ...
Aber es ist auch nicht soo umfassend, habe Codeausschnitte aus einem eigenen Editor dazugetan und für interessante Stellen Lösungsansätze vorgeschlagen, allerdings ist der Code, der enthalten ist, aus einem Projekt, wo der Code vllt. nicht der allersauberste ist (eben aus nem Projekt, wo es schnell gehen musste), es geht jedoch auch mehr um die Ideen an sich. Ich find aber auch, dass es nen interessantes Thema für ein Tutorial ist.
mfg
john

Faule Socke

Community-Fossil

Beiträge: 1 915

Wohnort: Schreibtischstuhl

  • Private Nachricht senden

5

19.11.2006, 17:09

hmm... ja sehr viel code und wenig text. Du kannst nicht davon ausgehen das jeder mit der Windowsprogrammierung vertraut ist und alle diese Funktionen die du da benutzt auch kennt. Du solltest zumindest die wichtigsten funktionen erklären bei denen man nicht sofort erkennen kann was sie bedeuten. Des weiteren solltest du den leser in deien Denkweisen einweihen und schreiben wo probleme entstehen können und wie DU sie gelöst hast und was es eventuell noch für moglichkeiten gibt!

Weil einfach nur den Quellcode zu posten und hoffen das ihn jeder versteht das wird nicht funktionieren!


mfg,

Faule Socke

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

6

19.11.2006, 17:14

Wie schafft man es nur einen fast 2 Monate alten Thread auszugraben? Mir sind die Beweggründe irgendwie einfach nicht ganz klar, aber es ist immerwieder irritierend :roll: ;)
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

Faule Socke

Community-Fossil

Beiträge: 1 915

Wohnort: Schreibtischstuhl

  • Private Nachricht senden

7

19.11.2006, 17:52

Zitat von »"Nox"«

Wie schafft man es nur einen fast 2 Monate alten Thread auszugraben? Mir sind die Beweggründe irgendwie einfach nicht ganz klar, aber es ist immerwieder irritierend :roll: ;)


hmmm hab ich gar nicht drauf geachtet... aber es ist immerhin ein tut... und das sollte(wenn es denn irgendwann noch fertig wird) auch noch in den Tutorialbereich kommen... ausserdem war es noch auf der ersten seite...


mfg,

Faule Socke

john

Alter Hase

Beiträge: 786

Beruf: Schüler

  • Private Nachricht senden

8

19.11.2006, 18:32

Zitat von »"Faule Socke"«

hmmm hab ich gar nicht drauf geachtet... [...] ausserdem war es noch auf der ersten seite...

Ist das logisch? Wenn du relativ weit runterscrollen musst, ist es doch klar, dass, wenn die Einträge nach Aktualität sortiert sind, du einen älteren Thread vor dir hast. Naja, ich kann Nox auf jeden Fall verstehen und muss ihm da schon Recht geben, manchmal kommt es einem so vor, als ob dir ein wenig langweilig wäre, aber ist ja deine Sache.. ;)
mfg
john

Das Gurke

Community-Fossil

Beiträge: 1 996

Wohnort: Pinneberg

Beruf: Schüler

  • Private Nachricht senden

Werbeanzeige