M2000 Δημοσ. 5 Ιανουαρίου 2016 Δημοσ. 5 Ιανουαρίου 2016 Δυστυχώς με το σκεπτικό ότι ένα πρόγραμμα πρέπει να παίζει σε διάφορα περιβάλλοντα, με διαφορές στο μέγεθος μνήμης..ή στο πόση μνήμη μπορεί να έχει διαθέσιμη, το να έχει κανείς ένα μόνιμο τρόπο καταχώρησης συνημμένων (βαρέθηκα αυτό το assets), δεν ευνοεί. Θυμάμαι ένα πραγματικό πρόβλημα που είχα, το 1988, με έναν υπολογιστή με 32Kbyte ram, από τα οποία έπαιρνε το λειτουργικό και η οθόνη και έπρεπε να κράταγε και έναν πίνακα στοιχείων. Αυτό που μπορούσα να "παίξω" ήταν να εγγράφω πάνω στη λίστα προγράμματος της Basic κομμάτια κώδικα. Είχα βρει μια πατέντα να περνάω όνομα συνάρτησης σε μια συνάρτηση και να την καλώ με Eval$("FN"+a$) αφού πρώτα δω σε ποιο overlay είναι και ποιο είναι το τρέχον, και αν δεν τα έβρισκα ίδια τότε τράβαγα από δίσκο (των 100Kbytes) το κομμάτι που ήθελα (με απευθείας εγγραφή στη μνήμη, χωρίς να σπάσω την συνδεδεμένη λίστα, είχε ένα δείκτη προς την επόμενη γραμμή.) Συνολικά πέντε overlays υπήρχαν για περίπου 20 διαδικασίες, κάθε overlay ήταν για κάθε dropdown menu, από μια γραμμή Menu (με πέντε στοιχεία). Με καλή ομαδοποίηση δεν είχα θέμα αργοπορίας. Το κάθε Overlay ήταν περίπου 3kbyte, άρα κέρδισα 12kbyte ακόμα! (είχα πειράξει και το chip της οθόνης για να κερδίσω μνήμη, αφαιρώντας οριζόντιες γραμμές, χωρίς να το "ξέρει" το λειτουργικό) Άρα πρώτα πρέπει να αποφασίσει κανείς το πώς το πρόγραμμα θα επιλέγει να φορτώσει-αποφορτώσει την μνήμη, και μετά βρίσκει το τρόπο να το κάνει. Δείτε όμως στο παράδειγμα που έδωσα..η ύπαρξη της κατάστασης, σε μια μεταβλητή, του overlay που έχουμε. Αν θέλουμε τα assets να έχουν νόημα πρέπει να σκεφτούμε πώς θα ξέρουμε ποια assets έχουμε. Αυτή η σκέψη θα μας οδηγήσει στη δομή. Και ο κανόνας για να λύνεις προγραμματιστικά προβλήματα, ακόμα και αν δεν έχεις κώδικα, είναι να σκεφτείς τις αρχικές τιμές. Άρα ξεκινάει το πρόγραμμα με τι assets και από που τα φορτώνω; Ποια είναι η συνάρτηση που θα λάβει κάτι για να αποφασίσει να έρθει νέο asset ή να φύγει ένα παλιό;
defacer Δημοσ. 5 Ιανουαρίου 2016 Δημοσ. 5 Ιανουαρίου 2016 Τι είναι η ζωή, τι είναι ο άνθρωπος... Εδώ έχουμε ένα one-stop shop για assets. Αυτό περιλαμβάνει ενδεχομένως και το factory μέρος και το lifetime management μέρος. Αυτά τα δύο είναι τελείως ορθογώνια. End of story. Kercyn, αν βρεις το Modern C++ Design του Alexandrescu δες την policy-based design τεχνική, είναι βασικά μια compile-time εκδοχή του να έχεις πολυμορφικά "strategy objects" για κάθε ένα από τα ορθογώνια concerns σε μια class η οποία ενορχηστρώνει τα πράγματα. Εφόσον δεν χρειάζεσαι να τα αλλάζεις αυτά at runtime (που σπάνια το χρειάζεσαι? ποτέ?) είναι πολύ χρήσιμη ιδέα. Και βασικά διάβασε όλο το βιβλίο, αυτά που κάνει μέσα είναι είτε old news πλέον είτε δε θα σου χρειαστούν, αλλά το πώς τα κάνει... άμα δεν είσαι ήδη hardcore templatιστας mind=blown. Very highly recommended. Αυτό που λέει ο groot για dependency injection είναι τελείως άλλο πράγμα και δε λύνει το πρόβλημα για το οποίο συζητάμε.
M2000 Δημοσ. 5 Ιανουαρίου 2016 Δημοσ. 5 Ιανουαρίου 2016 ftp://ftp.sbin.org/pub/doc/books/Addison_Wesley_-_Modern_C++_Design_Generic_Programming_and_Design_Patterns_Applied_Ebook-fly.pdf
groot Δημοσ. 5 Ιανουαρίου 2016 Δημοσ. 5 Ιανουαρίου 2016 Common interface για initialization και usage για objects δεν είναι το πρόβλημά "σας"; Εάν ναι, τότε γιατί policy pattern; Εάν όχι, ποιο είναι το πρόβλημά "σας";
defacer Δημοσ. 5 Ιανουαρίου 2016 Δημοσ. 5 Ιανουαρίου 2016 Το common interface για initialization δε βλέπω τι σχέση έχει μ' αυτά που συζητάμε. Το πώς γίνεται το initialization των assets (με τις LoadX στο παράδειγμα) είναι τελείως αδιάφορο. Το common interface για usage προφανώς δεν είναι το πρόβλημα εφόσον είναι φανερό από το γεγονός ότι h function που προσπαθεί να φτιάξει ο Kercyn είναι template και έχει επιστρεφόμενο template type. Αυτό δεν είναι κάτι που συμβαίνει κατα λάθος, συμβαίνει όταν πολύ επίτηδες θέλεις να έχεις διαφορετικό επιστρεφόμενο τύπο. Δεν έχω ιδέα τι σχέση έχουν τα παραπάνω με dependency injection ή με policy pattern. Καμία απολύτως βασικά. Το policy pattern δεν είναι κάτι που χρειάζεται να μπλεχτεί εδώ. Είναι κάτι που μάλλον βολεύει να μπλεχτεί αν θέλεις να μπλέξεις το factory με lifetime management. Και ο μόνος λόγος που το ανέφερα είναι ότι έχετε βάλει το lifetime management στην κουβέντα kind of με το ζόρι.
Moderators Kercyn Δημοσ. 5 Ιανουαρίου 2016 Μέλος Moderators Δημοσ. 5 Ιανουαρίου 2016 Το αρχικό πρόβλημα που είχα ήταν ότι δεν έκανε compile αυτό που είχα γράψει, και μου εξηγήσατε γιατί. Αποφασίζοντας λοιπόν να "λύσω" αυτό το πρόβλημα κάνοντας specialize τα functions, ξεκίνησε η συζήτηση για το θέμα της αρχιτεκτονικής. Γράφω εδώ τι έχω/τι θέλω να κάνω. Δεν έχω κάποιο "πρόβλημα" στην αρχιτεκτονική, απλώς λέω να το αναπτύξω εδώ για να πει όποιος έχει όρεξη τη γνώμη του και να μην υπάρχει σύγχυση Φτιάχνω έναν game resource manager. Ό,τι έχω διαβάσει από βιβλία/άρθρα/posts συνοψίζεται στα εξής: Ο manager είναι υπεύθυνος για το lifetime και τη διαχείριση των assets (όπου assets είναι εικόνες/κείμενο/ήχοι κλπ κλπ). Μερικά assets φορτώνονται on demand, μερικά γίνονται preload και μερικά γίνονται stream κατά τη διάρκεια του παιχνιδιού (το τελευταίο δε θα το κάνω). Υπάρχουν κάποια assets τα οποία μένουν φορτωμένα στη μνήμη ακόμα κι αν δε χρησιμοποιούνται (συνήθως είναι αυτά που χρησιμοποιούνται συχνά, όπως HUD, character models κλπ) και αυτά τα οποία φεύγουν όταν σταματήσουν να χρησιμοποιούνται. Είναι πιθανό να έχω ξεχάσει κάτι αλλά νομίζω καταλαβαίνετε το concept. Έχοντας υπ' όψη αυτά λοιπόν, νομίζω μπορείτε να δείτε γιατί είμαι τόσο διστακτικός και θέλω να αποφύγω μία λύση σαν αυτή του παπι. Επειδή όμως δεν είναι καθόλου απίθανο να έχω καταλάβει κάτι λάθος, θα ήθελα αν μπορείτε να μου εξηγήσετε γιατί ένα factory θα ήταν η κατάλληλη λύση γι' αυτό. Αυτό που έχω κάνει εγώ είναι το εξής: Έχω ένα class CAsset<T> το οποίο έχει μέσα πληροφορίες για το asset και το ίδιο το asset. Ο Resource Manager (RM) έχει ένα Cache<T> (όπου Cache alias για std::map<std::string, CAsset<T>>) στο οποίο κρατάει τα Assets για ένα συγκεκριμένο τύπο. Η RequestAsset<T>(AssetID) ψάχνει στο κατάλληλο cache και επιστρέφει ένα const reference του asset. Αν το asset που ψάχνουμε δεν υπάρχει στο cache, τότε το φορτώνει πρώτα και το επιστρέφει μετά. Στη συνέχεια θα μπουν περισσότερες πληροφορίες στο CAsset για να υποστηρίζονται persistent resources και άλλα πράγματα. Βάζω το CAsset.hpp και το MResourceManager.hpp. Όλον τον κώδικα μπορείτε να τον βρείτε εδώ. CAsset.hpp #pragma once /*! \file */ #include <string> class MResourceManager; /*! \brief Asset template class that is used by the Resource manager to store asset information. T must be move and copy-constructible. */ template <typename T> class CAsset { public: CAsset() {} ~CAsset() { MResourceManager::Instance().ReleaseAsset<T>(ID); } CAsset(CAsset const & Other) { //todo } CAsset & operator=(CAsset const & Other) { //todo } //αυτό μάλλον θ' αλλάξει operator T() { return Asset; } friend MResourceManager; private: T Asset; /*! Reference counter; how many objects use this asset. Asset is unloaded when its reference counter reaches 0. */ int RefCounter = 0; //! The asset's unique ID. std::string ID; }; MResourceManager.hpp #pragma once /*! \file */ #include <string> #include <map> #include "MManager.hpp" #include "CAsset.hpp" #include "OpenGLPackage.hpp" #include "BMixChunk.hpp" #include "BMixMusic.hpp" #include "MErrorManager.hpp" #include "OpenGLPackage.hpp" template <typename T> using AssetCache = std::map < std::string, CAsset<typename T> > ; /*! \brief Manager class responsible for all resource caching and handling. */ class MResourceManager final : public MManager<MResourceManager> { public: ~MResourceManager(); void Initialize() override; //todo specialize RequestAsset and ReleaseAsset for more asset types and add their appropriate load functions template <typename AssetType> AssetType const & RequestAsset(std::string ID); template <> GLuint const & RequestAsset(std::string ID) { if (Textures.count(ID) == 0) { LoadTexture(ID); } Textures[ID].RefCounter++; return Textures[ID].Asset; } template <> std::string const & RequestAsset(std::string ID) { if (Strings.count(ID) == 0) { LoadText(ID); } Strings[ID].RefCounter++; return Strings[ID].Asset; } template <typename AssetType> friend CAsset<AssetType>::~CAsset(); friend MManager; private: MResourceManager(); template <typename AssetType> void ReleaseAsset(std::string ID) {} template <> void ReleaseAsset<GLuint>(std::string ID) { Textures[ID].RefCounter--; if (Textures[ID].RefCounter == 0) { GL_CALL(glDeleteTextures(1, &Textures[ID].Asset)); Textures.erase(ID); } } template <> void ReleaseAsset<std::string>(std::string ID) { Strings[ID].RefCounter--; if (Strings[ID].RefCounter == 0) { Strings.erase(ID); } } /*! \brief Loads a texture to memory. Initially, an SDL surface is created from the desired image and then the surface is transformed into a GL texture. Finally, the texture is stored in the Textures map. */ void LoadTexture(std::string ID); void LoadText(std::string ID); const std::string ResourceFilePath = "resources.zip"; //! Relates asset IDs to their paths. std::map<std::string, std::string> AssetPaths; AssetCache<GLuint> Textures; AssetCache<std::string> Strings; }; ΥΓ. @Μ2000 "Asset" δεν είναι συνημμένο. Ένα game asset ή resource είναι αυτό που περιέγραψα παραπάνω (εικόνες/models/ήχοι κλπ). Αν δε σου αρέσει το asset μπορείς να το λες συνημμένο ή όπως αλλιώς θες, απλώς επειδή συνημμένο εγώ τουλάχιστον καταλαβαίνω το attachment, το ξεκαθαρίζω για να είμαστε σίγουροι ότι λέμε το ίδιο πράγμα. ΥΓ2: Μη δίνετε σημασία στο destructor του CAsset, θα αλλάξει γιατί γράφω βλακείες Επίσης η RequestAsset θα επιστρέφει CAsset<T> και όχι σκέτο Т.
M2000 Δημοσ. 5 Ιανουαρίου 2016 Δημοσ. 5 Ιανουαρίου 2016 Κατάλαβα τι λες! Απλά συνημμένο δεν είναι ντε και καλά το αρχείο που συμπληρώνει το email, αλλά θα μπορούσε να είναι και το embedded αρχείο που έρχεται με ένα πρόγραμμα. Resource είναι όταν αναφέρεσαι σε αυτό στην φάση που το επιλέγεις για τη δουλειά σου. Asset όταν είναι στη συλλογή για να κάνεις τη δουλειά σου. Αλλά ουσιαστικά στο τέλος Embedded (μέσα στο πρόγραμμα) ή linked (αν το έχεις χωριστά).
παπι Δημοσ. 6 Ιανουαρίου 2016 Δημοσ. 6 Ιανουαρίου 2016 Με αυτο που εμπλεξες θα εχεις πολυ πιο σοβαρα θεματα να λυσεις απο τα templates. Ασε το project στην ακρη, και φτιαξε ενα αλλο το οποιο θα εχει δυο objects. Game που θα διαχειριζεται τα inputs και την λογικη. Renderable το οποιο θα εχει τα resources του Στο Game::Render απο gl call θα εχεις ΜΟΝΟ το clear και το swap (present ή οπως λεγεται) Στο Renderable::Render θα εχεις ΟΛΑ τα σεταρισματα της pipeline που θελει.. αυτο που θες να ζωγραφισεις Edit Good luck.
M2000 Δημοσ. 6 Ιανουαρίου 2016 Δημοσ. 6 Ιανουαρίου 2016 Άμα ξεκινήσει με ένα δυο γραφικά, το φτιάχνει στην πορεία το πρόγραμμα..για περισσότερα!
AlexHello Δημοσ. 6 Ιανουαρίου 2016 Δημοσ. 6 Ιανουαρίου 2016 Καλή τύχη με το project σου! Κατ' εμέ μια game engine ή ένα game είναι απο τα πιο ενδιφέροντα (και δύσκολα) projects. Επειδή έχω αφιερώσει και εγώ χρόνο σε παρόμοια design decisions, αυτό το βιβλίο είναι χρυσός πανω σε αυτό το θέμα. Απο τις καλύτερες επενδύσεις που μπορεί να κάνει κανείς! http://www.gameenginebook.com/ Και αυτό επίσης το δωρεάν ebook με πολλά χρήσιμα design patterns ειδικά για games! http://gameprogrammingpatterns.com/
Moderators Kercyn Δημοσ. 6 Ιανουαρίου 2016 Μέλος Moderators Δημοσ. 6 Ιανουαρίου 2016 Ευχαριστώ για τις... ευχές! παπι δυστυχώς ή ευτυχώς δε μπορώ να παρατήσω το project γιατί θέλω σαν παιδί κι εγώ να πάρω το πτυχίο μου. Ντάξει δεν είπα ότι θα κάνω commercial game engine, αλλά κάποια βασικά πράγματα θα ήθελα να τα κάνω. AlexHello το Game Engine Architecture είναι το βασικό βιβλίο που διαβάζω, το άλλο δεν το ήξερα και θα το κοιτάξω. Ευχαριστώ!
παπι Δημοσ. 7 Ιανουαρίου 2016 Δημοσ. 7 Ιανουαρίου 2016 Δεν σου ειπα να το παρατησεις, ειπα να το βαλεις στην ακρη. Το θελω να βγαλω πτυχιο δεν μου λεει κατι, το θεμα ειναι να μαθεις να μαθαινεις. Τεσπα, βγαλε τα tamplates. Δεν τα θες. Και αν μπορεις, γραφε τα varable με μικρα. 1
warchief Δημοσ. 7 Ιανουαρίου 2016 Δημοσ. 7 Ιανουαρίου 2016 Μπορει να ειμαι τελειως εκτος θεματος, αλλα.. για πιο λογο να κανεις εσυ το ref counting και ολο αυτο το mambo jumbo και να μην χρησιμοποιησεις καποιον smart pointer type? Απο εκει και επειτα μπορεις να υλοποιησεις μια απλη dict based cache για να αποθηκευεις smart pointers των οποιων το lifetime θελεις να ελεγχεις εσυ explicitly.
παπι Δημοσ. 7 Ιανουαρίου 2016 Δημοσ. 7 Ιανουαρίου 2016 Λιγότερα bugs. Τώρα που λες για cache, ξέχασα να πω, πως καλύτερα είναι να την βγάλει. Kercyn, επικεντρωσου στο "πύρινα" του project και μετά αν έχεις χρόνο δες τα επιμέρους. Έχεις βάλει πολλά πράματα και θα χάσεις τη μπαλα. 1
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα