2016-09-16 74 views
0

我正在研究一个简单的游戏引擎,只是为了体验它。不过,我意识到,我不知道如何将用户的自定义游戏作为自己的独立可执行文件导出。例如(这不是我的实际游戏引擎,它只是提供了一个讨论一个简单的参考),假设我们有下面的代码非常简单:在C++中创建一个CUSTOM exe文件

#include "stdafx.h" 
#include <iostream> 
#include <string> 

using namespace std; 

void RunGame(string question, string answer) 
{ 
    string submission; 
    cout << question << endl; 
    getline(cin, submission); 
    if (submission == answer) 
     cout << "Correct!"; 
    else 
     cout << "Wrong!"; 
} 

int main() 
{ 
    string question; 
    string answer; 

    cout << "Enter Question:" << endl; 
    getline(cin, question); 
    cout << "Enter Answer:" << endl; 
    getline(cin, answer); 


    RunGame(question, answer); 
} 

在这个例子中,用户获得创建自己的定制位琐事,然后在RunGame被调用后立即测试它。现在我希望能够以他们自己的.exe提供的琐事信息来保存他们的游戏(基本上它会从调用到RunGame以后执行)。我会怎么做呢?

要清楚的是,这不是一个关于什么是制作游戏最简单/最快的方法的问题。它正在寻找如何从代码中构建独立的可执行文件。

+0

您是否尝试将客户的数据放入数据文件而不是可执行文件? –

+0

为什么你想把这个琐事当作可执行文件?你要*执行琐事数据吗? *(通常,数据不会被执行,而是被程序读取或写入。)* –

+0

数据文件可以与可执行文件一起安装。许多安装程序可以创建包含可执行文件和数据文件的安装。 –

回答

1

构建一个可执行文件并不重要。您首先需要遵守目标操作系统的ABI,以便它可以找到您的程序的入口点。下一步将决定你的程序如何能够访问系统资源:可能你会希望你的可执行文件实现动态链接,以便它可以访问共享库,并且你需要加载各种.dll或者.dll文件。所以你需要的文件。您需要为此编写的所有说明将随操作系统而变化,您可能需要引入逻辑来检测确切的平台并做出明智的决策,而且您需要改变32位和64位。

此时您已准备好开始为游戏发布机器说明。

这里的一个合理的替代方法是(由Unity完成)为您的引擎提供一个“空白”可执行文件。你的引擎本身就是一个共享库(.dll或.so),空白的可执行文件只是一个加载共享库的包装器,并在其中调用一个指向其数据部分的指针的函数。

生成您的用户可执行文件将包括加载适当的空白文件,对其进行特定于平台的修改,以告知其打算提供的数据部分的大小,并以适当的格式编写数据。或者,你可以简单地拥有一个具有在其中你写值的原始结构的嵌入式副本,就像在内存填充一个struct空白:

struct GameDefinition { 
    constexpr size_t AuthorNameLen = 80; 
    char author_[AutherNameLen+1]; 
    constexpr size_t PublisherNameLen = 80; 
    char publisher_[PublisherNameLen+1]; 
    constexpr size_t GameNameLen = 80; 
    char name_[GameNameLen+1]; 
    constexpr size_t QuestionLen = 80; 
    constexpr size_t AnswerLen = 80; 
    char question_[QuestionLen+1]; 
    char answer_[AnswerLen+1]; 
}; 

static GameDefinition gameDef; 

#include "engine_library.h" // for run_engine 

int main() { 
    run_engine(&gameDef); 
} 

你会编译这个againsst共享库存根您的引擎,并将其作为可执行文件发布,然后查找可执行文件的平台特定细节,找到其中的“gameDef”的位置。你会将空白读入内存,然后用基于用户输入的代码替换“gameDef”的定义。

但是很多引擎做的仅仅是运输或要求用户安装一个编译器(团结依靠C#)。因此,他们不必调整可执行文件并完成所有这些疯狂的平台特定的工作,而只需输出一个C/C++程序并编译它。

// game-generator 
bool make_game(std::string filename, std::string q, std::string a) { 
    std::ostream cpp(filename + ".cpp"); 
    if (!cpp.is_open()) { 
     std::cerr << "open failed\n"; 
     return false; 
    } 
    cpp << "#include <engine.h>\n"; 
    cpp << "Gamedef gd(\"" << gameName << "\", \"" << authorName << \");\n"; 
    cpp << "int main() {\n"; 
    cpp << " gd.q = \"" << q << \"\n"; 
    cpp << " gd.a = \"" << a << \"\n"; 
    cpp << " RunGame(gd);\n"; 
    cpp << "}\n"; 
    cpp.close(); 

    if (!invoke_compiler(filename, ".cpp")) { 
     std::cerr << "compile failed\n"; 
     return false; 
    } 
    if (!invoke_linker(filename)) { 
     std::cerr << "link failed\n"; 
     return false; 
    } 
} 

如果“RunGame”是不是你的发动机的一部分,但用户提供的,那么你可以发出,作为CPP代码的一部分。否则,这里的意图是它打电话到你的图书馆。

在Linux下你可能会

g++ -Wall -O3 -o ${filename}.o ${filename}.cpp 

编译这个,然后

g++ -Wall -O3 -o ${filename} ${filename}.o -lengine_library 

把它和你的引擎的链接库。

+0

不知道我的手机有多聪明,希望它清晰可辨。 – kfsone

+0

注意:Visual Studio的编译器等是独立的应用程序,并不内置于可视化ide中。 “invoke_compiler”和“invoke_linker”是你必须写的函数,它们可以像std :: string args =“g ++ -Wall -o”...一样简单; system(args.c_str());'对于linux和'#ifdfef WIN32'部分,它与Windows相当,但是您可能希望看看对子进程的更好的纹理控制。 – kfsone

1

你不想这样做。更好的方法是以某种自定义格式保存琐事(例如.txt,.dat,..)。

然后游戏只处理这些数据。

所以首先想想.txt里面的格式。

让我们先说一个数字,表明这是哪个条目。其次是问题,之后的答案如下。这个,你必须自己决定。

例琐事-的data.txt

1 
How old is actor X from show Y? 
32 years 
2 
... 
... 

#include <iostream> // std::cout, std::endl 
#include <fstream> // std::ifstream, std::ofstream 
using namespace std; 
int main() 
{ 
    // create file 
    ofstream ofile("trivia-data.txt"); 

    // define your data 
    int num_of_question = 1; 
    string question, answer; 
    getline(cin, question); 
    getline(cin, answer); 

    // write your data to the file 
    ofile << num_of_question << '\n'; 
    ofile << question << '\n'; 
    ofile << answer << '\n'; 

    // close the file 
    ofile.close(); 

    return 0; 
} 

既然已经创建了您的数据,你只需要在你想提出这样的方法来建立你的程序。您不应该写入文件,而应该从文件中读取并打印出问题并比较答案和不答案。查阅std::ifstream阅读您的文件。

在开始时,您可以询问您的用户他是想创建一个测验还是玩一个已经存在的测验。


编辑:

因为这听起来很像功课我只是提供一些伪代码。

我会去这样的(伪代码)的方法:

print "Would you like to create(c) or play(p) a quiz? Answer(c/p): " 
input = get_input() // 'c' or 'p' 
if input == 'c' 
    // now do what I posted with some loops to create a couple of questions 
else 
    print "Please provide an URL to the quiz-data you would like to play: " 
    url = get_input() // C:/test.txt 
    // read in data, print out questions, do comparisons and print answers etc 

这远比你的方法更容易,这也使得它可以为别人创造测验不只是你。

+1

我试图说服这条路线的OP,但OP坚持使用可执行文件。 :-( –

+0

@ThomasMatthews是的,我刚刚看到了,哦, –

3

如果你真的将数据存储中的.exe自身内部:

可执行具有定义它的大小,边界和其他有用的东西到操作系统的头,所以,本质上,操作系统知道代码和数据段开始和结束的位置,并且当它被要求运行时,它最终使用这些信息将.exe加载到内存中。

由于操作系统知道(除了.EXE文件的大小),其中可执行实际上结束,这也意味着 .exe文件的“计算”结束(通过头)后粘贴任何数据都不会产生负面影响的二进制文件。它仍然会加载并执行得很好。

您可以在可执行文件结束后滥用此属性来连接数据。

我将离开你与这个测试,使用Windows的捆绑写字板应用程序为‘其他一些数据主机’:

  • 转到C:\ WINDOWS和复制WRITE.EXE(写字板)到另一个文件夹,所以我们可以试验而不会损坏任何东西。

  • 带上该文件夹的另一个文件,任何文件都可以。在我的例子中,数据文件将一个PDF称为“myfancyfile.pdf”

  • 现在,打开一个命令提示符并使用COPY命令这两个文件拼接在一起,确保该.exe至上:

    copy /B write.exe+myfancyfile.pdf mynewprogram.exe 
    
    • 副本的/B标志的意思是“二进制复制”,所以基本上这两个文件都粘贴在一起,没有任何形式的文本或数据转换。
  • 尝试运行“mynewprogram.exe”。实现它运行得很好:-)

自我修改您的.exe数据不仅可行,它不会负面影响功能。话虽如此,但它仍然是一个难以持久数据的方式。

为您的解决方案编码很有趣。

+2

OP也可以修改全局字符串/数组,如果他在'.exe'中添加了某种标记来查找它们的话 – HolyBlackCat

+0

程序如何知道上传的数据是? –

+0

@HolyBlackCat谢谢你,以上和kfsone的评论上面我想我有我的答案 – ArcaneLight

相关问题