📜引擎使用说明

overview

nuwa的开发流程

1.1 引擎配置

在使用之前,开发者需要更新维护引擎的配置文件,配置文件位于nuwa/Config文件夹中,包括:

  • OpenAI API的配置文件: project/config/openai_config.json
  • 动作配置文件: project/config/action/your_action_XXX.json
  • NPC配置文件: project/config/npc/your_npc_nameXXX.json
  • 场景配置文件: project/config/knowledge/scenes/your_scenario_nameXXX.json

1.2 引擎启动

引擎可以使用对应平台的发行版,通过脚本或程序执行./nuwa来拉起引擎。

1.3 引擎交互

引擎端和游戏端通过UDP数据包按照UDP数据包格式进行交互,引擎端默认在8199端口监听游戏端数据包,游戏端默认在8084端口监听引擎端数据包。

1.4 引擎关闭

游戏端通过发送“close”功能数据包给引擎端来请求关闭引擎(详见数据包)。

✨配置文件结构

2.1 项目目录结构

  • dist(项目代码)
  • nuwa.exe (引擎执行入口)
  • project\
    • logs(运行日志)
    • src(源代码)
    • config(配置文件)
      • action(场景中允许的动作配置文件)
      • chat.json(自定义第一个动作的配置文件)
      • ...
      • npc(npc描述配置文件)
      • 村长.json(自定义第一个角色的配置文件)
      • ...
      • knowledge(知识、场景配置文件)
      • scenes(子场景配置文件)
        • 警察局.json(自定义第一个具体场景的配置文件)
        • ...

🎒UDP数据包

3.1 场景初始化数据包

在引擎初始化或者加载一个新场景的时候,游戏端需要先发送init数据包给引擎端。引擎端才会加载指定场景的NPC。

# 场景初始化的包
{
    "func":"init",   # 表示该传送的数据包是用于加载场景
    # 必填字段
    "scene_name":"雁栖村",   # 加载场景的名称,代表在什么场景下初始化
    "language":"C",   # 选择语言版本,“E”表示英文,“C”表示中文。默认且推荐使用中文。
    # 🉑️选字段
    "npc":[
        {
            "name":"李大爷",
            "desc":"是个好人",
            "npc_state": {
                  "position": "李大爷家",
                  "observation": {
                          "people": ["王大妈", "村长", "隐形李飞飞"],
                          "items": ["椅子#1","椅子#2","椅子#3[李大爷占用]","床"],
                          "locations": ["李大爷家大门","李大爷家后门","李大爷家院子"]
                                },
                  "backpack":["黄瓜", "1000元", "老报纸"]
                       },
            "mood":"正常",
            "action_space": ["mov", "chat"],  # 人物的动作空间(在实际执行的时候,场景的all_actions和人物action_space取交集) 
            "memory":[ ]
        },
        {
            "name":"王大妈",
            "desc":"是个好人",
            "npc_state": {
                  "position": "李大爷家",
                  "observation": {
                          "people": ["李大爷", "村长", "隐形李飞飞"],
                          "items": ["椅子#1","椅子#2","椅子#3[李大爷占用]","床"],
                          "locations": ["李大爷家大门","李大爷家后门","李大爷家院子"]
                                },
                  "backpack":["优质西瓜", "大砍刀", "黄金首饰"]
                       },
        "mood":"焦急",
        "action_space": ["mov", "chat"],  # 人物的动作空间(在实际执行的时候,场景的all_actions和人物action_space取交集)
        "memory":[ ]
        }], # 可以留空,默认按照scene.json初始化场景NPC。非空则在之前基础上添加。
}

3.2 引擎关闭数据包

在游戏结束的时候,engine需要一个close数据包,用于更新所有NPC的状态到json文件中。

# 引擎关闭的包
{
    "func":"close" # 关闭引擎,并保存所有NPC到json
}

3.3 NPC的动作数据包

NPC不会开始自主行动,除非你发送了wakeup包给它。 npc-engine接到wakeup包之后,会返回action行为数据包。 游戏端需要执行对应action,执行最终状态以action_done的形式返回给npc-engine engine接收到action_done包之后会继续返回action行为包。

Action

# wakeup包例:
{
    "func":"wake_up",
    "npc_name": "王大妈",

    "scenario_name": "李大爷家", 
    "npc_state": {
      "position": "李大爷家卧室",
      "observation": {
              "people": ["李大爷", "村长", "李飞飞"],
              "items": ["椅子#1","椅子#2","椅子#3[李大爷占用]","床"],
              "locations": ["李大爷家大门","李大爷家后门","李大爷家院子"]
                    },
      "backpack":["优质西瓜", "大砍刀", "黄金首饰"]
    },

    "time": "2021-01-01 12:00:00", # 游戏世界的时间戳 
}

# action_done包例
{
    "func":"action_done",
    "npc_name":"王大妈",
    "status": "success",
    "time": "2021-01-01 12:00:00", # 游戏世界的时间戳

    "scenario_name": "李大爷家", 
    "npc_state": {
      "position": "李大爷家卧室",
      "observation": {
              "people": ["李大爷", "村长", "李飞飞"],
              "items": ["椅子#1","椅子#2","椅子#3[李大爷占用]","床"],
              "locations": ["李大爷家大门","李大爷家后门","李大爷家院子"]
                    },
      "backpack":["优质西瓜", "大砍刀", "黄金首饰"]
    },

    "action":"mov",
    "object":"李大爷家",  # 之前传过来的动作对象
    "parameters":[], # 之前传过来的参数
    "reason": "", # "王大妈在去往‘警察局’的路上被李大爷打断"
}

# action_done、wakeup发给游戏包后返回的ACTION包
{
    "name":"action",
    "npc_name":"李大妈",
    "action":"mov",
    "object":"李大爷家",
    "parameters":[],
}

3.4 对话相关行为

游戏需要自己确认npc的群体对话触发机制,通常是一个包含固定半径的对话房间。 发送create_conversation给engine后,engine会根据提供的参数返回一个长剧本包,游戏需要自己实现剧本演出。 每一行剧本演出完成后,需要发送确认包给engine否则不会有记忆。

剧本有插入功能,比如玩家要插入对话或者一个新的npc进入了对话,这时候发送re_create_conversation包(带着之前的对话ID)便可,会重新生成一个考虑到插入npc的接续剧本。

Conversation

# create_conversation游戏端发给引擎的包
{
    "func": "create_conversation",
    "npc": ["王大妈","李大爷"],   # npc列表

    "scenario_name": "李大爷家",        
    "location": "李大爷家卧室",
    "topic": "王大妈想要切了自己的西瓜给李大爷吃,并收钱", # 可以留空,会自动生成topic
    "npc_states": [   # 该列表中的每个状态对应于npc列表的相应角色名称
                {
                  "position": "李大爷家",
                  "observation": {
                          "people": ["李大爷", "村长", "隐形李飞飞"],
                          "items": ["椅子#1","椅子#2","椅子#3[李大爷占用]","床"],
                          "locations": ["李大爷家大门","李大爷家后门","李大爷家院子"]
                                },
                  "backpack":["优质西瓜", "大砍刀", "黄金首饰"]
                },
                {
                  "position": "李大爷家",
                  "observation": {
                          "people": ["王大妈", "村长", "隐形李飞飞"],
                          "items": ["椅子#1","椅子#2","椅子#3[李大爷占用]","床"],
                          "locations": ["李大爷家大门","李大爷家后门","李大爷家院子"]
                                },
                  "backpack":["黄瓜", "1000元", "老报纸"]
                },
                ],
    "starting": "你好,嫩们在干啥腻?",  # 玩家说的话,可选留空
    "player_desc": "玩家是一个疯狂的冒险者,喜欢吃圆圆的东西",  # 玩家的描述,可选留空
    "memory_k": 3,  # npc的记忆检索条数,必须填写
    "length": "M"  # 可以选择的剧本长度,S M L X 可选。 
}

# 引擎端创造并生成剧本后传给游戏端的数据包
{
    "name": "conversation",
    "id": "123456789",  # conversation对象的索引号
    "length": "M",  # 可以选择的剧本长度,S M L X 可选。 
    "location": "李大爷家",  # 对话发生所在的地点
    "lines": [line1, line2, line3, line4, ...]  # 剧本信息,由若干行对话组成的序列
}

# 引擎端生成剧本的每一行的格式
{
    "type": "Interaction",  # 剧本行的类型,可以是State,Interaction,Error
    "state": "李大爷退出。剩下的角色:王大妈",  # 当剧本行类型是State和Error时,"state"才会有具体内容
    "name": "李大爷",  # 剧本行对应的角色姓名,当剧本行类型是Interaction时才不为空
    "mood": "开心",  # 剧本行对应角色的情绪,当剧本行类型是Interaction时才不为空
    "words": "我喜好吃西瓜",  # 剧本行对应角色的说话内容,当剧本行类型是Interaction时才不为空
    "action": {
              "type": "对话",
              "args": "王大妈"}  # 剧本行对应角色的动作,当剧本行类型是Interaction时不为空
}

# 游戏端传给引擎端的剧本演示确认包
{
    "func": "confirm_conversation_line",
    "conversation_id": "123456789",  # conversation对象的索引号
    "index": 2,  # 游戏端展示完成的剧本行索引号
}

# re_create_conversation游戏端发给引擎的包
{
    "func": "re_create_conversation",
    "id": "123456789",  # conversation对象的索引号
    "character": "警长",  # 新加入角色的名称
    "interruption": "大家好呀,你们刚刚在说什么",  # 玩家插入的说话内容
    "player_desc": "玩家是一个疯狂的冒险者,喜欢吃圆圆的东西",  # 玩家的描述,可选留空
    "length": "M"  # 可以选择的剧本长度,S M L X 可选。 
}

👮‍引擎交互注意事项

  • 游戏端发送init包后,引擎端会读取数据包中场景名称所对应的配置文件scene_name.json,然后初始化场景。
  • 如果init数据包中包含npc信息,那么引擎端会默认从该数据包中读入角色信息;如果不包含,则引擎端会从scene_name.json配置文件中读入角色信息。
  • 每个场景配置文件scene_name.json中的可支持动作和存在的角色名称都需要在\action和\npc中进行定义,如果未定义则会报错。
  • 每个npc在游戏中的自主行动需要游戏端对针对该角色向引擎端发送wakeup包来实现的。
  • 长时间没有自主行为的npc需要游戏端自行检测,发送wakeup包到引擎进行再次唤醒
  • 引擎端接收wakeup包后会生成npc的动作并返回action包给游戏端
  • 游戏端执行对应的action包之后,需要发送action_done包到引擎,这样引擎才会继续生成npc下一步行为。