Poległe projekty - porażka czy lekcja?
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!

Poległe projekty - porażka czy lekcja?

AutorWiadomość
Temat założony przez niniejszego użytkownika
Poległe projekty - porażka czy lekcja?
» 2017-10-21 23:56:16
Witam ponownie.
Ostatnie prawie dwa miesiące spędziłem nad pisaniem (oryginalnie) gry.
Niestety, dzisiaj doszedłem do wniosku, że ciągnięcie tego dalej nie ma sensu i w sumie sam nie do końca wiem czemu.
Jest to na pewno spowodowane brakiem porządku w kodzie. Mam krótkie funkcje, w większości dobrze nazwane i tu jest problem, jest ich za dużo.
Poza tym coś sobie kiedyś wymyśliłem, popisałem, zrobiłem w połowie i uznałem, że wrócę do tego później (bo na ten czas nie mogę więcej zrobić).
Wracam po 3 tygodniach - nie pamiętam o co mi w ogóle chodziło.

Wszystko jest do d**y, klasy robią rzeczy, których robić nie powinny, ale robią, bo tak jest wygodnie, bo tak jest łatwiej, bo to wymusiła na mnie konstrukcja kodu, czy złe plany, który w momencie ich wymyślania były dobre, ale nie aż tak dalekosiężne. Jedna funkcja zależy od drugiej, nie wiem jak połączyć puzzle, które do siebie nie pasują.
Wszystko było pięknie i nawet nie wiem kiedy kod mi się popsuł...

Oczywiście nie piszę tego wszystkiego, by z siebie wyrzucić to (no, może troszeczkę), chciałbym w tym luźnym dziale zapytać Was, Czy zdarza(ło) Wam się coś takiego, jak do tego podchodzicie oraz jak i czy da się unikać takich sytuacji?
P-165952
» 2017-10-22 06:13:48
P-165956
» 2017-10-22 09:44:37
Polecam Ci przeczytać: Dobre praktyki wytwarzania oprogramowania.

Zwróć szczególną uwagę na dział: Prowadzenie projektów domowych.

Długoterminowym rozwiązaniem Twojego problemu jest sekcja Dbaj o dokumentowanie kodu oraz sekcja Projektowanie nowych komponentów.

Ja potrafię odkładać projekty na półkę na ładnych kilka miesięcy, a nawet lat. Po powrocie do nich nie ma najmniejszego problemu ze zrozumieniem o co chodzi, bo jest dokumentacja, z którą bardzo szybko można się zapoznać i dostać informację jaka była intencja danej klasy/metody.

W szczególnych przypadkach tworzę również testy jednostkowe, ale to dotyczy tylko tych komponentów, które mają sporą if-ologię (czytaj: robią jakieś złożone obliczenia i jest co najmniej kilka scenariuszy o których należy pamiętać podczas modyfikowania implementacji).

/edit:
Przykładowy header z Wormsów (projekt był pisany przez 7 dni z pekfosem):
C/C++
#pragma once
#ifndef LIBWORMS_COMMON_UNITPHYSICS_HPP
#define LIBWORMS_COMMON_UNITPHYSICS_HPP

#include <ddt/graphics/Camera.hpp>
#include <SFML/System/Vector2.hpp>
#include <SFML/Graphics.hpp>
#include <functional>
#include <list>

#include "SpriteCollision.hpp"

namespace worms
{
    namespace cmn
    {
        //===================================================================//
        class WormsMap;
        //===================================================================//
        // Klasa do liczenia fizyki.
        class UnitPhysics final
        {
        public:
            typedef std::function < sf::Vector2f( sf::Vector2f ) > ActionUpdateVelocity;
           
            // Domyślny konstruktor.
            UnitPhysics();
           
            // Ustawia położenie początkowe obiektu.
            void setPosition( const sf::Vector2f & _position );
           
            // Zwraca aktualne położenie obiektu.
            const sf::Vector2f & getPosition() const;
           
            // Ustawia prędkość wiatru w osi X.
            void setWind( float _fWindVelocityX );
           
            // Ustawia poziom wody (wartość graniczna w osi Y).
            // Jeżeli obiekt przekroczy granicę poziomu wody to będzie stosowany opór dla wody zamiast dla powietrza.
            void setWaterLevel( float _fWaterY );
           
            // Ustawia wagę obiektu.
            // Aktualnie waga wpływa tylko na to jak mocno wiatr działa na jednostkę.
            void setObjectWeight( float _fWeight );
           
            // Ustawia siłę odbicia obiektu. Wartość powinna być z przedziału od 0.0f do 1.0f.
            void setReflection( float _fReflection );
           
            // Zwraca siłę odbicia obiektu.
            float getReflection() const;
           
            // Ustawia prędkość początkową obiektu.
            void setVelocity( const sf::Vector2f & _velocity );
           
            // Zwiększa prędkość obiektu.
            void addVelocity( const sf::Vector2f & _velocityDelta );
           
            // Zwraca aktualną prędkość obiektu.
            const sf::Vector2f & getVelocity() const;
           
            // Ustawia obraz jaki ma być używany do kolizji jednostki.
            void setCollisionImage( const sf::Image & _image, bool _bCalcCollisionAngle = false );
           
            // Ustawia współczynnik tarcia jaki ma być stosowany dla gruntu (z przedziału od 0.0f do 1.0f).
            void setGroundFriction( float _friction );
           
            // Zwraca współczynnik tarcia jaki jest stosowany dla gruntu (z przedziału od 0.0f do 1.0f).
            float getGroundFriction() const;
           
            // Zwraca true, jeżeli jest włączone obliczanie kąta odbicia.
            bool isCalculatingCollisionAngle() const;
           
            // Zwraca współczynnik oporu jaki jest stosowany dla wiatru (z przedziału od 0.0f do 1.0f).
            float getAirFriction() const;
           
            // Zwraca współczynnik oporu jaki jest stosowany w wodzie (z przedziału od 0.0f do 1.0f).
            float getWaterFriction() const;
           
            // Zwraca prędkość wiatru.
            float getWindVelocity() const;
           
            // Aktualizuje położenie obiektu.
            void update( float _fDeltaTime, const WormsMap & _map, const sf::Vector2f & _origin );
           
            // Zwraca true, jeżeli worms stoi na terenie.
            bool isOnGround() const;
           
            // Zwraca true, jeżeli obiekt jest poniżej poziomu wody.
            bool isBelowWaterLevel() const;
           
            // Zwraca aktualny stan kolizji.
            const SpriteCollision::State & getCollisionState() const { return m_actualCollisionState; }
           
            // Ustawia delegat jaki będzie wywoływany w celu modyfikacji prędkości na gruncie.
            void setGroundVelocityDelegate( ActionUpdateVelocity _delegate ) { m_delegateGroundVelocity = _delegate; }
           
            // Ustawia delegat jaki będzie wywoływany w celu modyfikacji prędkości na gruncie.
            void setAirVelocityDelegate( ActionUpdateVelocity _delegate ) { m_delegateAirVelocity = _delegate; }
           
            // Renderuje pomocnicze diagnostyczne informacje związane z kolizją.
            void renderDebugCollision( const ddt::gfx::Camera & _camera ) const;
           
            // Zwraca ostatnią znaną prędkość przed wystąpieniem kolizji.
            float getSpeedBeforeCollision() const;
           
            // Czyści ostatnią znaną prędkość przed wystąpieniem kolizji.
            void clearSpeedBeforeCollision();
        private:
            // Zwraca stan kolizji jednostki z mapą dla nowej pozycji.
            SpriteCollision::State calcCollision( const WormsMap & _map, const sf::Vector2f & _newPosition, const sf::Vector2f & _origin );
           
            // Zwraca stan kolizji jednostki z mapą dla nowej pozycji.
            // Zapobiega występowaniu efektu przechodzenia przez ściany.
            // W przypadku wykrycia kolizji modyfikowane jest położenie _newPosition.
            SpriteCollision::State calcCollisionProtectTunneling( const WormsMap & _map, sf::Vector2f & _newPosition, const sf::Vector2f & _origin, float _fMaxStepSize = 1.0f );
           
            // Zapobiega zapadaniu się obiektu w grunt oraz podnosi obiekt do góry tak, aby stał on na terenie.
            sf::Vector2f keepObjectAtGround( const WormsMap & _map, const sf::Vector2f & _origin, const sf::Vector2f & _oldPosition, SpriteCollision::State _oldCollisionState, const sf::Vector2f & _newPosition );
           
        private:
            sf::Vector2f m_position;
            sf::Vector2f m_velocity;
            float m_fWindVelocityX;
            float m_fObjectWeight;
            float m_fReflection;
            float m_fGroundFriction;
            float m_fWaterLevelY;
            float m_fSpeedBeforeCollision;
            SpriteCollision::State m_actualCollisionState;
            SpriteCollision m_collisionDetector;
            ActionUpdateVelocity m_delegateGroundVelocity;
            ActionUpdateVelocity m_delegateAirVelocity;
            std::list < std::pair < sf::Vector2f, int > > m_debugContainer;
        }; //class UnitPhysics
        //===================================================================//
       
    } // namespace cmn
} // namespace worms

#endif //LIBWORMS_COMMON_UNITPHYSICS_HPP
Jak widzisz dokumentacja kodu jest, więc jak będziemy mieli ochotę dorobić np. AI do kodu to i tak możemy do tego projektu szybko powrócić i ze zrozumieniem go dalej rozwijać.

/edit:
Dodam również, że są przeciwnicy dokumentowania kodu, ponieważ wyznają zasadę, że "dokumentacja kodu szybko się deaktualizuje i programista zapomina go uaktualnić jak robić modyfikację kodu". Jeżeli kod jest udokumentowany, a programista nie dba o jego aktualizację, to po prostu nie dba on o jakość kodu. Jeżeli programista twierdzi, że kod się szybko zmienia i może się on zdezaktualizować to znaczy, że modyfikuje on kod gdzie popadnie bez większego zrozumienia i w praktyce generuje nowe błędy w kodzie. Skoro ktoś udokumentował, że funkcja/metoda ma robić określoną funkcjonalność to funkcjonalność ta nie może ulec zmianie (ponieważ zależy od niej inny kod). Bugfixy dążą do uzyskania tego, aby kod działał zgodnie z oczekiwaniami i zgodnie z opisem w dokumentacji. Jeżeli opis jest zły to można go poprawić, aby za x-miesięcy było wiadomo jaka jest odpowiedzialność danej metody/funkcji.
P-165957
» 2017-10-22 13:51:18
W poległym projekcie domowym porażka jest tylko wtedy, jeśli nic z tego projektu nie wyciągniesz. Nie chodzi tylko o naukę, ale też o kod. Projekt umarł - to trudno, ale teraz możesz go zezłomować i wyciągnąć elementy które nadają się do ponownego użycia. Może je trochę popraw, rozbuduj, dopisz testy, itp. Zawsze coś czego nie musisz pisać ponownie, jeśli będzie potrzebne. W przyszłych projektach nie zostawiaj tego na post mortem, tylko od razu oceniaj, czy klasa którą chcesz napisać nie powinna od razu trafić do biblioteki z narzędziami. W nagłówku podanym przez DejaVu <ddt/*> to własnie takie biblioteki, których nazbierało się już prawie 2MB kodu.
P-165961
Temat założony przez niniejszego użytkownika
» 2017-10-22 16:50:28
Ciężko też jest połączyć tyle rzeczy na raz, prosty przykład, próba ataku gracza:
- sprawdź, czy gracz się porusza (trzeba go zatrzymać)
- sprawdź czy skacze (wtedy nie może atakować)
- sprawdź czy w tym momencie już nie atakuje
- sprawdź, czy minął pewien czas między atakami (ażeby nie siekał jak berserk)
- sprawdź, czy w tym momencie nie został odepchnięty (knockback).

- A jak długo on atakuje?
- Tyle ile trwa animacja ataku.
- Ale jak sprawdzić, czy trwa animacja ataku?
- Sprawdzić, czy aktualna tekstura postaci należy do animacji ataku...
 upss, nie da się, bo obiekt postać dostaje od klasy Engine kolejne tekstury i nie ma pojęcia, czym jest ta tekstura. A to pech :)
Pewnie jest jakieś rozwiązanie, ale chciałem zwrócić uwagę na to, że po prostu wiele rzeczy wychodzi później "w praniu" - przy tworzeniu systemu animacji wszystko wyglądało ok.
P-165967
» 2017-10-22 16:59:52
- Ale jak sprawdzić, czy trwa animacja ataku?
- Sprawdzić, czy aktualna tekstura postaci należy do animacji ataku...
Wystarczy np. trzymać status postaci, co dokładnie robi, zamiast porównywać tekstury.

po prostu wiele rzeczy wychodzi później "w praniu"
Wystarczy mieć dobry projekt. Ale żeby go stworzyć, potrzeba wprawy i umiejętności, które przychodzą z czasem. Im więcej tworzysz, tym więcej się uczysz ;)
P-165968
» 2017-10-22 20:55:55
porażka czy lekcja?
doświadczenie

+ do expa za każdym razem

Ja aktualnie mam chyba lvl 5 :D
P-165976
» 2017-10-22 22:12:00

- sprawdź, czy gracz się porusza (trzeba go zatrzymać)
albo zawsze zatrzymać bez sprawdzania.
P-165984
« 1 » 2
 Strona 1 z 2Następna strona