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

23.11.2014, 12:14

Kleines SDL2 Spiel - Kollisionsfehler

Hallo

Seit kurzen schreibe ich mit SDL2 und einem code aus dem Netz (wie man Klassen benutz)
an ein ganz einfachen ScrollShooter. Dabei hab ich folgendes Problem:

Wenn ich ein feind abschießen will, muss ich erst ganz offt schießen bis sich was tut
- ich gehe davon aus dass sich irgendwo was auf NULL wieder stellt
und erst wenn Max_BULLET und MAX_ENEMYS gleich sind gibs ne Kollosion

Hier mein Code - schussklasse muss ich auch noch abändern (tuts aber soweit)

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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#include <SDL.h>
#include <time.h>
#include <cstdlib>

#define enemy_height 32
#define enemy_width  32

#define bullet_height 12
#define bullet_width  6

const int MAX_BULLETS = 50;
const int MAX_ENEMYS = 50;

SDL_Surface* ScreenSurface =NULL;
SDL_Surface* window = NULL;

SDL_Surface* Background = NULL;
SDL_Surface* Player = NULL;
SDL_Surface* Enemy = NULL;
SDL_Surface* Bullet = NULL;

SDL_Rect posPlayer, posBullet, posEnemy;

SDL_Rect dstrect;

SDL_Event event;

bool quit = false;

class thePlayer
{
    public:
    thePlayer();

    void player_movement();
    void show_player();

    private:
};

//-----------------------------

class theBullet
{
public:
theBullet();

bool isActive;
int x_position;
int y_position;

void bullet_movement();
void add_new_bullet();
void show_bullet();


private:

};
theBullet arrayofBullets[MAX_BULLETS];

//-----------------------------

class theEnemy
{
public:
theEnemy();

bool EnemyisActive;
int x_Enemy;
int y_Enemy;

void enemy_movement();
void show_enemy();

private:

};
theEnemy arrayofEnemy[MAX_ENEMYS];

//-----------------------------

theBullet::theBullet()
{
/*posBullet.x;
posBullet.y;
posBullet.w = 10;
posBullet.h = 15;*/
}

void theBullet::bullet_movement()
{
/*if(keystate[SDL_SCANCODE_SPACE])
{
    posBullet.x = posPlayer.x + 25;
    posBullet.y = posPlayer.y + 10;
}

posBullet.y -= 2;

if(posBullet.y < 0)
{
    posBullet.y = -50;

}*/
}

void theBullet::show_bullet()
{
 SDL_BlitSurface(Bullet, NULL, ScreenSurface, &posBullet);
}

//-----------------------------

theEnemy::theEnemy()
{
 x_Enemy = 1;
 y_Enemy = -300;
}

void theEnemy::enemy_movement()
{
 for (int i=0; i<MAX_ENEMYS; i++)
 {
  if (arrayofEnemy[i].EnemyisActive == true)
  {
   arrayofEnemy[i].y_Enemy += 1;

   if (arrayofBullets[i].y_position > 600)
   {
    arrayofEnemy[i].EnemyisActive = false;
   }

  }
 }
}

void theEnemy::show_enemy()
{
  for (int i=0; i<MAX_ENEMYS; i++)
    {
     if (arrayofEnemy[i].EnemyisActive == true)
     {
      SDL_Rect dstrect2;
      dstrect2.x = arrayofEnemy[i].x_Enemy;
      dstrect2.y = arrayofEnemy[i].y_Enemy;
      dstrect2.w = Bullet->w;
      dstrect2.h = Bullet->h;
      SDL_BlitSurface(Enemy, NULL, ScreenSurface, &dstrect2);
      SDL_SetColorKey(Enemy, SDL_TRUE, SDL_MapRGB(Enemy->format, 255, 0, 255));
     }
    }
}

//-----------------------------

thePlayer::thePlayer()
{
    posPlayer.x = 400;
    posPlayer.y = 300;
    posPlayer.w = 20;
    posPlayer.h = 30;

}

void thePlayer::player_movement()
{
 while (SDL_PollEvent(&event))
 {
  switch(event.type)
  {
   case SDL_QUIT:
   quit= true;
   break;
  }

  if(event.type == SDL_MOUSEMOTION)
  {
   posPlayer.x = event.motion.x;
   posPlayer.y = event.motion.y;
  }

  if(event.type == SDL_MOUSEBUTTONDOWN)
  {
   for (int i=0; i<MAX_BULLETS; i++)
   {
    if (arrayofBullets[i].isActive == false)
    {
     arrayofBullets[i].x_position = posPlayer.x + 25;
     arrayofBullets[i].y_position = posPlayer.y + 10;
     arrayofBullets[i].isActive = true;
     break;
    }
   }
  }

 }//end of poll_event
}

void thePlayer::show_player()
{
 SDL_BlitSurface( Player, NULL, ScreenSurface,&posPlayer);
 SDL_SetColorKey(Player, SDL_TRUE, SDL_MapRGB(Player->format, 255, 0, 255));
}

//-----------------------------



int main(int argc, char** argv)
{
 SDL_Init(SDL_INIT_VIDEO);

 SDL_Window * window = SDL_CreateWindow("SDL TestGame",
 SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,   800, 600, 0);

 ScreenSurface = SDL_GetWindowSurface(window);

 Background= SDL_LoadBMP("background.bmp");
 Player = SDL_LoadBMP("ship.bmp");
 Bullet = SDL_LoadBMP("shot.bmp");
 Enemy = SDL_LoadBMP("enemy.bmp");

 thePlayer myPlayer;
 theEnemy  myEnemy;

 srand(time(NULL));
 //makes all bullets false
 for (int i=0; i<MAX_BULLETS; i++)
 {
  arrayofBullets[i].isActive = false;
 }

//create random pos and active the enemy
 for (int i=0; i<MAX_ENEMYS; i++)
 {
  arrayofEnemy[i].x_Enemy += rand()%800+1;
  arrayofEnemy[i].y_Enemy += rand()%200+27;
  arrayofEnemy[i].EnemyisActive = true;
 }


 //GAME LOOP
 while (quit == false)
 {
  SDL_BlitSurface(Background, NULL, ScreenSurface, NULL);

  myPlayer.show_player();
  myPlayer.player_movement();

  //draw the enemy ..
  myEnemy.show_enemy();
  myEnemy.enemy_movement();

    //update game objects
    for (int i=0; i<MAX_BULLETS; i++)
    {
     if (arrayofBullets[i].isActive == true)
     {
      arrayofBullets[i].y_position -= 2;
      if (arrayofBullets[i].y_position < 0)
      {
       arrayofBullets[i].isActive = false;
      }
     }
    }

    for (int i=0; i<MAX_BULLETS; i++)
    {
     if (arrayofBullets[i].isActive == true)
     {

      dstrect.x = arrayofBullets[i].x_position;
      dstrect.y = arrayofBullets[i].y_position;
      dstrect.w = Bullet->w;
      dstrect.h = Bullet->h;
      SDL_BlitSurface(Bullet, NULL, ScreenSurface, &dstrect);
     }
    }

   for (int i=0; i<MAX_ENEMYS; i++)
    {
     if (arrayofEnemy[i].EnemyisActive == true)
     {
      if ((((arrayofBullets[i].x_position + bullet_width >= arrayofEnemy[i].x_Enemy) && (arrayofBullets[i].x_position + bullet_width <= arrayofEnemy[i].x_Enemy + enemy_width)) ||

            ((arrayofEnemy[i].x_Enemy + enemy_width >= arrayofBullets[i].x_position) && (arrayofEnemy[i].x_Enemy + enemy_width <= arrayofBullets[i].x_position + bullet_width)))

      && (((arrayofBullets[i].y_position + bullet_height >= arrayofEnemy[i].y_Enemy) && (arrayofBullets[i].y_position + bullet_height <= arrayofEnemy[i].y_Enemy + enemy_height)) ||

           ((arrayofEnemy[i].y_Enemy + enemy_height >= arrayofBullets[i].y_position) && (arrayofEnemy[i].y_Enemy + enemy_height <= arrayofBullets[i].y_position + bullet_height))))

     {
      arrayofBullets[i].isActive = false;
      arrayofEnemy[i].EnemyisActive = false;
     }
    }
   }


  SDL_UpdateWindowSurface(window);
 }

 SDL_DestroyWindow(window);
 SDL_FreeSurface(Background);
 SDL_FreeSurface(Player);
 SDL_FreeSurface(Bullet);
 SDL_FreeSurface(Enemy);
}


für die Kollision benutze ich ein codeschnippsel aus 3D Gamestudio/Aum82 (workshop)
und habe ihn verändert

so sieht der Schnippsel aus:

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
      if ((((enemy_bullets[i].pos_x + ebullet_width >= ship_pan.pos_x) && (enemy_bullets[i].pos_x + ebullet_width <= ship_pan.pos_x + player_width)) ||

                       ((ship_pan.pos_x + player_width >= enemy_bullets[i].pos_x) && (ship_pan.pos_x + player_width <= enemy_bullets[i].pos_x + ebullet_width)))

                       && (((enemy_bullets[i].pos_y + ebullet_height >= ship_pan.pos_y) && (enemy_bullets[i].pos_y + ebullet_height <= ship_pan.pos_y + player_height)) ||

                       ((ship_pan.pos_y + player_height >= enemy_bullets[i].pos_y) && (ship_pan.pos_y + player_height <= enemy_bullets[i].pos_y + ebullet_height))))

               {

                       players_health -= 10;

                       snd_play(playerhit_wav, 90, 0);

                       ship_pan.bmap = shiphit_tga;

                       reset(enemy_bullets[i], VISIBLE);

                       wait (3);

                       ship_pan.bmap = ship_tga;

                       break; // get out of the while loop

               }

               wait (1);

       }

       enemy_bullets[i] = NULL;

}


Was mache ich falsch ?

so siehts momentan aus


(Link)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Patty1991« (23.11.2014, 12:19)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

23.11.2014, 12:26

Du machst diverse Dinge falsch, wenn Du so fragst.
1) Du verwendest Arrays und durchläufst die komplett statt einen std::vector zu benutzen und nur so viele Elemente zu verarbeiten, wie überhaupt da sind.
2) Deine Kollisionsbedingung erscheint mir reichlich unlogisch oder übervölkert.
3) SDL hat mit Sicherheit eine Methode, die berechnet, ob sich zwei Rechtecke überschneiden. Nutze die.
4) Deine Benennung von Variablen folgt keinem sinnvollen Schema. Mal beginnen neue Worte mit einem Großbuchstaben und mal nicht. "arrayofBullets" vs. "arrayOfBullets".
5) Einem "Enemy" die Eigenschaft "enemyisActive" zu geben ist Quatsch. "isActive" würde völlig reichen. Man weiß doch schon, dass man den Enemy meint.
6) Du verwendest Properties in Klassen, die da scheinbar nur zur Datenhaltung existieren, aber nicht zum Zweck der eigentlichen Klasse beitragen. Z.B. "enemyisActive". Wenn diese nicht zur Logik der Klasse selbst beiträgt, gehört sie dort nicht hin. Stattdessen fummelst Du auf der Eigenschaft von außen herum. Das ist kein guter Stil. Entferne solche Eigenschaften und verwalte sie dort, wie sie benötigt werden. Mit std::vector wäre sie übrigens sogar komplett unnötig. Was uns zu 7. führt:
7) Verwende statt öffentlich zugänglicher Eigenschaften Methoden, die das tun, was Du willst. Lass nicht einfach irgendwen an den Eigenschaften der Klasse herumfuhrwerken.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

3

23.11.2014, 13:24

Ganz schön viel Code mit nicht gerade ausreichender Beschreibung, um das Problem schnell zu finden. Hast du schon einmal versucht, das Problem etwas einzukreisen?
Ansonsten zu Blue's 3. Punkt:
Auf der SDL Wiki Seite zu SDL_Rect gibt es dazu einige Funktionen:
https://wiki.libsdl.org/SDL_HasIntersection
https://wiki.libsdl.org/SDL_IntersectRect
Die sollten soweit selbsterklärend sein. :)
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

4

23.11.2014, 13:27

:hmm:

tja das sagt schon einiges aus aber...


groß und kleinschreibung nicht beachtet ? da hätte mein Compiler aber gemeckert (4)

und (6) sagt mir irgendwie garnichts (fachchinesisch) -> was meinst du damit?
Du verwendest Properties in Klassen, die da scheinbar nur zur Datenhaltung existieren, aber nicht zum Zweck der eigentlichen Klasse beitragen. Z.B. "enemyisActive". Wenn diese nicht zur Logik der Klasse selbst beiträgt, gehört sie dort nicht hin. Stattdessen fummelst Du auf der Eigenschaft von außen herum. Das ist kein guter Stil. Entferne solche Eigenschaften und verwalte sie dort, wie sie benötigt werden. Mit std::vector wäre sie übrigens sogar komplett unnötig. Was uns zu 7. führt:
7) Verwende statt öffentlich zugänglicher Eigenschaften Methoden, die das tun, was Du willst. Lass nicht einfach irgendwen an den Eigenschaften der Klasse herumfuhrwerken.


ich bin halt wie gesagt neuling - bitte nicht aufregen 8o
(hab programmieren weder in der schule gehabt noch studiert) -> alles irgendwie versucht selbst beizubringen

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

5

23.11.2014, 15:31

groß und kleinschreibung nicht beachtet ? da hätte mein Compiler aber gemeckert (4)
Nein, weil Du sie wenigstens konsequent so schreibst. Schön ist es dennoch nicht, dass manche Anfangsbuchstaben bei zusammengesetzten Wörtern klein und manche groß sind. Wie gesagt: "arrayOfEnemy" statt "arrayofEnemy".
Generell ist so ein Variablen-Name übrigens auch unschön. Man sollte anhand des Namens schon wissen, dass es nicht ein einzelner Enemy ist, sondern mehrere. Z.B. durch "enemies" als Name. Den Variablen-Typ da nochmal doppelt aufzuführen fällt unter "Code smell", ist also müffelnder Code.

bitte nicht aufregen
Macht gar keiner. Ich habe lediglich aufgezählt, was mir auffällt.
Zu 6 und 7 schreibe ich nicht mehr als ohnehin schon da steht. Nochmal lesen.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

FSA

Community-Fossil

  • Private Nachricht senden

6

23.11.2014, 16:25

Nein, weil Du sie wenigstens konsequent so schreibst. Schön ist es dennoch nicht, dass manche Anfangsbuchstaben bei zusammengesetzten Wörtern klein und manche groß sind. Wie gesagt: "arrayOfEnemy" statt "arrayofEnemy".


Da kann ich nur nochmal LetsGo zitieren:

Zitat von »LetsGo«

Der RCCSWU (RandomCamelCaseSomtimesWithUndersquare) Stil bricht auch mal mit den veralteten strukturieren Denkmustern und erlaubt dem Entwickler seine Kreativität zu entfalten.

:D

Zitat

Der RCCSWU (RandomCamelCaseSomtimesWithUndersquare) Stil bricht auch mal mit den veraltet strukturierten Denkmustern und erlaubt dem Entwickler seine Kreativität zu entfalten.

7

23.11.2014, 17:57

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
for (int i=0; i<MAX_BULLETS; i++)
    {
     if (arrayofBullets[i].isActive == true)
     {

      dstrect.x = arrayofBullets[i].x_position;
      dstrect.y = arrayofBullets[i].y_position;
      dstrect.w = Bullet->w;
      dstrect.h = Bullet->h;
      SDL_BlitSurface(Bullet, NULL, ScreenSurface, &dstrect);
     }
    }

   for (int i=0; i<MAX_ENEMYS; i++)
    {
     if (arrayofEnemy[i].EnemyisActive == true)
     {
      if ((((arrayofBullets[i].x_position + bullet_width >= arrayofEnemy[i].x_Enemy) && (arrayofBullets[i].x_position + bullet_width <= arrayofEnemy[i].x_Enemy + enemy_width)) ||

            ((arrayofEnemy[i].x_Enemy + enemy_width >= arrayofBullets[i].x_position) && (arrayofEnemy[i].x_Enemy + enemy_width <= arrayofBullets[i].x_position + bullet_width)))

      && (((arrayofBullets[i].y_position + bullet_height >= arrayofEnemy[i].y_Enemy) && (arrayofBullets[i].y_position + bullet_height <= arrayofEnemy[i].y_Enemy + enemy_height)) ||

           ((arrayofEnemy[i].y_Enemy + enemy_height >= arrayofBullets[i].y_position) && (arrayofEnemy[i].y_Enemy + enemy_height <= arrayofBullets[i].y_position + bullet_height))))

     {
      arrayofBullets[i].isActive = false;
      arrayofEnemy[i].EnemyisActive = false;
     }
    }


Also wenn ich das richtig verstehe willst du hier deine Schüsse und deine Gegner durchlaufen und dabei auf Kollisionen prüfen. Schau dir nochmal genau an was dort passiert.
Die beiden For-Schleifen sind unabhängig voneinander. Interessant ist hier besonders die zweite. Was du hier machst: Du durchläufst mithilfe der Zählervariable i dein Gegner-Array. Ist dieser Gegner aktiv bzw. am Leben wird er auf Kollision mit einer Kugel geprüft. Hier ist der Knackpunkt! Du verwendest hier die gleiche Zählervariable, d.h. du prüfst (vorausgesetzt alle Gegner sind am Leben) nur Gegner 1 auf Kollision mit Kugel 1, Gegner 2 auf Kollision mit Kugel 2, usw... ;)
Deshalb trifft eine Kugel eben nur sporadisch einen Gegner, wenn eben genau der Index des Gegners dem Index der Kugel entspricht.

8

29.11.2014, 14:19

@SUCKER <- Danke für den tipp !!!

ich hab mir ne ganze woche fast nen kopf zerbrochen wie ich das bewerkstelligen kann :D

hier meine lösung:

- zunächst alle arrays des bullets auf i2 stellen (überall)
- dann oben int i2; unter der header


C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int i2;

...
  for (int i=0; i<MAX_ENEMYS; i++)
    {

      if ((((arrayofBullets[i2].x_position + bullet_width >= arrayofEnemy[i].x_Enemy) && (arrayofBullets[i2].x_position + bullet_width <= arrayofEnemy[i].x_Enemy + enemy_width)) ||

            ((arrayofEnemy[i].x_Enemy + enemy_width >= arrayofBullets[i2].x_position) && (arrayofEnemy[i].x_Enemy + enemy_width <= arrayofBullets[i2].x_position + bullet_width)))

      && (((arrayofBullets[i2].y_position + bullet_height >= arrayofEnemy[i].y_Enemy) && (arrayofBullets[i2].y_position + bullet_height <= arrayofEnemy[i].y_Enemy + enemy_height)) ||

           ((arrayofEnemy[i].y_Enemy + enemy_height >= arrayofBullets[i2].y_position) && (arrayofEnemy[i].y_Enemy + enemy_height <= arrayofBullets[i2].y_position + bullet_height))))

     {
      arrayofBullets[i2].isActive = false;
      arrayofEnemy[i].EnemyisActive = false;
     }

   }


funktioniert schonmal deutlich besser als vorher - direkt beim 1 schuss ;)
aber ab und an muss ich noch mehrmals schießen ... ich schau mal weiter

Danke nochmals

9

29.11.2014, 19:40

Aus deinem Codeschnipsel kann ich jetzt nicht super viel deuten, aber ich glaube dass du auf dem richtigen Weg bist. Das grundsätzliche Vorgehenen ist jeden Schuss mit jeden Gegner auf Kollision zu prüfen also in Pseudocode etwa so:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for(int i=0; i!=MAX_BULLETS;i++)
{
     if bullets[i].active == true
     {
          for(int j=0; j!=MAX_ENEMIES; j++)
          {
                if enemies[j].active == true
                {
                       if collision(bullets[i], enemies[j])
                       {
                               bullets[i].active = enemies[j] = false;
                               // break;            // Je nach Anwendungsfall...
                       }
                }
          }
     }
}


Ich prüfe JEDE Kugel auf Kollision mit JEDEM Gegner indem ich zwei for-Schleifen verschachtele. Natürlich brauche ich zwei verschiedene Indizes dafür. Z.B. i für Kugeln, j für Gegner. Bei einer Kollision werden beide Elemente auf inaktiv gesetzt, so dass sie beim nächsten Prüfen nicht beachtet werden. Je nach Anwendung kann es sinnvoll sein, bei einer Kollision die zweite Schleife zu unterbrechen (in c++ break).
Wenn du das nicht machst, dann würde das bedeuten, dass wenn eine Kugel mit einem Gegner kollidiert, bevor diese inaktiv wird, noch geprüft wird, ob eventuell noch andere Gegner mit diesem Schuss kollidieren - du also mehrere Gegner mit einem Schuss treffen kannst.

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Sucker« (29.11.2014, 19:48)


Werbeanzeige