My game has a similar battle system like Final Fantasy however in my system the player and the AI will only fight with one character at a time.
I’m fairly new to game developing and AI systems so I followed the advice of this article.
It suggests to use formulas to simulate certain desires instead of hard coded values like
if(ammo == 0) {
reload();
}
I tried my best and the result does look okay-ish. Could someone help me to improve it?
Here is the code of my behavior class. Each time it’s my AI’s turn the method getBehavior(Hero target) will be called.
private static final String BEHAVIOR_ATTACK = "attack";
private static final String BEHAVIOR_DEFEND = "defend";
private static final String BEHAVIOR_REGSTAM = "regstam";
private static final String BEHAVIOR_SKILL = "skill";
private Enemy host;
private float attackBias;
private float skillBias;
private float defendBias;
private float lastTurnHealth; // currently unused
private float lastTurnStamina; // currently unused
private Hero target;
private float lastTurnTargetHealth; // currently unused
public Behavior(Enemy enemy) {
this.host = enemy;
this.attackBias = 0.2f;
this.skillBias = 0.1f;
this.defendBias = 0.2f;
this.lastTurnHealth = 1;
this.lastTurnStamina = 1;
this.lastTurnTargetHealth = 1;
}
public void reset(){
this.lastTurnHealth = 1;
this.lastTurnStamina = 1;
this.lastTurnTargetHealth = 1;
this.target = null;
}
/**
* The desire rises as the target has less health.
* The basic formula is 1/(0.75 + e^(targetHealthPercent / attackBias))
* @return the desire to attack the target
*/
private double desireOfAttack() {
return 1/(0.75 + Math.pow(Math.E, (target.getHealthPercent() / attackBias)));
}
/**
* The desire rises as the target has less health but lowers as the host's stamina gets low.
* The basic formula is 1 / (0.74 + e^((targetHealthPercent / skillBias) / hostStaminaPercent))
* @return the desire to attack the target with a skill
*/
private double desireOfSkillUse(){
return 1 / (0.74 + Math.pow(Math.E,((host.getHealthPercent() / skillBias) / host.getStaminaPercent())));
}
/**
* The desire rises as the host has less health.
* The basic formula is 1/(1 + e^(10*hostHealthPercent - 10*defendBias)
* @return the desire to defend against the targets attacks
*/
private double desireOfDefend() {
return 1/(1 + Math.pow(Math.E, (10 * host.getHealthPercent()) - (10 * defendBias)));
}
/**
* The desire rises as the host has less stamina.
* The basic formula is 1/(1 + e^(10 * hostStaminaPercent)
* @return the desire to defend and regenerate stamina
*/
private double desireOfStaminaRegeneration(){
return 1/(1 + Math.pow(Math.E, 10*host.getStaminaPercent()));
}
public CombatAction getBehavior(Hero target) {
TreeMap<Double, String> desireMap = new TreeMap<>();
if(target != this.target) {
this.target = target;
this.lastTurnTargetHealth = target.getHealthPercent();
}
desireMap.put(desireOfAttack(), BEHAVIOR_ATTACK);
desireMap.put(desireOfDefend(), BEHAVIOR_DEFEND);
desireMap.put(desireOfStaminaRegeneration(), BEHAVIOR_REGSTAM);
if(host.getSkills().size() > 0){
desireMap.put(desireOfSkillUse(), BEHAVIOR_SKILL);
}
System.out.println("AnxietyMap: "+ desireMap.descendingMap().toString());
this.lastTurnStamina = host.getStaminaPercent();
this.lastTurnHealth = host.getHealthPercent();
return getBehaviorAction(desireMap.descendingMap().values().toArray()[0].toString());
}
private CombatAction getBehaviorAction(String behavior){
CombatAction action = new CombatAction();
switch (behavior) {
case BEHAVIOR_ATTACK:
action.skillUse(host, target, SkillBook.Enemy.attack);
break;
case BEHAVIOR_REGSTAM:
case BEHAVIOR_DEFEND:
host.isDefending(true);
host.regenerateStamina(true);
break;
case BEHAVIOR_SKILL:
action.skillUse(host, target, getRandomSkill());
break;
default:
break;
}
return action;
}
private Skill getRandomSkill(){
return host.getSkills().get(MoreMath.random(0, host.getSkills().size()-1));
}
I want to extend the methods later to consider the “lastTurnXYZ” variables to simulate hesitation (when the player hits really really hard), euphoria (the player lost a really good chunk of health) and desperation.
Is this approach fine or should I redo it in another way?