Michahide
Michahide

Reputation: 33

Call OnActionReceived or RequestDecision to make Unity ML-Agents do their turn, Fewer observations (0) made

I make a Turn-Based RPG Game using unity ML-Agents. My Unity ML-Agents here as the enemy and I want my Unity ML-Agents do their turn based on reward they received, but i still can't grasp the logic.

Here is my code in my EnemyAgentAI.cs

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Sensors.Reflection;
using UnityEngine;

public class EnemyAIAgent : Agent
{
    public float attackPower = 10f;
    public float weaknessMultiplier = 2f;
    public float resistanceMultiplier = 0.5f;
    public float blockMultiplier = 0.2f;

    private int NUM_ELEMENT;
    private float reward = 0f;

    public override void OnEpisodeBegin()
    {
        
    }

    public override void CollectObservations(VectorSensor sensor)
    {
        // Observe the attack element
        AttackScript attackScript = GetComponent<AttackScript>();
        NUM_ELEMENT = (int)AttackScript.magicElement.LastElement;


        if (attackScript != null)
        {
            sensor.AddObservation(NUM_ELEMENT);
        }
    }

    public void AgentAttack(ActionSegment<int> act)
    {
        AttackScript attackScript = GetComponent<AttackScript>();
        FighterStats fighterStats = GetComponent<FighterStats>();
        FighterAction fighterAction = GetComponent<FighterAction>();
        var dirToGo = Vector3.zero;
        var rotateDir = Vector3.zero;
        var physicalAttack = act[0];
        var fireAttack = act[1];

        if(physicalAttack == 1)
        {
            attackScript = GameObject.Find("EMeleePrefab").GetComponent<AttackScript>();
            fighterAction.SelectAttack("melee");
            Debug.Log("Melee attack");
        } else if (fireAttack == 1)
        {
            attackScript = GameObject.Find("ERangePrefab").GetComponent<AttackScript>();
            fighterAction.SelectAttack("range");
            Debug.Log("Range attack");
        } 

        // If the agent is trying to attack

        if (physicalAttack == 1 || fireAttack == 1)
        {
            if (attackScript.IsBlockingAttack)
            {
                // reward = -attackPower * blockMultiplier;
                reward = -1f;
            } else if (attackScript.IsResistingAttack)
            {
                // reward = -attackPower * resistanceMultiplier;
                reward = -0.5f;
            } else if (attackScript.IsWeakToAttack)
            {
                // reward = attackPower * weaknessMultiplier;
                reward = 1f;

            } else
            {
                // reward = attackPower;
                reward = 0.5f;
            }
        }

    }


    public override void OnActionReceived(ActionBuffers actions)
    {

        AgentAttack(actions.DiscreteActions);

        // Apply the reward
        AddReward(reward);
        Debug.Log("Reward: " + reward);

        EndEpisode();
    }
}


Now, i have three problems:

  1. I want my Agents using attack with the best reward. I'm try calling OnActionReceived at my GameController.cs but it will make my agent do static things (not choosing or trying other attacks, just do physical attack).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Transactions;
using UnityEngine.SocialPlatforms;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;

public class GameController : MonoBehaviour
{
    private List<FighterStats> fighterStats;

    private GameObject battleMenu;

    public Text battleText;

    private void Awake()
    {
        battleMenu = GameObject.Find("ActionMenu");
    }
    void Start()
    {
        fighterStats = new List<FighterStats>();
        GameObject hero = GameObject.FindGameObjectWithTag("Hero");
        FighterStats currentFighterStats = hero.GetComponent<FighterStats>();
        currentFighterStats.CalculateNextTurn(0);
        fighterStats.Add(currentFighterStats);

        GameObject enemy = GameObject.FindGameObjectWithTag("Enemy");
        FighterStats currentEnemyStats = enemy.GetComponent<FighterStats>();
        currentEnemyStats.CalculateNextTurn(0);
        fighterStats.Add(currentEnemyStats);

        fighterStats.Sort();
        

        NextTurn();
    }

    public void NextTurn()
    {
        battleText.gameObject.SetActive(false);
        FighterStats currentFighterStats = fighterStats[0];
        fighterStats.Remove(currentFighterStats);
        if (!currentFighterStats.GetDead())
        {
            GameObject currentUnit = currentFighterStats.gameObject;
            currentFighterStats.CalculateNextTurn(currentFighterStats.nextActTurn);
            fighterStats.Add(currentFighterStats);
            fighterStats.Sort();
            if (currentUnit.CompareTag("Hero"))
            {
                battleMenu.SetActive(true);
                Debug.Log("Hero's turn");
            }
            else
            {
                battleMenu.SetActive(false);
                Debug.Log("Enemy's turn");

                // Scripted AI
                // string attackType = Random.Range(0, 2) == 1 ? "melee" : "range";
                // currentUnit.GetComponent<FighterAction>().SelectAttack(attackType);

                // ML-Agents AI
                float[] actions = new float[] { 0, 1, 0, 0, 0, 0, 0 };
                ActionBuffers actionBuffers = ActionBuffers.FromDiscreteActions(actions);
                currentUnit.GetComponent<EnemyAIAgent>().OnActionReceived(actionBuffers);
            }
        } else
        {
            NextTurn();
        }
    }
}

  1. When I'm using OnActionReceived on my GameController.cs, a warning appears: Fewer observations (0) made than vector observation size (1). The observations will be padded.

  2. Then I try using RequestDecision, but the agent do nothing in their turn.


                ...
                // ML-Agents AI
                currentUnit.GetComponent<EnemyAIAgent>().RequestDecision();
                ...

Upvotes: 0

Views: 84

Answers (0)

Related Questions