发布于 2026-01-06 2 阅读
0

我在谷歌助手上制作了一个名为 PokéPartner 的语音激活宝可梦图鉴!方法如下。

我在谷歌助手上制作了一个名为 PokéPartner 的语音激活宝可梦图鉴!方法如下。

“嘿,谷歌,和宝可梦伙伴对话”

我在谷歌助手上制作了一个名为 PokéPartner 的语音激活宝可梦图鉴!方法如下。

毋庸置疑,我是个不折不扣的宝可梦粉丝。自从初代动画在美国播出,我看着小智和皮卡丘征服关都地区,或者在《宝可梦 蓝》以及之后的每一款宝可梦游戏中投入无数时间……我就彻底迷上了宝可梦。现在我的女儿也在追新动画,玩《精灵宝可梦 Let's Go!伊布》,而在这二十多年的时间里,我一直渴望拥有一个会说话的宝可梦图鉴。好吧,我等得不耐烦了,所以就自己动手做了一个。在本文中,我将详细介绍我的创意和PokéPartner的实现过程,这是一款可以和Google Home/Assistant对话的宝可梦应用。

资源:

网站

源代码

什么是宝可梦伙伴?

PokePartner 是一款 Google Assistant 应用,你可以通过语音快速获取关于宝可梦的答案。我开发它的初衷是为了解决我家遇到的一个问题。在与宝可梦对战时,我希望能够直接问我的 Google Home “什么宝可梦克制草/火属性”,并立即得到答案,而无需拿出手机查看属性克制表。最初,我只是想了解哪些宝可梦克制哪些宝可梦。

应用程序设计

我当时完全不知道该如何为 Google Assistant 开发应用程序。一番摸索之后,我发现了Dialogflow,这是一款用户友好、直观易用的自然语言处理工具,隶属于 Google。它直接处理用户和 Google Assistant 之间的通信,大大简化了应用程序的开发过程。我开始粗略地绘制流程图,思考如何处理数据并将其返回给用户。

我在谷歌助手上制作了一个名为 PokéPartner 的语音激活宝可梦图鉴!方法如下。
PokePartner应用程序的简单架构。

我不可能手动添加这个级别应用所需的所有数据,我需要一个现成的、成熟的 API。幸运的是,PokeAPI已经存在了。PokeAPI将所有你需要的宝可梦数据集中在一个地方,并且可以通过现代化的 RESTful API 轻松访问。完美,正是我想要的,而且还是免费的!现在我唯一的问题是如何以有意义的方式将数据返回给我的用户。这意味着我必须构建自己的应用程序来使用 PokeAPI 的数据,然后通过 Google Assistant 将数据返回给使用 Dialogflow 的用户。

构建后端

我最终使用 C# 和 .NET Core 3.0 构建了一个私有 Web API。我没有采用模型-视图-控制器 (MVC) 模式,而是将目录结构设置为“功能文件夹”,因为我发现这种格式更简洁,尤其是在不使用传统视图的情况下。通过创建功能文件夹,您可以将所有相关文件集中到一个易于访问的目录中。对于大型应用程序来说,这种方式比将所有文件分散存放要合理得多。

我在谷歌助手上制作了一个名为 PokéPartner 的语音激活宝可梦图鉴!方法如下。
PokePartner.Api 项目的功能文件夹构想。

该应用程序将分为三个主要目录:Pokemon、Types 和服务。虽然有 Pokemon 文件夹,但其中只有一个模型文件,这是因为目前 PokePartner 仅支持有关宝可梦属性的问题。不过,PokemonModel.cs 文件允许用户通过名称获取宝可梦属性,这是我在构建应用程序后添加的一个实用功能。

我想要解决的最大问题是:如何判断面对单属性或双属性的对手宝可梦时应该使用哪种类型的宝可梦。为此,我创建了一个 TypesController,并构建了几个不同的 API 接口。该控制器可以回答关于宝可梦的进攻或防守能力,以及单属性或双属性宝可梦的问题。它还可以仅根据宝可梦的名称来回答关于其属性的问题。以下是一个示例:

[HttpGet("defense/{type1}/{type2}")]
        public async Task<IActionResult> DualTypeChartAsDefender(string type1, string type2)
        {
            var types = new List<string>();
            types.Add(type1);
            types.Add(type2);

            var noEffect = new List<string>();
            var notVeryEffectiveRaw = new List<string>();
            var superEffectiveRaw = new List<string>();

            foreach (var t in types)
            {
                var apiType = await _pokeApi.RequestData($"type/{t}");
                var resultsType = JsonConvert.DeserializeObject<TypesModel>(apiType);

                foreach (var rt in resultsType.DamageRelations.DoubleDamageFrom)
                {
                    superEffectiveRaw.Add(rt.Name);
                }

                foreach (var rt in resultsType.DamageRelations.HalfDamageFrom)
                {
                    notVeryEffectiveRaw.Add(rt.Name);
                }

                foreach (var rt in resultsType.DamageRelations.NoDamageFrom)
                {
                    noEffect.Add(rt.Name);
                }
            }

            var json = TypesCalculator.CalculateDamage(superEffectiveRaw, notVeryEffectiveRaw, noEffect);
            return Ok(json);

        }

控制器上的每个结果都会返回该宝可梦属性是否对对手造成超强效果、效果不佳或无效。然后,我们将原始数据发送到 TypesCalculator.cs 文件来处理更详细的结果。我们将其放入单独的类和方法中,以便高效地重用,并返回属性克制效果是超强克制 x4、超强克制 x2、效果不佳 x1/2、效果不佳 x2/4 还是无效。最终,这些结果会返回给 Google Assistant 和用户。

为了顺利使用来自 PokeAPI 的数据,我为感兴趣的数据构建了模型,并创建了一个服务来处理服务器和 PokeAPI 之间的所有通信。由于宝可梦数据的复杂性,我决定使用一个能够很好地兼容嵌套 JSON 格式的模型。最终我设计了以下模型:

public class TypesModel
    {
        [JsonProperty("damage_relations")]
        public DamageResult DamageRelations { get; set; }
    }

    public class DamageResult
    {
        [JsonProperty("double_damage_from")]
        public List<DamageData> DoubleDamageFrom { get; set; }

        [JsonProperty("double_damage_to")]
        public List<DamageData> DoubleDamageTo { get; set; }

        [JsonProperty("half_damage_from")]
        public List<DamageData> HalfDamageFrom { get; set; }

        [JsonProperty("half_damage_to")]
        public List<DamageData> HalfDamageTo { get; set; }

        [JsonProperty("no_damage_from")]
        public List<DamageData> NoDamageFrom { get; set; }

        [JsonProperty("no_damage_to")]
        public List<DamageData> NoDamageTo { get; set; }
    }

    public class DamageData
    {
        [JsonProperty("name")]
        public string Name { get; set; }
    }

至于与 PokeAPI 的通信,我的服务最终变得极其简单。我只需创建一个 HttpClient,将请求传递给正确的 URI,等待响应,然后将原始 JSON 数据返回给请求数据的方法即可。RESTful API 的入门就是这么简单。

public async Task<string> RequestData(string endpoint) 
        {
            using (var client = new HttpClient())
            {
                var url = new Uri($"https://pokeapi.co/api/v2/{endpoint}");
                var response = await client.GetAsync(url);
                var results = await response.Content.ReadAsStringAsync();
                return results;
            }
        }

这就是我后端需要做的全部工作了。现在是时候进入我不太熟悉的领域——Dialogflow 了。

构建界面

所以,我并不是 Dialogflow 或 Firebase 的专家,而且可能永远也不会是。我不会提供 Dialogflow 的使用教程,也不会提供关于如何为 Dialogflow 设置 Firebase 的深入指南。不过,这里有一个我在开始使用 Dialogflow 之前观看的更详细的指南。这个视频让我不再犹豫要构建一个 Google Assistant 应用。为了让这篇文章更有意义,我需要简要介绍 Dialogflow 的两个主要方面:

1. 意图:用户如何与你的应用交互。

我需要用户能够向 PokePartner 提出诸如“什么属性克制火属性宝可梦?”、“飞行属性和水属性宝可梦分别弱什么属性?”或“皮卡丘弱什么属性?”之类的问题。我至少需要三个不同的意图,所以我最终创建了三个意图:SingleType(单属性)、DualType(双属性)和 PokemonWeakness(宝可梦弱点)。

我在谷歌助手上制作了一个名为 PokéPartner 的语音激活宝可梦图鉴!方法如下。

添加这些意图非常简单。我只需要添加一个常见问题的列表,Dialogflow 内置的自然语言处理工具就能自动处理类似的句子。接下来要解决的问题是如何获取像“我应该用什么来对付虫系宝可梦?”这样的问题的相关数据?理论上,Dialogflow 可以返回一个预先写好的答案,但你能想象要为每种情况都这样做吗?那简直太疯狂了!幸运的是,我已经编写了后端逻辑来处理这个问题,它从 PokeAPI 获取原始数据,然后将其转换为我需要的格式。但我该如何将这些数据导入 Dialogflow 呢?

2. 实现:将 Webhook 集成到 Intent 中,以提供有意义的结果。

通过编写简单的 JavaScript 代码并使用 Firebase,您可以接收来自 Dialogflow 的 POST 请求。您可以通过在 Google Cloud 中使用 Dialogflow 的内联编辑器来实现这一点,该编辑器由Firebase云函数提供支持。以下是一个请求示例:

// Single Type 
  function singleTypeHandler(agent){
    const type = agent.parameters.type.toLowerCase();  
    return axios.get(`/type/defense/${type}`)
    .then((result) => {   
      agent.add(`${type} Type Pokémon are weak to ${result.data.superEffective}. So if you want to do the most damage, use one of those types.`); 
      if (result.data.notVeryEffective.length == 0 && result.data.noEffect.length == 0) {
        // If pokemon has notVeryEffect and NoEffect
      } else if (result.data.noEffect.length == 0) {
        // If pokemon has Weakness but no NoEffects
        agent.add(` Do not use ${result.data.notVeryEffective} As those types are not very effective against ${type} Type Pokemon.`);
      } else {
        // If Pokemon has NoEffect only
        agent.add(`By the way, don't use ${result.data.noEffect} because it will have no effect.`);
      }
    }); 
  }

差不多就是这样了。Firebase 会处理所有复杂的部分。如果你会编写 JavaScript 和 RESTful API,那么你就有能力构建 Google Assistant 应用。

发布应用程序:

我的应用终于到了发布的时候。我决定使用谷歌云计算引擎来发布使用 C# 和 .NET Core 编写的私有后端 API。我还发布了一个与谷歌助手应用配套的“预览网站”,由于该网站只是一个简单的静态落地页,所以我使用了存储桶来存放它。

我在谷歌助手上制作了一个名为 PokéPartner 的语音激活宝可梦图鉴!方法如下。
屏幕截图来自谷歌云。

谷歌的Actions Console让发布应用变得非常简单。你只需填写必要的信息,几天之内就能知道申请是否获得批准。我的申请最初花了三天时间才被拒绝。被拒绝的原因是名字的发音与我填写的名字不符。说实话,这倒是个不错的发现,我改正之后,当天就通过了。

我在谷歌助手上制作了一个名为 PokéPartner 的语音激活宝可梦图鉴!方法如下。
Google 操作控制台。

以上应该大致涵盖了所有内容。我已经把这个项目上传到我的GitHub 仓库了,发布网站在这里。注意,如果你要尝试使用这个应用,只需说“嘿 Google,和宝可梦伙伴对话”即可。但需要注意的是,你一定要把“é”的发音拉长(读作“Poke-EE-Partner”)。

希望有人能用得上它!我个人在玩《宝可梦 剑/盾》的时候用这个应用玩得很开心。不过需要注意的是,截至本文撰写之时,PokeAPI 的 API 中还没有加入最新一代的宝可梦,所以你暂时还不能使用它们的名称

我希望以后能为这个项目添加更多内容,也欢迎大家在Github上提出任何建议!我希望它能成为宝可梦社群免费且易于使用的资源。

魔人王的《精灵宝可梦之夜》艺术作品

文章来源:https://dev.to/iamtravisw/i-made-a-voice-activated-pokedex-on-google-assistant- Called-pokepartner-here-s-how-m20