我做了一个状态机。
您可以按0和1在假设的菜单和播放状态之间切换。
我希望你能帮我进一步改进。
#pragma once
#ifndef StateManager_H
#define StateManager_H
#include <iostream>
#include "State.h"
/*
State classes
*/
class StateManager {
public:
// Define m_current_state upon the creation of this class (since that prevents there being a nullptr error in change_state
StateManager(State* state)
: m_current_state(state)
{}
void change_state(State* state) {
m_current_state->on_exit();
m_current_state = state;
m_current_state->on_enter();
}
void update_state(StateManager* manager) {
m_current_state->on_update(manager);
}
State* get_state() {
return m_current_state;
}
private:
State* m_current_state;
};
#endif#pragma once
#ifndef State_H
#define State_H
#include "StateManager.h"
// I'm using forward declaration here since I've had a problem with circular dependency
class StateManager;
class State {
public:
// All states have to have these three functions
virtual void on_enter() = 0;
virtual void on_update(StateManager* state_manager) = 0;
virtual void on_exit() = 0;
};
#endif#pragma once
#ifndef IntroState_H
#define IntroState_H
#include "StateManager.h"
#include "State.h"
#include "GameState.h"
/*
Basic test state for demonstration
*/
class MenuState : public State {
public:
MenuState();
virtual void on_enter() override;
virtual void on_update(StateManager* state_manager) override;
virtual void on_exit() override;
private:
bool should_change_state;
};
#endif#include "IntroState.h"
MenuState::MenuState()
: should_change_state(false)
{}
void MenuState::on_enter(){
std::cout << "Entering the menu state" << std::endl;
}
void MenuState::on_update(StateManager* state_manager){
// update the menu
std::cout << "Should the state be changed to the playing state" << std::endl;
std::cin >> should_change_state;
if (should_change_state == true) {
state_manager->change_state(new PlayingState());
}
}
void MenuState::on_exit(){
std::cout << "Exiting the menu state" << std::endl;
}#pragma once
#ifndef GameState_H
#define GameState_H
#include "IntroState.h"
#include "State.h"
/*
Basic test state for demonstration
*/
class PlayingState : public State {
public:
PlayingState();
virtual void on_enter() override;
virtual void on_update(StateManager* state_manager) override;
virtual void on_exit() override;
private:
bool should_change_state;
};
#endif#include "GameState.h"
PlayingState::PlayingState()
: should_change_state(false)
{}
void PlayingState::on_enter() {
std::cout << "Entering the playing state" << std::endl;
}
void PlayingState::on_update(StateManager* state_manager) {
// update the menu
std::cout << "Should the state be changed to the menu state" << std::endl;
std::cin >> should_change_state;
if (should_change_state == true) {
state_manager->change_state(new MenuState());
}
}
void PlayingState::on_exit() {
std::cout << "Exiting the playing state" << std::endl;
}#include "StateManager.h"
#include "State.h"
#include "IntroState.h"
int main() {
StateManager* manager = new StateManager(new MenuState());
int exit = 0;
while (exit != 1) {
manager->update_state(manager);
}
return 0;
}谢谢!
发布于 2018-10-09 10:14:30
在我忘记它之前(因为它在您的代码中是外围的),我将指出new StateManager(new MenuState());,它是不属于C++的。原始指针是危险的!如果之后不删除分配的内存,就会留下漏洞(而不是这样)。因此,使用智能指针(std::unique_ptr或std::shared_ptr)代替。这是一个很有趣的话题,但你必须自己学习。或者更好的是,在堆栈上分配对象(State_manager manager;)
manager->update_state(manager);
有点奇怪,你不觉得吗?写manager->update_state(other_manager)意味着什么?可能不多。因此,您可以像这样重写update_state方法:
void update_state() {
m_current_state->on_update(this); // instead of current->on_update(removed_argument)
}它更干净,完全等价。但是,你必须写这样的东西:
同时(退出!= 1) { manager->update_state();}
这并不明显。外部输入从何而来?在线程之后,我看到state本身负责管理转换:它将询问用户输入和控制是否从当前状态(即自身)更改到下一个状态(它也必须确定)是否可行。不用说,这是否是一个好的设计,我不知道它是否是有用的,无论如何,有一个国家的经理。它的唯一角色似乎是调用on_enter和on_exit,但将其放入State类并不困难,因为on_enter和on_exit是其接口的一部分(甚至可以放弃它;只需在update函数的开头和结尾添加指令)。它还跟踪当前状态,但是一个简单的current_state变量可以做到这一点:
std::unique_ptr<State> current_state = std::make_unique<Intro_state>();
while (exit != 1) current_state->update();但是,您可能也会想知道,为什么这个exit不在状态机之外。如果有一个内部状态,一个游戏状态
在这一点上,我想休息一下,告诉您用同义词或它包含的类的引语命名您的文件是一个非常糟糕的想法。
Game_state类应该在Game_state.h中,而不是在gaming_state.h中,这没有任何意义。
那为什么没有退出状态呢?它可以做一些清理,显示学分,我不知道。所以现在我们有:
std::unique_ptr<State> current_state = std::make_unique<Intro_state>();
current_state->update();但是current_state变量本身以及随之而来的分配也是多余的。Intro_state().update();看起来会很奇怪,但是intro();呢?因此,我们有一个函数void intro(),它询问它是需要调用void game()还是void exit(),还有一个void game(),它询问是否需要再次调用void intro()或void exit()。设计模式已经消失,但我们没有删除任何功能或可用性功能,只是简化了。从这个观察,我觉得我可以安全地得出结论,在你的设计中有一个问题。
许多C++手册坚持使用面向对象的语言和所谓的设计模式。这并不是,IMHO (也就是说,它不是我自己的,而是被C++现场的大多数著名人物所采用的),是学习C++的好方法。尝试探索语言的鲁棒性或多功能性。
发布于 2018-10-09 09:03:45
只在该文件中包含实际需要的标题。无需在StateManager.h中使用#include <iostream>。
到处都是泄密。你new每个州,但我没有看到任何地方你delete他们。使用智能指针,或者让州清理自己的on_exit。不过,我更喜欢聪明的指针。
在change_state中调用on_update将导致令人惊讶的效果,因为在此期间调用了on_exit。
相反,返回下一个状态:
void update_state(StateManager* manager) {
unique_ptr<State> tmp = m_current_state->on_update(manager);
if(tmp != nullptr)
change_state(std::move(tmp));
}发布于 2018-11-08 13:54:57
在我看来,游戏是一个地方的原始指针是可以接受的。它们是可以的,因为您希望严格控制对象的创建和销毁,并且通常在活动播放期间避免内存分配,因此您从智能指针获得的功能不是特别有价值的,主要是在Debug构建中混乱代码和减慢速度。也就是说,如果您正在学习C++,您确实想学习智能指针;它们是重要的和有用的。
无论如何,您滥用原始指针是因为您没有仔细管理对象的生存期。您必须、必须、必须考虑对象在何时何地会在新对象更新时被删除。你不能忽视这一点。就你而言,我认为你做错了:
您可能希望只创建一次状态(将它们存储在将管理其生存期的对象中),而不是每次进入菜单状态时执行new MenuState操作,然后在更改状态时将指向现有新状态的指针传递给StateManager。您还可以考虑隐藏指针,而是为新状态传递一条合适的消息。
当状态发生变化时,您可能希望将更改推迟到游戏更新循环中的适当点,这样您就可以正确地同步菜单上的声音/图形/等等。您还可能希望将退出和条目分离,以便您可以从一个菜单转换到另一个菜单,这意味着您希望在下一个菜单在接下来的几个框架中开始其条目之前,通过几个框架更新其退出。
https://codereview.stackexchange.com/questions/205202
复制相似问题