我正在创建一个基于文本的,用C ++选择你自己的冒险游戏。
在这个游戏中,您可以选择去哪里,选择做什么等等。
我的问题是,如何防止这种情况变得非常混乱。
示例:
让我们说,在比赛的某个时刻,你可以被问到是去森林还是沙漠。如果你选择沙漠,这就是森林里完全不同的故事情节。
那么我如何阻止我的代码看起来像这样。
if (player goes to the desert)advice? {
/*Whole story line of the desert*/
else if (player goes to the forest) {
/*Whole story line of the forest */
在这些故事情节中会有更多这样的条件,以及更精细的故事情节,所以有什么方法可以在一个单独的文件中编写一个故事线的代码,然后只为该条件运行该文件?无论如何我可以单独做,而不是在条件内写出一切?如果我这样做,代码会很快变得很长,并且难以查看/编辑。
我正在考虑做标题并在写出故事线的标题内部创建函数,所以我只需输入函数,但如果我这样做,那么我就无法访问游戏中的全局变量例如playerName
或playerRace
等
赞赏任何和所有建议。我是C ++的新手所以请原谅我,如果我错过了一些非常明显的东西。
答案 0 :(得分:2)
我将对 Trevor Hickey 状态机命题进行一些扩展,因为这是一个好主意。
首先,您需要意识到您的故事情节可以使用好的旧图表进行建模
在编程术语中,它可能意味着:虚拟故事类
struct Story
{
virtual std::string name() = 0;
virtual int play() = 0;
};
故事弧,它连接故事。它需要一个触发条件,这可能是最后一个故事返回的原因
struct StoryConnection
{
std::string nameStorySource;
std::string nameStoryDestination;
int condition;
};
有了这个,你可以在一边写个别故事,然后分别写故事弧。您还可以通过修改故事弧来调整和修改游戏的逻辑。您可以进行多个游戏,每个游戏只是一组StoryConnections。
逻辑很简单:
Story* s = new InitStateStory;
while(!endOfGame(s))
{
int decision = s.play();
StoryConnection conn = getConnection(s.name(), decision);
Story* nextstory = creatNextStory(conn.nameStoryDestination);
delete s;
s = nextstory;
}
答案 1 :(得分:1)
可能是基于类的解决方案。这个问题引用广泛,所以不太确定哪种设计模式适合。但是,示例类可能是CrossroadsDesicision
,它会导出选项["Go to desert", "Go to city", ...]
并且有一个方法apply
,它应该从数组中接收选项并返回相关的决策类以供下一个使用步骤
修改强> 基类应包含:
possibleDecisions - 一系列可能的决定(你可以在这里使用一个选项类,由一个名字组成(字符串或枚举 - 你应该在这里使用模板)和描述)
申请 - 接受决定,对其采取行动并返回下一个决定的职能
答案 2 :(得分:1)
您需要构建代码。所以你有一个类Player,一个类Place,然后你需要一个数组来存储这些地方,在那个地方发生的任何事情都将由一个虚函数来处理:
编辑:
我已经更改了代码来处理目的地,如果你想在列表中维护目的地以便于添加/删除,你只需要另一个类:
#include <iostream>
#include <vector>
#include <climits>
class Place;
const int PLACE_TAVERN = 0;
const int PLACE_FOREST = 1;
const int PLACE_DESERT = 2;
const int NUMPLACES = 3;
std::vector<Place *>vPlaces;
Place * Destination[UCHAR_MAX];
class Place {
private:
bool connections[NUMPLACES]; // This is a simple and inefficient way of doing it: you can also use a linked list with nodes for more flexibility/efficiency
protected:
int id;
void listConnections()
{
int n = 0;
for (int i=0; i<NUMPLACES; i++) {
if (connections[i]) {
if (n>0) {
std::cout << ", ";
}
else {
n++;
}
std::cout<< vPlaces[i]->name;
}
}
std::cout << std::endl;
}
public:
std::string name;
virtual void describe()
{
std::cout << "You are in " << name << std::endl;
std::cout << "From there you can go to: " ;
this->listConnections();
}
Place(int p, std::string n, char l)
{
id = p;
name = n;
Destination[(int)l] = this;
for (int i=0; i<NUMPLACES; i++) {
connections[i] = false;
}
}
void setConnection(int placeId) {
connections[placeId] = true;
}
bool canGoTo(Place *destination) {
return (NULL != destination) && connections[destination->id];
}
};
class Tavern : public Place {
public:
Tavern() : Place(PLACE_TAVERN, "the (T)avern", 'T') {} // the move letters should be unique
};
class Forest : public Place {
public:
Forest() : Place(PLACE_FOREST, "the (F)orest", 'F') {}
};
class Desert : public Place {
public:
Desert() : Place(PLACE_DESERT, "the (D)esert", 'D') {}
};
int main(void)
{
for (int i = 0; i<UCHAR_MAX; i++) {
Destination[i] = NULL;
}
Tavern* tavern = new Tavern();
Forest* forest = new Forest();
Desert* desert = new Desert();
tavern->setConnection(PLACE_FOREST) ; // you can do this manually or maintain an array of bool
forest->setConnection(PLACE_TAVERN) ;
forest->setConnection(PLACE_DESERT) ;
desert->setConnection(PLACE_FOREST) ;
vPlaces = {tavern, forest, desert};
Place* currentPlace;
Place* newPlace;
currentPlace = tavern;
newPlace = NULL;
char key = 0;
do {
currentPlace->describe();
std::cout << "Choose a destination by their letter or (q)uit?";
std::cin >> key;
do {} while (std::cin.get() != '\n'); // flush keyboard
newPlace = Destination[(int)key];
if (currentPlace->canGoTo(newPlace)) {
currentPlace = newPlace;
}
else if (key != 'q') {
if (NULL == newPlace) {
std::cout << "You cannot go into the void like that!" << std::endl;
}
else {
std::cout << "You cannot go to " << newPlace->name << " from " << currentPlace->name << "!" << std::endl;
}
std::cout << "Press Enter to continue...";
do {} while (std::cin.get() != '\n');
}
} while (key != 'q');
std::cout << "bye" << std::endl;
return 0;
}
编译:
g++ -o file file.cc -Wall -std=c++11
答案 3 :(得分:0)
您可以使用enum
和switch
:
class Player
{
public:
enum class CurrentLocation {Forest, Desert, Undefined};
CurrentLocation currentLocation;
//...
}
//and in checking
switch(player.currentLocation)
{
case Player::CurrentLocation::Forest:
//player is in forest
break;
case Player::CurrentLocation::Desert:
//player is in desert
break;
default:
//player is dead or sth
break;
}
您还可以将所有内容包装在类或函数中,并将其地址存储在player
对象中,这样您甚至不必检查状态player
,您只需编写:< / p>
class Player
{
Location* currentLocation;
public:
void setCurrentLocation(Location* loc) {currentLocation = loc;}
Location* getCurrentLocation(void) {return currentLocation;}
//...
}
//and use it, of course you have to implement Location class/struct
player.currentLocation()->showMap();
但选择好的游戏设计是一个复杂的主题,并不意味着“简单回答”