Files

361 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 4.创建对象
*在我们继续之前,我们在这里使用的是[哲学意义上](https://en.wikipedia.org/wiki/Object_(philosophy))的“对象”一词。它与[面向对象编程](https://en.wikipedia.org/wiki/Object-oriented_programming)无关也与JavaC#和Python等编程语言中预定义的“对象”类型没有任何共同之处。下面我将定义一个名为 object 的结构体。*
冒险游戏中的大多数谜题都围绕着**物品**。例子:
- 必须找到一把钥匙,然后用来解锁某扇门。
- 必须杀死守卫或者诱骗守卫才能开启房间
所以,为了表示这个物品,我们可以使用如下[结构](http://en.wikipedia.org/wiki/Struct_(C_programming_language))
- **\*description: 对物品的描述**
- **\*tag: 物品的类型**
- **\*location: 物品所在的位置。这是对应上一章中定义的物品位置的指针。**
```c
struct object {
const char *description;
const char *tag;
struct location *location;
}
objs[] = {
{"a silver coin", "silver", &locs[0]},
{"a gold coin" , "gold" , &locs[1]},
{"a burly guard", "guard" , &locs[0]}
};
```
我们发现这一章的物品的信息和上一章好像也差不多呀,所以我们直接把他合并好了!
```c
struct object {
const char *description;
const char *tag;
struct object *location;
}
objs[] = {
{"an open field", "field" , NULL},
{"a little cave", "cave" , NULL},
{"a silver coin", "silver", &objs[0]},
{"a gold coin" , "gold" , &objs[1]},
{"a burly guard", "guard" , &objs[0]}
};
```
这样子我们的代码就会看起来更加简洁!
我们发现 OBJECT 的结构体里面有一个指针和自己长得一样,不用担心,这和链表的操作类似。
::: warning 🤔 思考题:
链表是什么,为什么要有这么一个操作指针?
链表和数组有什么异同点,他们分别在增删改查上有什么优劣?
:::
为了更容易地用那些所谓的物品或者是地点,我们将为每个元素定义一个名字
```c
#define field (objs + 0)
#define cave (objs + 1)
#define silver (objs + 2)
#define gold (objs + 3)
#define guard (objs + 4)
```
如何用各个元素的指针来方便的进行操作呢?
```c
printf("You are in %s.\n", field->description);
```
然后用这样的操作可以列出一个物品里面所有的小东西
```c
struct object *obj;
for (obj = objs; obj < objs + 5; obj++)
{
if (obj->location == cave)
{
printf("%s\n", obj->description);
}
}
```
::: warning 🤔 暂停理解一下吧
:::
那么,我们有合并这个物品(或地点)列表有什么好处呢?答案是这会让我们的代码变得更加简单,因为许多函数(如上面的函数通过这样的列表)只需要扫描单个列表就可以实现,而不是三个列表。有人可能会说没必要,因为每个命令仅适用于一种类型的对象:
- 命令 *go* 适用于位置对象。
- 命令 *get* 应用于获得物品。
- 命令 kill 适应用于杀死人物。
但这种方法不太对劲,原因有三:
1. 某些命令适用于多种类型的对象,尤其是*检查*。
2. 有时候会出现很没意思的交互方式,比如说你要吃掉守卫,他说不行。
3. 某些对象在游戏中可能具有多个角色。比如说队友系统NPC 可以是你的物品也可以是对象
将所有对象放在一个大列表中很容易添加一个名为“type”的属性来*构造对象*,以帮助我们区分不同类型的对象。
::: warning 🤔 怎么做怎么遍历呢?先思考吧
:::
但是,对象通常具有同样有效的其他特征:
- **Locations通过道路连接将在后面介绍。如果一个物体无法通过一条通道到达那么它就不是一个位置。就是这么简单。**
- **Items玩家唯一可以捡起的物品;可以给他们整一个重量的属性**
- **Actors玩家唯一可以与之交谈交易战斗的对象;当然,前提是他们还活着!可以加一个 HP 属性**
我们还要向数组中添加一个对象:玩家自己。
在上一章中,有一个单独的变量 *locationOfPlayer*。我们将删除它,然后换上用户的位置属性取代他!
例如,此语句会将玩家移入洞穴:
```c
player->location = cave;
```
此表达式返回玩家当前位置的描述:
```c
player->location->description
```
是时候把它们放在一起了。我们从对象数组的全新模块开始
## Object.h
```c
typedef struct object {
const char *description;
const char *tag;
struct object *location;
} 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 endOfObjs (objs + 6)
```
## Object.c
```c
#include <stdio.h>
#include "object.h"
OBJECT objs[] = {
{"an open field", "field" , NULL },
{"a little cave", "cave" , NULL },
{"a silver coin", "silver" , field },
{"a gold coin" , "gold" , cave },
{"a burly guard", "guard" , field },
{"yourself" , "yourself", field }
};
```
<strong>注意:</strong>要编译此模块,编译器*必须*支持 Constant folding。这排除了一些更原始的编译器如 [Z88DK](http://en.wikipedia.org/wiki/Z88DK)。
以下模块将帮助我们找到与指定名词匹配的对象。
## noun.h
```c
extern OBJECT *getVisible(const char *intention, const char *noun);
```
::: warning <font size=5>**🤔 指针?函数?希望你已经掌握这是什么了**</font>
:::
## noun.c
```c
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "object.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 ||
//玩家当前位置的物体
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;
//感受到注释有多伟大了吧
}
```
这是另一个辅助程序的函数。它打印存在于特定位置的对象物品NPC的列表。它将用于函数 *executeLook*,在下一章中,我们将介绍另一个需要它的命令。
## misc.h
```c
extern int listObjectsAtLocation(OBJECT *location);
```
## misc.c
```c
#include <stdio.h>
#include "object.h"
int listObjectsAtLocation(OBJECT *location)
{
int count = 0;
OBJECT *obj;
for (obj = objs; obj < endOfObjs; obj++)
{
if (obj != player && obj->location == location)
//排除玩家在玩家的位置这种蠢东西
{
if (count++ == 0)
//我们需要保证找到一个东西之前他不会打印 you see
{
printf("You see:\n");
}
printf("%s\n", obj->description);
}
}
return count;
//返回的是数目的数量,下一章对此做额外操作
}
```
*location.c* 中,命令环*顾四周的实现*,并根据新的数据结构进行调整。旧的位置数组被删除,变量 *locationOfPlayer* 也是如此。
## 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)
{
//消除了函数*executeGo*中的循环,代码更优雅了~
OBJECT *obj = getVisible("where you want to go", noun);
if (obj == NULL)
{
// already handled by getVisible
}
else if (obj->location == NULL && obj != player->location)
{
printf("OK.\n");
player->location = obj;
executeLook("around");
}
else
{
printf("You can't get much closer than this.\n");
}
}
```
你可以自由添加对象哦,自己设计一个游戏道具一定很有意思
现在金银宝物散落一地可是我们捡不起来,下一章我们会试着解决问题
测试样例:
Welcome to Little Cave Adventure.
You are in an open field.
You see:
a silver coin
a burly guard
--> go cave
OK.
You are in a little cave.
You see:
a gold coin
--> go field
OK.
You are in an open field.
You see:
a silver coin
a burly guard
--> go field
You can't get much closer than this.
--> look around
You are in an open field.
You see:
a silver coin
a burly guard
--> quit
Bye!