ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

记录自己从零开发2D游戏引擎的过程(1)

2022-07-31 16:05:08  阅读:192  来源: 互联网

标签:std cursor char code 游戏 int 2D instance 引擎


这个项目已发布在github上:https://github.com/zenitsh/glbox2d 因此以后不会贴大段代码。

6.创建Object类

Unity使用了GameObject存储游戏对象,Godot Engine使用了Node,因此我们也需要一个游戏对象类。这个过程中也踩了不少坑,大多数是C++的坑。

上次说b2Body可以直接使用,但是实际上它用的是自己的内存回收机制,因此不能用delete直接删除。因此为了将b2Body和其他对象统一起来,还是需要写一个类来包装它们。这个类就起名叫GB2Pack(我不是很会起名)。析构函数是虚函数,这样它销毁的时候就会自动选择析构函数。这里特别建了两种Pack:析构存储对象的Pack,和不析构存储对象的Pack。

class GB2BodyPack : public GB2Pack{
    public:
    GB2BodyPack(b2Body * body):m_body(body){
    }
    virtual ~GB2BodyPack(){
        m_body->GetWorld()->DestroyBody(m_body);
    }
    b2Body * m_body;
    b2Body * GetBody(){
        return m_body;
    }
};

然后就是写Object基类了,其实是主要为了派生出两种子类:字典对象和数组对象。暂时使用STL库提供的容器,设计上是可以任意选择喜欢的容器,所以使用STL的部分全写在头文件里。

特别注意的是,is_storage用于标记所有权,如果标记了storage,那么该GB2Pack存储的对象也会随GB2Pack销毁而销毁。

class GB2BaseObject{
public:
    GB2BaseObject();
    virtual ~GB2BaseObject();
    virtual void DebugPrint(unsigned int level);
    void SetName(std::string name);
    void SetInt(int n);
    int GetInt();
    void SetFloat(float f);
    float GetFloat();
    void SetPointer(GB2Pack * p, bool is_storage);
    GB2Pack * GetPointer();
    unsigned int m_type;
    std::string m_name;
    char m_data[64];
};

这里只贴出字典对象的实现

class GB2Object : public GB2BaseObject{
public:
    GB2Object():GB2BaseObject(){
        m_type = GB2_TYPE_OBJECT;
    }
    virtual ~GB2Object(){
        for(std::map<std::string, GB2BaseObject*>::iterator i=m_children.begin();i!=m_children.end();i++){
            delete i->second;
        }
    }
    virtual void DebugPrint(unsigned int level){
        GB2BaseObject::DebugPrint(level);
        for(int j=level;j>0;j--)std::cout<<"\t";std::cout<<"{\n";
        for(std::map<std::string, GB2BaseObject*>::iterator i=m_children.begin();i!=m_children.end();i++){
            for(int j=level;j>0;j--)std::cout<<"\t";std::cout<<i->first<<":\n";
            i->second->DebugPrint(level+1);
        }
        for(int j=level;j>0;j--)std::cout<<"\t";std::cout<<"}\n";
    }
    std::map<std::string, GB2BaseObject*> m_children;
    void SetChild(std::string name, GB2BaseObject* object){
        m_children[name] = object;
    }
    void DeleteChild(std::string name){
        m_children.erase(name);
    }
    GB2BaseObject* operator[](std::string name){
        std::map<std::string, GB2BaseObject*>::iterator i=m_children.find(name);
        if(i==m_children.end()){
            return nullptr;
        }
        return i->second;
    }
};

7.游戏脚本的实现

有了游戏对象,需要一个游戏脚本来驱动。这个脚本必须是容易更改的,以满足不同种类游戏的需求,并且方便mod的编写。如果选择编译型语言的话,会要求游戏或游戏开发工具附带一个编译器,而Python之类的脚本语言运行环境体积太庞大了。故选择自己实现一个解释型语言。

整个脚本运行在一个GB2Instance里面,这个对象存储了一个游戏运行时的几乎所有参数。

由于学过汇编,所以就按照汇编的思路写了。用32个int伪寄存器,32个float伪寄存器,以及两个字符串伪寄存器(用的是std::string),一个指针伪寄存器存储输入参数,这样就可以把执行的命令的函数指针统一成一个格式。

用栈来存储要操作的对象的指针,但是所有的对象都挂在m_root下。默认栈顶是主要操作的对象。m_root是一个数组,可以存储需要大量访问的数据,而不用查找map消耗时间。

int m_reg_int[32];
float m_reg_float[32];
std::string m_reg_str_1;
std::string m_reg_str_2;
char * m_reg_pointer;

std::stack<GB2BaseObject*> m_stack;
GB2Array * m_root;

typedef void (GB2Instance::*SingleFunc)();

然而这里踩坑了,执行类中的函数指针时,即使是在类的成员函数中,也必须使用这样一种语法:

(this ->* m_func[temp])();

其中一个函数的写法:

    void AddObject(){
        GB2BaseObject * obj = new GB2Object();
        ((GB2Object*)m_stack.top())->SetChild(m_reg_str_1, obj);
        obj->SetName(m_reg_str_1);
        m_stack.push(obj);
    }

暂时没有写对数组操作的函数,先试验一下效果。

test2.cpp

#include "glbox2d/glbox2d.h"

int main(){

    GB2Instance * p_instance = new GB2Instance();

    p_instance->Init();

    p_instance->SetRegString1("stage");
    p_instance->AddObject();

    p_instance->SetRegString1("ground");
    p_instance->AddObject();

    p_instance->SetRegFloat(0, 0.0f);
    p_instance->SetRegFloat(1, -10.0f);
    p_instance->SetRegFloat(2, 50.0f);
    p_instance->SetRegFloat(3, 10.0f);
    p_instance->SetRegInt(0,0);

    p_instance->CreateBody();

    p_instance->PopObject();

    p_instance->SetRegString1("box");
    p_instance->AddObject();

    p_instance->SetRegFloat(0, 0.0f);
    p_instance->SetRegFloat(1, 4.0f);
    p_instance->SetRegFloat(2, 1.0f);
    p_instance->SetRegFloat(3, 1.0f);
    p_instance->SetRegInt(0, 1);

    p_instance->CreateBody();

    p_instance->PopObject();

    p_instance->SetRegString1("ui");
    p_instance->AddObject();

    p_instance->SetRegString1("info");
    p_instance->AddObject();

    p_instance->DebugPrint();

    p_instance->Run();

    delete p_instance;

    return 0;

}

运行结果:

[unnamed]empty
[
        [unnamed]empty
        {
        stage:
                [stage]empty
                {
                box:
                        [box]pointer
                        {
                        }
                ground:
                        [ground]pointer
                        {
                        }
                ui:
                        [ui]empty
                        {
                        info:
                                [info]empty
                                {
                                }
                        }
                }
        }
]

8.代码的解释和编译

目前没有想到好的方法,只能用switch来控制不同种类的操作,效率可能会比较低。以后会全部替换成函数指针。std::string以后也会换。

    void ReadCode(const char* code, unsigned int length){
        unsigned int cursor = 0;
        unsigned int temp;
        char op;
        while(cursor<length){
            op = *(char*)(code+cursor);
            std::cout<<"Read: "<<(int)op<<"\n";
            cursor += sizeof(char);
            switch(op){
                case 0:
                    temp = *(unsigned int*)(code+cursor);
                    cursor+=sizeof(unsigned int);
                    (this ->* m_func[temp])();
                    break;
                case 1:
                    temp = *(unsigned int*)(code+cursor);
                    cursor+=sizeof(unsigned int);
                    m_reg_float[temp] = *(float*)(code+cursor);
                    cursor+=sizeof(float);
                    break;
                case 2:
                    temp = *(unsigned int*)(code+cursor);
                    cursor+=sizeof(unsigned int);
                    m_reg_float[temp] = *(float*)(code+cursor);
                    cursor+=sizeof(float);
                    break;
                case 3:
                    m_reg_str_1 = (char*)(code+cursor);
                    while(*(char*)(code+cursor))cursor+=sizeof(char);
                    cursor+=sizeof(char);
                    break;
                case 4:
                    m_reg_str_2 = (char*)(code+cursor);
                    while(*(char*)(code+cursor))cursor+=sizeof(char);
                    cursor+=sizeof(char);
                    break;
            }
        }
    }

粗略地写一个简单的编译器,测试下。

test3.cpp

#include <glbox2d/glbox2d.h>

int main(){
    char source[1024] =
    "3 stage\n"
    "0 3\n"
    "3 ground\n"
    "0 3\n"
    "2 0 0.0\n"
    "2 1 -10.0\n"
    "2 2 50.0\n"
    "2 3 10.0\n"
    "1 0 0\n"
    "0 100\n"
    "0 2\n"
    "0 0\n";
    char code[1024];
    unsigned int len;
    gb2CompileCode(code, &len, source);
    std::cout<<"Compiled:\n";
    for(int i=0;i<len;i++)std::cout<<std::hex<<(int)code[i]<<" ";
    GB2Instance * p_instance = new GB2Instance();
    p_instance->Init();
    p_instance->ReadCode(code, len);
    p_instance->Run();
    delete p_instance;
}

运行结果:

Compiled:
3 73 74 61 67 65 0 0 3 0 0 0 3 67 72 6f 75 6e 64 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 20 ffffffc1 2 2 0 0 0 0 0 48 42 2 3 0 0 0 0 0 20 41 1 0 0 0 0 0 0 0 0 0 64 0 0 0 0 2 0 0 0 0 0 0 0 0
[unnamed]empty
[
        [unnamed]empty
        {
        stage:
                [stage]empty
                {
                ground:
                        [ground]pointer
                        {
                        }
                }
        }
]

标签:std,cursor,char,code,游戏,int,2D,instance,引擎
来源: https://www.cnblogs.com/zwh12/p/16537074.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有