Add most of research

This commit is contained in:
Bram verhulst
2025-05-31 15:54:23 +02:00
commit 70f128f092
270 changed files with 80847 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
using UnityEngine;
[ExecuteInEditMode]
public class AI_Area : MonoBehaviour
{
[Header("Area Settings")]
public Vector2 size = new Vector2(10f, 10f); // Width x Depth
public Color gizmoColor = new Color(0f, 1f, 0f, 0.25f);
public Vector3 GetRandomPointInArea()
{
float halfX = size.x / 2f;
float halfZ = size.y / 2f;
float randomX = Random.Range(-halfX, halfX);
float randomZ = Random.Range(-halfZ, halfZ);
Vector3 localOffset = new Vector3(randomX, 0f, randomZ);
return transform.position + transform.rotation * localOffset;
}
void OnDrawGizmos()
{
Gizmos.color = gizmoColor;
Matrix4x4 oldMatrix = Gizmos.matrix;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawCube(Vector3.zero, new Vector3(size.x, 0.01f, size.y));
Gizmos.matrix = oldMatrix;
Gizmos.color = Color.green;
Gizmos.DrawWireCube(transform.position, new Vector3(size.x, 0.01f, size.y));
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 22f280aea26c8374aa78095f232b885f

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d301a715802c4bd3bc42de723b5e93e2
timeCreated: 1748549866

View File

@@ -0,0 +1,29 @@
using UnityEngine;
public class Charge : AI_Action
{
public GameObject m_Resource;
public override bool PrePerform()
{
m_Resource = AI_World.Instance.RemoveCharger();
if (m_Resource == null)
{
return false; // No charger available
}
inventory.AddItem(m_Resource);
target = m_Resource;
return true;
}
public override bool PostPerform()
{
agentWorldStates.RemoveState("Depleted");
AI_World.Instance.AddCharger(m_Resource);
m_Resource = null; // Clear the resource after use
return true;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d65acb659c17edb43b07f1984ce199a1

View File

@@ -0,0 +1,14 @@
using UnityEngine;
public class CheckIn : AI_Action
{
public override bool PrePerform()
{
return true;
}
public override bool PostPerform()
{
return true;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: da447a39bb3a3c144a8a1245dac6953d

View File

@@ -0,0 +1,36 @@
using UnityEngine;
public class GetPatient : AI_Action
{
GameObject m_Resource;
public override bool PrePerform()
{
target = AI_World.Instance.RemovePatient();
if(target == null)
{
return false;
}
m_Resource = AI_World.Instance.RemoveTreatmentBed();
if (m_Resource != null)
{
inventory.AddItem(m_Resource);
} else {
AI_World.Instance.AddPatient(target);
target = null;
return false;
}
return true;
}
public override bool PostPerform()
{
AI_World.Instance.GetWorld().ModifyState("Waiting", -1);
if (target)
{
target.GetComponent<AI_Agent>().inventory.AddItem(m_Resource);
}
return true;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1a3594ff153540945b38f6c6e7969913

View File

@@ -0,0 +1,23 @@
using Unity.VisualScripting;
using UnityEngine;
public class GetTreated : AI_Action
{
public override bool PrePerform()
{
target = inventory.FindItemWithTag("Bed");
if(target == null)
return false;
return true;
}
public override bool PostPerform()
{
AI_World.Instance.GetWorld().ModifyState("Treated", 1);
agentWorldStates.ModifyState("IsCured", 1);
inventory.RemoveItem(target);
return true;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b134cecb1ecf86140a6bef65725746b3

View File

@@ -0,0 +1,17 @@
using Unity.VisualScripting;
using UnityEngine;
public class GoHome : AI_Action
{
public override bool PrePerform()
{
return true;
}
public override bool PostPerform()
{
AI_World.Instance.GetWorld().ModifyState("Home", 1);
Destroy(this.gameObject);
return true;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ee0072bcc9d306949b2510eb33147033

View File

@@ -0,0 +1,24 @@
using UnityEngine;
public class GoToBed : AI_Action
{
public override bool PrePerform()
{
target = inventory.FindItemWithTag("Bed");
if(target == null)
return false;
AI_World.Instance.GetWorld().ModifyState("TreatingPatient", 1);
return true;
}
public override bool PostPerform()
{
AI_World.Instance.GetWorld().ModifyState("TreatingPatient", -1);
AI_World.Instance.AddTreatmentBed(target);
inventory.RemoveItem(target);
// AI_World.Instance.GetWorld().ModifyState("FreeBeds", 1);
return true;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9744404dabc38e24390e5f04a158b73d

View File

@@ -0,0 +1,14 @@
using UnityEngine;
public class GoToHospital : AI_Action
{
public override bool PrePerform()
{
return true;
}
public override bool PostPerform()
{
return true;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: df82dab2aef220d4eaf8d1953b5668d3

View File

@@ -0,0 +1,17 @@
using UnityEngine;
public class GoToWaitingRoom : AI_Action
{
public override bool PrePerform()
{
return true;
}
public override bool PostPerform()
{
AI_World.Instance.GetWorld().ModifyState("Waiting", 1);
AI_World.Instance.AddPatient(this.gameObject);
agentWorldStates.ModifyState("InWaitingRoom", 1);
return true;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e80a344ad86244145a0a6408b77f4a9d

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ecdfd6b0e8c2f3442b5ea0b58b60b592
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public abstract class AI_Action : MonoBehaviour
{
public string actionName = "Undefined Action";
public float cost = 1.0f;
public GameObject target;
public string targetTag;
public float duration = 0;
public AIState[] preConditions;
public AIState[] afterEffects;
public NavMeshAgent agent;
public Dictionary<string, int> preConditionsDict;
public Dictionary<string, int> effectsDict;
public AI_Inventory inventory;
public AIStates agentWorldStates;
public bool isRunning = false;
public AI_Action()
{
preConditionsDict = new Dictionary<string, int>();
effectsDict = new Dictionary<string, int>();
}
public void Awake()
{
agent = GetComponent<NavMeshAgent>();
if(preConditions != null)
{
foreach (AIState w in preConditions)
{
preConditionsDict.Add(w.key, w.value);
}
}
if (afterEffects != null)
{
foreach (AIState w in afterEffects)
{
effectsDict.Add(w.key, w.value);
}
}
inventory = GetComponent<AI_Agent>().inventory;
agentWorldStates = GetComponent<AI_Agent>().agentWorldStates;
}
public bool IsAchievable()
{
return true;
}
public bool IsAchievableGiven(Dictionary<string, int> conditions)
{
foreach(KeyValuePair<string, int> p in preConditionsDict)
{
if (!conditions.ContainsKey(p.Key))
return false;
}
return true;
}
public abstract bool PrePerform();
public abstract bool PostPerform();
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e9e7c04605c1fa4438b738c124267f4c

View File

@@ -0,0 +1,135 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class SubGoal
{
public Dictionary<string, int> goals;
public bool shouldRemove;
public SubGoal(string goalName, int value, bool shouldRemove)
{
goals = new Dictionary<string, int>
{
{ goalName, value }
};
this.shouldRemove = shouldRemove;
}
}
public class AI_Agent : MonoBehaviour
{
public List<AI_Action> actions = new List<AI_Action>();
public Dictionary<SubGoal, int> goals = new Dictionary<SubGoal, int>();
public AI_Inventory inventory = new AI_Inventory();
public AIStates agentWorldStates = new AIStates();
public float taskRange = 2.5f;
protected AI_Planner planner;
protected Queue<AI_Action> actionQueue;
protected AI_Action currentAction;
protected SubGoal currentGoal;
protected bool actionInvoked;
protected virtual void Start()
{
AI_Action[] actionsArray = GetComponents<AI_Action>();
foreach (AI_Action action in actionsArray)
{
actions.Add(action);
}
}
private void CompleteAction()
{
currentAction.isRunning = false;
currentAction.PostPerform();
actionInvoked = false;
}
private void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, taskRange);
}
private void LateUpdate()
{
if(currentAction != null && currentAction.isRunning)
{
float distanceToTarget = Vector3.Distance(currentAction.target.transform.position, transform.position);
if(currentAction.agent.hasPath && distanceToTarget < taskRange)
{
if(!actionInvoked)
{
Invoke(nameof(CompleteAction), currentAction.duration);
actionInvoked = true;
}
}
return;
}
if(planner == null || actionQueue == null)
{
planner = new AI_Planner();
var sortedGoals = from entry in goals orderby entry.Value descending select entry;
foreach(KeyValuePair<SubGoal, int> subGoal in sortedGoals)
{
actionQueue = planner.Plan(actions, subGoal.Key.goals, agentWorldStates);
if (actionQueue != null)
{
currentGoal = subGoal.Key;
break;
}
}
}
if(actionQueue != null && actionQueue.Count == 0)
{
if(currentGoal.shouldRemove)
{
goals.Remove(currentGoal);
}
planner = null;
}
if (actionQueue != null && actionQueue.Count > 0)
{
currentAction = actionQueue.Dequeue();
if (currentAction.PrePerform())
{
if (currentAction.target == null && !string.IsNullOrEmpty(currentAction.targetTag))
{
currentAction.target = GameObject.FindWithTag(currentAction.targetTag);
}
if (currentAction.target != null)
{
currentAction.isRunning = true;
// Check for AI_Area component
AI_Area area = currentAction.target.GetComponent<AI_Area>();
Vector3 destination;
if (area != null)
{
destination = area.GetRandomPointInArea();
}
else
{
destination = currentAction.target.transform.position;
}
currentAction.agent.SetDestination(destination);
}
}
else
{
actionQueue = null;
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c48e5729f78aac64c8dbc08eb09aa525

View File

@@ -0,0 +1,49 @@
using System.Collections.Generic;
using UnityEngine;
public class AI_Inventory
{
private List<GameObject> items = new List<GameObject>();
public void AddItem(GameObject item)
{
items.Add(item);
}
public GameObject FindItemWithTag(string tag)
{
foreach(GameObject item in items)
{
if(item.CompareTag(tag))
{
return item;
}
}
return null;
}
public void RemoveItem(GameObject item)
{
int indexToRemove = items.IndexOf(item);
if(indexToRemove >= 0)
{
items.RemoveAt(indexToRemove);
}
}
// Additional helper methods
public bool ContainsItem(GameObject item)
{
return items.Contains(item);
}
public void ClearInventory()
{
items.Clear();
}
public int ItemCount
{
get { return items.Count; }
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 06be48fd5f3a4298a422a72827aa61f0
timeCreated: 1748549167

View File

@@ -0,0 +1,154 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class PlanNode
{
public PlanNode parent;
public float cost;
public Dictionary<string, int> states;
public AI_Action action;
public PlanNode(PlanNode parent, float cost, Dictionary<string, int> allStates, AI_Action action)
{
this.parent = parent;
this.cost = cost;
states = new Dictionary<string, int>(allStates);
this.action = action;
}
public PlanNode(PlanNode parent, float cost, Dictionary<string, int> allStates,
Dictionary<string, int> agentStates, AI_Action action)
{
this.parent = parent;
this.cost = cost;
states = new Dictionary<string, int>(allStates);
foreach(KeyValuePair<string, int> state in agentStates)
if(!states.ContainsKey(state.Key))
states.Add(state.Key, state.Value);
this.action = action;
}
}
public class AI_Planner
{
private string planDescription;
public Queue<AI_Action> Plan(List<AI_Action> actions, Dictionary<string, int> goal, AIStates agentStates)
{
List<AI_Action> usableActions = new List<AI_Action>();
foreach (AI_Action action in actions)
{
if(action.IsAchievable())
usableActions.Add(action);
}
List<PlanNode> leaves = new List<PlanNode>();
PlanNode start = new PlanNode(null, 0, AI_World.Instance.GetWorld().GetStates(),
agentStates.GetStates(), null);
bool success = BuildGraph(start, leaves, usableActions, goal);
if(!success)
{
Debug.Log("No plan could be constructed");
return null;
}
PlanNode cheapest = null;
foreach (PlanNode leaf in leaves)
{
if (cheapest == null)
cheapest = leaf;
else if (leaf.cost < cheapest.cost)
cheapest = leaf;
}
List<AI_Action> result = new List<AI_Action>();
PlanNode n = cheapest;
while (n != null)
{
if(n.action != null)
{
result.Insert(0, n.action);
}
n = n.parent;
}
Queue<AI_Action> queue = new Queue<AI_Action>();
foreach(AI_Action action in result)
{
queue.Enqueue(action);
}
planDescription = "";
int index = 0;
foreach(AI_Action action in queue)
{
planDescription += index + ": " + action.actionName + "\n";
index++;
}
return queue;
}
private bool BuildGraph(PlanNode parent, List<PlanNode> leaves, List<AI_Action> usableActions,
Dictionary<string, int> goal)
{
bool foundPath = false;
foreach(AI_Action action in usableActions)
{
if(action.IsAchievableGiven(parent.states))
{
Dictionary<string, int> currentState = new Dictionary<string, int>(parent.states);
foreach(KeyValuePair<string, int> effect in action.effectsDict)
if(!currentState.ContainsKey(effect.Key))
currentState.Add(effect.Key, effect.Value);
PlanNode node = new PlanNode(parent, parent.cost + action.cost, currentState, action);
if(GoalAchieved(goal, currentState))
{
leaves.Add(node);
foundPath = true;
}
else
{
List<AI_Action> subset = ActionSubset(usableActions, action);
bool found = BuildGraph(node, leaves, subset, goal);
if(found)
foundPath = true;
}
}
}
return foundPath;
}
private bool GoalAchieved(Dictionary<string, int> goals, Dictionary<string, int> state)
{
foreach(KeyValuePair<string, int> goal in goals)
{
if (!state.ContainsKey(goal.Key))
return false;
}
return true;
}
private List<AI_Action> ActionSubset(List<AI_Action> actions, AI_Action removeAction)
{
List<AI_Action> subset = new List<AI_Action>();
foreach(AI_Action action in actions)
{
if(!action.Equals(removeAction))
subset.Add(action);
}
return subset;
}
public string GetPlanDescription()
{
return planDescription;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 105223c6dcaa56146870d3dd0b4f3a13

View File

@@ -0,0 +1,93 @@
using System.Collections.Generic;
using UnityEngine;
public sealed class AI_World
{
private static readonly AI_World instance = new AI_World();
private static AIStates worldStates;
private static Queue<GameObject> patients;
private static Queue<GameObject> chargers;
private static Queue<GameObject> treatmentBeds;
static AI_World()
{
worldStates = new AIStates();
patients = new Queue<GameObject>();
chargers = new Queue<GameObject>();
treatmentBeds = new Queue<GameObject>();
GameObject[] cubicleArray = GameObject.FindGameObjectsWithTag("Charger");
foreach(GameObject cubicle in cubicleArray)
{
chargers.Enqueue(cubicle);
}
if (cubicleArray.Length > 0)
{
worldStates.SetState("FreeCharger", cubicleArray.Length);
}
GameObject[] bedArray = GameObject.FindGameObjectsWithTag("Bed");
foreach(GameObject bed in bedArray)
{
treatmentBeds.Enqueue(bed);
}
if (bedArray.Length > 0)
{
worldStates.SetState("FreeBeds", bedArray.Length);
}
}
private AI_World() { }
public void AddPatient(GameObject patient)
{
patients.Enqueue(patient);
worldStates.ModifyState("Patients", 1);
}
public GameObject RemovePatient()
{
if (patients.Count == 0) return null;
worldStates.ModifyState("Patients", -1);
return patients.Dequeue();
}
public void AddCharger(GameObject charger)
{
worldStates.ModifyState("FreeCharger", 1);
chargers.Enqueue(charger);
}
public GameObject RemoveCharger()
{
if (chargers.Count == 0) return null;
worldStates.ModifyState("FreeCharger", -1);
return chargers.Dequeue();
}
public void AddTreatmentBed(GameObject bed)
{
treatmentBeds.Enqueue(bed);
worldStates.ModifyState("FreeBeds", 1);
}
public GameObject RemoveTreatmentBed()
{
if (treatmentBeds.Count == 0) return null;
worldStates.ModifyState("FreeBeds", -1);
return treatmentBeds.Dequeue();
}
public static AI_World Instance
{
get { return instance; }
}
public AIStates GetWorld()
{
return worldStates;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2c9831f822b94a8dbdae2de4a63ab6e7

View File

@@ -0,0 +1,70 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class AIState
{
public string key;
public int value;
}
public class AIStates
{
public Dictionary<string, int> states;
public AIStates()
{
states = new Dictionary<string, int>();
}
public bool HasState(string key)
{
return states.ContainsKey(key);
}
void AddState(string key, int value)
{
states.Add(key, value);
}
public void ModifyState(string key, int value)
{
if (states.ContainsKey(key))
{
states[key] += value;
if (states[key] <= 0)
{
RemoveState(key);
}
}
else
{
AddState(key, value);
}
}
public void RemoveState(string key)
{
if (states.ContainsKey(key))
{
states.Remove(key);
}
}
public void SetState(string key, int value)
{
if (states.ContainsKey(key))
{
states[key] = value;
}
else
{
AddState(key, value);
}
}
public Dictionary<string, int> GetStates()
{
return states;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 121b61efa26f4a91a871ce8e3b0049ac
timeCreated: 1748548853

View File

@@ -0,0 +1,41 @@
using TMPro;
using UnityEngine;
namespace Scrips
{
public class Patient : AI_Agent
{
[SerializeField] TextMeshProUGUI _currentStatePrint;
[SerializeField] private Material m_Healthy;
[SerializeField] private Material m_Sick;
protected override void Start()
{
base.Start();
var s1 = new SubGoal("IsWaiting", 1, true);
goals.Add(s1, 1);
var s2 = new SubGoal("IsTreated", 1, true);
goals.Add(s2, 3);
var s3 = new SubGoal("IsHome", 1, true);
goals.Add(s3, 5);
}
private void Update()
{
if (_currentStatePrint != null && planner != null)
{
// _currentStatePrint.text = "Nurse 1 Action: " + currentAction.actionName + "\n" + planner.GetPlanString();
_currentStatePrint.text = "Patient 1 Action: " + currentAction.actionName + "\n";
}
if( agentWorldStates.GetStates().ContainsKey("IsHealthy") &&
agentWorldStates.GetStates()["IsHealthy"] > 0)
{
GetComponent<Renderer>().material = m_Healthy;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5bc6d86f29a648b39e86210d37733785
timeCreated: 1748552892

View File

@@ -0,0 +1,38 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class Robot : AI_Agent {
[SerializeField] TextMeshProUGUI _currentStatePrint;
// Start is called before the first frame update
protected override void Start()
{
base.Start();
SubGoal s1 = new SubGoal("TreatPatient", 1, false);
goals.Add(s1, 3);
var s2 = new SubGoal("Charged", 1, false);
goals.Add(s2, 5);
Invoke(nameof(GetDepleted), Random.Range(10, 20));
}
private void Update()
{
if (_currentStatePrint != null && planner != null && currentAction != null)
{
// _currentStatePrint.text = "Nurse 1 Action: " + currentAction.actionName + "\n" + planner.GetPlanString();
_currentStatePrint.text =
"Nurse 1 Action: " + currentAction.actionName + "\n" + planner.GetPlanDescription();
}
}
void GetDepleted()
{
agentWorldStates.ModifyState("Depleted", 0);
Invoke(nameof(GetDepleted), Random.Range(10, 20));
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 94220d7855458a249b83ebdc1f084bbe

View File

@@ -0,0 +1,42 @@
using System;
using UnityEngine;
public class Spawner : MonoBehaviour
{
[SerializeField] private GameObject _spawnTemplate = null;
[SerializeField] private int _spawnCount = 1;
[SerializeField] private float _spawnInterval = 1.0f;
private float _nextSpawnTime = 0.0f;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
_nextSpawnTime = Time.time + _spawnInterval;
}
private void Update()
{
if (Time.time >= _nextSpawnTime)
{
for (int i = 0; i < _spawnCount; i++)
{
Spawn();
}
_nextSpawnTime = Time.time + _spawnInterval;
}
}
public GameObject Spawn()
{
if (_spawnTemplate == null)
{
Debug.LogError("Spawn template is not assigned in the Spawner component.");
return null;
}
GameObject spawnedObject = Instantiate(_spawnTemplate, transform.position, transform.rotation);
spawnedObject.transform.SetParent(transform); // Optional: Set parent to keep hierarchy organized
return spawnedObject;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7ea4287f49c95674daed326c6191fe7e

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class WorldVisualizor : MonoBehaviour
{
[SerializeField] public TextMeshProUGUI states;
void LateUpdate()
{
Dictionary<string, int> worldStates = AI_World.Instance.GetWorld().GetStates();
states.text = "";
foreach (KeyValuePair<string, int> pair in worldStates)
{
states.text += pair.Key + ", " + pair.Value + "\n";
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ef85eecb98eba9a41bfb33950b4751e0