Blog and documentation for Game Off 2021 attempt.
November 07, 2021
"csharp.referencesCodeLens.enabled": false
in user settings to stop VSCode from displaying x references
above every variablejson.net
package by searching the package manager, but manually added "com.unity.nuget.newtonsoft-json": "2.0.2"
to the dependencies seems to have worked (and now it shows up in the manager)Project Settings > Player > Configuration > API Compatibility Level: 4.X
for .NET support of dynamic
type. I don’t know what other implications this hasApplication.persistentDataPath
Just going to program as it comes naturally to me.
Overarching BattleManager
which will keep track of things like the actors involved, turn order, and UI updates.
BattleActor
which implements some stat-line (imported from JSON), tags, and actions that can be taken.
Actions are some simple composition of targets and effects (which may have some conditional component).
Remember to have all the control objects separate from the graphical implementations (generally that means empty parent objects with graphical child objects?).
Just started programming with the “goal” in mind. I ended up with several micro-refactors as I went along, implementing small toy examples to keep things in mind. For example I started with:
BattleActor target = targets[0];
int damage = ((EffectBasicAttackDamage)effects[0]).effect(origin, target);
if (damage > 0)
{
if (((EffectReduceAttackDamage)effects[1]).effect(origin, target))
damage /= 2;
target.currentHealth -= damage;
}
As a method to calculate basic attack damage with a possible 50% damage reduction modifier.
Looking at this, it’s obvious it doesn’t scale well. I don’t ever want to specify certain subclasses in the source code. If the effects are composable and can be specified by JSON files outside the source, it becomes much easier for me to add new effects to the game. This led to:
BattleActor target = targets[0];
int damage = 0;
foreach (var effect in effects)
damage = effect.process(origin, target, damage);
if (damage > 0)
target.currentHealth = target.currentHealth - damage;
Every effect involved in the “BasicAttack” action will affect the damage dealt in some way, so now I can use a different loading process to grab all the effects that specify themselves as attack damage modifiers.
That led me to changing around the effects (which themselves went through several revisions abstraction) to:
public class EffectBasicAttack : AttackDamageEffect
{
// Calculate damage from attack and defense values
public EffectBasicAttack() { }
public override int effect(BattleActor origin, BattleActor target, int damage)
{
int moreDamage = origin.currentAttack - target.currentDefense;
if (moreDamage > 0)
return damage + moreDamage;
else
return damage;
}
}
public class EffectPercentModifyDamage : AttackDamageEffect
{
public float percentReduction { get; set; }
public string targetTag { get; set; }
// Modify current damage based on percentage if tags match
public EffectPercentModifyDamage(float in_percent, string in_tag)
{
percentReduction = in_percent;
targetTag = in_tag;
}
public override int effect(BattleActor origin, BattleActor target, int damage)
{
int index = target.tags.FindIndex(tag => tag.name == targetTag);
if (index != -1)
return damage * percentReduction;
else
return damage;
}
}
In this way, a “multi-attack” could just be the same EffectBasicAttack
applied three times in a row.
However, why limit the effect to a single tag on the target? This led to some thought on using a JSON file to specify the possible effect conditions.
At first I began by adding extra fields to EffectPercentModifyDamage
like originTags
and targetTags
but then I decided that was dumb and should just use boolean logic defined in the JSON.
public class EffectPercentModifyDamage : AttackDamageEffect
{
// TODO: Figure out set values of effectValues and use that
public float percentChange { get; set; }
// Modify current damage based on percentage if tags match
public EffectPercentModifyDamage() { }
public override int process(BattleActor origin, BattleActor target, int damage)
{
if (this.evaluateCondition(origin, target))
return (int)(damage * percentChange); // TODO: determinism worries? non-float ways to calculate?
else
return damage;
}
}
As can be seen, I’m not done with setting up loading data from JSON files yet. I don’t need variables in the subclass if the JSON data model has a well-defined place for “values” that are used in the effects.
I also haven’t implemented the boolean logic parser (the this.evaluateCondition
method) but it should be straightforward.
Given a field:
"condition": {"and": {"target": ["light"], "origin": ["heavy"]}}
It should parse the rule as:
True if "light" in target.tags and "heavy" in origin.tags
From the top-level condition
key, there will only ever be one or two fields and those fields will have one of four keys (and
, or
, target
, and origin
). The value of the fields will be either another dictionary or a list of strings.
10.75 + 5hr