用户:
ParseYPasy查看:4 回复:1 评论:4 创建时间:2022-04-28T15:48:17
在游玩Minecraft等沙盒游戏的时候,我们会接触到一个叫做“区块”的概念,它是一个游戏地图的基本单元,方便游戏进行动态加载地图。
我们将以C+喵基础,从头写出一个与Minecraft类似的终端交互式沙盒游戏,仅包括C++标准库内容,不使用图形控件。
我们假设在阅读这份文档前,你已经学习了大部分C++语法,并能熟练运用STL和OOP编程。
好的,我们开始吧!
先上自己游戏的实现~
class chunk{
private:
long long id;//区块号
world* related_world;//所属的世界
block_t block[worldMaxinumHeight * chunkWidth];//地形信息
std::vector<basic_entity>entity_list;//实体信息
public:
chunk(long long __id,world* __related_world);// 创建一个指定id与所属世界的chunk
bool operator()(const chunk& op2) const;// 按照id排序
long long getid() const;// 获取id
block_t getblock(const position& inpos) const;// 按照position对象获取方块(仅当position在区块范围内)
block_t getblockindex(long long index) const;// 直接通过下标获取方块
void editblock(const position& inpos,block_t block_in,bool just_push = false);// 按照position对象修改区块内方块(仅当position在区块范围内)
void applyBlockMerge();// 按照所在世界的方块操作序列修改自身方块信息
void print() const;// 绘制整个区块的完整图像(使用标准的方块颜色)
friend block_t getblock(world* target_world,const position& inpos);//按所给的坐标获取方块(仅在本区块内)
friend int editblock(world* target_world,const position& inpos,block_t editid);//按照所给的坐标修改方块(仅在本区块内)
friend void basic_worldgenerator::setRawTerrainX(chunk* inchunk,long long x,long long y);//生成单x坐标的地形
friend void basic_worldgenerator::setRawTerrain(chunk* inchunk);//生成一级地形
friend void basic_worldgenerator::setSecondaryTerrain(chunk* inchunk,world* _inworld);//生成二级地形
friend void basic_worldgenerator::setTreeBranch(chunk* inchunk,world* _inworld,const position& pos1,const position& pos2,int size_branch,int type);//生成一个树的枝干
friend void basic_worldgenerator::setTree(chunk* inchunk,world* _inworld,const position& inpos,int size_tree,int type);//生成树
friend int ui_readfile();
};
倘若自己没做过这种游戏那也就没法教各位写沙盒游戏(bushi
我们拆分代码,逐个讲解类的原型:
private:
long long id;//区块号
world* related_world;//所属的世界
block_t block[worldMaxinumHeight * chunkWidth];//地形信息
std::vector<basic_entity>entity_list;//实体信息
遵循隐藏数据原则,我们规定区块号,地形信息等都是不可直接访问的,这能提高数据的安全性。
接着说明下前置的一些定义:
typedef short block_t;// 是方块存储的基础类型,在这里我们存储它的数字ID,等价于short
class world;// 一个世界类,区块通过related_world指针来表明自己所属的世界
class position;// 标准的坐标类,包括x,y坐标信息
class basic_entity;// 基础的实体类,现在还不需要知道怎么使用
class __worldgenerator;// 世界生成器类,这个是单独分出来的一个大型类,会在以后讲解,用于地形生成
我们一行一行解释。
long long id;//区块号
区块存储在世界里,必须有一个能表明其位置序号的数字,并且要保证唯一,因而我们的区块首先需要一个成员,表示它的id号。
world* related_world;
这是一个指向区块所属的世界类的指针,区块用它标明自己所属的世界是什么。
block_t block[worldMaxinumHeight * chunkWidth];
区块当然要存储方块的信息,我们用两个常量定义区块的高度与宽度:
const long long worldMaxinumHeight = 512l;// 世界(区块)的高度
const long long chunkWidth = 16l;// 区块的宽度
两个常量相乘,就是一个区块所包含的方块数量:8192个
block_t前面提到过,它是short的别名,存储方块的id
实体的话我们等之后讲解相关内容时再叙述,现在我们着重于地形方面的实现。
public:
chunk(long long __id,world* __related_world);// 创建一个指定id与所属世界的chunk
chunk类自然需要一个自行定义的构造函数,实现如下:
chunk::chunk(long long __id,world* __related_world){
id = __id;
related_world = __related_world;
for(long long i = 0;i<worldMaxinumHeight * chunkWidth;i++)
block[i] = id_block_air;
}
这个构造函数会为所有的方块赋值为空气(id_block_air是一个常量,代指空气方块的id)
现在相信各位可以写一个简单的区块了(我这份实现是服务于自己的沙盒游戏的,部分代码可能各位不熟悉,如果各位想写一份自己的,可以改动一些代码),示例如下:
class mychunk{
private:
long long id;
short block[8192];
public:
mychunk(long long _id){
id = _id;
for(long long i = 0;i < 8912;i++)
block[i] = 0;
}
};
接下来,我们将开始接触一些方便其他功能函数调用的API:
long long getid() const{ return id; }
这个相信各位都懂,返回这个区块的id
block_t getblockindex(long long index) const{
if(index >= 0 && index < worldMaxinumHeight * chunkWidth)
return block[index];
else
return id_block_air;
}
我们可以直接通过这个函数访问区块的方块,如果你认为这个函数传入的参数不会有问题,可以缩成如下形式:
block_t getblockindex(long long index) const{ return block[index]; }
(当然不推荐为了省代码而这么做,否则会对开发的错误追踪造成麻烦)
我们自制的mychunk就可以写成这样:
class mychunk{
private:
long long id;
short block[8192];
public:
mychunk(long long _id){
id = _id;
for(long long i = 0;i < 8912;i++)
block[i] = 0;
}
long long getid() const{
return id;
}
short getblockindex(long long index) const{
if(index >= 0 && index < 8192)
return block[index];
else
return 0;
}
};
在之后的教程中,我们将开始使用标准的坐标类访问区块信息,这一节我们首先学习了应如何写出一个好的区块类,并添加它的功能,各位也可自行设计mychunk的其他功能和成员。