C#, Portfolio Projects

Augmented Tactics: A role playing game prototype

Hi,

Today, I’ll be talking about a special project I have not only designed from A to Z but also implemented. Well, it’s not as  big as this sentence let’s it sound. I worked on developing a prototype of a mobile game in augmented reality. Being a huge fan of tactical RPGs, I found it natural to try to develop a tactical RPG prototype. Augmented Tactics tries to replicate the same experience players had when playing Final Fantasy Tactics. After working on this for about 2 months, and I will do it again this fall, I feel compel to talk about the work I did so that others might found inspiration for their project. When I’ll complete the next phase of the prototype, this time called “SmartTactics”, I will then talk about its progress.

What did I want to achieve ?

Being a huge fan of TRPGs like I’ve mentioned earlier, what I wanted to achieve was to re-create a typical battle system. Such a system would include a certain number of characters of different class ( Magician, Swordsman, Lancer, etc) in both parties. Those classes would have the special abilities such as attacking with a weapon (sword, axe or whatever) or spells and also,each of those classes would have a tactical advantage to one or more.

In many games, players must move their character in a squared field where depending of the character’s stats and class, he/she can move from 1 to a maximum squares (let’s say 7) during the movement phase. I wanted also to give different options to the player during the attack phase. With that in mind,  attacking an enemy from the side would give a bonus when attacking, which would be added to the attack bonus if that character had any special role advantage.

Characters coming from a Magician sub-class such as Spellsword, Healer, Dark Mage, etc, would have each have 1 to 2 different spells depending of the sub-class and the strength of those spells. Something that would be a fun mechanic (in my honest opinion) would spells that could interact with the environment. For instance, a spell that could create a line of fire and if a character goes through that line, he/she will suffer burn side-effects until the side-effect goes away or a special item is used to counteract that effect.

Talking about fun mechanics for my prototype,  each class would have certain number of action points which would include actions such as moving, attacking, rotating and so on and so on. The player would need to correctly use those points to form a strategy to defeat the AI. Not only that, working with Unity would provide me the chance to play with augmented reality.  Vuforia is an easy API that seamlessly integrates with the game engine and can project the 3D models using a special anchor in the real world.

Developing under TDD

In an effort to develop this game correctly, I design the tests using NUnit. I don’t have to tell you that TDD enabled me to think of the code I wanted to do in order implement, for instance, a character builder. The character builder creates a character, gives it stats and different kind of information. There’s one example below.


[TestMethod]
 [TestCategory("Character Builder")]
 public void CharacterBuilder_BuildAnNormalHealer_Successful()
 {
var character = new CharacterBuilder().CreateCharacter()
        .WithHealth(100)
        .WithStats(0, 10, 10, 10, 10, 10, 10, 10)
        .WithDirection(2)
        .WithType(8)
        .BuildCharacter();
    Assert.IsNotNull(character);
    Assert.IsInstanceOfType(character, typeof(BaseEnemy));
    Assert.IsInstanceOfType(character, typeof(NormalHealer));
 }

 

Player abstraction

I know that some systems are faulty  because of their design. Such design problems can be categorized as “too coupled”. Having that in mind,  most of the code is based on the following classes : CharacterObservable ,IChoiceAction and ICharacterActionCommand. The first abstract class give the power to manipulate characters at any stages, whether I’m building that healer class or that berseker is about to die and I have to notify the game manager to launch the dying sequence before destroying the game object. For those who find CharacterObservable  name funny, I based the work on the Observer pattern.


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Assets.GameCoreLogic.Managers;
using Assets.Map_AI;
using Assets.Scripts.CharactersUtil;
using UnityEngine;

namespace Assets.GameCoreLogic.Human_Game_Objects
{
public abstract class CharacterObservable : MonoBehaviour,IGameObjectObservable, ICharacter
{
protected ArrayList ObserversList = new ArrayList();
public BaseCharacterStats BaseStats { get; set; }
public Health Health { get; set; }
public List<ICharacter> CurrentEnnemies { get; set; }
public List<ICharacter> TeamMembers { get; set; }
public int MovementPoints { get; set; }
public PlayerDirection Direction { get; set; }
public Cell CurrentCoordinates { get; set; }
public Cell OldCoordinates { get; set; }
public bool IsDead { get; set; }
public int[] Position { get; set; } // [0]=x, [1]=y
public Animator Animator { get; set; }
public CharacterType CharacterType { get; set; }
public Role CharacterRole;
public bool isDamage;
public bool healthWasRaised;

public abstract bool CanDoExtraDamage();
public abstract void CheckIfCharacterDead();

public void Attach(IGameObjectObserver observer)
{
if (observer == null)
throw new ArgumentNullException(nameof(observer));
if (ObserversList.Contains(observer))
throw new InvalidOperationException("Cannot have duplicate observers.");
ObserversList.Add(observer);
}

public void Detach(IGameObjectObserver observer)
{
if(observer == null)
throw new ArgumentNullException();
if(!ObserversList.Contains(observer))
throw new InvalidOperationException("Cannot remove observer when observer not in the list.");
if(ObserversList.Count == 0)
throw new InvalidOperationException("The observer list is empty. Cannot detach.");
ObserversList.Remove(observer);
}

public abstract void Notify();
}
}

IChoiceAction interface let’s me create the sequence of action the AI can do. Through the interface, the AI can build an attack sequence to either attack the closest human player or the one most likely to get a maximum damage due to a certain number of parameters.


using Assets.Scripts.Artificial_Intelligence;
using Assets.Scripts.CharactersUtil;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Assets.Map_AI;

namespace Assets.GameCoreLogic.AI_Core_Logic
{
public interface IChoiceAction
{
#region Movement Decisions
bool ArePlayersWithinRange(List<BaseCharacter> humans);

bool CanMoveAway();

bool ShouldMoveAway();

void MoveAway();

bool IsFacingPlayer(BaseCharacter bs);

KeyValuePair<BaseCharacter, Tile> FindClosestTarget(List<BaseCharacter> humans);

KeyValuePair<BaseCharacter, Tile> FindEasiestTarget(List<BaseCharacter> humans);

void MoveTowardsTarget(BaseCharacter bs);

#endregion

#region Attack and Defend

bool ShouldInvokeDefend();

void InvokeDefend();

void AttackPlayer(BaseCharacter bs);

void AttackPlayer(List<BaseCharacter> bsList);

#endregion

#region Healer Needed
bool IsHealNeeded(BaseEnemy be);

Tile FindHealer(List<BaseEnemy> myParty);

bool IsHealerWithinRange(Tile healerPosition);

void MoveTowardHealer(bool healerIsInRange);

#endregion

void FinishTurn();
}
}

ICharacterActionCommand interface let’s me build the battle system. Through that interface, I can notify the command manager when the player wants to attack an enemy or when I need to move to a certain place.


using Assets.GameCoreLogic.Managers;
using Assets.Scripts.CharactersUtil;
using Assets.Scripts.GameCommands;

namespace Assets.GameCoreLogic.Game_Command_Actions
{
public interface ICharacterActionCommand
{
IReceiver Receiver { get; set; }
bool IsExecuted { get; set; }

void Execute(ICharacter caller);
void Execute(ICharacter caller, ICharacter characterToAttack);
void Execute(ICharacter caller, int raiseDefense);
}
}

Acting this way, my code looks for either interfaces or abstract classes. Being not couple, the code becomes very reusable in my opinion and it made that much easier for testing after designing my tests.

Artificial Intelligence

Map Intelligence

After designing the map as a List of List of tiles,  I made it simple to highlight the tiles in the game to show the entire range of movement of a given character during the move phase, if the player desires to move the character, it’s up to him. In order to know what to highlight, I look at the move points available for the character, and I try to create paths that are still “opened” for the A* pathfinder algorithm. That algorithm enables to find the shortest path between point A and point B. That an useful algorithm for the character intelligence.

Character Intelligence & Move Selector

All characters all have the same the same intelligence. Being that I only had 2 months to build a working prototype, I couldn’t put too much time working in a field in which I wasn’t familiar (AI). Basically, characters can decide to

  • Move towards the closest enemy and attack it (ClosestTargetCommandSequence)
  • Go towards the enemy which will suffer the greatest damage (AttackMostDamageableTargetCommandSequence)
  • Upgrade temporally the defense stats for the course of a turn (DefensiveCommandSequence)
  • Efficient Attack which would be able to plan 2-3 turns ahead to see which would mostly be always attacking most damageable targets and staying clear of small parties that could kill you. (That was planned but neither designed or implemented)
  • Move towards the healer when health is low or heal member in party with the lowest health.
  • Move away from danger which could be something like this character has a tactical advantage on self or if I attack this character, I’m putting myself in danger because I’ll be close to a party of three and health is below X %.

The move selector enables the AI to select which command sequence to perform at each turn. Depending of the situation, each of the command sequences are created and weighted. For instance, moving to the fullest of the available move points doesn’t give a big weight. But, having a low health (~30 % for example) gives a much bigger weight. The sequence with the highest weight is perform through the use of a List<Action>. This is custom AI. I wasn’t really using AI algorithms except the A* implementation that I found online.

What I’m planning for phase 2 -> SmartTactics

For phase 2, I’m thinking of using many AI algorithms to help build the hard mode of the prototype. Using supervised and unsurpervised learning algorithms, I will be able to create a better AI that I had developed last fall. First, I’ll train the AI to adjust itself against players with high offensive stats and low defensive stats. Then, I’ll make so to train it using high defensive stats and low offensive stats. I’ll see how the AI reacts when placed in a simulated environment where characters have mixed stats.

To make things more stimulating, I will let the AI get experience and be able to level up its characters. Doing so, I will have to let it learn how best upgrade the stats using its brain to see which stats give better reaction in the field for a given class after leveling up. I would also like to see if that smarter brain can pick the best equipment from the merchant for any class.

I would use many different parameters to help the smart brain to practice and get better over time, things such as getting bonus points if brain get beat its best time, the experience concerning when a given strategy was used or the remaining health points of a targeted enemy.

I’ve been dabbling with F# during my internship and at my part-time job this summer. With that in mind and wanting to pursue my work in functional programming, I was thinking to port the game in F# in either Urho3D, Xenko, Duality or even Unity game engine. So the Augmented Tactics prototype will be ported from C# to F#. Being that I remain in .NET, the work wouldn’t be herculean. Another thing would be to make fixes and implement small lacking features from the first prototype.

You can look at the work I did during those 2 months here :https://github.com/Kavignon/Augmented_Tactics

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s