feat: 好几章
This commit is contained in:
336
3.编程思维体系构建/3.4.6.6.绘制地图.md
Normal file
336
3.编程思维体系构建/3.4.6.6.绘制地图.md
Normal file
@@ -0,0 +1,336 @@
|
||||
# 6.绘制地图
|
||||
|
||||
作为一个 RPG 游戏怎么能没有地图呢,<em>是时候绘制地图了!</em>
|
||||
|
||||
绘制地图的最佳工具始终是:一支铅笔和一张纸。基本地图由<strong>位置</strong>(矩形)组成,由道路(箭头)连接。我们已经在第 3 章中创建了位置,现在我们将开始添加道路。
|
||||
|
||||
在虚拟世界中,“道路”可能是连接两个位置的任何东西:一条路,一扇门,沙漠中。基本上,一段经文具有以下属性:
|
||||
|
||||
- 起点(位置)。
|
||||
- 目标(位置)。
|
||||
- 叙述性描述,例如“森林小径”。
|
||||
- 在 <em>go</em> 命令中往哪里走的描述性标记
|
||||
|
||||
考虑到这些属性,第 4 章中定义的结构对象就非常适合存储道路了。事实上,一个道路与一个项目或 NPC 并没有太大的不同,它作为“可见出口”存在于某个位置(该位置是起点)。它只是与某些命令的行为不同,特别是命令“go”:应用于道路,<em>go</em>将改变玩家的位置。
|
||||
|
||||
```c
|
||||
struct object {
|
||||
const char *description;
|
||||
const char *tag;
|
||||
struct object *location;
|
||||
struct object *destination;
|
||||
};
|
||||
```
|
||||
|
||||
注意:
|
||||
|
||||
- 显然,<em>目的地</em>在大多数其他对象(物品,NPC)中都没有使用
|
||||
- 通道总是朝一个方向运行;要双向连接两个位置,我们总是必须创建两个单独的通道。乍一看,这似乎很笨拙,但它确实给了我们很大的灵活性来完善命令“go”的行为
|
||||
- 在大地图上,你可能会发现手动创建所有通道很乏味。所以,我强烈建议你使用自定义工具<em>生成</em>地图中重复性更强的部分。这里不会介绍这一点,但您可能会在第 9 章中找到一些灵感,我们将在其中讨论自动胜场。
|
||||
|
||||
思考题:为什么创建两个通道可以使我们的程序更加灵活?
|
||||
|
||||
接下来我们将展开对象数组
|
||||
|
||||
# object.h
|
||||
|
||||
```c
|
||||
typedef struct object {
|
||||
const char *description;
|
||||
const char *tag;
|
||||
struct object *location;
|
||||
struct object *destination;
|
||||
} OBJECT;
|
||||
|
||||
extern OBJECT objs[];
|
||||
|
||||
#define field (objs + 0) //是不是觉得这个很熟悉
|
||||
#define cave (objs + 1)
|
||||
#define silver (objs + 2)
|
||||
#define gold (objs + 3)
|
||||
#define guard (objs + 4)
|
||||
#define player (objs + 5)
|
||||
#define intoCave (objs + 6)
|
||||
#define exitCave (objs + 7)
|
||||
|
||||
#define endOfObjs (objs + 8)
|
||||
```
|
||||
|
||||
# object.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include "object.h"
|
||||
|
||||
OBJECT objs[] = {
|
||||
{"an open field" , "field" , NULL , NULL },
|
||||
{"a little cave" , "cave" , NULL , NULL },
|
||||
{"a silver coin" , "silver" , field, NULL },
|
||||
{"a gold coin" , "gold" , cave , NULL },
|
||||
{"a burly guard" , "guard" , field, NULL },
|
||||
{"yourself" , "yourself", field, NULL },
|
||||
{"a cave entrance", "entrance", field, cave },
|
||||
{"an exit" , "exit" , cave , field }
|
||||
};
|
||||
```
|
||||
|
||||
我们将在 <em>misc.c</em> 中添加一个小的帮助函数,以确定两个给定位置之间是否存在通道。
|
||||
|
||||
# misc.h
|
||||
|
||||
```c
|
||||
extern OBJECT *getPassage(OBJECT *from, OBJECT *to);
|
||||
extern OBJECT *actorHere(void);
|
||||
extern int listObjectsAtLocation(OBJECT *location);
|
||||
```
|
||||
|
||||
# misc.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include "object.h"
|
||||
|
||||
OBJECT *getPassage(OBJECT *from, OBJECT *to)
|
||||
{
|
||||
if (from != NULL && to != NULL)
|
||||
{
|
||||
OBJECT *obj;
|
||||
for (obj = objs; obj < endOfObjs; obj++) //寻找物品之间是否具有通道
|
||||
{
|
||||
if (obj->location == from && obj->destination == to)
|
||||
{
|
||||
return obj; //找到了
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;//找不到
|
||||
}
|
||||
|
||||
OBJECT *actorHere(void)
|
||||
{
|
||||
OBJECT *obj;
|
||||
for (obj = objs; obj < endOfObjs; obj++)
|
||||
{
|
||||
if (obj->location == player->location && obj == guard)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int listObjectsAtLocation(OBJECT *location)
|
||||
{
|
||||
int count = 0;
|
||||
OBJECT *obj;
|
||||
for (obj = objs; obj < endOfObjs; obj++)
|
||||
{
|
||||
if (obj != player && obj->location == location)
|
||||
{
|
||||
if (count++ == 0)
|
||||
{
|
||||
printf("You see:\n");
|
||||
}
|
||||
printf("%s\n", obj->description);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
```
|
||||
|
||||
我们将在命令“go”的实现中使用新功能<em>getPassage</em>来确定是否存在可以将玩家带到所需位置的通道。
|
||||
|
||||
# location.h
|
||||
|
||||
```c
|
||||
extern void executeLook(const char *noun);
|
||||
extern void executeGo(const char *noun);
|
||||
```
|
||||
|
||||
# location.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "object.h"
|
||||
#include "misc.h"
|
||||
#include "noun.h"
|
||||
|
||||
void executeLook(const char *noun)
|
||||
{
|
||||
if (noun != NULL && strcmp(noun, "around") == 0)
|
||||
{
|
||||
printf("You are in %s.\n", player->location->description);
|
||||
listObjectsAtLocation(player->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("I don't understand what you want to see.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void executeGo(const char *noun)
|
||||
{
|
||||
OBJECT *obj = getVisible("where you want to go", noun);
|
||||
if (obj == NULL)
|
||||
{
|
||||
// already handled by getVisible
|
||||
}
|
||||
else if (getPassage(player->location, obj) != NULL)
|
||||
//go只会在有地方的时候才会运行起来
|
||||
{
|
||||
printf("OK.\n");
|
||||
player->location = obj;
|
||||
executeLook("around");
|
||||
}
|
||||
else if (obj->location != player->location)
|
||||
{
|
||||
printf("You don't see any %s here.\n", noun);
|
||||
}
|
||||
else if (obj->destination != NULL)
|
||||
{
|
||||
printf("OK.\n");
|
||||
player->location = obj->destination;
|
||||
executeLook("around");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("You can't get much closer than this.\n");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
我们还将使用新功能<em>getPassage</em>来确定从玩家站立的位置是否可以看到某个位置。未通过通道连接到当前位置的位置不被视为可见。
|
||||
|
||||
# noun.h
|
||||
|
||||
```c
|
||||
extern OBJECT *getVisible(const char *intention, const char *noun);
|
||||
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);
|
||||
```
|
||||
|
||||
# noun.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "object.h"
|
||||
#include "misc.h"
|
||||
|
||||
static bool objectHasTag(OBJECT *obj, const char *noun)
|
||||
{
|
||||
return noun != NULL && *noun != '\0' && strcmp(noun, obj->tag) == 0;
|
||||
}
|
||||
|
||||
static OBJECT *getObject(const char *noun)
|
||||
{
|
||||
OBJECT *obj, *res = NULL;
|
||||
for (obj = objs; obj < endOfObjs; obj++)
|
||||
{
|
||||
if (objectHasTag(obj, noun))
|
||||
{
|
||||
res = obj;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
OBJECT *getVisible(const char *intention, const char *noun)
|
||||
{
|
||||
OBJECT *obj = getObject(noun);
|
||||
|
||||
if (obj == NULL)
|
||||
{
|
||||
printf("I don't understand %s.\n", intention);
|
||||
}
|
||||
else if (!(obj == player ||
|
||||
obj == player->location ||
|
||||
obj->location == player ||
|
||||
obj->location == player->location ||
|
||||
getPassage(player->location, obj) != NULL || //检查两个位置是否相邻
|
||||
(obj->location != NULL &&
|
||||
(obj->location->location == player ||
|
||||
obj->location->location == player->location))))
|
||||
{
|
||||
printf("You don't see any %s here.\n", noun);
|
||||
obj = NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
|
||||
{
|
||||
OBJECT *obj = NULL;
|
||||
if (from == NULL)
|
||||
{
|
||||
printf("I don't understand who you want to %s.\n", verb);
|
||||
}
|
||||
else if ((obj = getObject(noun)) == NULL)
|
||||
{
|
||||
printf("I don't understand what you want to %s.\n", verb);
|
||||
}
|
||||
else if (obj == from)
|
||||
{
|
||||
printf("You should not be doing that to %s.\n", obj->description);
|
||||
obj = NULL;
|
||||
}
|
||||
else if (obj->location != from)
|
||||
{
|
||||
if (from == player)
|
||||
{
|
||||
printf("You are not holding any %s.\n", noun);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("There appears to be no %s you can get from %s.\n",
|
||||
noun, from->description);
|
||||
}
|
||||
obj = NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
```
|
||||
|
||||
显然,此示例中的地图是微不足道的:只有两个位置,并且它们在两个方向上都连接在一起。第 12 章将增加第三个地点。
|
||||
|
||||
思考题:你能否绘制一张更精细的地图,并将其变成对象列表(位置和通道)
|
||||
|
||||
```
|
||||
注:不用当成任务,自行实验即可
|
||||
```
|
||||
|
||||
输出样例
|
||||
|
||||
Welcome to Little Cave Adventure.
|
||||
You are in an open field.
|
||||
You see:
|
||||
a silver coin
|
||||
a burly guard
|
||||
a cave entrance
|
||||
|
||||
--> go entrance
|
||||
OK.
|
||||
You are in a little cave.
|
||||
You see:
|
||||
a gold coin
|
||||
an exit
|
||||
|
||||
--> go exit
|
||||
OK.
|
||||
You are in an open field.
|
||||
You see:
|
||||
a silver coin
|
||||
a burly guard
|
||||
a cave entrance
|
||||
|
||||
--> go cave
|
||||
OK.
|
||||
You are in a little cave.
|
||||
You see:
|
||||
a gold coin
|
||||
an exit
|
||||
|
||||
--> quit
|
||||
|
||||
Bye!
|
||||
Reference in New Issue
Block a user