ariogato / koramu Goto Github PK
View Code? Open in Web Editor NEWA selfmade 2D RPG
A selfmade 2D RPG
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:
Es muss eine ganz normale Klasse, Camera
, nach folgendem Klassendiagramm erstellt werden.
Des Weiteren sollen get
& set
Methoden für jede einzelne Membervariable erstellt werden.
Nachdem die Klasse erstellt wurde, muss sie samt all ihrer Methoden und Attribute (auch get
& set
Methoden) in unser Klassendiagramm eingefügt werden.
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.
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.
TextureManager
deklarieren. (#pragma once
am Anfang nicht vergessen)
// 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)
{}
std::map
nutzen zu können muss die Bibliothek map
einbezogen werden (#include <map>
)Die Deklarationen der Memberfunktionen sollten auch schon in die Quelldatei (.cpp) geschrieben werden, aber müssen noch nicht befüllt werden.
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.
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!
Dieses Ticket bearbeite ich selber. Deshalb keine Beschreibung
Wir setzen uns gemeinsam hin, um eine Skizze der Map zu erstellen.
Diese Skizze wird später zum Erstellen der Tilemap verwendet.
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.
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);
Die von "Layer" erbende Klasse "ObjectLayer":
Die von "Layer" erbende Klasse "TileLayer":
Nachträglich hinzugefügt (vom Lord):
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
):
Folgende Methoden sollten zumindest schon deklariert sein (alle void
):
Ario und ich entwerfen und schreiben den StateParser. Noch wissen wir nicht so genau, wie.
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.
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.
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:
enum
als auch die Klasse müssen in einen namespace
(Was ist ein namespace?) namens FiniteStateMachine
gesteckt werden.Die Methoden von NPC für Lua zugänglich machen.
Außerdem soll die interact()
Funktion aus dem Skript aufgerufen werden.
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)
Wir wollen einen Stapel. ASAP!
Ario's Domäne! Hier habt ihr nichts zu suchen!!!!
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.
Der RORO und Ario machen das schon
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.
Anmerkungen:
namespace
seperat stehen// 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;
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 (GameObject
s) 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.
Die Klasse GameObject ist eine Klasse von der jede andere Klasse, die - einfach gesagt - etwas auf dem Bildschirm macht, erbt.
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.
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
Eine Klassenkarte, die als Bauplan dient dient, wird folgen....
Hierbei bedeutet die Tatsache, dass etwas kursiv gedruckt ist, dass die Methode (und somit auch die Klasse) abstrakt/rein virtuell ist.
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
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.
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 :-) )
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.
init()
in der Klasse Game
muss befüllt werden.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;
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.
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
return
t werden, denn sobald die Stelle im Code erreicht wurde, ist alles glatt gelaufen.
std::cout << "Fenster wrude erfolgreich erstellt" << std::endl;
) oder im Fall eines Errors, std::cerr
+ Errormessage.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:
Andere Funktionen:
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.
Statt violetUML benutzen wir jetzt UMLet. Für weiteres siehe unser Wiki
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.
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:
In diesem Fall ist das unerwünscht:
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.
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_GameObject
s 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.
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.
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:
drawCharacter(char character, ...)
mit einem character aufgerufen wird, der sich nicht im Alphabet befindet?ObjectRectangle
(oder SDL_Rect
je nachdem was der Geschmack des Entwicklers ist) entgegennimmt und den text innerhalb der Grenzen dieses Rechtecks rendert. 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.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.
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:
collision()
von "SDL_GameObject" überladencollision()
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.RoRo und Ario regeln.
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.
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.
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.
outputStream
mit std::cout
initialisiert. Ja das geht.outputStream
wieder mit std::cout
initialisiert werden. (Nicht vergessen hier auch eine Fehlermeldung auszugeben)outputStream << msg << endl;
ErrorLog()
und PrintLog()
sollen fürs erste noch das gleiche machen.Anmerkungen:
#include <iostream>
& #include <fstream>
fstream::close
evtl. wieder geschlossen werden.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:
TheGame
mit entsprechenden Methoden abrufen.Außerdem muss an anderen Stellen im Code folgendes gemacht werden
Storyparser
: Eine neue Methode erstellen, die den Spielstand wieder abruftStory::init()
: Den Spielstand über den Storyparser einlesenPlayer::init()
: DiePosition über den Storyparser einlesenNun 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.
std::list
oder std::vector
ersetzt werden).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.CommandQueue
muss mit der Warteschlange in die GameObject
s eingebaut werden. Das heißt die Befehle müssen ausführbar seinWir 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.
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:
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.
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
Zuletzt sollen diese Methoden für Lua aufrufbar gemacht werden.
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):
Folgende Methoden sollten zumindest schon deklariert sein:
@ariogato befüll das hier mal sinnvoll und erklärs mir bitte.
vorläufig:
Zudem muss in der Klasse GameState der Vector aus GameObjects durch einen Stack aus Maps oder eine MapMachine (wie wollen wirs?) ersetzt werden.
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.
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...
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.
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
).
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.
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:
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.