我接受了一个人的游戏设计,现在我遇到了问题。
下面是Base类的实现,其中定义了窗口的所有初始参数,并将其所有字段带入全局作用域。
Base.h:
#pragma once
#include "Define.h"
#include "Audio.h"
#include "Font.h"
#include "Texture.h"
#include "GameObject.h"
class Base {
public:
static sf::RenderWindow wnd;
static Texture texture;
static Font font;
static Audio audio;
static sf::Event event;
static int scr_w;
static int scr_h;
static v2f cur_p;
static v2f cur_p_wnd;
static vector<unique_ptr<GameObject>> objects;
static float time;
static void SystemUpdate() {
time = float(clock.getElapsedTime().asMicroseconds()) / 1000.f, clock.restart();
cur_p = wnd.mapPixelToCoords(sf::Mouse::getPosition(wnd));
cur_p_wnd = v2f(sf::Mouse::getPosition(wnd));
}
static void CloseEvent() {
if (event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && event.key.code == Keyboard::Escape)) wnd.close();
}
static bool IsKeyPressed(const sf::Keyboard::Key& code) {
if (event.type == sf::Event::KeyPressed)
if (event.key.code == code) return true;
return false;
}
static bool IsKeyReleased(const sf::Keyboard::Key& code) {
if (event.type == sf::Event::KeyReleased)
if (event.key.code == code) return true;
return false;
}
template <class T>
static void new_object(T* obj) {
B::objects.push_back(unique_ptr<GameObject>(obj));
}
template <class T>
static void delete_object(T* obj) {
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
if ((*itr)->ID == obj->ID) {
B::objects.erase(itr);
return;
}
}
}
static bool IsMouseInRect(Shape& s) {
return s.getGlobalBounds().contains(B::wnd.mapPixelToCoords(sf::Mouse::getPosition(B::wnd)));
}
Base(string init) {
if (init == "init") {
scr_w = sf::VideoMode::getDesktopMode().width;
scr_h = sf::VideoMode::getDesktopMode().height;
font = Font();
texture = Texture();
audio = Audio();
wnd.create(sf::VideoMode(scr_w, scr_h), "Sea Battle", sf::Style::Close, sf::ContextSettings(0, 0, 8));
cur_p = v2f(0, 0);
cur_p_wnd = v2f(0, 0);
wnd.setMouseCursorVisible(true);
wnd.setFramerateLimit(30);
srand(::time(0));
}
}
Base(void) {}
private:
static sf::Clock clock;
};
sf::RenderWindow B::wnd;
Texture B::texture;
Font B::font;
Audio B::audio;
sf::Event B::event;
int B::scr_w;
int B::scr_h;
v2f B::cur_p;
v2f B::cur_p_wnd;
float B::time;
sf::Clock B::clock;
vector<unique_ptr<GameObject>> B::objects;
它包含在Game类中,在Game类中调用它的构造函数。
Game.cpp:
#include "Game.h"
Game::Game() {
B("init");
game_state = GameState::Planning;
GameObject* background = new GameObject(v2f(5000, 5000));
background->getRect().setFillColor(Color(255, 255, 255));
new_object(background);
new_object(new Field());
}
void Game::Update() {
switch (game_state) {
case GameState::MainMenu:
break;
case GameState::Planning:
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
(*itr)->Update();
}
break;
case GameState::Settings:
//
break;
case GameState::Game:
//
break;
default: break;
}
UpdateUI();
}
void Game::Action() {
switch (game_state) {
case GameState::MainMenu:
break;
case GameState::Planning:
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
(*itr)->Action();
}
break;
case GameState::Settings:
//
break;
case GameState::Game:
//
break;
default: break;
}
ActionUI();
}
void Game::Draw() {
wnd.clear();
switch (game_state) {
case GameState::MainMenu:
break;
case GameState::Planning:
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
(*itr)->Draw(wnd);
}
break;
case GameState::Settings:
//
break;
case GameState::Game:
//
break;
default: break;
}
DrawUI();
wnd.display();
}
void Game::UpdateUI() {}
void Game::ActionUI() {}
void Game::DrawUI() {}
void Game::Play() {
while (wnd.isOpen()) {
SystemUpdate();
Update();
while (wnd.pollEvent(event)) {
CloseEvent();
Action();
}
Draw();
}
}
Game::~Game(){}
int main() {
Game game;
game.Play();
return 0;
}
主要的游戏循环在game类中,这个类必须为对象向量中包含的每个对象调用Draw, Action, Update方法。(对象向量在Base.h)
但是,如果我创建任何新的对象类,并尝试实现它的绘制,动作,更新方法在其中,那么我将无法在他们所有的变量在Base.h中使用,带到全局作用域,没有他们,我不能正确地实现我的对象,我怎么能改变架构,使一切工作?
///例如,如果我希望对象的位置改变为光标的当前位置,那么我需要Base.h中的一个变量,我将其命名为B::cur_p,但是编译器只是给出一个错误:&;unknown authenticator&;
全局变量的答案是不全局变量(就像优化一样,只有在你知道它值得的时候才使用它们)。它不利于可维护性,也不利于测试!(在大型项目中,对组件依赖和构建时间都不利)如果你想了解架构,现在是阅读依赖注入的好时机。例如https://en.wikipedia.org/wiki/Dependency_injection
下面是你的游戏代码示例:
#include <vector>
//-----------------------------------------------------------------------------
// just some structs to be able to use names from your Game to recognize.
struct RenderWindow {};
struct Texture {};
struct Font {};
struct Audio {};
struct GameObject
{
void update() {};
};
// etc
//-----------------------------------------------------------------------------
// global_data_itf.h
// model of a screen, those values belong together
struct screen_size_t
{
unsigned int width{ 3440 };
unsigned int height{ 1440 };
};
// Now define an interface for accessing your data.
// This also allows you
// to create mocks (e.g. with different screen sizes) for testing
// not something you can do with static or real global variables
//
// Note you should really make functions const and return const&
// if data is readonly!
//
class global_data_itf
{
public:
virtual RenderWindow& render_window() = 0;
virtual Texture& texture() = 0;
virtual Font& font() = 0;
virtual Audio& audio() = 0;
virtual const screen_size_t& screen_size() = 0;
// unique_ptr probably not needed. Give GameObject a move constructor
// and emplace gameobjects
virtual std::vector<GameObject>& objects() = 0;
virtual float& time() = 0;
virtual ~global_data_itf() = default;
protected:
global_data_itf() = default;
};
//-----------------------------------------------------------------------------
// global_data.h
// #include "global_data_itf.h"
// For the game you need a default implementation of the global_data_itf
// this is it.
class global_data final :
public global_data_itf
{
// todo constructor with proper initialization
virtual RenderWindow& render_window() override
{
return m_render_window;
}
virtual Texture& texture() override
{
return m_texture;
}
virtual Font& font() override
{
return m_font;
}
virtual Audio& audio() override
{
return m_audio;
}
virtual const screen_size_t& screen_size() override
{
return m_screen_size;
}
virtual std::vector<GameObject>& objects() override
{
return m_objects;
}
virtual float& time() override
{
return m_time;
}
private:
RenderWindow m_render_window;
Texture m_texture;
Font m_font;
Audio m_audio;
screen_size_t m_screen_size;
std::vector<GameObject> m_objects;
float m_time;
};
//-----------------------------------------------------------------------------
// Game.h
// #include "global_data_itf.h"
class Game
{
public:
explicit Game(global_data_itf& data);
void Update();
private:
global_data_itf& m_data;
};
//-----------------------------------------------------------------------------
// Game.cpp
// include "game.h"
Game::Game(global_data_itf& data) :
m_data{ data }
{
}
void Game::Update()
{
for (auto& object : m_data.objects())
{
object.update();
}
}
//-----------------------------------------------------------------------------
// main.cpp
// #include "global_data.h"
// #include "game.h"
int main()
{
global_data data;
Game game(data); // inject the dependency on data into game.
// if you have other classes needing access to global data
// you can inject global_data into those classes as well.
return 0;
}
首先,如果你想从任何地方访问你的变量,你可以把它们变成真正的全局变量,把它们放在头文件中,等等。如果我没弄错的话,这就解决了整个问题,因为变量和方法是分开的。