Projekt 3: "Angry Birds im Terminal"
4. Die Spielschleife konstruieren
Nachdem du in den letzten beiden Teilen gesehen hast, wie die Klassenstruktur entwickelt und das Spielfeld aufgebaut wurde, ist es in diesem letzten Teil Zeit für das Entwickeln des eigentlichen Spiel-Ablaufs.
Genau wie bei der berühmten Vorlage sollen drei Vögel nacheinander auf die Schweine abgeschossen werden können. Außerdem soll die Flugbahn sichtbar gemacht werden, damit beim nächsten Schuss das Zielen leichter fällt. Wurden alle Schweine getroffen (1 Treffer reicht), dann ist das Spiel gewonnen. Sind nach dem Abschuss des letzten Vogels allerdings noch Schweine übrig, dann ist das Spiel verloren.
Winkel in Grad und Geschwindigkeit in m/s eingeben:
// Klasse für das Spielfeld-Objekt class GameArea { public: // Konstruktor inkl. Aufbau des Spielfelds GameArea(int num_rows, int num_cols) { // Spielfeld-Größe festlegen m_num_rows = num_rows; m_num_cols = num_cols; cout << "Spielfeld mit " << num_rows << " Zeilen und " << num_cols << " Spalten\n"; // Bodenebene und Schleuder positionieren m_ground_level = m_num_rows - 1; m_slingshot_pos = 5; // Spielfeld initialisieren m_game_area.resize(m_num_rows,vector<char>(m_num_cols,' ')); // Bodenebene einzeichnen for (int col = 0; col < m_num_cols; ++col) { m_game_area[m_ground_level][col] = '_'; } // Schleuder einzeichnen m_game_area[m_ground_level][m_slingshot_pos] = '|'; m_game_area[m_ground_level - 1][m_slingshot_pos] = '|'; m_game_area[m_ground_level - 2][m_slingshot_pos - 1] = '\\'; m_game_area[m_ground_level - 2][m_slingshot_pos + 1] = '/'; m_game_area[m_ground_level - 3][m_slingshot_pos - 2] = '\\'; m_game_area[m_ground_level - 3][m_slingshot_pos + 2] = '/'; } // Hinzufügen von Spielobjekten in die jeweiligen Listen void AddGameObject(GameObject *object) { // Objekt in passende Liste hinzufügen if (object->m_type == BIRD) m_game_birds.push_back((Bird *)object); else if (object->m_type == PIG) m_game_pigs.push_back((Pig *)object); cout << "Objekt-Typ " << object->m_type << " hinzugefügt (" << m_game_birds.size() << " Vögel, " << m_game_pigs.size() << " Schweine)\n"; // y-Koordinate anpassen, da y=0 am unteren Rand sein soll object->m_position.y = m_ground_level-object->m_position.y; // Objekt zeichnen DrawObject(object, object->m_symbol); } // Objekt mit Symbol in Spielfeld einzeichnen void DrawObject(GameObject *object, char symbol) { // Objekt nur einzeichnen, falls im darstellbaren Bereich int col = round(object->m_position.x); int row = round(object->m_position.y); if (row>=0 && row<m_num_rows && col>=0 && col<m_num_cols) m_game_area[row][col] = symbol; } // Den Inhalt des Spiefelds im Terminal ausgeben void PrintGameArea() { // Spielfeld in Kommandozeile ausgeben for (int y = 0; y < m_num_rows; y++) { for (int x = 0; x < m_num_cols; x++) { cout << m_game_area[y][x]; } cout << endl; } } // Den nächsten Vogel in die Schleuder setzen Bird *GetNextBird() { // Nach noch "unbenutztem" Vogel suchen Bird *next_bird = nullptr; for (Bird *bird : m_game_birds) { if (bird->m_has_been_used == false) { next_bird = bird; // neuer Vogel gefunden next_bird->m_has_been_used = true; break; // Schleife verlassen } } // Vogel in Schleuder positionieren if (next_bird != nullptr) { DrawObject(next_bird, '_'); // Alte Position löschen next_bird->m_position.x = m_slingshot_pos; next_bird->m_position.y = m_ground_level - 2; DrawObject(next_bird, next_bird->m_symbol); cout << next_bird->m_attack_cry << endl; } return next_bird; } // Position und Geschwindigkeit des Vogels aktualisieren void UpdateBird(Bird *bird, double dt) { // Fallgeschwindigkeit aktualisieren double dvg = -9.81 * dt; bird->m_velocity.y += dvg; // Position aktualisieren double dx, dy; dx = bird->m_velocity.x * dt; dy = bird->m_velocity.y * dt; bird->m_position.x += dx; bird->m_position.y -= dy; } // Auf Treffer prüfen und Objektart zurückliefern ObjType HasHit(Bird *bird) { // Prüfen, ob Schwein getroffen int tol = 1; // Treffer-Toleranz for (Pig *pig : m_game_pigs) { if (pig->m_has_been_used == false) { // Auf Treffer in x und y prüfen bool hit_x_l, hit_x_r, hit_y; hit_x_l = round(bird->m_position.x) >= (pig->m_position.x - tol); hit_x_r = round(bird->m_position.x) <= (pig->m_position.x + tol); hit_y = bird->m_position.y >= m_ground_level; // Hat Vogel Schwein getroffen? if (hit_x_l && hit_x_r && hit_y) { // Punkte vergeben cout << pig->m_hit_cry << endl; m_total_score += pig->m_score; // Schwein entfernen pig->m_has_been_used = true; DrawObject(pig, 'x'); return PIG; } } } // Prüfen, ob Boden "getroffen" if (bird->m_position.y >= m_ground_level) return GROUND; else return AIR; } // Auf ungetroffene Schweine prüfen bool PigsLeft() { for (Pig *pig : m_game_pigs) { if (pig->m_has_been_used == false) { return true; } } return false; } // Member-Variablen vector<Bird *> m_game_birds; // Liste aller Vögel vector<Pig *> m_game_pigs; // Liste aller Schweine int m_num_cols; // Feldgröße in x (Zelle = 1m) int m_num_rows; // Feldröße in y vector<vector<char>> m_game_area; // 2D-Spielfeld int m_slingshot_pos; // x-Position der Schleuder int m_ground_level; // Lage der Bodenebene int m_total_score{0}; // Gesamtzahl der erreichten Punkte }; /*******************/ int main() { // Spielobjekte erzeugen Bird b1(0.0, 0.0); Pig p1(30.0, 0.0); // Spielobjekte erzeugen int rows{15}, cols{70}; GameArea area(rows, cols); // Spielwelt bevölkern area.AddGameObject(&p1); area.AddGameObject(&b1); // Ersten Vogel in Schleuder laden Bird *next_bird = area.GetNextBird(); // Schleife über alle Vögel while (next_bird != nullptr) { // Abschusswinkel in Grad abfragen cout << "Bitte Abschusswinkel in Grad eingeben : "; double angle_deg{0.0}; cin >> angle_deg; double angle = angle_deg * M_PI / 180; // Abschussgeschwindigkeit in m/s abfragen cout << "Bitte Abschussgeschwindigkeit in m/s eingeben : "; double speed{0.0}; cin >> speed; // Vogel-Geschwindigkeit in x und y berechnen next_bird->m_velocity.x = speed * cos(angle); next_bird->m_velocity.y = speed * sin(angle); // Zeitdauer pro Animationsschritt double dt = 1 / 25.0; // Sekunden pro Frame // Flugbahn schrittweise berechnen while (area.HasHit(next_bird) == AIR) { // Vogel an alter Position löschen area.DrawObject(next_bird, '.'); // Position aktualisieren area.UpdateBird(next_bird, dt); // Vogel an neuer Position zeichnen area.DrawObject(next_bird, next_bird->m_symbol); } // Spielwelt neu zeichnen area.PrintGameArea(); // Noch Schweine vorhanden? if (area.PigsLeft() == false) { cout << "Du hast GEWONNEN\n"; cout << "Punkte = " << area.m_total_score << endl; break; // Vogelschleife beenden } else { next_bird = area.GetNextBird(); // Neuen Vogel "laden" if(next_bird==nullptr) cout << "Du hast VERLOREN\n"; } } // Ende der Vogelschleife return 0; }