/// <summary> /// Move any memory or collections of memories that have an influence over the long term memory threshold /// from short to long term memory. For a collection to be moved the `about` Game Object must be identical /// and the total influence must be over the threshold level. /// </summary> internal void MoveFromShortToLongTermMemory() { var grouped = from memory in m_ShortTermMemories group memory by new { memory.about, memory.stat, memory } into grp select new { about = grp.Key.about, traitName = grp.Key.stat, totalInfluence = grp.Sum(i => i.influence) }; foreach (var item in grouped) { if (item.totalInfluence >= m_LongTermMemoryThreshold) { MemorySO memory = ScriptableObject.CreateInstance <MemorySO>(); memory.about = item.about; memory.stat = item.traitName; memory.influence = item.totalInfluence; AddToLongTermMemory(memory); ForgetShortTermMemories(item.about); } } }
/// <summary> /// Add a memory to the short term memory. /// If there is an existing short term memory that is similar to the new one then do not duplicate; /// instead strengthen the existing memory. /// If there is space left in the memory then is simply inserted. /// If there is no space left then discard the weakest memory. /// </summary> /// <param name="memory">The memory to add</param> public void AddMemory(MemorySO memory) { MemorySO existingMemory = GetSimilarShortTermMemory(memory); if (existingMemory != null) { if (existingMemory.isGood != memory.isGood) { if (Math.Abs(existingMemory.influence) < Math.Abs(memory.influence)) { existingMemory.isGood = memory.isGood; } } existingMemory.influence += memory.influence; existingMemory.time = memory.time; return; } MoveFromShortToLongTermMemory(); if (m_ShortTermMemories.Count == m_ShortTermMemorySize) { DiscardShortTermMemory(); } m_ShortTermMemories.Add(memory); }
/// <summary> /// Checks to see if any stats are not in their desired states. If any are not then /// look in memories to see if the character knows of a place to address that. If /// a place is recalled and they are ready to return then they move towards it. /// Otherwise wander. /// </summary> protected override void UpdateMove() { StatSO[] stats = statsController.GetStatsNotInDesiredState(); nearestMemoryOfInterest = null; focusedStat = null; float sqrMagnitude = float.PositiveInfinity; for (int i = 0; i < stats.Length; i++) { //Debug.Log(gameObject.name + " desires to " + stats[i].goal + " " + stats[i].name + " to " + stats[i].desiredState.targetValue); //Debug.Log(stats[i].name + " is currently " + stats[i].value); // Find the nearest place that is in current memory that helps achieve one of the characters goals MemorySO[] memories = memory.GetMemoriesInfluencingStat(stats[i].name); for (int y = 0; y < memories.Length; y++) { if (memories[y].readyToReturn) { if (stats[i].goal == DesiredState.Goal.Increase && memories[y].influence < 0) { continue; } else if (stats[i].goal == DesiredState.Goal.Decrease && memories[y].influence > 0) { continue; } Collider col = memories[y].about.GetComponent <Collider>(); if (!col.bounds.Contains(transform.position)) { float distance = Vector3.SqrMagnitude(memories[y].about.transform.position - gameObject.transform.position); if (distance < sqrMagnitude) { focusedStat = stats[i]; nearestMemoryOfInterest = memories[y]; sqrMagnitude = distance; } } } } // Head to the remembered place if (nearestMemoryOfInterest != null) { //Debug.Log("It looks like " + nearestMemoryOfInterest.about + " is the best place to achieve that goal"); GameObject target = nearestMemoryOfInterest.about; Collider col = target.GetComponent <Collider>(); SetCurrentTargetWithinTriggerOf(col); return; } } // if no place in memory wander randomly base.UpdateMove(); }
/// <summary> /// Retrive all memories (long and short term) about influencers of a stat. /// </summary> /// <param name="statTemplate">Template of the stat we are interested in.</param> /// <returns>A set of influencers known to affect the desired stat.</returns> public MemorySO[] GetMemoriesInfluencingStat(StatSO statTemplate) { MemorySO[] shortTermMemories = m_ShortTermMemories.Where(m => m.stat.name == statTemplate.name).ToArray <MemorySO>(); MemorySO[] longTermMemories = m_LongTermMemories.Where(m => m.stat.name == statTemplate.name).ToArray <MemorySO>(); MemorySO[] all = new MemorySO[shortTermMemories.Length + longTermMemories.Length]; shortTermMemories.CopyTo(all, 0); longTermMemories.CopyTo(all, shortTermMemories.Length); return(all); }
/// <summary> /// Create a memory about an influencer object. See `AddMemory(MemorySO memory)`. /// </summary> /// <param name="influencer">The influencer that this memory should record.</param> /// <param name="isGood">Is this a good memory that represents an experience to be repeated?</param> internal void AddMemory(StatInfluencerSO influencer, bool isGood) { MemorySO memory = ScriptableObject.CreateInstance <MemorySO>(); memory.about = influencer.generator; memory.stat = influencer.stat; memory.influence = influencer.maxChange; memory.cooldown = influencer.cooldown; memory.isGood = isGood; AddMemory(memory); }
/// <summary> /// Add a memory to the long term memory if there is space. If there is not space then remove the /// weakest memory that is weaker than the memory being added, or discard the added memory. /// </summary> /// <param name="memory">The memory to try to add</param> /// <returns>True if the memory was committed to long term memory.</returns> private bool AddToLongTermMemory(MemorySO memory) { if (m_LongTermMemories.Count <= m_LongTermMemorySize) { m_LongTermMemories.Add(memory); return(true); } else { throw new NotImplementedException("Make space in long term memory"); } }
internal override void FinishBehaviour() { base.FinishBehaviour(); m_BuiltPrefab.InstantiatePrefabs(transform.position, "built by " + Brain.Actor.name); MemorySO memory = ScriptableObject.CreateInstance <MemorySO>(); memory.about = this.gameObject; memory.interactionName = DisplayName; memory.isGood = true; Brain.Memory.AddMemory(memory); }
/// <summary> /// Get all memories (long and short term), about a Game Object. /// </summary> /// <param name="go">The Game Object to retrieve memories about.</param> /// <returns></returns> public MemorySO[] GetAllMemoriesAbout(GameObject go) { MemorySO[] shortTerm = m_ShortTermMemories.Where(m => ReferenceEquals(go, m.about)) .ToArray(); MemorySO[] longTerm = m_LongTermMemories.Where(m => ReferenceEquals(go, m.about)) .ToArray(); MemorySO[] memories = new MemorySO[longTerm.Length + shortTerm.Length]; longTerm.CopyTo(memories, 0); shortTerm.CopyTo(memories, longTerm.Length); return(memories); }
/// <summary> /// Scan short term memory to see if there is an existing memory similar to this one. /// </summary> /// <param name="memory">The memory we are looking for similarities to</param> /// <returns>Return an existing short term memory that is similar, if one exists, or null.</returns> public MemorySO GetSimilarShortTermMemory(MemorySO memory) { MemorySO[] memories = GetShortTermMemoriesAbout(memory.about); if (memories.Length > 0) { for (int i = 0; i < memories.Length; i++) { if (memories[i].stat == memory.stat) { return(memories[i]); } } } return(null); }
/// <summary> /// Create a memory about an influencer object. See `AddMemory(MemorySO memory)`. /// </summary> /// <param name="influencer">The influencer that this memory should record.</param> /// <param name="isGood">Is this a good memory that represents an experience to be repeated?</param> internal void AddMemory(StatInfluencerSO influencer, bool isGood) { if (influencer.Generator == null) { return; // don't hold memories about behaviours only influencers } MemorySO memory = ScriptableObject.CreateInstance <MemorySO>(); memory.about = influencer.Generator; memory.interactionName = influencer.InteractionName; memory.stat = influencer.stat; memory.influence = influencer.maxChange; memory.cooldown = influencer.CooldownDuration; memory.isGood = isGood; AddMemory(memory); }