{"id":89,"date":"2025-05-09T15:52:00","date_gmt":"2025-05-09T13:52:00","guid":{"rendered":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/?p=89"},"modified":"2025-05-08T18:26:45","modified_gmt":"2025-05-08T16:26:45","slug":"dalle-voz-propia-e-dinamica-aos-teus-personaxes-en-godot","status":"publish","type":"post","link":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/2025\/05\/09\/dalle-voz-propia-e-dinamica-aos-teus-personaxes-en-godot\/","title":{"rendered":"Dalle voz propia e din\u00e1mica aos teus personaxes en Godot"},"content":{"rendered":"\n<iframe loading=\"lazy\" title=\"Voces no Metarcade\" width=\"560\" height=\"315\" src=\"https:\/\/fediverse.tv\/videos\/embed\/6d69a24c-40b7-47ba-a578-5d43b1112183\" frameborder=\"0\" allowfullscreen=\"\" sandbox=\"allow-same-origin allow-scripts allow-popups\"><\/iframe>\n\n\n\n<p>O son nos videoxogos \u00e9 unha parte que a mi\u00fado pasamos por alto cando desenvolvemos os nosos proxectos de afeccionados. Como se pode ver en calquera artigo sobre &#8220;espremer o zume&#8221; na creaci\u00f3n de videoxogos a presencia do audio e m\u00fasica xoga unha parte fundamental no xeito que os xogadores perciben os nosos produtos.<\/p>\n\n\n\n<p>O xogo que estamos a desenvolver, Metarcade, cont\u00e9n pequenas partes de xesti\u00f3n do personaxe e subida de niveis pero consiste principalmente en interactuar con diferentes personaxes para avanzar a historia. Para que os usuarios non se cansen de ler tanto texto, pareceunos fundamental facer a s\u00faa lectura m\u00e1is apetecible.<\/p>\n\n\n\n<p>Seguindo un <a href=\"https:\/\/www.gamedeveloper.com\/art\/the-creator-of-tactical-breach-wizards-is-right-animate-words-not-letters-\">artigo en gamedeveloper<\/a>, comezamos por animar a aparici\u00f3n de texto palabra a palabra no canto de letra a letra. Axuda sobre todo en casos como o noso que queremos potenciar a parte c\u00f3mica do texto. Pero, como poder\u00edas asumir polo t\u00edtulo do post, a parte que fixo maior cambio foi engadir un pequeno son con cada nova palabra.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sintetizador<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"410\" src=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/05\/image-1-1024x410.png\" alt=\"\" class=\"wp-image-105\" srcset=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/05\/image-1-1024x410.png 1024w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/05\/image-1-300x120.png 300w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/05\/image-1-768x307.png 768w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/05\/image-1-1536x615.png 1536w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/05\/image-1.png 1566w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Dadas as pretensi\u00f3ns do noso xogo, engadir actuaci\u00f3ns para cada li\u00f1a de texto non \u00e9 viable, como ser\u00eda de esperar. Ademais que isto ser\u00eda contraproducente \u00e1 hora de traducir o xogo a outros idiomas. Queremos facelo principalmente en galego, pero \u00e9 certo que chegar\u00e1 o momento que para aumentar a base de usuarios o te\u00f1amos que traducir e, con voces reais isto ser\u00eda o triple de traballo.<\/p>\n\n\n\n<p>Por outra banda, tampouco queriamos engadir o mesmo son unha e outra vez e por riba para todos os personaxes, xa que un <em>bip<\/em> continuado pode acabar taladrando os t\u00edmpanos do xogador e facendo que baixe o volume por completo.<\/p>\n\n\n\n<p>A soluci\u00f3n foi usar o plugin <a href=\"https:\/\/github.com\/timothyqiu\/gdfxr\">GDFXR<\/a>, baseado no popular sintetizador <a href=\"https:\/\/www.drpetter.se\/project_sfxr.html\">SFXR<\/a>. SFXR \u00e9 unha pequena ferramenta desenvolta de xeito altruista para xerar sons de videoxogos. Hai unha versi\u00f3n en li\u00f1a, <a href=\"https:\/\/sfxr.me\/\">JSFXR<\/a> que te convido a probar se nunca o fixeches.<\/p>\n\n\n\n<p>A idea deste tipo de sintetizadores \u00e9 que a partir de s\u00f3 uns poucos par\u00e1metros se poden conseguir sons moi distintos e, como se pode experimentar nos seus axustes predefinidos, se poden axeitar a explosi\u00f3ns, saltos, recollida de obxectos ou moitas outras acci\u00f3ns com\u00fans.<\/p>\n\n\n\n<p>A maneira habitual de utilizar este sintetizador \u00e9 creando os sons en formato WAV para logo engadilos ao xogo. Este ser\u00eda o modo habitual de usar tam\u00e9n o plugin GDFXR, pero no noso caso quer\u00edamos dar m\u00e1is expresividade a cada interacci\u00f3n.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sons algor\u00edtmicos<\/h2>\n\n\n\n<p>Xa que o sintetizador se pode executar dentro do propio Godot e dende o c\u00f3digo podemos modificar os par\u00e1metros para a xeraci\u00f3n, \u00e9 posible reproducir sons \u00fanicos para cada nova palabra sen aumentar o tama\u00f1o do xogo.<\/p>\n\n\n\n<p>Para isto comezamos definindo un son &#8220;base&#8221; para cada personaxe. A maneira foi experimentando na web JSFXR ata dar con algo que m\u00e1is ou menos se axeitase ao car\u00e1cter que quer\u00edamos dar a ese NPC. Unha vez demos con algo axeitado, usamos a opci\u00f3n de GDFXR de importar a configuraci\u00f3n directamente dende a web.<\/p>\n\n\n\n<p>Hai moitos par\u00e1metros que se poden modificar, e probablemente no futuro engadamos alg\u00fan m\u00e1is din\u00e1mico, pero \u00e9 suficiente con modificar lixeiramente a frecuencia para que os sons sexan o suficientemente distintos.<\/p>\n\n\n\n<p>Como queremos que cada novo son sexa parecida \u00e1 &#8220;base&#8221;, utilizamos unha funci\u00f3n aleatoria con <a href=\"https:\/\/en.wikipedia.org\/wiki\/Normal_distribution\">distribuci\u00f3n normal<\/a>, <code>randfn<\/code>, deste xeito ser\u00e1n m\u00e1is habituais os sons achegados ao inicial con alg\u00fan que outro mais diferente.<\/p>\n\n\n\n<p>O c\u00f3digo quedou as\u00ed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Usamos AudioStreamPlayer como base\nextends AudioStreamPlayer\n\n# Necesitamos inclu\u00edr os scripts do addon\nconst SFXRConfig = preload(\"res:\/\/addons\/gdfxr\/SFXRConfig.gd\")\nconst SFXRGenerator = preload(\"res:\/\/addons\/gdfxr\/SFXRGenerator.gd\")\n\nvar sfxr_config = SFXRConfig.new()\nvar sfxr_gen = SFXRGenerator.new()\nvar orig_base_freq = sfxr_config.p_base_freq\n\n# Cada vez que se cambia de NPC actualizamos o son base\nfunc update_sound_base(path: String):\n  sfxr_config.load(path)\n  orig_base_freq = sfxr_config.p_base_freq\n\nfunc play_new_sound():\n  sfxr_config.p_base_freq = randfn(orig_base_freq, 0.1)\n  # os par\u00e1metros son para 8 bit, 22050Hz\n  stream = sfxr_gen.generate_audio_stream(sfxr_config, 0, 1)\n  play()\n<\/code><\/pre>\n\n\n\n<p>A chamada a <code>generate_audio_stream<\/code> queda un pouco cr\u00edptica, xa que os enum est\u00e1n definidos no script SFXGenerator e non se exportan. A raz\u00f3n de usar estes \u00e9 porque \u00e9 un pouco m\u00e1is r\u00e1pido.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Exportar a Android<\/h2>\n\n\n\n<p>Con isto xa ser\u00eda suficiente para reproducir os sons no PC, pero por desgraza hai unha serie de problemas ao intentar usar este c\u00f3digo en dispositivos m\u00f3biles, sendo o problema m\u00e1is grande a diferenza de velocidade, pero non o \u00fanico.<\/p>\n\n\n\n<p>O arquivo usado para gardar os datos \u00e9 un &#8220;.sfxr&#8221;. Cont\u00e9n por unha parte os par\u00e1metros para xerar o son e por outra o arquivo xa xerado en WAV. \u00c1 hora de exportar o proxecto a Android, Godot fai unha conversi\u00f3n do arquivo WAV, seguramente para comprimilo, pero decide prescindir dos par\u00e1metros por completo. Por isto \u00e9 necesario modificar os par\u00e1metros de improtaci\u00f3n para &#8220;preservar o arquivo como \u00e9&#8221;.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"765\" src=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/05\/image.png\" alt=\"\" class=\"wp-image-104\" srcset=\"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/05\/image.png 640w, https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-content\/uploads\/sites\/8\/2025\/05\/image-251x300.png 251w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<p>Na imaxe superior vemos que hai tres arquivos sfxr marcados cunha &#8220;x&#8221;, que significa que non se modificar\u00e1n ao exportalos. O outro, que si ten unha icona de audio decid\u00edn mantelo coa importaci\u00f3n por defecto, xa que ese \u00e9 o que se utiliza cando se preme unha opci\u00f3n de di\u00e1logo e ao escoitarse menos a mi\u00fado se pode manter est\u00e1tica.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Seguintes pasos<\/h2>\n\n\n\n<p>GDFRX suxire non crear os arquivos no momento, sen\u00f3n gardalos previamente. Isto \u00e9 porque leva moito tempo, varias veces o que debera durar un <em>frame<\/em>. A pesar diso, polo menos polo momento nos decidimos mantelo como se explicou neste artigo.<\/p>\n\n\n\n<p>No seguinte artigo explicarei como puidemos xerar o audio no momento sen afectar ao rendemento incluso en dispositivos limitados co uso de varios f\u00edos de procesamento.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>O son nos videoxogos \u00e9 unha parte que a mi\u00fado pasamos por alto cando desenvolvemos os nosos proxectos de afeccionados. Como se pode ver en calquera artigo sobre &#8220;espremer o zume&#8221; na creaci\u00f3n de videoxogos a presencia do audio e m\u00fasica xoga unha parte fundamental no xeito que os xogadores perciben os nosos produtos. O [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":106,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[1],"tags":[],"class_list":["post-89","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-sen-categoria"],"_links":{"self":[{"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/posts\/89","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/comments?post=89"}],"version-history":[{"count":2,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/posts\/89\/revisions"}],"predecessor-version":[{"id":107,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/posts\/89\/revisions\/107"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/media\/106"}],"wp:attachment":[{"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/media?parent=89"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/categories?post=89"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.amarinha.gal\/metarcade_bitacora\/wp-json\/wp\/v2\/tags?post=89"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}