2017-09-06 5 views
-3

Ich habe ein ernstes Problem mit meinem SFML-Spiel. Ich habe den ganzen Tag versucht, eine Lösung zu finden, versuchte verschiedene Dinge, aber nichts funktionierte für mich bisher. Das sind meine H-Dateien: Bullet.hVector stürzt mein Programm

#pragma once 
#include <SFML\Graphics.hpp> 
#include <iostream> 
#include <vector> 

class Bullet 
{ 
    friend class Player; 
    friend class Game; 
    float width; 
    float height; 
    float x; 
    float y; 
    std::vector<Bullet*> projectiles; 
    sf::RectangleShape bullet; 
    void draw_projectiles(sf::RenderWindow &window); 
    void make_projectiles(); 

public: 
    void check(); 
    Bullet(); 
    ~Bullet(); 
}; 

Game.h

#pragma once 
#include <SFML\Graphics.hpp> 
#include "Player.h" 
#include "Bullet.h" 
#include <vector> 
//#include "Enemy.h" 

class Game 
{ 
    friend class Player; 
    sf::RenderWindow* window; 
    sf::Event* evnt; 
    Player* player; 
    Bullet* bullet; 

public: 
    void Loop(); 
    void game_func(); 
    Game(); 
    ~Game(); 
}; 

Player.h

#pragma once 
#include <SFML\Graphics.hpp> 
#include <iostream> 
#include "Game.h" 
#include "Bullet.h" 

class Player 
{ 
    sf::RectangleShape player; 
    Bullet* bullet; 
    int ammo; 
    float width; 
    float height; 
    int x; 
    int y; 
    float vel; 

public: 
    void draw(sf::RenderWindow &window); 
    void move(sf::Event &evnt, sf::RenderWindow &window); 
    Player(); 
    ~Player(); 
}; 

Hier kommen CPP-Dateien Bullet.cpp

#include "Bullet.h" 

void Bullet::check() 
{ 
    x = bullet.getPosition().x; 
    y = bullet.getPosition().y; 
} 

void Bullet::draw_projectiles(sf::RenderWindow &window) 
{ 

    for (int i = 0; i < 10; i++) 
    { 
     window.draw(projectiles[i]->bullet); 
    } 
} 

void Bullet::make_projectiles() 
{ 
    projectiles.push_back(new Bullet()); 
} 

Bullet::Bullet() 
{ 
    std::cout << "zostal utworzony nowy obiekt" << std::endl; 
    width = 50; 
    height = 50; 
    bullet = sf::RectangleShape(sf::Vector2f(width, height)); 
    bullet.setFillColor(sf::Color::Yellow); 
    bullet.setPosition(0, 0); 
    x = bullet.getPosition().x; 
    y = bullet.getPosition().y; 
} 

Bullet::~Bullet(){} 

Game.cpp

#include "Game.h" 

Game::Game() 
{ 
    window= new sf::RenderWindow(sf::VideoMode(1280, 720), "SFML Game",    
    sf::Style::Close); 
    player = new Player(); 
} 

Game::~Game(){} 

void Game::Loop() 
{ 
    while (window->isOpen()) 
    { 
     sf::Event evnt; 
     while (window->pollEvent(evnt)) 
     { 
      //events 
      if (evnt.type==sf::Event::Closed) 
       window->close(); 

      player->move(evnt, *window); 
      window->clear(); 
      player->draw(*window); 
      window->display(); 
      bullet->draw_projectiles(*window); 
     } 
    } 
} 

void Game::game_func() 
{ 
    Game::Loop(); 
} 

Player.cpp

#include "Player.h" 

void Player::draw(sf::RenderWindow &window) 
{ 
    window.draw(player); 
} 

void Player::move(sf::Event &evnt, sf::RenderWindow &window) 
{  
    x = player.getPosition().x; 
    y = player.getPosition().y; 
    float width = window.getSize().x; 
    float height = window.getSize().y; 
    Bullet obj; 

    if (evnt.type == sf::Event::KeyPressed) 
    { 
     //movement 
     if (evnt.key.code == sf::Keyboard::Key::W) 
     { 
      if (y <= 0) 
      { 
       return; 
      } 
      player.move(0, -1 * vel); 
     } 

     if (evnt.key.code == sf::Keyboard::Key::S) 
     { 
      if (y >= height - Player::height) 
      { 
       return; 
      } 
      player.move(0, 1 * vel); 
     } 

     if (evnt.key.code == sf::Keyboard::Key::A) 
     { 
      if (x <= 0) 
      { 
       return; 
      } 
      player.move(-1 * vel, 0); 
     } 
     if (evnt.key.code == sf::Keyboard::D) 
     { 
      if(x>width-Player::width) 
      { 
       return; 
      } 
      player.move(1 * vel, 0); 
     } 
     if (evnt.key.code == sf::Keyboard::Space) 
     { 
      obj.make_projectiles(); 
     } 
    } 
} 

Player::Player() 
{ 
    width = 100; 
    height = 100; 
    vel = 10; 
    player = sf::RectangleShape(sf::Vector2f(width, height)); 
    player.setFillColor(sf::Color::Red); 
    player.setPosition(sf::Vector2f(15, 20)); 
} 

Player::~Player(){} 

Und main.cpp

#include <SFML/Graphics.hpp> 
#include <iostream> 
#include <cstdlib> 
#include <ctime> 
#include <vector> 
#include "Game.h" 

int main() 
{ 
    Game gme; 
    gme.game_func(); 
    return 0; 
} 

Ich habe viele verschiedene Dinge ausprobiert und kann nicht herausfinden, warum es nicht funktioniert. Im Laufen in auf Visual Studio 15. Hier ist also Fehler, die ich bekomme:

Exception thrown: read access violation. 

std::_Vector_alloc<std::_Vec_base_types<Bullet *,std::allocator<Bullet *> > 
>::_Mylast(...) returned 0x18. 

Ich bin mir bewusst, dass Code nicht perfekt und wenig chaotisch, aber ich bin nur ein begginer und versuchen, Neues zu lernen Sachen. Ich werde jede Hilfe zu schätzen wissen!

+6

Bitte lernen Sie Ihren Code einzurücken. Es ist fast unlesbar – Justin

+1

Sie verletzen die Regel 3/5/0.Sie verlieren Speicher. Verwenden Sie std :: unique_ptr anstelle von rohen Zeigern zum Besitz von Zeigern. –

+0

Ich nehme an, Sie verwenden Einrückung in Ihrer IDE. Wählen Sie Ihren Code aus und verwenden Sie die Option {} im Editor, nachdem Sie den Code aus Ihrer IDE eingefügt haben. Es sollte den Einzug behalten. – drescherjm

Antwort

2

ich Ihre Frage in meinen letzten Absätzen beantworten, können Sie überspringen zu Dieser Absatz, aber ich nehme an, Sie sehen sich all dies an. Zunächst solltest du verstehen, wie ein einfaches Spiel im Code aussehen sollte.

The Game Logic

Sie die Spiellogik in zwei Hauptfunktionen trennen kann. Die Initialisierung und die Schleife.

Initialisierung

In der Initialisierungsfunktion, laden Sie im Grunde alles, was für das Spiel benötigt (also nur für kleine Spiele zur Verfügung zu laufen, da möglicherweise nicht die beste Lösung, zig Konzerte von Sprites in dem Speicher zu laden für größere. Mit der Zeit werden Sie den richtigen Zeitpunkt zum Laden und Freigeben von Ressourcen herausfinden).

Die Schleife

Dies ist die oder die Spielschleife Hauptschleife aufgerufen wird. Diese Schleife sollte 3 Hauptfunktionen ausführen. Handle Benutzereingabe, Welt zu aktualisieren und die Welt. Diese Schleife sollte ausgeführt werden, während das Spiel läuft (dh, während das Fenster geöffnet ist)

So Ihre Haupt in pseudo-C++ wie folgt aussehen sollte:

Init(); 
while (window.isOpen()) 
{ 
    HandleEvents(window); //user input 
    Update(elapsedTime); 
    Render(window); 
} 

Ich werde erklären, was die Funktionen tun, was die Argumente bedeuten und wie diese Funktionen Ihrem Code zugeordnet sind. Beachten Sie, dass jede Funktion nur eine bestimmte Aufgabe hat. Ich werde nicht prüfen, ob der Benutzer eine Taste drückt, während ich die Sprites auf dem Bildschirm zeichne.

Benutzereingabe

Alles aus Knopfdruck und Mausklick die Exit-Taste zum Drücken und Ändern der Größe des Fensters Benutzereingabe aufgerufen wird. Benutzeraktionen erzeugen die sogenannten Ereignisse, die wir am Anfang jeder Schleife behandeln. Jetzt sind diese Ereignisse fensterspezifisch (Sie können den Player nicht steuern, wenn das Fenster minimiert oder nicht fokussiert ist). Das bedeutet, dass das Fenster die Ereignisse generiert (wenn ich mich technisch irren ließe, bitte korrigiert mich). Dies ist der Grund, dass Sie, wenn Sie Ereignisse behandeln, das Fenster passieren müssen.

Event

Bevor die Behandlung von Ereignissen, müssen Sie verstehen, wie sf :: Ereignis (siehe mehr auf der Seite sfml) hergestellt wird. Lange Rede, kurzer Sinn: Das sf :: Event ist eine Union (nur ein Feld ist gleichzeitig gültig). Das heißt, wenn Sie versuchen, auf zugreifen, wenn die window.pollEvent() zurückgegeben sf::Event::JoystickEvent erhalten Sie ein undefiniertes Verhalten (Ich lebte ein langes glückliches Leben ohne zu wissen, was Gewerkschaften sind, nie benutzt und wahrscheinlich nie, aber sie sind ein ziemlich interessantes Konzept, dass Lohnt sich zumindest zu lesen). Ok, ein Ereignisobjekt wird erstellt, indem window.pollEvent() aufgerufen und eine sf :: Event-Instanz an es übergeben wird. Diese Funktion gibt Ihnen Ereignisse aus der Warteschlange, bis keine weiteren Ereignisse mehr ausgegeben werden. In diesem Sinne würde aussehen, Ihre Event-Handling-Code so etwas wie:

sf::Event ev; 
while (window.pollEvent(ev)) 
{ 
    switch (ev.type) 
    { 
     //code for each type needed by your application 
    } 
} 

Denken Sie daran, dass wichtige Ereignisse behandeln keine Echtzeit-Eingang (sf::Keyboard::isKeyPressed das tut). Das heißt, wenn Sie möchten, dass sich Ihr Charakter bewegt, wenn Sie eine Taste gedrückt halten, führt die Behandlung nach Ereignissen zu einer Verzögerung, die am besten durch die Art der Eingabe erklärt wird (wenn Sie zum Beispiel das erste Zeichen gedrückt halten) sofort geschrieben, der Rest der Eingabe wird vor der Registrierung um eine Sekunde verzögert). Dies ist eine Art, es zu erklären, aber vielleicht nicht die technischste (ich bitte um ein wenig Hilfe :)). Wie auch immer, dieses Problem kann entweder durch Verwendung der statischen Methoden von sf :: Keyboard gelöst werden, oder durch Einhalten einer bool in Ihrer Player-Klasse, die auf die Ereignisse KeyPressed und KeyReleased antwortet (das Update wird basierend auf diesem Bool gehandhabt).

Welt aktualisieren

Hier ist Ihre Logik-Code (obwohl Spielerbewegung auch im Veranstaltungs Abschnitt behandelt werden, da es auf ihnen basiert).Hier aktualisierst du deine Entitäten (bewege den Feind einen weiteren Block basierend auf seiner KI), bewege die Sonne um die Karte usw. Denk daran, dass dies nichts mit dem Zeichenteil zu tun hat, in diesem Abschnitt änderst du nur den Zustand deines Objekte. In Ihrem Spiel bedeutet dies, nachdem Sie ein projektives Ereignis durch einen vom Benutzer ausgelösten Ereignis ausgelöst haben, bewegen Sie jeden einzelnen Rahmen das Projektil. Dieser Code erfordert normalerweise eine Art von Frame-Counting-Methode.

Frames

Ein Rahmen ist eine Iteration der Schleife, kann man sagen, dass die Spiele-Updates und zieht sich jeden Rahmen. Frames sind ein sehr wichtiges Konzept, weil sie einige Probleme ergeben. Wenn das Spiel sich selbst jeden Frame aktualisiert, bedeutet das, dass jeder Frame das Projektil bewegt, so dass seine Bewegung abhängig ist von der FPS die dein PC ausführen kann. Dies ist ein Problem, denn während Ihr Spiel auf Ihrem PC mit einer stabilen 60 FPS-Rate laufen kann, läuft es bei 53 oder einem anderen zufälligen Wert. Das bedeutet, dass sich die Projektile auf meinem PC langsamer bewegen, und das wollen wir nicht.

Rahmen unabhängige Bewegung

Dies wird durch Zählen der Frames erreicht werden kann. Eine Möglichkeit besteht darin, die Sekunden zu zählen, die seit dem letzten Frame verstrichen sind. In diesem Fall können Sie die Menge an Speicherplatz ermitteln, die Ihr Element benötigt, um sich in diesem bestimmten Frame zu bewegen. Zum Beispiel möchten Sie Ihr Projektil mit 100px/sec bewegen. Wenn Sie 2FPS haben, bedeutet das, dass in 2 Bildern 100 Pixel verschoben werden müssen, also bewegt sich jedes Bild um 100/2 Pixel. Also ist die Formel finalDistance/framerate. Es gibt mehr Möglichkeiten, dies zu tun, aber meiner Meinung nach ist dies zu Beginn am einfachsten zu verstehen. Wie wird das in SFML implementiert? Sie halten grundsätzlich eine Uhr, die Sie am Ende jedes Updates neu starten. getElapsedTime und Neustart tut das, aber Neustart gibt die elapsedTime zurück, so dass es besser ist, es einmal aufzurufen, da das Aufrufen von ihnen nacheinander zu unterschiedlichen Zeiten und Desyncs führen kann.

sf::Clock clock; 
while (window.isOpen()) 
{ 
    HandleEvents(window); 
    Update(clock.restart()); 
    Render(window); 
} 

Und Sie einfach Ihre Entitäten mit move(vector * clock.getElapsedTime().asSeconds()) bewegen, da sf::Vector hat operator* für Schwimmer überlastet (der Rückgabetyp asSeconds()).

Rendering

Das Rendering Teil sein kann sehr kompliziert, aber sfml macht es "einfach und schnell". Im Grunde funktioniert es so: Sie löschen den Bildschirm, Sie zeichnen Ihre Entitäten, Sie zeigen den Bildschirm an. Die technische Antwort lautet wie folgt: Das Fenster besteht aus 2 Puffern, einer sichtbaren und einer versteckten. Der sichtbare ist der, den Sie auf dem Bildschirm sehen. Wenn Sie clear() anrufen, löschen Sie im Wesentlichen die versteckte, draw() zeichnet auch auf dem versteckten Fenster, und schließlich display() tauscht die Puffer. Das bedeutet, dass Sie keine Ergebnisse sehen werden, es sei denn, Sie rufen window.display(), und Sie erhalten ein Fenster xp Erfahrung, wenn Sie vor dem Zeichnen nicht clear() aufrufen. So ist die Render-Funktion könnte wie folgt aussehen:

window.clear(); 
window.draw(player); //or player.draw(window) based on your implementation 
//other draws 
window.display(); 

Ihre Frage

Was im Code passiert, ist, dass Sie versuchen, die Dinge zugreifen, die nicht existieren. Sie fügen ein Projektil auf einmal hinzu, aber jedes Bild, von dem Sie 10 zeichnen.

Die Lösung

einen Zähler Ihrer Objekte halten. Da Sie einen Vektor verwenden, der bereits vorhanden ist, haben Sie std::vector::size das genau wieder, was Sie erwarten, so dass Ihr Code in so etwas wie verwandeln:

for (int i = 0; i < yourProjectiles.size(); i++) 
{ 
    window.draw(yourProjectiles[i]->bullet); 
} 

Alternativ können Sie verwenden Iteratoren (schauen sie nach oben):

for (auto it = yourProjectiles.begin(); it != yourProjectiles.end(); ++it) 
{ 
    window.draw(it->bullet); 
} 

Speicherverwaltung

Sie ausplanen nicht Gedächtnis. Sie müssen in die dynamische Speicherzuweisung schauen. Das Grundprinzip ist, dass für jedes neue ein Löschen vorhanden sein sollte. Der Deallokationsteil sollte die meiste Zeit im Destruktor der Klasse behandelt werden. Ich denke, jemand könnte vorschlagen, Smartpointer zu verwenden (std::shared_ptr), also verwalten Sie Ihr Gedächtnis, aber ich kann Ihnen das nicht empfehlen, da Sie am Anfang sind. Intelligente Zeiger sind ein Konzept, das Sie im Auge behalten sollten, aber während Sie angefangen haben, ist es besser, sich den Schwierigkeiten des manuellen Speichermanagements zu stellen (bis Sie sich daran gewöhnt haben).

-Code

Eine Klasse der Organisation sollte nur zu einem Zweck hergestellt werden. Wenn Sie eine Klasse mit dem Namen Bullet erstellen, wird erwartet, dass diese Bullet ein Projektil in Ihrem Spiel darstellt, aber wenn Ihr Bullet Projektile erstellt und Projektile speichert, wird es zu einer paranormalen Entität. Ihr bullet atm enthält Zeiger auf Instanzen anderer Aufzählungszeichen, die Zeiger auf Instanzen anderer Aufzählungszeichen enthalten. Das ist eine totale Sauerei. Wenn Sie nicht ein Diagramm oder eine Baumstruktur erstellen möchten, haben Sie keinen Grund, Zeiger von Instanzen derselben Klasse zu speichern.

Zu viele Freunde

Wenn jede Klasse Freund mit jeder Klasse ist, was ist Ihr Grund private Bereiche zu schaffen? Freund ist ein sehr starkes Konzept und sollte mit Vorsicht verwendet werden, nur in Fällen, in denen Sie keine anderen Optionen haben. Der einzige Grund, warum ich dieses Schlüsselwort vermeiden würde, ist die Unordnung, die es verursacht. Es erzeugt den gleichen Effekt wie öffentliche Attribute. Wenn alles von überall her zugänglich ist, kann alles von überall her zerstört werden. Wenn Sie eine kleine Gruppe von Methoden erstellen, die Ihre Attribute manipulieren, wissen Sie, wo das Problem liegt.

Fazit

Ich schlage vor, vielleicht ein wenig mehr in c suchen ++ und nach dem Debug-Spiel, oder es von Grund auf neu erstellen. Während ich weiß, wie es sich anfühlt, etwas Neues auszuprobieren, sollte man immer darauf achten, sich nicht in das Bein zu schießen, und keine Angst haben, zu den Grundlagen zurückzukehren, wenn man auf solche Fehler stößt. Sie haben Probleme beim Verwalten des Speichers? Lesen Sie mehr über die dynamische Speicherzuweisung, führen Sie einige Beispielanwendungen aus, die diese verwenden. Außerdem habe ich bemerkt, dass Sie immer noch am Anfang sind, wenn Sie Klassen benutzen. Ich würde sagen, Übung macht den Meister. Schauen Sie sich andere Leute Code an, auch diese 3rd-Party-Bibliotheken wie sfml können Ihnen einige Hinweise auf gute Klassenpraktiken geben. Die gute Sache ist, dass es nicht notwendig ist, den Quellcode dieser Bibliotheken zu betrachten, Sie benutzen einfach ihre Schnittstelle. Wenn Sie es mögen, bedeutet es, dass es gut geschrieben ist und Sie können einen Teil dieses Stils ausleihen und in Ihren Klassen implementieren. Ich schließe das, indem ich sage, dass ich sehr glücklich und eifrig bin, Ihnen per E-Mail zu helfen, wenn Sie irgendeine andere Frage bezüglich irgendetwas haben.

+0

Neugierig falsch positiv. Gibt es viele Links in Ihrer Antwort in voller Länge? – Quentin

+0

Hmm, es gab einen auf der sfml-Seite, aber ich habe ihn entfernt (ohne Erfolg). Die Antwort war jedoch ziemlich lang (vielleicht 50 Zeilen) –

+0

Wie wäre es mit Pastebin und ich versuche es zu bearbeiten? – Quentin

1

Ich glaube, Sie versuchen, zehn Geschosse zuzugreifen:

for (int i = 0; i < 10; i++) 
{ 
    window.draw(projectiles[i]->bullet); 
} 

Aber nur einen nach dem anderen hinzufügen:

projectiles.push_back(new Bullet()); 
+0

Eigentlich ist diese 10 Sache von einem meiner Experimente übrig geblieben, weil ich immer wieder Fehler mit vector.size() bekommen habe. Ich möchte jedes Mal ein neues Objekt von bullet machen, wenn ich auf space drücke. Fügen Sie es zu Vektor hinzu und zeichnen Sie auf jedem Rahmen. – Sniadek

+0

Nun, es wird definitiv einen Absturz verursachen, weil es versucht, auf Elemente zuzugreifen, die nicht existieren. –

+0

Was empfehlen Sie mir? Was sollte ich lesen, um mehr davon zu verstehen? – Sniadek