Wednesday, January 28, 2015

Creating dynamic scrolling menus in Unity UI

Menus are a fact of life in any RPG.  There are menus for abilities, items, stores, quests, and tons of other things.  Thankfully, Unity UI has some nice tools to quickly build dynamic menus with custom content.  Here’s the recipe I’ve been using:

In Unity:

1. Create a panel to hold the Scrolling content.  Give this a name “AbilitiesPanel”.

2. Add a Game Object as a child.  This is the actual Scroll Container.  Add the Scroll Rect Component.

3. Add an Image Component, and a Mask Component.  These two are used so the scrollable content is “cut off” by the boundaries of the scroll container.


4. Create the scrolling content Panel, as a child of the Scroll Rect GameObject.  Give this the Vertical Layout Group Component.  Give the scrolling content a name and a tag (AbilityContentPanel) so you can quickly get a reference in script.



5. Drag the Scrolling Content to the content field in the parent Scroll Rect Component.




6. Create the UI content of each item inside the scrollable menu.  In this case, we have a parent Panel, with a child Button and Text UI elements, in a horizontal layout.



7. Add a Prefab to the Assets, in the Resources folder.  This prefab will need to be loaded at runtime, so it needs to be in Resources.  Give it a name (AbilityPrefab).  Drag your menu item to the prefab.  Once it turns blue, it’s synced with the prefab.  The original UI elements can now be deleted from the Hierarchy.



In Script:


1. Get a reference to your scroll content container.
Transform AbilityPanel = GameObject.FindGameObjectWithTag("AbilityContentPanel").transform;

2. Initialize the Prefab. Hold onto the created Game Object.
      
   var AbilityItemPrefab = Resources.Load<GameObject>("AbilityPrefab");
foreach (var ab in usableAbilityList)
 {
            GameObject abilityItem = (GameObject)Instantiate(AbilityItemPrefab);
updateAbilityButton(abilityItem, assetLibrary.getSprite(SpritesheetType.Particles, 0), ab);
            abilityItem = updateAbilityItem(abilityItem, ab);
            abilityItem.transform.SetParent(AbilityPanel, true);
        }
3. Update the GameObject components with the custom content from your list.

private void updateAbilityButton(GameObject parent, Sprite sprite, Ability selectedAbility)
{
        Image buttonImage = parent.GetComponentInChildren<Image>();
        buttonImage.overrideSprite = sprite;

        Button buttonClick = parent.GetComponentInChildren<Button>();
        buttonClick.onClick.AddListener(() => PlayerAbilityStart(selectedAbility));
}

private GameObject updateAbilityItem(GameObject abilityItem, Ability a)
{
        string abilityText = string.Format("{0} - {1}. AP: {2} Uses: {3}",a.name,a.description,a.ap,a.uses);
       
        //set click on icon
        UpdateTextComponent(abilityItem, "AbilityText", abilityText);

        return abilityItem;
}


4. Change the transform parent of the Initiated Prefab GameObject to the Scroll Content container.

   abilityItem.transform.SetParent(AbilityPanel, true);
 
That’s pretty much it. You’ll have to have methods that are called when you click on the button. And remember if you want to update your Menu, you’ll need to remove all the Initialized Game Objects from the prefab first.

Here’s a helper method:

private void DestroyAllChildren(Transform parent)
{
        for (int i = parent.childCount - 1; i >= 0; i--)
        {
            Destroy(parent.GetChild(i).gameObject);
        }
}
 
 

Tuesday, January 27, 2015

Game Progress

Short video showing the current progress:

Design in Brief

Here are the design goals I have for the game:

  • Turn based tactical battle system.  Multiple player characters, NPCs and enemies.  Complex spells, abilities, items that affect both the board and characters

  • Multiple character classes.  Level up both stats and abilities.  Can also find special abilities throughout game world (scrolls).  Abilities are defined by both the effect, and the pattern they have in the tileset.  When viewing abilities, should show both.

  • Nested and well-written dialog with NPCs.  Communication of the game narrative is primarily through dialog / stylized cut scenes?
    • Can have 2+ people involved in dialog.
    • Format is similar to old Bioware games: Portrait of speaker with text.  Multiple options for responses.
    • Traveling down certain paths locks the dialog / unlocks quests or items.

  • Exploration is done through a world map that populates with locations.  Locations can lead to further Location maps, which in turn contain scripted battles, NPC dialog, stores, etc
    • The tile-based map will only be used for tactical battles. Dungeon exploration / towns / world exploration simply done through maps that unlock visitable locations.  Non-combat exploration via tile based world is frankly boring but takes a lot of work.  Would rather have a single artistic backsplash + icons to represent town, dungeon, etc

  • Straightforward but system with depth: inventory, equipment, stores, crafting
    • Lists of icons + name.  Hover over to see detail / compare.  Auto-stacks.  Use checkmark to buy/sell multiples.
    • Crafting (discover by luck + find recipes)

  • Quests are revealed via dialog.  Quests are maintained in a game journal.  Instead of having everything defined directly (kill x, collect 5 widgets), the quest entry should simply paraphrase the game narrative, but also be able to review narrative so far.  

  • Data Driven: The majority of the game (98%) is defined by external data files and assets.  This will allow modders and players to change the game on the fly, create custom quests, even full games.  

Unity project on Github!

I've finally pushed the Unity RPG project up to Github: https://github.com/tdonlan/UnityRPG

This is the full source of what I've been working on, the integration of the turnbased battle game with UI / tileset in Unity.

Other oldschool RPGs

Some pretty good discussion on other RPGs being released or developed in the same style that I'm going for: http://www.reddit.com/r/gamedev/comments/2ts0js/are_there_still_decent_turn_based_rpgs_being_made/

Saturday, January 10, 2015

Screenshot!


Here's a screenshot of our game world, rendered in Unity using the Angband spriteset.  #screenshotsaturday

Sunday, January 4, 2015

Happy New Year!

Happy New Year!

It's always nice to get a free dose of motivation when the calendar resets.

We've made some good progress on the game so far - we have a semi-functioning battle game written in .NET that can be played through the command line.  The goals outlined in the first post are probably 80% complete.

Of course, there's a lot of code to be written for the AI, items and abilities, but the framework is there.  My philosophy with this game is to move fast and get a working structure in place, rather than fiddling around till something is 100%.

This month I'm going to be switching gears and mostly experimenting around in Unity.  I've done some small Unity projects in the past, but I'm a little rusty right now.  Spending some time in the environment is key to solving the big challenges I'll have down the line.

Here are my goals for January:

  • Unity
    • Get familiar  doing stuff in Unity
    • Accessing GameObject / Components
    • UI - user input, building menus, etc
    • Assets - Spritesets, animations, etc
  • Game
    • Linking the .NET gameworld to user entry / display in Unity


At the end of the month, I'd love to be able to load the game in Unity with an existing tileset (I'll be using the free Angbad TK spritesets from here ) and play using the mouse (move / attack).

Artificial Intelligence - Weighted Heuristics

One of the goals of my game is to have enemy AI that's robust enough to drive interesting and challenging battles, and also defined by external data. Ideally, enemy behavior would simply be defined by a few enumerated types, and from there, the state of the world (items, HP, positioning) and the enemy type (mage, warrior, priest) would lead to good gameplay.

It's going to be a while before I can say I'm at that point, but for now I've come up with an approach I'm calling weighted heuristics.


Watch live video from daydalus9 on Twitch

Each AI Actor has a collection of Actions, which have a weight and cost.

public Dictionary<AIActionType, int> actionWeightDictionary { get; set; } //static for life of character
public List<AIAction> AIActionList { get; set; } //updates every turn

    public enum AIActionType
    {
        Attack,
        RangedAttack,
        Heal,
        Buff,
        Nuke,
        Flee
    }

    public class AIAction
    {
        public AIActionType actionType { get; set; }
        public List battleActionList { get; set; }
        public int cost { get; set; }
        public int weight { get; set; }
    }


The default weights are defined by the enemy type, and range from 0-100. They can be thought of as a percentage that the action would be chosen.

A warrior would have something like:

case EnemyType.Warrior:
  dict.Add(AIActionType.Attack, 80);
  dict.Add(AIActionType.Heal, 10);
  dict.Add(AIActionType.Flee, 5);


while a priest would have something like:

case EnemyType.Priest:
  dict.Add(AIActionType.Attack, 10);
  dict.Add(AIActionType.Buff, 50);
  dict.Add(AIActionType.Heal, 75);
  dict.Add(AIActionType.Nuke, 50);
  dict.Add(AIActionType.Flee, 25);


The weights can also be altered each round. Healing self can be a function of current hitpoints:

int healWeight = 100 - ((character.hp / character.totalHP) * 100);


The cost of each action is driven by the actual battle actions available.

Ex: A warrior trying to attack someone 5 spaces away would cost 5 + weapon AP.

Once you have a collection of Actions, with weights and costs, you can sort them based on this formula:

AIActionList.Sort((x1, x2) => (x1.cost * (100 - x1.weight)).CompareTo(x2.cost * (100 - x2.weight)));


Once you have your sorted list, you can return the first action and get the optimum BattleAction the AI should take. If any Action Points are remaining, you can recalculate the list and get the next available Battle Action.

Right now I've built out the logic for attacking, ranged attacking (which will seek out the nearest LOS to fire the ranged weapon), and healing self. There's still a good bit more code to write, but the framework is there for the enemies to fight somewhat intelligently.