Giter VIP home page Giter VIP logo

koramu's Introduction

Koramu

Project: Story-based 2D-RPG for the P-Seminar at LFG in the 11th & 12th grade
Participants: Tobias Mahler, Roman Mayr, Ario Dastmaltschi
Technology: Built from scratch using C++ & Lua by implementing a simple game engine based on the state machine pattern any by building a tile map using Tiled
Submission: Januar 2018

Screenshots

image image image image image image image

Documentation

Klassendiagramm_all

koramu's People

Contributors

ariogato avatar romman8 avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

koramu's Issues

Eine Log Klasse

Es wäre ganz praktisch wenn wir eine Log Klasse hätten und somit entscheiden könnten wohin unsere Fehlermeldungen transportiert werden.
Mit einer Log Klasse, die im Grunde genommen nur eine oder zwei Methoden hat, die das Geschriebene einem Stream übergeben. (d.h. in eine Datei schreibt oder auf den Bildschirm schmeißt).

Die Klasse an sich ist durchaus einfach zu erstellen.
Einfach nach der Klassenkarte vorgehen und alles wird gut sein.

klassenkarte_log

Die Magie passiert nämlich erst im Konstruktor oder besser gesagt in den Konstruktoren:
Wir brauchen zwei Konstruktoren. Dies bewerkstelligen wir, indem wir den Konstruktor überladen, wobei er einmal keine Parameter entgegennimmt und das zweite Mal einen string mit dem Pfad zur Log-Datei.

  1. In dem Konstruktor ohne Parameter:
  • Es wird einfach die Membervariable outputStream mit std::cout initialisiert. Ja das geht.
  1. In dem Konstruktor mit Parametern:
  • Zuerst soll gecheckt werden, ob die Datei existiert, wenn nicht soll outputStream wieder mit std::cout initialisiert werden. (Nicht vergessen hier auch eine Fehlermeldung auszugeben)
  • Wir müssen die Datei öffnen. Das machen wir mit der Funktion fstream::open.
  1. In den Memberfunktionen müsst ihr einfach so vorgehen wie gehabt bei Ausgaben.
  • outputStream << msg << endl;
  • ErrorLog() und PrintLog() sollen fürs erste noch das gleiche machen.

Anmerkungen:

  1. Wir brauchen in der Datei Logger.h sowohl iostream als auch fstream... also #include <iostream> & #include <fstream>
  2. Im Destruktor soll die Datei mit fstream::close evtl. wieder geschlossen werden.

FontManager extended

Wir haben nun einen wunderbaren FontManager (Ticket #38), der uns einwandfrei einen Text auf den Bildschirm zeichnen kann.

Zwei Kleinigkeiten könnten an ihm jedoch noch verbessert werden:

  1. Was passiert wenn drawCharacter(char character, ...) mit einem character aufgerufen wird, der sich nicht im Alphabet befindet?
  2. Der zweite Aspekt ist zwar durchaus keine Kleinigkeit, aber trotzdem machbar (mit ein Bisschen Arbeit selbstverständlich ;-) )

    Wir wollen eine Funktion im Fontmanager, die ein ObjectRectangle (oder SDL_Rect je nachdem was der Geschmack des Entwicklers ist) entgegennimmt und den text innerhalb der Grenzen dieses Rechtecks rendert.

    Die aktuelle Form der drawText() Funktion rendert den Text zwar, aber würde - falls der Text ein wenig mehr Zeichen enthält - über den Rand des Rechtecks hinausrendern. Das heißt es bedarf eines Algorithmus, der erkennt, wenn ein Wort nicht mehr in die Zeile passt und es folglich in die nächste rendert.

ObjectLayer parsen

Hierzu gehört bestimmt ziemlich viel. Leider habe ich keinen blassen Schimmer was wie gemacht werden muss, deshalb bearbeite ich dieses Ticket.

(Mit 'ObjectLayer' ist dasjenige aus der TMX-Datei gemeint)

Tile Klasse

Obwohl noch nicht zu 100% geklärt ist wie genau wir unsere Map implementieren wollen, können wir schonmal mit der Implementierung einer Klasse (nämlich Tile) beginnen.

Mit der Implementierung soll auch das Klassendiagramm der Klasse erstellt werden.

Folgende Attribute sollte die Klasse haben (alle private):

  • m_tileTextureID : string
  • m_positionVector : Vector2D
  • m_message : string
  • evtl. auch:
    • m_width : int & m_height : int

Folgende Methoden sollten zumindest schon deklariert sein (alle void):

  • load()
  • update()
  • render()
  • getter-Funktionen

#36 Screenshot im PauseState

Hinter den Buttons soll ein ausgegrautes Bild des pausierten Spiels erscheinen.
Für das Ausgrauen ist schon gesorgt, es muss nur noch ein Screenshot gerendert werden.
Dieser soll bei erneutem Pausieren auch aktualisiert werden.

Test Klassen

Vielleicht sollte jemand mal eine Klasse speziell zum Testen erstellen.
Dazu würde ich spontan vorschlagen in der Hauptschleife des Programms eine Methode der Klasse aufzurufen, in der wiederum die eigenenen Testmethoden aufgerufen werden.

Dazu muss die Klasse natürlich erst erstellt werden. Das einzig komplizierte daran ist, die Klasse zum Singleteon zu machen, aber auch das wurde z.B. in Game.h gut dokumentiert.

Fürs erste soll eine Methode void testFunctions() (die wird auch in der Hauptschleife aufgerufen) implementiert werden.

Aufgabe ist also: Singleton-Klasse mit einer Methode. Den Rest (die jetzigen testStuffs in die Klasse zu verschieben) mache ich.

Kommentare auf keinen Fall vergessen!

Z-Order Rendering

Abhängig von Ticket #33

Einführung:
Durch unsere eindeutig hierarchische Render-Reihenfolge (Nach Layer siehe Map::render()) werden einige Tiles über den Player gerendert.

In diesem Fall ist das erwünscht:

behind_log

In diesem Fall ist das unerwünscht:

behind_log

Dieser Effekt zeigt uns auf, dass wir nicht einfach pauschal ein Layer nach dem anderen rendern können! Eine neue, eine intelligentere Renderreihenfolge muss her!

Zu diesem Zwecke bedienen wir uns eines Vorgangs, der sich Z-Ordering nennt.

In Map.cpp muss jedes Objekt, Tile, etc. in einer Liste, basierend auf einem errechneten Z-Wert, sortiert werden.
Diese Liste wird anschließend von vorne bis hinten durchgerendert.

Speichern

Im Pausemenü hat der Benutzer die Option zu speichern ("Save").
Ein Klick darauf soll die Speicherung des aktuellen Stands des Spiels (in einer xml-Datei) bewirken.
Dieser gespeicherte Stand soll das nächste mal, wenn der Spieler auf "Play" drückt geladen werden.
Dies soll ein Fortschreiten in der Story ermöglichen, ohne alles auf einmal durchspielen zu müssen.

Die Callback Funktion, die beim betätigen von "Save" aufgerufen wird, muss folgendes machen:

  1. mainQuest und partQuest sollen vom Story Objekt in TheGame mit entsprechenden Methoden abrufen.
  2. Mithilfe des Tiniyxml Frameworks sollen die beiden Werte irgendwie in einer xml Datei, die zu Beginn einmal manuell erstellt wird, untergebracht werden.
  3. Die Position des Spielers genauso in die xml Datei schreiben
  4. Als Zusatz: Der Save Button sollte nachher nicht mehr betätigbar sein und das grüne Bild (Spalte 3) anzeigen

Außerdem muss an anderen Stellen im Code folgendes gemacht werden

  1. Storyparser: Eine neue Methode erstellen, die den Spielstand wieder abruft
  2. Story::init(): Den Spielstand über den Storyparser einlesen
  3. Player::init(): DiePosition über den Storyparser einlesen

Story

Unser Spiel ist story-basiert, es braucht also überraschenderweise eine Story. Dies muss auch im Code umgesetzt werden. Um eine detaillierte Konzipierung dessen, was genau im Code umgesetzt werden muss, kümmern sich @LemmingFaunTM und @NewLordVile .

Die Implementierung der Story wird bei uns auf dem Konzept eines Zustandsautomaten basieren. Die Zustände heißen hier Quests. Bei den Quests gibt es eine Hauptquest und mehrere dazugehörige Unterquests.

Für die Umsetzung in Code brauchen wir eine Klasse, die aus einer xml Datei alle im Spiel vorhandenen Quests (id) parst und diese speichert. Diese Klasse (muss nicht dieselbe Klasse sein, die die xml Datei parst) soll, dann Methoden haben, die

  1. Die nächste Quest (oder eine Quest mit gegeber ID) einleiten
  2. die ID der Quest (Hauptquest + Unterquest) zurückgeben

Zuletzt sollen diese Methoden für Lua aufrufbar gemacht werden.

Map Scrolling

Der für den Spieler sichtbare Teil unserer Map, ist logischerweise kleiner, als die gesamte Map.
Damit der Spieler die gesamte Map erkunden kann, muss sich der zu sehende Ausschnitt mit dem Spieler bewegen.
Dieser Ausschnitt wird mit Hilfe der Kamera Klasse abgebildet und bewegt.
Dabei soll unsere Spielfigur zentral abgebildet werden (die Kamera könnte ggf. auch auf ein anderes Spielobjekt zentriert werden), außer die "Kamera" befindet sich am äußersten Rand der Map.
In letzterem Fall soll der "Kamera" die Bewegungsfreiheit in Richtung des Rands genommen werden, der Spieler soll sich aber weiterhin frei Bewegen können (bis er den Rand der Map erreicht - diese Einschränkung muss ebenfalls im Rahmen dieses Tickets implementiert werden).
Nach dem Zurückkehren aus dem Pausezustand, soll die Kamera weiterhin auf dem selben Objekt, wie vor dem Betreten des Pausezustands zentriert sein.

Die obigen Zeilen fassen die Aufgaben zusammen, die im Rahmen dieses Tickets zu bewältigen sind.
Die genaue Implementierung sollte aber mit dem Bearbeiter von #37 abgestimmt werden.

Map skizzieren - Kreative Arbeit

Wir setzen uns gemeinsam hin, um eine Skizze der Map zu erstellen.
Diese Skizze wird später zum Erstellen der Tilemap verwendet.

Scripting Engine Registrations

Klassen müssen ihre Methoden für Lua offenlegen, damit das Scripting auch Spaß macht.
Hierzu müssen die ...LuaRegistration Klassen, die alle von BaseLuaRegistration erben implementiert werden.

Collision Detection Parsing

Einführung:
Im Moment können wir uns frei und ungebremst auf der Map herumbewegen wie wir lustig sind. So schön das auch klingt, das darf nicht der Fall sein. Wir müssen den Nutzer auf irgendeine Art und Weise daran hindern, durch Wände oder andere feste Objekte laufen zu können.
Zu diesem Zweck nutzen wir das Prinzip der Kollisionsboxen.

In den .tmx-Dateien können Kollisionsboxen (Im Grunde sind das nur Rechtecke bzw. Position, Breite & Höhe) zu bestimmten Tiles in ihrem jeweiligen Tileset spezifiziert sein. Zu einer TileId können mehrere Kollisionsboxen angegeben sein!

Aufgabe:
Das Muster der Kollisionsboxen in den .tmx-Dateien sieht folgendermaßen aus:

<tileset [...]>
  [...]
  <tile id="96">
   <objectgroup draworder="index">
    <object id="1" x="28" y="0" width="36" height="64"/>
   </objectgroup>
  </tile>
  <tile id="431">
   <objectgroup draworder="index">
    <object id="1" x="9" y="35" width="53" height="27"/>
    <object id="2" x="20" y="0" width="42" height="32"/>
    <object id="3" x="8.90909" y="20.5455" width="40.7273" height="13.4545"/>
    <object id="4" x="13.2727" y="7.45455" width="22.1818" height="13.4545"/>
   </objectgroup>
  </tile>
</tileset>

Hierbei ist <object/> die Box.

Diese Informationen müssen nun vom MapParser in die jeweiligen Tilesets geladen werden. Wie diese Informationen am Besten gespeichert werden sollten weiß ich nicht.

Anmerkung:
Später werden durch diese Informationen sowohl der Rendervorgang, als auch der Updatevorgang beeinflusst. Ferner werden mit einem ähnlichen Algorithmus alle Animationen für bestimmte Tiles geparst. Folglich heißt das für denjenigen, der dieses Ticket hier bearbeitet, dass er sich Gedanken um die Speicherstruktur machen muss.

TextureManager Klasse fertigstellen

Da unser Projekt doch ziemlich umfangreich ist muss ich drastische Maßnahmen ergreifen, damit wir mal ein Bisschen vorankommen.
Ich schreibe mir selber keine Anleitung, deshalb bleibt das Ticket leer.
Auf die Funktionsweise etc. gehe ich wahrscheinlich in den Kommentaren oder im Wiki ein.

Dialoge

Der Spieler soll mit den, in #44 implementierten, "NPC"s "kommunizieren" können. Bei einem Ansprechen (Interaktionstaste), soll die Möglichkeit bestehen einen Dialog auslösen zu können.

Es gibt mehrere Möglichkeiten, dies zu implementieren. Eine Lösung wäre, einen neuen Spielzustand, den dialogState zu implementieren.
Eine schönere Lösung wäre: eine Klasse zu implementieren, die sobald sie instanziiert wird, eine Dialogbox erstellt und den Objekten, die sich nicht mehr bewegen sollen, einen Befehl (Klasse die von BaseCommand erbt) zuteilt, der sie davon abhält sich zu bewegen.

Die Dialogbox soll immer einen Text bis zu einem bestimmten Zeichen enthalten (wahrscheinlich '\n'), bis die Interaktionstaste gedrückt wird. Diese soll den Dialog beenden, falls kein Text mehr übrig ist, oder den nächsten Text (ab dem letzten '\n') anzeigen.

Zuletzt soll es Lua Skripten möglich sein Objekte dieser Klasse erstellen zu können.

GameStateMachine

Dieses Ticket zu bearbeiten kann sehr lange dauern. Also ist es zu empfehlen dieses Ticket zu zweit anzunehmen.

Wir brauchen eine Klasse, die für uns die GameStates händelt.

Wir versuchen hierbei den Zustandsautomaten als Stapel zu implementieren. Für meht Info siehe unser Wiki

Anmerkung: Alles was im Rahmen dieses Tickets an Code geschrieben wird, soll in einen Namensraum und zwar in FiniteStateMachine gepackt werden.

Die Klassen sollen so wie auf dem Klassendiagramm implementiert werden.
klassendiagramm_all

Anmerkungen:

  • Nach wie Vor soll für jede Klasse eine Header und eine Quelldatei erstellt und befüllt werden. (bei abstrakten Klassen: Nur Headerdatei)
  • Um Klassen aus einer Datei zu verwenden muss in der Headerdatei, die Headerdatei der anderen Klassen einbezogen werden.
  • In jeder Datei kann der namespace seperat stehen

    Bsp.:
//  in foo.h
namespace poo
{
    class foo {};
};

//  in roo.h
namespace poo
{
    class roo {};
};

Wahrscheinlich wird dabei das ein oder andere Problem dabei auftreten. In diesem Fall einfach an Ario wenden und ihn beschimpfen, da er das Klassendiagramm (mit Roman) erstellt hat.

Update 02.01.17:
Natürlich müssen die Membervariablen bei GameState protected statt private sein.
Update ende;

NPC LuaRegistration

Die Methoden von NPC für Lua zugänglich machen.
Außerdem soll die interact() Funktion aus dem Skript aufgerufen werden.

Neue Libraries

Wir brauchen neue Bibliotheken!
Nämlich SDL_ttf -> Um Text auf dem Bildschirm anzeigen zu können
& SDL_mixer ->Um Musik & Sounds abspielen zu können

Zuerst einmal pullen.

Sobald ihr fertig seid alles einzurichten, kommentiert bitte dieses Ticket mit: "Ich bin ein toller Mensch.".

Nachdem ihr alles eingerichtet habt, könnt ihr das Programm einmal laufen lassen. Ihr solltet eine tolle Animation mit Musik und Text wahrnehmen können.

CommandQueue

Nun da die Scripting Engine weitesgehend implementiert ist, können wir damit anfangen uns in die Richtung eines eventbasierten Programmierens zu begeben. In den Skripts wird idealerweise keine Methode wie onFrame() mehr aufgerufen, sondern nur noch beispielsweise von der Form onCollision(). Damit wir trotzdem volle Kontrolle über die Objekte im Spiel haben, brauchen wir eine Schnittstelle zwischen Script und Engine, über die wir ihnen Befehle geben können. Diese Schnittstelle wird mit Hilfe der CommandQueue realisiert.

  1. Zuerst muss eine Warteschlange als template Datenstruktur implementiert werden, die dann später die einzelnen Commands speichern kann (Diese kann fürs erste auch einfach mit std::list oder std::vector ersetzt werden).
  2. Als nächstes muss eine Klasse BaseCommand implementiert werden, von der andere Befehlsklassen, wie z.B. MoveCommand erben. Diese Klassen implementieren dann Methoden wie update() und haben eine Referenz auf das Objekt, zu dem sie gehören.
  3. Die CommandQueue muss mit der Warteschlange in die GameObjects eingebaut werden. Das heißt die Befehle müssen ausführbar sein
  4. Funktionen, die Befehle an die Objekte weitergeben müssen für die Scripts zugänglich gemacht werden.

Häuser

Derzeit besteht unsere Spielwelt aus einer einzigen großen "Main Map". Auf dieser sind Häuser zu sehen (später sollen es noch mehr werden). Diese soll der Spieler teilweise betreten können.
Bei Häusern, die nicht betreten werden können, ist abzuwägen, was zu tun ist (Bsp: Nachricht: "Door is locked").
Unser "MapParser" erlaubt es uns, ohne Probleme, Maps zum jeweiligen Spielzustand hinzuzufügen.
Die Maps des Spielzustands werden in seinem Dictionary m_mapDict gespeichert.
Soll nun eine andere Map, als die "Main Map", geupdated und gerendert werden, so muss diese einfach nur auf den Stapel m_maps des Spielzustands aufgestapelt werden. Der aktuelle Zustand der Map, die vorher oben auf dem Stapel war, bleibt erhalten.
Kollidiert der Spieler nun mit der Tür eines betretbaren Hauses, so soll die Map dieses Hauses automatisch aufgestapelt werden.
Kollidiert er nun wiederum auf der Map dieses Hauses mit der Ausgangstür, so soll die Map wieder abgestapelt werden.

Voraussichtlicher Weg oben Beschriebenes zu implementieren:

  • rudimentäre Skizze der Map eines Hauses in Tiled erstellen
  • erstellte Map in "maps.xml" hinzufügen und "Koramu\assets" beifügen
  • Klasse "Door" erstellen und von "SDL_GameObject" erben lassen (vgl. "Player")
  • Methode collision() von "SDL_GameObject" überladen
  • Eingangstüre für ein in Tiled in der "Main Map" skizziertes Beispielhaus festlegen (Textur für die Tür festlegen oder Platzhalter verwenden - "states.xml"!). Dies ist vermutlich in Tiled (evtl. "states.xml") zu erledigen (in Beiden können Spielobjekte definiert werden).
  • vorherigen Schritt für die Ausgangstür der vorher erstellten Haus Map wiederholen
  • Methode collision() in "Door" so befüllen, dass oben Beschriebenes erreicht wird. Vermutlich müssen hierzu zusätzliche Methoden in "GameState.h" implementiert werden, um das auf und abstapeln zu erreichen.
  • Welche Haus-Map (es werden später mehrere sein) genau aufgestapelt werden soll, ist mittels eines Skripts für die betreffende Türe festzulegen. Für das verlassen eines Hauses lässt sich vermutlich für alle betreffenden Türen ein gemeinsames Skript festlegen (z. B. "exitDoor"), da hier nur die oberste Map auf dem Stapel abgestapelt werden muss, um wieder auf die "Main Map" zu kommen --> Nein, wir wollen aus Gründen der Flexibilität für jede Türe ein eigenes Skript.

TextureManager Klasse

TextureManager Klasse erstellen

  1. TextureManager.h & TextureManager.cpp anlegen
  2. In TextureManager.h, die Klasse TextureManager deklarieren. (#pragma once am Anfang nicht vergessen)
    • Erinnerung: in der Headerdatei (.h) wird noch nichts (außer evtl. kleine get-Funktionen) definiert. Wir sagen dem Compiler lediglich, dass die Memberfunktionen (aka Methoden) und/oder die Membervariablen (aka Attribute) existieren.

      z.B.:
// foo.h

#pragma once

class Foo {
public:
   Foo();       // Konstruktor
   ~Foo();    // Destruktor
private:
   // Membervariablen (Attribute)
   int x;
   int y;
public:
   // Memberfunktionen (Methoden)
   void doSomething(int someValue);

   // kleine get-Funktionen können auch in der Headerdatei definiert werden
   int getX() { retrun x; }
   int getY() { return y; }
};

// foo.cpp

#include "foo.h"

void Foo::doSomething(int someValue)
{}

  1. Eine erweiterte Klassenkarte, die als Bauplan für diese Klasse dient, wird folgen...

    image

    um std::map nutzen zu können muss die Bibliothek map einbezogen werden (#include <map>)

    Konstruktor und Destruktor nicht vergessen.

    Die Technik, die bei dieser Klassenkarte verwendet wurde, nennt sich UML (Unified Modeling Language) und wird bei so gut wie jedem Softwareprojekt verwendet.

    Auf den ersten Blick mag das alles ein wenig kryptisch und verwirrend wirken, aber sobald man die Form einmal verstanden hat ist das super einfach.


uml klassenkarte beschreibung - page 1 2

  1. Die Deklarationen der Memberfunktionen sollten auch schon in die Quelldatei (.cpp) geschrieben werden, aber müssen noch nicht befüllt werden.

  2. freiwillig: Die Klasse TextureManager soll, wie Game eine Singleton Klasse sein.

    Wie das geht lässt sich anhand des sehr ausführlich kommentierten Codes in Game.h und Game.cpp herausfinden.

Eine Vektor Klasse

Wir wollen z.B. die Geschwindigkeit, die Beschleunigung und auch die Position eines Objekts mithilfe einer zweidimensionalen Vektorklasse (Namensvorschlag: Vector2D) beschreiben.

Wie auch in der Mathematik, besteht unser zweidimensionaler Vektor aus einer x-Komponente und einer y-Komponente.

Bei den Memberfunktionen wird das Ganze schon ein wenig komplizierter, aber im Grunde genommen kennen wir die Funktionsweise schon aus der Mathematik. Wir müssen die mathematischen Sachverhalte lediglich in C++ übersetzen.
Außerdem müssen wir ein paar Operatoren überladen, um Vektoren z.B. miteinander addieren zu können.

Operatoren, die ihr auf jeden Fall überladen solltet:

  • Addition mit +; Addition mit += (Vector2D + Vector2D)
  • Subtraktion mit -; Subtraktion mit -= (Vector2D - Vector2D)
  • Der = Operator, d.h. einfach die Werte kopieren
  • Die Multiplikation mit einem Skalar * (Vector2D * 2) -> Vektoren in der Länge variieren lassen

Andere Funktionen:

  • Länge eines Vektors
  • getter-Funktionen (jeweils für x- und y-Komponente)

Es gibt diesmal leider kein Klassendiagramm, weil ich nicht daheim bin und folglich nur auf dem Laptop arbeiten kann, auf dem UMLet nicht benutzbar ist.

Falls die Testklasse aus Ticket #15 schon existiert, kann darin getestet werden. Ansonsten bitte jede Art von Testcode vor dem commit entfernen.

Scripting Engine

Einführung:
In unserem Programm wollen wir ganz eindeutig technische Einzelheiten vom Game Design trennen. Nachdem unsere Engine in Sachen Rendern, Spielumgebung, etc. weitgehend vollendet ist, geht es nun darum konkret das Spiel mit seinen Dialogen, Sequenzen und Geschichten umzusetzen.
Da o.g. Aspekte von der Engine selber getrennt werden sollen, nehmen wir diese nicht in unseren bisherigen Haufen an C++-Code auf, sondern Skripten sie.

Aufgabe:
Als Skripting Sprache verwenden wir Lua. Zuerst muss also diese Sprache ins Projekt eingebunden werden, sodass überhaupt eine grundsätzliche Möglichkeit besteht Skripte in dieser Sprache zu laden und auszuführen.

Anschließend braucht es ein Konzept diese Skripte innerhalb der Engine sinnvoll da einzusetzen, wo sie hingehören (i.e. Skript für NPC 'A' zu NPC 'A').

Zugleich wollen wir beim Skripting mit Lua einige von der Engine zur Verfügung gestellte Funktionen, Klassen, etc. benutzen können; Beispielsweise onCollision(), onStart(), onPlayerDetect(). Aus den Bezeichnungen für diese Funktionen ist zu entnehmen, dass wir eventbasierte Skripts schreiben wollen, d.h. das muss auch implementiert werden.

Collision Detection

Abhängig von Ticket #32

Aufgabe:
In der update() Methode von SDL_GameObject soll gecheckt werden, ob eine Kollision bei den gegebenen Werten stattfinden wird. Diese Kollision soll dann verhindert werden, indem man die x- & yPosition des SDL_GameObjects so manipuliert, dass unser Objekt das andere nicht berührt.

Was in welche Methode geschrieben werden muss, kann ich zu diesem Zeitpunkt noch nicht sagen.

Animationen

Unter Animationen verstehen wir hier, eine Reihe von zusammengehörigen Bildern, die hintereinander abgespielt werden und somit aussehen wie z.B. eine Bewegung.
Diese Bilder werden in einem sog. Spritesheet festgehalten...

Während ich dieses Ticket geschrieben habe, merkte ich, dass keiner außer mir in nächster Zeit verfügbar sein wird um dieses Ticket zu bearbeiten, deshalb bearbeite ich jetzt einfach selber

FontManager

Einführung:

In unserem Spiel wollen wir für verschiedenste Situationen die Möglichkeit haben, dem Benutzer einen Text (bestehend aus lesbaren Buchstaben) an den Kopf zu werfen (z.B. Dialoge, Lesen von Schildern, etc.).
Um dieses Ziel erfolgreich erreichen zu können, brauchen wir eine Klasse,
die das Rendern von einzelnen Buchstaben - und am Ende das Tages ganze Sätze (bzw. std::string) - effizient erledigt.
Eine vom Konzept ähnliche Klasse existiert bereits unter dem Namen TextureManager
(aka. TheTextureManager).


Jetzt zum praktischen Teil:
Die Klasse FontManager soll als singleton implementiert werden. Für mehr Informationen zu singletons: siehe TextureManager oder InputHandler (oder frag einfach Ario :-) )

Layer Klasse

Mit der Implementierung der abstrakten Klasse "Layer" und der erbenden Klassen "TileLayer" und "ObjectLayer" soll auch das Klassendiagramm dieser erstellt werden.

Alles bezüglich der Map kommt in den Namensraum "Environment"

Als Übergangslösung (bis sich mal jemanden ganz kurz Gedanken bzgl. Variablen und Methoden macht) folgendes implementieren:

  • "Layer" als abstrakte Klasse mit den rein virtuellen Methoden (public);

    • update() : void
    • render() : void
  • Die von "Layer" erbende Klasse "ObjectLayer":

    • Membervariablen (private):
      • m_gameObjects : vector
      • m_player : Player
    • Methoden von "Layer" überladen
  • Die von "Layer" erbende Klasse "TileLayer":

    • Membervariablen (private):
      • m_tiles : vector<vector> // mehrdimensionaler Vector
    • Methoden von "Layer" überladen

Nachträglich hinzugefügt (vom Lord):

  • Die von "Layer" erbende Klasse "AnimationLayer":
    • Membervariablen (private):
      • m_animations : vector<Animation*> // die Klasse Animation gibt es (noch) nicht, das ist nur vorläufig
    • Methoden von "Layer" überladen

StateParser

Ario und ich entwerfen und schreiben den StateParser. Noch wissen wir nicht so genau, wie.

Main Map

Die Main Map soll grob nach der Zeichnung in #24 (unten) skizziert werden.
Dabei festgestellte Unstimmigkeiten (Bsp: Dimensionierung) und Verbesserungsvorschläge sind zu notieren und mit den anderen Teammitgliedern zu besprechen.
Noch nicht zur Verfügung stehende Texturen sollen durch Platzhalter (ähnliche Gebilde aus verfügbaren Texturen) substituiert werden.

GameObject Klasse

Die Klasse GameObject ist eine Klasse von der jede andere Klasse, die - einfach gesagt - etwas auf dem Bildschirm macht, erbt.

  1. Die Datei gameObject.h und die obligatorische erste Zeile #pragma once einfügen. An dieser Stelle ist es wichtig zu sagen, dass wir keine Quelldatei brauchen, da die Klasse GameObject abstrakt sein wird.

    Für mehr Informationen zum allgemeinen Erstellen von Klassen, siehe unser Wiki.

  2. Die Klasse soll abstrakt sein, sodass wir später von den Vorzügen der Polymorphie profitieren können

    In C++ geht das leider nicht ganz so einfach wie in Java mit den abstrakten Klassen.

    In C++ muss eine Klasse , um abstrakt sein zu können, mindestens eine rein virtuelle Memberfunktion besitzen. Informationen zu abstrakten Funktionen, siehe unser Wiki

  3. Eine Klassenkarte, die als Bauplan dient dient, wird folgen....
    gameobject

    Hierbei bedeutet die Tatsache, dass etwas kursiv gedruckt ist, dass die Methode (und somit auch die Klasse) abstrakt/rein virtuell ist.

  4. Bitte auch schon Konstuktor und Destruktor deklarieren (nicht rein virtuell). Wobei der Destruktor als virtuell (nicht rein virtuell) deklariert werden muss. Grund: siehe unser Wiki

SDL_GameObject

Dieses Ticket bearbeite ich selber. Deshalb keine Beschreibung

Map Klasse

Mit der Implementierung der Klasse "Map" soll auch das Klassendiagramm dieser erstellt werden.

Alles bezüglich der Map kommt in den Namensraum "Environment"

Folgende Attribute sollte die Klasse haben (alle private):

  • m_positionVector : Vector2D
  • m_layers : std::map<string, Layer> // Dictionary von Layers; Elemente werden über den Schlüssel (string) identifiziert

Folgende Methoden sollten zumindest schon deklariert sein:
@ariogato befüll das hier mal sinnvoll und erklärs mir bitte.
vorläufig:

  • update()
  • render()

Zudem muss in der Klasse GameState der Vector aus GameObjects durch einen Stack aus Maps oder eine MapMachine (wie wollen wirs?) ersetzt werden.

GameStates

Wir wollen unser Spiel auf dem Konzept einer FSM (Finite State Machine) aufbauen.
Für mehr siehe unser Wiki

Um die verschiedenen Zustände zu implementieren, bedienen wir uns einer abstrakten Base-Klasse GameState von der alle anderen Zustände erben.

Dazu soll die Klasse, modelliert auf der folgenden Klassenkarte, implementiert werden.

klasse_gamestates

Zusatzinfo: Was ist eine enum, enumeration (zu deutsch: Aufzählung)? Einfach gesagt ein eigener Datentyp für den man ein paar Werte selber definiert/"aufzählt".
Bsp.:

enum class Wochentage 
{
   montag, dienstag, mittwoch, donnerstag, freitag, samstag, sonntag
};

//////////////////////

//  Variable mit Datentyp 'Wochentage' erstellen
Wochentage heute = Wochentage::montag;

//  vergleichen
if (heute == Wochentage::freitag)
   std::cout << "Endlich Freitag!" << std::endl; 

Mehr dazu erfährt ihr hier

Aufgabe ist es nun diese Klasse mit enum wie auf dem Diagramm zu implementieren.
Hierbei muss auf folgendes geachtet werden:

  1. Header- & Quelldatei erstellen und den Code wie gehabt aufteilen. Anleitung
  2. Destruktor muss bei abstrakten Klassen virtuell sein!!! mehr Info
  3. Alle Methoden sind abstrakt. Wie?
  4. Sowohl die enum als auch die Klasse müssen in einen namespace (Was ist ein namespace?) namens FiniteStateMachine gesteckt werden.
  5. Beim bearbeiten dieses Tickets (so wie bei allen anderen) wird Spaß sehr groß geschrieben ;-)

Logging einbauen

Wir haben jetzt eine stabile Log Klasse, die leider aufwändiger war als erhofft.
Nun sollte jemand jedes cout oder cerr im Code durch unsere Logmethode ersetzen.
Wie logge ich?

Bei Unklarheiten, bitte fragen.
Und Ja. Ich gebe die Drecksarbeit mit diesem Ticket gerade einfach an irgendjemand anderen weiter.

Camera Klasse

Einführung:
Zur Zeit können wir nur den Teil unserer Map rendern, der auch in unser Fenster passt. Das ist jedoch nicht wünschenswert, da so ein beachtlicher Teil unserer wunderschönen Map zu kurz kommt, aber trotzdem gerendert wird, also CPU-Leistung saugt. Deshalb brauchen wir eine eigene Klasse, die sich quasi über einen bestimmten Bereich der Map legt und genau diesen (und nur diesen) dann rendert.

Aufgabe:

  1. Es muss eine ganz normale Klasse, Camera, nach folgendem Klassendiagramm erstellt werden.

    camera

    Des Weiteren sollen get & set Methoden für jede einzelne Membervariable erstellt werden.

  2. Nachdem die Klasse erstellt wurde, muss sie samt all ihrer Methoden und Attribute (auch get & set Methoden) in unser Klassendiagramm eingefügt werden.

  3. Nun soll unsere zentrale Klasse Game (aka TheGame) ein Referenzattribut auf eine Instanz der Klasse Camera haben. Hierzu muss noch eine get (diesmal keine set) Methode für das Attribut implementiert werden.
    Auch dies soll im Klassendiagramm festgehalten werden.

    Anmerkung:
    Die Membervariable in Game soll vom Typ Camera* sein. Das heißt gleichzeitig, dass im Konstruktor bzw. Destruktor von Game entsprechende Speicherverwaltung mit (new & delete) stattfinden muss.

InputHandler

Ario's Domäne! Hier habt ihr nichts zu suchen!!!!

SDL_Renderer

Die Arbeit wird in Game.h verrichtet.

Zu jedem SDL_Window gehört genau ein SDL_Renderer. Während das SDL_Window die Informationen über z.B. Position und Größe des Fensters besitzt, weiß es eigentlich gar nicht was in ihm angezeigt wird.
Diese Aufgabe übernimmt der dazugehörige SDL_Renderer.

Für mehr Informationen diese tolle Zusammenfassung.

Wir wollen...

  1. Als Erstes soll in der Funktion Game::init() in Game.cpp, nachdem des Fenster initialisiert wurde und gecheckt wurde ob dabei alles gut lief, unser m_pRenderer initialisiert werden.
    Dies geschieht, ähnlich wie beim Window, mit der Funktion SDL_CreateRenderer(), der wir als ersten Parameter unseren Pointer aufs Window m_pWindow, als zweiten -1 und als dritten 0 übergeben. Für mehr Informationen siehe SDL-Wiki.

  2. Nach der Initialisierung muss natürlich wieder geschaut werden ob alles gut gelaufen ist. Wie gewohnt, wenn etwas schief gelaufen ist: Error-Message (SDL_GetError() in Kombination mit std::cerr) + return false;.

    Man erkennt, dass etwas kaputt ist, wenn m_pRenderer nach der Initialisierung immernoch nullptr ist (oder NULL).

  3. Als winziger Zusatz sollen in dem Konstruktor der Klasse Game sowohl m_pWindow als auch m_pRenderer mit nullptr initialisiert werden. Dies ist nicht unbedingt notwendig, aber es ist immer besser Pointer mit einem null-wert zu initialisieren.

NPCs

Unser Spiel basiert auf einer Story. Man schreitet in dieser Story über die Interaktion mit anderen Charakteren voran.
Wir brauchen also diese "anderen Charaktere". Sie sollen vom Programm und nicht vom Spieler gesteuert werden. Es sollen also "Non-player charakter"s sein.
Diese gilt es als Spielobjekte zu implementieren.

Auf dem Weg dorthin:

  • soll die Klasse "NPC" als Unterklasse von "SDL_GameObject" implementiert werden,
  • soll die Interaktion des Spielers mit "NPC" ermöglicht werden.

Die Implementierung dieser Klasse wird ähnlich wie die von Player (natürlich ohne das Inputhandling).

Die Schwierigkeit in diesem Ticket besteht darin die Interaktion mit anderen Objekten zu implementieren. Beim Druck einer Interaktionstaste (eventuell Leertaste), soll gecheckt werden, ob sich direkt vor dem Playerobjekt (Blickrichtung beachten) ein weiteres Objekt befindet (kann u.a. mit collision detection mit einem neuen collisionRect umgesetzt werden).
Falls ja, soll die Interaktionsmethode (Signatur: interact(Player* pPlayer)) aufgerufen werden, welche jedoch noch nicht befüllt werden soll.

FPS Counter

in main.cpp soll zuerst geschaut werden, wie lange ein Durchlauf der Schleife für die Ausführung gebraucht hat.
Tipp:
SDL_GetTicks() gibt in ms an wie viel Zeit seit der Initialisierung von SDL vergangen ist. Somit muss man lediglich die Zeit vom anfang von der Zeit am Ende der Schleife subtrahieren und hat die Dauer des Frames.

Als nächstes muss gecheckt werden wie oft diese Zeit in den Wert 1000.0 reinpasst (ms -> s).
Dieses Ergebnis ist dann unsere FPS-Zahl, Welche dann noch in irgendeiner Art und Weise auf den Bildschirm kommen muss. Fürs erste soll aber noch nichts ausgegeben werden.

Window erstellen

Die Funktion init() in der Klasse Game muss befüllt werden.

  1. SDL soll initialisiert werden. Dabei wollen wir, dass jedes Subsystem initialisiert wird.
    Dies lässt sich leicht realisieren indem man der Funktion int SDL_Init(Uint32 flags) das Makro SDL_INIT_EVERYTHING als Parameter auf den Weg gibt.

    Auch schön wäre es, wenn gleich geschaut wird ob die Funktion erfolgreich abgeschlossen wurde.
    ("if (SDL_Init...."). Falls etwas schiefgelaufen ist:

    • std::cerr << "SDL wurde nicht erfolgreich initialisiert!" << std::endl;
    • return false;

      Hierzu hilft folgender Ausschnitt der SDL Dokumentation: "Returns 0 on success or a negative error code on failure; call SDL_GetError() for more information. " .
  2. Ein Fenster erstellen. Die Membervariable (ein anderes Wort dür Attribut) m_pWindow der Klasse Game muss nun (idealerweise) mit einem SDL_Window befüllt werden.

    Hierfür gibt es eine Funktion in SDL, die so ausschaut: SDL_Window* SDL_CreateWindow(...) und die wir verwenden um unsere Membervariable zu befüllen.
    Wir müssen im Grunde genommen nur die Parameter, die Game::init(...) mitgegeben worden sind, in der richtigen Reihenfolge, an SDL_CreateWindow(...) "weitergeben"

    Vorsicht: Der string title muss vor der Übergabe in einen C-style-string umgewandelt werden, weil SDL in C geschrieben wurde! Dazu einfach title.c_str() übergeben statt einfach title.

    Um hier zu checken ob alles gut verlief, muss nur gecheckt ob m_pWindow befüllt wurde if(m_pWindow).
    Für näheres bitte die Wiki-Seite dazu anschauen.

  3. Nun muss nur noch die Membervariable m_running true gesetzt werden (bitte diesen Schritt noch auskommentiert lassen... sonst läuft das Programm in einer endlosen Schleife). Außerdem muss true returnt werden, denn sobald die Stelle im Code erreicht wurde, ist alles glatt gelaufen.

Wichtig zu beachten

  • Bitte ausreichend kommentieren
  • Nach jedem erfolgreichen Schritt sollte eine Konsolenausgabe erfolgen (z.B. std::cout << "Fenster wrude erfolgreich erstellt" << std::endl;) oder im Fall eines Errors, std::cerr+ Errormessage.

Tiles

Die in diesem Ticket aufgeführten Tiles/Einheiten sollen bis zum 07.03.17 fertiggestellt werden.
Gemalte Tiles/Elemente sollen dem Repository hinzugefügt werden und im Ticket als erledigt markiert werden (einfach abhaken). Außerdem sollte man markieren, wenn man an einem Tile/Element arbeitet (dazu schreiben).
Bei Tiles/Elementen, die über einen Hintergrund gelegt werden sollen, ist auf einen transparenten Hintergrund zu achten.
Unsere Notizen inklusive der Skizze zur Map sind unten zu sehen.

Wir verwenden 64x64 Pixel große Tiles. Zur Orientierung: eine Tür sollte etwa 1,66 Tiles (ca. 107px) hoch sein und einen Tile breit. Bei Fragen und Problemen bezüglich der Dimensionen einfach in die Gruppe schreiben, dann klären wir das gemeinsam.

@LemmingFaunTM: Lad dir evtl. mal GraphicsGale für das Malen der Tiles herunter (Piskel ist irgendwie komisch mit dem Dateiformat und GraphicsGale scheint besser zu sein).

Folgende Einheiten sind zu malen:
Anmerkung: bisher werden Tiles für das Innere von Gebäuden vernachlässigt

Wohnhaus:

  • Dach + Schornstein (Tiefe nicht vergessen)

  • Tür + Klingel

  • Fenster (evtl. mit Gardinen)

  • Fassade

  • Briefkasten (einzeln)

Polizeigebäude:

  • Tür

  • Fassade

  • Aufschrift (POLIZEI)

  • (Pferd)

  • Dach (Flachdach?)

  • Fenster & Fenster mit Gittern

Rathaus (zur Orientierung: etwa 11 Tiles breit und etwa 7 hoch):

  • Säule

  • Dach

  • Fassade

  • Fenster (evtl größer als normal)

  • Eingangstor

  • (Uhr)

  • Treppenaufgang zum Tor

  • (Balkon)

Kirche:

  • Friedhof:

  • Gräber (ca. drei kommen auf den Friedhof -> Größe approximieren) + Grabsteine (wird bearbeitet-Tobi)

  • Kiesweg

  • Blumen

  • Kirchturm:

  • Spitzdach (dunkelrot) (wird bearbeitet - Roman)

  • Kreuz

Unser Shop/Haus:

  • spezielles, größeres Gebäude (siehe Skizze)

  • Logo (Uhr) + Aufschrift

Umgebung:

  • Holzzaun (nicht nur horizontal sondern auch vertikal)

  • ("Metallzaun" (nicht nur horizontal sondern auch vertikal))

  • Laterne

  • Bäume

  • Kopfsteinpflaster (vorhandenes etwas verändern -> nicht ganz so dunkel)

  • (Wasser - haben schon Wasser; müssen schauen, wie das mit der Animation funktioniert)

  • (Brücke)

  • Busch

  • Wasserspeier + Uhr (für den Marktplatz)

  • Gras (mit verschiedenen Varianten/Abwechslung)

  • (Felsen/Steinbrocken)

  • Brunnen

Spielplatz (wollen wir den noch?):
ToDo: Tiles überlegen

Bäcker, Tante-Emma-Laden, Metzger, Apotheke (auf dem Marktplatz):
Idee:

  • Häuser seitlich darstellen (wie auf der Skizze) (wird bearbeitet - Roman)
  • herausstehendes Schild mit dem Namen/Logo des Geschäfts

Notizen:
map_requirements

View

Einführung:

  1. Wir wollen, wie schon in Ticket #31 angesprochen, nicht zu jedem Zeitpunkt die gesamte Map mit all ihren Objekten und Tiles rendern. Wir wollen lediglich den für den Benutzer sichtbaren Teil rendern.
  2. Wir wollen mithilfe der Kamera die Bewegung des Spielers als map scrolling darstellen.
  3. Wir wollen eine stärkere Trennung von Darstellung und Programmlogik.
  4. Aus 3. folgt: Wir wollen uns generell Sachen vereinfachen.

Um unseren horrenden Forderungen gerecht zu werden, brauchen wir die View Klasse.

Diese Klasse ist für die Darstellung der Spielumgebung und somit sämtlicher Spielobjekte (GameObjects) zuständig.
In diesem Rahmen wird u.a. anhand der Camera (#31) berechnet welcher Teil der Map tatsächlich gerendert wird, um die insgesamte Performance des Programmes zu verbessern.
Außerdem passiert noch viel mehr Magie in diesem Code, aber aufgrund der Tatsache, dass ich selber für dieses Ticket verantwortlich bin, gehe ich nicht weiter ins Detail.

Zusammenfassung der Aufgaben:

  • Code schreiben
  • Eine gute Dokumentation erstellen

Stapel

Wir wollen einen Stapel. ASAP!

Fps limitation

Unsere Methode fps zu limitieren ist leider nur eine Übergangslösung und folglich durchaus schlecht...
Deshalb ist es Zeit für einen Neuen! Wie das geht kann ich leider nicht sagen, weshalb der Bearbeiter dieses Tickets eine Ahnung von Sachen haben und sich nicht scheuen sollte Recherche auf diesem Gebiet zu betreiben.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.