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

04.08.2012, 22:39

Tilemap - Bewegungsgeschwindigkeit

Guten Abend,

ich bastel gerade an einem Tile-based Spiel rum, habe aber Probleme mit der Geschwindigkeit des Spielers.

Meine Tiles haben eine Größe von 20x20 Pixeln.
Die Framerate beträgt 60 fps.

Wenn ich die Pfeiltasten bediene, bewegt sich die Figur entsprechend nach oben, unten, links oder rechts. Allerdings viel zu schnell, weil ja das Tastatursignal abgefangen wird und nicht nur ein Drücken sondern gleich mehrere Anschläge in der kurzen Zeit erkannt werden und die Figur sich gleich 4-6 Tiles, also 80-120 Pixel bewegt. (Ich hoffe das war verständlich, weiß gerade nicht, wie ich das besser ausdrücken kann.) :hmm:

Die Framerate will ich aber nicht runtersetzen, weil es dann stockt und "hackig" aussieht.


Ich habe auch schon probiert, mich nicht in Tile-Einheiten sondern in Pixel fortzubewegen. Das geht auch ganz gut soweit. Die Probleme kommen allerdings wenn ich dann versuche eine Kollisionserkennung zu machen.

Bsp:

Die Map: (X = Spieler)
map [5][7] =

1111111
1000001
100X001
1000001
1111111

Ich kann ja jetzt checken:

Quellcode

1
if (key[Up] && map[y-1][x] == 0)
ob also das Tile über mir Boden ist.

Mein Spieler sitz auf der Pos (x,y) aber in PIXELN.

Jetzt muss ich also checken:

Quellcode

1
if (key[Up] && map[(y/20)-1][x] == 0)
um die Pixel in Tile-Einheiten umzurechnen.

Wenn ich aber auf einem ungeraden Pixel stehe zB 637 Pixel, dann treten ja beim Dividieren Rundungsfehler auf, weil es ein Tile an der Position 31,85 ja nicht gibt. Daher wird die ganze Kollisionserkennung ungenau.

Ich hoffe ich habe euch jetzt nicht all zu sehr verwirrt...

Kann mir vielleicht jemand einen Tip geben, wie ich aus der Misere herauskomme? Oder gehe ich ganz falsch an die Sache ran?

Vielen Dank schon einmal.

Beste Grüße

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

2

04.08.2012, 22:58

Speicher dir eine bool Variable für jede Taste die man drücken kann und beweg die Figur in deinem main loop dann einfach wenn in die entsprechende Richtung. Wenn die Taste losgelassen wird setzt du die Variable auf false. Für die Geschwindigkeit nimmst du einfach einen Wert den die Figur pro Sekunde laufen kann, z.B. 60 Pixel = 3 Tiles/Sekunde und teilst diese durch die FPS. Dann hast du die Geschwindigkeit pro Frame. Wenn du also auf der x-Achse nach rechts laufen willst: spieler.x += 60/fps

Für die "Rundungsfehler" guck dir mal Modulo an, also % ;)

3

04.08.2012, 23:28

Also den Bool hatte ich schon von vornherein drin.

Dein Tipp mit der Geschwindigkeit bezieht sich aber darauf, dass sich der Spieler in Pixeln fortbewegt, oder? Weil wenn ich mich eh in Tiles bewege kann ich ja nicht mehr teilen...

Meinst du mit spieler.x += 60/fps das hier: spieler.x = spieler.x + 60 / fps ??? Weil dann hätte ich ja wieder spieler.x + 1. Und wenn sich das auf Pixel bezieht wäre die Bewegung ziemlich langsam...

*confused*

idontknow

unregistriert

4

04.08.2012, 23:36

Wegen der Geschwindigkeit: ich habe keine Ahnung was du benutzt für die Keyabfrage, aber wie schon erwähnt ist das Ziel, dass du speicherst ob eine Taste gedrückt wurde. Du kannst z.b. bei einem Keydown Event eine Spielergeschwindigkeit setzen und beim Keyup Event für die selbe Taste die Geschwindigkeit auf 0 setzen. Das ist ein relativ guter Ansatz. z.b. unter Windows mit WinAPI wäre das quasi GetAsyncKeySate (dein Ansatz) vs. Window-Events (in WndProc).

Was die Kollision angeht kannst du wenn du eine Tilebasierte Map hast immer anhand der Spielerposition das Tile bestimmen in dem sich der Spieler befindet das ist schonmal kein Problem.
Natürlich musst du bei Tiles und einer Spielerpositon irgendwie noch bestimmen wie groß dein Spieler ist und eben das Umfeld um die Position auf Kollision prüfen nicht nur den Punkt an dem du den Spieler zeichnest.

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

5

05.08.2012, 00:01

Zitat

Meinst du mit spieler.x += 60/fps das hier: spieler.x = spieler.x + 60 / fps ??? Weil dann hätte ich ja wieder spieler.x + 1. Und wenn sich das auf Pixel bezieht wäre die Bewegung ziemlich langsam...


Das war nur ein Beispiel. Du kannst die Geschwindigkeit dann beliebig halten. Wenn du z.B. willst das der Spieler 5 Tiles in einer Sekunde zurücklegen kann kannst du das für jedes Frame so errechnen (hier für eine Bewegung auf der x-Achse):

spieler.x = spieler.x+tile.breite*5/fps

In deinem main loop könnte das dann so aussehen:

Quellcode

1
2
3
mainloop:
    if(tasten["d"]):
        spieler.position += spieler.geschwindigkeit/fps

6

05.08.2012, 00:09

Ich hänge einfach mal meinen Code ran, ist in Allegro geschrieben, aber die grundsätzlichen Sachen sind ja unabhängig von Allegro.

Ich möchte letztendlich erreichen, dass sich die Figur (also mein Kreis) beim Drücken der Taste nur ein Tile (20 Pixel) bewegt. Wenn ich eine Taste drücke werden aber mehrere Anschläge erkannt, weil das ganze einfach alles sehr schnell abläuft.

Aber schaut am besten selber mal:

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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include <stdio.h>
#include <iostream>
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>

using namespace std;

const float FPS = 60.0f;
const int SCREEN_W = 800;
const int SCREEN_H = 600;

enum MYKEYS {KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT};

int main (int argc, char **argv)
{
    //#######################//
    //ALLEGRO INITIALISIERUNG//
    //#######################//

    ALLEGRO_DISPLAY *display = NULL;
    ALLEGRO_EVENT_QUEUE *event_queue = NULL;
    ALLEGRO_TIMER *timer = NULL;

    bool redraw = true;
    bool doexit = false;
    bool key[4] = {false, false, false, false};

    int x = 19;
    int y = 14;

    int radius = 10;
    
    //Karte ausgeben
    int map [30][40] = {{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,1},
    {1,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,2,2,1},
    {1,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,1},
    {1,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,1},
    {1,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,2,2,1},
    {1,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,1},
    {1,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,1},
    {1,0,0,0,3,3,3,3,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,2,2,1},
    {1,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,1},
    {1,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,1},
    {1,0,0,0,0,0,0,0,0,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,1},
    {1,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,1},
    {1,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,1},
    {1,0,0,0,3,3,3,3,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,2,2,1},
    {1,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,1},
    {1,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,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};

    al_init(); 

    timer = al_create_timer(1.0 / FPS);
    display = al_create_display(SCREEN_W, SCREEN_H);
    al_install_keyboard();
    al_init_primitives_addon();
    al_init_image_addon();
    event_queue = al_create_event_queue();
    
    al_register_event_source(event_queue, al_get_display_event_source(display));
    al_register_event_source(event_queue, al_get_timer_event_source(timer));
    al_register_event_source(event_queue, al_get_keyboard_event_source());
    al_clear_to_color(al_map_rgb(0,0,0));
    al_flip_display();
    
    al_start_timer(timer);

    //#######//
    //UPDATEN//
    //#######//

    while(!doexit)
    {
        ALLEGRO_EVENT ev;
        al_wait_for_event(event_queue, &ev);

        if(ev.type == ALLEGRO_EVENT_TIMER) 
        {
            if (key[KEY_UP] && map[y-1][x] == 0)
            {
                cout << "up" << endl;
                y--;
                cout << y << endl;
            }
            if (key[KEY_DOWN] && map[y+1][x] == 0)
            {
                cout << "down" << endl;
                y++;
                cout << y << endl;
            }
            if (key[KEY_LEFT] && map[y][x-1] == 0)
            {
                cout << "left" << endl;
                x--;
                cout << x << endl;
            }
            if (key[KEY_RIGHT] && map[y][x+1] == 0)
            {
                cout << "right" << endl;
                x++;
                cout << x << endl;
            }


            redraw = true;
        }

        else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
        {
            break;
        }

        else if (ev.type == ALLEGRO_EVENT_KEY_DOWN)
        {
            switch (ev.keyboard.keycode)
            {
            case ALLEGRO_KEY_UP:
                key[KEY_UP] = true;
                break;

            case ALLEGRO_KEY_DOWN:
                key[KEY_DOWN] = true;
                break;

            case ALLEGRO_KEY_LEFT:
                key[KEY_LEFT] = true;
                break;

            case ALLEGRO_KEY_RIGHT:
                key[KEY_RIGHT] = true;
                break;
            }
        }

        else if (ev.type == ALLEGRO_EVENT_KEY_UP)
        {
            switch (ev.keyboard.keycode)
            {
            case ALLEGRO_KEY_UP:
                key[KEY_UP] = false;
                break;

            case ALLEGRO_KEY_DOWN:
                key[KEY_DOWN] = false;
                break;

            case ALLEGRO_KEY_LEFT:
                key[KEY_LEFT] = false;
                break;

            case ALLEGRO_KEY_RIGHT:
                key[KEY_RIGHT] = false;
                break;

            case ALLEGRO_KEY_ESCAPE:
                doexit = true;
                break;
            }
        }

        //###################//
        //BILDSCHIRM ZEICHNEN//
        //###################//

        if (redraw && al_is_event_queue_empty(event_queue))
        {
            redraw = false;

            al_clear_to_color(al_map_rgb(0,0,0));

            for (int i=0; i<=30; i++)
            {
                for (int t=0; t<=40; t++)
                {
                    if (map[i][t] == 1)
                        al_draw_filled_rectangle(t*20, i*20, (t+1)*20, (i+1)*20, al_map_rgb(128, 255, 255));
                    else if (map[i][t] == 2)
                        al_draw_filled_rectangle(t*20, i*20, (t+1)*20, (i+1)*20, al_map_rgb(255, 255, 0));
                    else if (map[i][t] == 3)
                        al_draw_filled_rectangle(t*20, i*20, (t+1)*20, (i+1)*20, al_map_rgb(0, 255, 0));
                }
            }

            al_draw_filled_circle((x*20)+10, (y*20)+10, radius, al_map_rgb(0,0,255));

            al_flip_display();
        }
    }

    //Speicher wieder freigeben
    al_destroy_timer(timer);
    al_destroy_display(display);
    al_destroy_event_queue(event_queue);

    return 0;
}



Wenn ich zB oben habe

C-/C++-Quelltext

1
2
if (key[KEY_UP] && map[y-1][x] == 0) 
y--;

dann kann der Geschwindigkeitswert nicht kleiner als 1 sein, weil ja die Tilemap nur ganze Zahlen kennt.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Eyes Only« (05.08.2012, 00:23)


NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

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

  • Private Nachricht senden

7

05.08.2012, 00:23

Ohh Gott! Geht ihr davon aus, dass die fps konstant sind? Das kann man einfach nicht garantieren. Multipliziere die Geschwindigkeit mit der frametime, die du natürlich messen musst und es wird nicht mehr stocken oder ruckeln. In der Wiki gibt es einen Artikel zu fixed timesteps. Den würde ich dir empfehlen.
"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?

idontknow

unregistriert

8

05.08.2012, 00:57

Wenn er sagt, dass er 60 fps hat und kein Code dabei ist, ist mein erster Gedanke einfach mal, dass die Frametime irgendwie begrenzt wurde..

9

05.08.2012, 01:06

Die Framerate habe ich auf 60 FPS festgelegt.
In der Main-Funktion läuft ein Timer, der auf die 60 FPS eingestellt ist, sodass der Teil des Codes, der regelmäßig aktualisiert werden muss, konstant läuft. (In meinem Fall die Tastaturabfrage.)

Ich will nur erreichen, das beim Drücken einer Richtungstaste die Figur sich gleichmäßig bewegt, zB wie bei Blocks 5. Wenn ich die Pfeiltaste drücke bewegt sie sich genau ein Tile weiter, wenn ich gedrückt halte, mehrere.

10

05.08.2012, 02:29

Zur Kollision:
Du kannst doch auch einfach deine Tiles in Pixel umrechnen (tile.x * tilewidth) und dann sogar eine Pixelgenaue Kollision vornehmen.
Außerdem würde ich zuerst die neue Position berechnen, dann schauen ob diese Kollidiert und dann den Player solange zurücksetzen bis er nicht mehr kollidiert. Dann die Loop fortsetzen ;)

Mfg
Akktta
Diese Angaben sind (wie immer) ohne Gewähr :D

Werbeanzeige