Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Nimelrian

Alter Hase

  • »Nimelrian« ist der Autor dieses Themas

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

1

11.04.2014, 12:32

Mögliche (logische) Optimierung?

Huhu zusammen,

bevor gleich die Schreie kommen "Hör auf mit premature optimization": Es geht mir hier nicht um die Optimierung der Performance (Da wäre der erste Anhaltspunkt wahrscheinlich multi-threading), sondern um ein kleines Review meines Codes bzw. insbesondere der Logik. Kann ich mir hier und da Zeilen sparen, mache ich hier etwas doppelt, etc.

Von daher, hier der Code, lasst euch dran aus :D

.hpp:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#pragma once
#include <SFML/Graphics.hpp>
#include "StateManager.hpp"
#include "Background.hpp"
#include "Terrain.hpp"
#include "Base.hpp"
#include "ResourceManager.hpp"
#include "ParticleSystem.hpp"
#include "Missile.hpp"
#include <vector>

#ifdef _DEBUG
#include <sstream>
#endif

class Game {
public:
    Game();
    void run();

private:
    void handleEvents();
    void spawnEnemyMissile();
    void spawnPlayerMissile();
    void updateMissiles();
    void updatePlayerMissiles();
    void updateEnemyMissiles();
    bool basesAlive();

    sf::RenderWindow window;
    ResourceManager resMan;
    Background bg;
    Terrain terrain;
    ParticleSystem pSys;
    StateManager stateManager;

    std::vector<std::unique_ptr<Missile> > enemyMissiles;
    std::vector<std::unique_ptr<Missile> > playerMissiles;

    sf::Clock clock;
    float elapsedTimeInSec = 0.f;

#ifdef _DEBUG
    sf::Font font;
    sf::Text fpsText;
#endif
};

.cpp:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include "Game.hpp"

/*
 *  Initializes members.
 */
Game::Game() :
    window(sf::VideoMode(800, 600), "Lunar Defense"),
    resMan("res/img/"),
    bg (&resMan),
    terrain(&resMan) {

    srand(static_cast<unsigned int>(time(NULL)));
    window.setVerticalSyncEnabled(true);
#ifdef _DEBUG
    font.loadFromFile("res/fonts/consola.ttf");
    fpsText.setFont(font);
    fpsText.setCharacterSize(16);
    fpsText.setPosition(sf::Vector2f(0.f, 600.f - 16.f));
    fpsText.setColor(sf::Color::Yellow);
#endif
}

/*
 *  Main game loop.
 */
void Game::run() {
    while (window.isOpen()) {
        elapsedTimeInSec = clock.restart().asSeconds();
        
        handleEvents();

        window.clear();
        window.draw(bg);
        window.draw(terrain);

        updateMissiles();
        pSys.update(elapsedTimeInSec);
        pSys.render(&window);

#ifdef _DEBUG
        int fps = static_cast<int>(1 / elapsedTimeInSec + .5f);
        std::stringstream ss;
        ss << fps;
        fpsText.setString(ss.str());
        window.draw(fpsText);
#endif _DEBUG

        window.display();
    }
}

/*
 * Polls events and reacts accordingly.
 */
void Game::handleEvents() {
    sf::Event event;
    while (window.pollEvent(event)) {
        if ((event.type == sf::Event::Closed) ||
            (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)) {

            window.close();
        }

        if (event.type == sf::Event::MouseButtonPressed &&
            event.mouseButton.button == sf::Mouse::Left) {

            spawnPlayerMissile();
        }

        if (event.type == sf::Event::KeyPressed &&
            event.key.code == sf::Keyboard::Space) {

            spawnEnemyMissile();
        }
    }
}

/*
 *  Spawns an enemy missile from a random location at the top of the screen.
 *  The target is a random undestroyed base.
 */
void Game::spawnEnemyMissile() {
    if (basesAlive()) {
        Base* rBase = nullptr;
        rBase = terrain.getBase(Base::slots(random(0, 4)));
        while (!rBase->isAlive()) {
            rBase = terrain.getBase(Base::slots(random(0, 4)));
        }

        sf::Vector2f rBasePos = rBase->getPosition();
        std::unique_ptr<Missile> missile(new Missile(&resMan,
            sf::Vector2f(randomf(0.f, 800.f), 0.f),
            rBasePos,
            2.f,
            &pSys));
        enemyMissiles.push_back(std::move(missile));
    }
}

/*
 *  Spawns a new missile of the player from the base which is nearest to the mouse cursor and still alive.
 *  The missile's target is the position of the mouse cursor.
 */
void Game::spawnPlayerMissile() {
    if (basesAlive()) {
        Base* rBase = nullptr;
        
        for (size_t i = 0; i < 4 && rBase == nullptr; i++) {
            if (terrain.getBase(Base::slots(i))->isAlive()) {
                rBase = terrain.getBase(Base::slots(i));
            }
        }
        sf::Vector2f clickPosition(sf::Mouse::getPosition(window));
        sf::Vector2f dVector = rBase->getPosition() - clickPosition;
        float minDistance = length(dVector);
        float baseDistance = 0.f;
        Base* minBase = rBase;

        for (size_t i = 0; i < 4; i++) {
            if (terrain.getBase(Base::slots(i))->isAlive()) {
                rBase = terrain.getBase(Base::slots(i));
                dVector = rBase->getPosition() - clickPosition;
                baseDistance = length(dVector);
                if (baseDistance < minDistance) {
                    minDistance = baseDistance;
                    minBase = rBase;
                }
            }
        }

        sf::Vector2f minBasePos = minBase->getPosition();

        std::unique_ptr<Missile> missile(new Missile(&resMan,
            minBasePos,
            clickPosition,
            2.f,
            &pSys));
        playerMissiles.push_back(std::move(missile));
    }
}

/*
 *  Updates enemy and player missiles.
 */
void Game::updateMissiles() {
    updatePlayerMissiles();
    updateEnemyMissiles();
}

/*
 *  Updates player missiles according to elapsed time.
 *  Checks if a missile has reached it's target in which case the missile get destroyed.
 */
void Game::updatePlayerMissiles() {
    for (auto m = playerMissiles.begin(); m != playerMissiles.end();) {
        bool reachedTarget = false;
        sf::Vector2f mPos = (**m).getPosition();
        sf::Vector2f tPos = (**m).getTargetPosition();
        if (length(mPos - tPos) < 10.f) {
            reachedTarget = true;
        }

        if (reachedTarget) {
            m = playerMissiles.erase(m);
        }
        else {
            (**m).update(elapsedTimeInSec);
            window.draw(**m);
            m++;
        }
    }
}

/*
 *  Updates enemy missiles according to elapsed time.
 *  Checks if a missile has reached it's target base in which case base and missile get destroyed.
 */
void Game::updateEnemyMissiles() {
    for (auto m = enemyMissiles.begin(); m != enemyMissiles.end();) {
        (**m).update(elapsedTimeInSec);
        window.draw(**m);
        bool collision = false;
        for (size_t i = 0; (i < Terrain::BASE_COUNT && !collision); i++) {
            Base* b = terrain.getBase(Base::slots(i));
            if (b->isAlive() &&
                b->getBoundingRect().contains((**m).getPosition())) {
                collision = true;
                b->destroy();
            }
        }

        sf::FloatRect viewRect = sf::FloatRect(sf::Vector2f(0, 0), window.getDefaultView().getSize());
        if (collision || !(viewRect.contains((**m).getPosition()))) {
            m = enemyMissiles.erase(m);
        }
        else {
            m++;
        }
    }
}

/*
 *  Returns true if there is at least one base have not been destroyed so far.
 *  Returns false if all bases have been destroyed.
 */ 
bool Game::basesAlive() {
    bool baseAlive = false;
    for (size_t i = 0; i < 4 && !baseAlive; i++) {
        if (terrain.getBase(Base::slots(i))->isAlive()) {
            baseAlive = true;
        }
    }
    return baseAlive;
}


Wie immer bin ich für jede (sinnvolle) Kritik offen.

Danke im Voraus

Nim
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

2

11.04.2014, 13:16

C-/C++-Quelltext

1
2
std::unique_ptr<Missile> missile(new Missile(...));
enemyMissiles.push_back(std::move(missile));

Du brauchst kein Temp-Objekt:

C-/C++-Quelltext

1
enemyMissiles.emplace_back(new Missile(...));


Wieso eigentlich ein vector von Zeigern?

C-/C++-Quelltext

1
std::vector<std::unique_ptr<Missile> > enemyMissiles;

Speicher doch die Missiles direkt in einem Vector.
"Theory is when you know something, but it doesn’t work. Practice is when something works, but you don’t know why. Programmers combine theory and practice: Nothing works and they don’t know why." - Anon

eXpl0it3r

Treue Seele

Beiträge: 386

Wohnort: Schweiz

Beruf: Professional Software Engineer

  • Private Nachricht senden

3

11.04.2014, 13:52

Wieso eigentlich ein vector von Zeigern?

C-/C++-Quelltext

1
std::vector<std::unique_ptr<Missile> > enemyMissiles;

Speicher doch die Missiles direkt in einem Vector.

Wenn du kein Polymorphism brauchst, dann verwende wie gesamt direkt ein vector von Missiles.

Falls du ein Smartpointer auf die Missile behältst, dann kannst du nur eine Forward-Deklaration für die Missile Klasse machen, anstatt den ganzen Header zu inkludieren.

Deine beiden Spawn Funktionen enthalten zu einem grossen Teil den selben Code, da könnte man also noch generalisieren und z.B. eine Entity Factory erstellen, welche dann sowohl Players wie auch Enemies erstellen kann. Ähnliches gilt für die Update Funktionen.
Im Allgemeinen, wenn man ein paar Funktionen, welche mit dem selben "Prefix" anfangen (updateX, updateY, updateZ, etc.), dann gibt es da meist einen generischeren Weg. Wie weit es dann jedoch Sinn macht zu generalisieren, muss natürlich auch immer beachtet werden.

Darüber lässt sich streiten, aber ich fügen zu meinen Membervariable immer noch ein m_ Prefix hinzu, so dass es bei der Implementierung sofort klar ist, von wo diese Variable nun kommt.

Ich persönliche würde jetzt die ElapsedTime bei der Funktion übergeben, anstatt als Membervariable zu speichern.

srand wird per C++14 als deprecated markiert sein, also tust du gut jetzt schon auf den <random> header umzusteigen.

Evtl. könnte man den allgemeinen Alive Status auch anders tracken, z.B. beim Erstellen und Zerstören mitzählen, dann wäre eine Abfrage eine Instruktion und man muss nicht über den ganzen vector iterieren.

Gibt sicher noch das eine oder andere, aber dies sind die Dinge, welche mir so schnell aufgefallen sind. ;)
Blog: https://dev.my-gate.net/
—————————————————————————
SFML: https://www.sfml-dev.org/
Thor: http://www.bromeon.ch/libraries/thor/
SFGUI: https://github.com/TankOs/SFGUI/

Werbeanzeige