ceshi
This commit is contained in:
360
技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.4.创建对象.md
Normal file
360
技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.4.创建对象.md
Normal file
@@ -0,0 +1,360 @@
|
||||
# 4.创建对象
|
||||
|
||||
*在我们继续之前,我们在这里使用的是[哲学意义上](https://en.wikipedia.org/wiki/Object_(philosophy))的“对象”一词。它与[面向对象编程](https://en.wikipedia.org/wiki/Object-oriented_programming)无关,也与Java,C#和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!
|
||||
Reference in New Issue
Block a user