2019 Mar 10

Fire Emblem CiphAR - Release 2

Release 2 (Store page) has been rolled out to production. Here are the major fixes made/features added:

1) Reduced text jitter considerably. Instead of updating text position every frame, it's now updated every 180 frames. Additionally, when updating position, it now linearly interpolates between the current position and the new position. I also updated the image database used by ARCore with the actual size of the scanned cards, which apparently helps in tracking image targets.

2) Based on feedback, I added a few changes to overlaid text to increase readability. There's now a thin black outline around the rendered text. Additionally, I added a color picker that can be used to change font color.

3) Added some cards that were missing from series 15. There are some other cards that aren't working as well, that are listed below.


From starter decks:

  1. S01-003ST Loyal Retainer, Jagen
  2. S02-004ST(+) Steel Swordsman, Ogma
  3. S04-004ST(+) Leo: Gravity Master
  4. S06-001ST(+) Itsuki Aoi: Chosen Young Lord
  5. S06-002ST(+) Tsubasa Oribe: The Pegasus Idol
  6. S06-004ST(+) Kiria Kurono: Quirky Charisma
  7. S06-005ST(+) Eleonora Yumizuru: Aiming for Hollywood!

From series:

  1. B01-006HN Caeda: Princess of Talys
  2. B01-010N Abel: Green Knight
  3. B01-047N Tiki: Divine Dragon Princess
  4. B01-055N Lucina: Sacred Descendant
  5. B01-056HN Lucina: Swordsman Known as Marth
  6. B01-079N Nowi: Tomboyish Manakete
  7. B01-082HN Practitioner of Dark Arts, Tharja
  8. B01-096N Noire: Bipolar Archer
  9. B01-099HN Tiki: Sentimental Divine Dragon
  10. B01-002N Corrin (Male): Hidden Dragon Prince
  11. B02-005HN Azura: Songstress of the Fountain
  12. B01-007N Ryoma: First Prince of Hoshido
  13. B02-009N Hinoka: First Princess of Hoshido
  14. B02-031N Felicia: Clumsy Maid
  15. B01-036N Rinkah: Daughter of Fire
  16. B02-055HN Azura: Songstress of Water
  17. B02-063N Elise: Princess of the Dark Sky
  18. B01-036N Charlotte: Lovely Warrior Lady
  19. B03-017N Rolf: Vendure Brother
  20. B04-003HN Tsubasa Oribe: Dreaming Schoolgirl
  21. B04-027N Catria: Swift Whitewing
  22. B04-053HN Itsuki Aoi: Normal Kid
  23. B04-059R(+) Eleonora Yumizuru: Precious Actress
  24. B04-060N Eleonora Yumizuru: Forceful Heroine
  25. B04-061HN Eleonora Yumizuru: Ambitious Young Actress
  26. B04-095N Severa: Hesitant Blade
  27. B04-097N Laurent: Seeker of Truth
  28. B06-045R Mahnya: Sky-Dancing Hero
  29. B05-056SR(+) Leo: Dark Prince of Chilling Magic
  30. B06-059N Elise: Sweet Sister
  31. B06-068HN Arthur: Knight of Bad Luck
  32. B06-069N Arthur: Unlucky Hero
  33. B06-071N Felicia: Refreshing Ice Sister
  34. B06-032R Raven: Blade of Love and Revenge
  35. B06-036N Fiora: Pure White Wings
  36. B06-037N Pent: Silver Mage General
  37. B06-042N Jaffar: Heartless Assasin
  38. B07-052N Ryoma: Master Swordsman of Hoshido
  39. B76-060R(+) Saizo: Garbed in Honor
  40. B06-073N Jakob: Faithful Staff Knight
  41. B06-083N Yukimura: Bespectacled Wiseman
  42. B06-085N Shiro: Prince Polishing his Spearmanship
  43. B06-087HN Mitama: Sullenly Waking
  44. B06-099HN Yuzu: Crane Dreadknight
  45. B08-003HN Chrom: Branded Prince
  46. B08-007N Robin (Female): The Exalt's Other Half
  47. B06-032N Morgan (Male): Amnesiac Boy
  48. B06-036HN Noire: Ballistic Sniper
  49. B06-037N Noire: A Shot from the Dark
  50. B06-042N Emmeryn: Compassionate Exalt
  51. B08-047aN Risen: Defiled Soldier
  52. B08-047bN Risen: Defiled Soldier
  53. B08-047cN Risen: Defiled Soldier
  54. B76-060N Larcei: Gallant Myrmidon
  55. B06-073N Johalvier: Fighter of Love and Justice
  56. B06-083HN Ares: The Young Black Knight
  57. B06-087R(+) Febail: Gallant Divine Marksman
  58. B06-099N Emma: Kaching Dragon
  59. B09-014N Kliff: Surly Passion
  60. B09-018N Lukas: Messenger from the Deliverance
  61. B06-032N Mathilda: Beautiful Knight
  62. B06-036HN Catria: Drifting Pegasus Knight
  63. B06-037N Est: Drifting Youngest Pegasus
  64. B09-075N Yuzu: Purple-Gleaming Protecto
  65. B09-093N Mist: Girl of the Mercenaries
  66. B10-034N Mareeta: The Sword Saint's Flowing Blood
  67. B06-036SR(+) Tina: Mischievous Saint
  68. B11-101+X(S04-003ST) Camilla: Bewitching Malig Knight
  69. B12-015N Soren: Wise Man of the Mercenaries
  70. B13-096N Princess of Múspell, Laevatein
  71. B14-012HN Olivia: Soothing Dancer
  72. B14-055N Azura: Requiem
  73. B15-012N Roderick: Serene Squire
  74. B15-021N Catria: Macedonian Pegasus Knight
  75. B15-022R(+) Palla: Greenwind Dracoknight
  76. B15-023N Palla: Elder Macedonian Pegasus
  77. B15-049HN Alice: Dauntless Mounted Princess
  78. B15-050HN Valjean: Armored Sorcerer
  79. B15-078SR Altena: Wishing for a Warless World
  80. B15-079N Altena: Thracian-Raised Princess

I'll work on figuring these out this week. Let me know if there's anything not on the list that isn't working as well.


2019 Mar 09

QMK - Added some songs and note types

I've decided to start adding songs to the QMK song list on a weekly basis (since my music format conversion framework makes it really easy to do).

Here is my weekly commit - I added La Campanella by Liszt, Kataware Doki from Kimi no na wa and Megalovania from Undertale.

I also added the breve note type to musical_notes.h, since the default tempo in QMK is very high, so a higher duration note seemed necessary for a lot of slower songs.


2019 Mar 09

Cambiare - A Lightweight Music Conversion Framework

I was messing around with QMK (popular firmware used with mechanical keyboards) a few days ago and was adding custom songs to my Preonic keyboard, and it was taking forever. So I wrote a small utility to convert MusicXML files to a string output format that could easily be added to a QMK song list.

Here's an example input:

Here's an example output:

I later decided to use this utility as the basis of a small framework to convert single staff music inputs into custom outputs, so I could use it for other output types later.

Inputs are modules that implement the MusicImporter abstract class, and outputs must implement the MusicWriter abstract class. The process of adding a new input module mainly consists of converting notes in the input type to a CNote, and adding a new output module mainly consists of converting a CNote into the output format.

The next type of output format I'm planning to implement is for Ocarina tabs, specifically for 12 hole ocarina's with a range of A4-F6.

Github: Link


2019 Mar 05

Tactics RPG - Element Effectiveness Chart

One major addition I'm making to the normal Tactics RPG formula is adding a Pokemon-esque element effectiveness system. Elements can be attached as a component to any other component, such as a unit, an ability or a weapon.

Here is a (tentative, as always) type effectiveness chart for attacks and unit types:

Here is a sample implementation, where the effectiveness chart is parsed at run-time and written into a static class, so it can be accessed by any other entity in the game scene.


using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public static class ElementChart {

    public static Dictionary<ElementTypes, Dictionary<ElementTypes, float>> elementChart = new Dictionary<ElementTypes, Dictionary<ElementTypes, float>>();

    static ElementChart()
    {
        ParseElementChart();
    }

    static void ParseElementChart()
    {
        string readPath = string.Format("{0}/Settings/tactics_rpg_element_chart.csv", Application.dataPath);
        string[] readText = File.ReadAllLines(readPath);
        string[] elementOrder = readText[0].Split('\t');
        for (int i = 1; i < readText.Length; ++i)
            ParseElement(readText[i], elementOrder, elementChart);
    }

    static void ParseElement(string line, string[] elementOrder, Dictionary<ElementTypes, Dictionary<ElementTypes, float>> elementChart)
    {
        string[] elements = line.Split('\t');
        ElementTypes element = (ElementTypes)Enum.Parse(typeof(ElementTypes), elements[0]);
        Dictionary<ElementTypes, float> elementDict = new Dictionary<ElementTypes, float>();
        elementChart.Add(element, elementDict);
        for (int i = 1; i < elements.Length; i++)
        {
            ElementTypes currentElement = (ElementTypes)Enum.Parse(typeof(ElementTypes), elementOrder[i]);
            if (!elementChart[element].ContainsKey(currentElement))
            {
                elementChart[element].Add(currentElement, float.Parse(elements[i]));
            }
        }
    }

And finally, here is an example implementation of the Element class.


public class Element : MonoBehaviour {

    public List<ElementTypes> elements = new List<ElementTypes>();

    public void AddElements(List<ElementTypes> elements)
    {
        this.elements = elements;
    }

    public float GetTypeMultiplier(List<ElementTypes> defenderElements)
    {
        float totalMultiplier = 1;
        for(int i = 0; i < elements.Count; i++)
        {
            totalMultiplier *= GetTypeMultiplier(elements[i], defenderElements);
        }
        return totalMultiplier;
    }

    public float GetTypeMultiplier(ElementTypes attackerElement, List<ElementTypes> defenderElements)
    {
        float elementMultiplier = 1;
        for(int i = 0; i < defenderElements.Count; i++)
        {
            elementMultiplier *= ElementChart.elementChart[attackerElement][defenderElements[i]];
        }
        return elementMultiplier;
    }

}

This implementation assumes that the attacking component (whether it's a weapon or an ability) has this Element component attached to it, and the defending unit has an element attached to it as well. The output is then an elementMultiplier, which is calculated as the product of each ElementType in the attackers Element class against each ElementType in the defenders Element.

For example, an attacker with an Element component that has ElementTypes [Fire, Light] against a defender with an Element component that has ElementTypes [Water, Dark] would result in calculation ElementChart[Fire][Water] * ElementChart[Fire][Dark] * ElementChart[Light][Water] * ElementChart[Light][Dark] = 0.5 * 1 * 1 * 2 = 1.


2019 Mar 05

Tactics RPG - Experience

Right now, experience is rewarded as a weighted amount based on a units level to the entire party. VERY temporary, I don’t like it. It seems like it would make it way too easy to have a balanced party, and way too easy to raise new units. However, I also don’t want to have the fire emblem issue where you get a bunch of potentially good units that become useless because you have no way of raising them (besides grinding, which should probably also be discouraged). Potential ideas:

  1. Some kind of pair up system, similar to Awakening/Fates, where a paired unit gets some share of the EXP when the main unit gets a kill (only the main unit and the paired unit get exp)

    a) Not sold on this, since the unit getting the EXP doesn’t really have to do much

    b) Also runs into the “chaperone” problem, feels similar to escort missions

    c) If implemented, would need to be balanced REALLY well

  2. Some external ways of getting exp? Would need to be very limited, but would probably also have to scale based on where the player is in the game/the levels of their other units maybe?

  3. An item allowing a unit holding it to share exp

    a) Similar to the EXP share (old version) in Pokemon b) Again, feels a bit too easy c) There would probably need to be a heavy downside to using it (ie. cannot move, cannot attack, lowered defense maybe)? But these downsides seem to de-incentivize combat, which I probably don’t want.

  4. NOT “last hit” based

    a) I like this. Basically, instead of having EXP given to a unit just because they got the killing blow, it should be awarded based on the units who actually damaged the enemy b) Equally to all units? Based on damage dealt? Based on level? Honestly not too sure. c) Downsides - this seems like it might make it harder to raise weaker units, unless it’s made to be level based. But, if it’s level based, it makes it harder to prioritize units you really like (unless you make them the sole unit participating in that combat, which kind of seems like it would exacerbate the issue of having useless units). Either way, this is a trade off that needs to be carefully balanced.


Currently, I’m leaning towards something like this example:

  1. Units involved in combat: Lvl 10, 5, 1, 1
  2. Enemy unit killed: Gives 100 xp
  3. Total levels = 10 + 5 + 1 + 1 = 17
  4. Weighted amounts = 10/17, 5/17, 1/17, 1/17
  5. 1 - weighted amounts = 7/17, 12/17, 16/17, 16/17
  6. Easing values = map(lambda x: easing_function(minlvl, maxlvl, (total_lvl-x) / total_lvl, 1-weighted_amounts list)
  7. Eased total = sum of easing values
  8. Eased lvl percents = map(lambda x: x / eased total, easing values)
  9. Eased exp values = map(lambda x: exp amount * x, eased lvl percents)
  10. In the given example: a) Easing values = [8.9, 8.9, 5.5, 2.5] for [1,1,5,10] b) Eased total = ~25.8 c) Eased lvl percents = [.344,.344,.212,.097] d) Eased exp values = [34.4, 34.4, 21.2, 9.7]