Monday, December 5, 2016

Back to the Basics (Needs)

hey all,

I've been refactoring my code for needs over the last few days, and here are some of the changes I've made.


using UnityEngine;
using System.Collections;
using Gamelogic.Extensions.Algorithms;

public class Need
{
 [Range(0, 100)]
 public float severity;
 float maxTime;
 MeetNeedEvent typicalMNE;
 //public float changeRate;
 public ResponseCurveFloat curve;
 float timeSinceMet;
 public bool isBeingMet;

 //used for determining the effect during decision making
 public float multiplier;


These are the variables that my Need class contains. The severity just tracks the severity of the need, which can only be from 0 to a maximum of 100. For physical needs such as food and water, I'm planning on causing death to occur when it reaches the maximum. maxTime was the most difficult. If you recall from my previous post, I am using variations on the equation


to calculate how severe a need should be after x time has passed. The problem is that this is a logistic equation, and it has a horizontal asymptote at max_y, so you cannot just use




to try and find the x value when y = max_y, because that value does not exist. At the moment, I'm using a y value of 99.999999 and it seems to be working OK, although I do get some floating point errors every now and then.

In any case, I use the first equation to set up the response curve in the PhysicalNeeds and EmotionalNeeds classes, and also create a default MeedNeedsEvent.
 public Need(float sev, ResponseCurveFloat rc, float maxT, MeetNeedEvent mne ,float mult)
 {
  severity = sev;
  curve = rc;
  multiplier = mult;
  isBeingMet = false;
  timeSinceMet = 0f;
  typicalMNE = mne;
  maxTime = maxT;
 }

 // Use this for initialization
 void Start ()
 {
 
 }
 
 // Update is called once per frame
 void Update ()
 {
 
 }

 public void IncreaseNeed(float timePassed)
 {
  if (!isBeingMet)
  {
   timeSinceMet += timePassed;
   severity = curve[timeSinceMet];
   
  }
 }

This is another change from before. Instead of having those ugly incremental changes in an Update function, I'm now calling a coroutine that just ticks along by itself until the MeetNeedEvent is finished. I'm actually thinking that having the IncreaseNeed function be a coroutine might be a much better idea as well, and just use body states in the PhysicalNeeds class to start and stop the appropriate coroutines.
 public IEnumerator MeetNeed(MeetNeedEvent mne=new MeetNeedEvent())
 {
  //this is because the default parameter must be a constant at compile time
  //so if a value is not passed in, use the typical MeetNeedEvent for this need
  if (mne.eventLength <= 0)
  {
   mne = typicalMNE;
  }
  isBeingMet = true;
  float t = 0f;
  while (t < mne.eventLength)
  {
   t += Time.deltaTime;
   //this value needs to be clamped!!!
   severity = Mathf.Clamp(severity - (t / mne.eventLength) * mne.potency, 0f, 100f);
   yield return null;
  }
  isBeingMet = false;
  timeSinceMet = maxTime-(maxTime/100f*mne.potency);
 }
}


The point of having maxTime subtract a fraction of itself is that the potency of the event should affect the level of the need afterwards. If I have a snack, my hunger need will not be as satisfied as if I had had a hearty bowl of sweet and sour pork.

public struct MeetNeedEvent
{
 public float eventLength;
 [Range(0,100)]
 public float potency;

 public MeetNeedEvent(float el=-1f, float p =-100f)
 {
  eventLength = el;
  potency = p;
 }
}


So that's my base Needs class. PhysicalNeeds and EmotionalNeeds inherit from this class. I hope that I can start implementing how these needs affect mood.The current AI model is something like this:

No comments:

Post a Comment