- changed status to resolved
Error when displaying
Hi Iam geeting an error when opening a GO with a monobehaviour below. I cannot seem to be able to trace what is the problem as this is somehow connected to Odin. Is it? Can you please help? I do not see anything in the inspector for this monobehaviour and the error is repeating fast all the time.
I have a version 3.1.12.2 and Unity 2022.3.1f (Silicon) on Mac M1, not Editor only mode
NullReferenceException: Object reference not set to an instance of an object
UnityEditor.EditorGUI.DoTextField (UnityEditor.EditorGUI+RecycledTextEditor editor, System.Int32 id, UnityEngine.Rect position, System.String text, UnityEngine.GUIStyle style, System.String allowedletters, System.Boolean& changed, System.Boolean reset, System.Boolean multiline, System.Boolean passwordField, UnityEngine.GUIStyle cancelButtonStyle, System.Boolean checkTextLimit) (at /Users/bokken/build/output/unity/unity/Editor/Mono/EditorGUI.cs:1346)
UnityEditor.EditorGUI.DoTextField (UnityEditor.EditorGUI+RecycledTextEditor editor, System.Int32 id, UnityEngine.Rect position, System.String text, UnityEngine.GUIStyle style, System.String allowedletters, System.Boolean& changed, System.Boolean reset, System.Boolean multiline, System.Boolean passwordField) (at /Users/bokken/build/output/unity/unity/Editor/Mono/EditorGUI.cs:928)
UnityEditor.EditorGUI.TextFieldInternal (UnityEngine.Rect position, System.String text, UnityEngine.GUIStyle style) (at /Users/bokken/build/output/unity/unity/Editor/Mono/EditorGUI.cs:1848)
UnityEditor.EditorGUI.TextField (UnityEngine.Rect position, System.String text, UnityEngine.GUIStyle style) (at /Users/bokken/build/output/unity/unity/Editor/Mono/EditorGUI.cs:8021)
Sirenix.Utilities.Editor.SirenixEditorGUI.SearchField (UnityEngine.Rect rect, System.String searchText, System.Boolean forceFocus, System.String controlName) (at C:/Sirenix/Sirenix Solution/Sirenix.Utilities.Editor/GUI/SirenixEditorGUI.cs:1978)
Sirenix.OdinInspector.Editor.PropertySearchFilter.DrawDefaultSearchFieldLayout (UnityEngine.GUIContent label) (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Misc/PropertySearchFilter.cs:302)
Sirenix.OdinInspector.Editor.PropertyTree.DrawSearch () (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Core/PropertyTree.cs:569)
Sirenix.OdinInspector.Editor.PropertyTree.DrawProperties () (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Core/PropertyTree.cs:553)
Sirenix.OdinInspector.Editor.PropertyTree.Draw (System.Boolean applyUndo) (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Core/PropertyTree.cs:448)
Sirenix.OdinInspector.Editor.OdinEditor.DrawTree () (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Drawers/OdinEditor.cs:97)
Sirenix.OdinInspector.Editor.OdinEditor.DrawOdinInspector () (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Drawers/OdinEditor.cs:254)
Sirenix.OdinInspector.Editor.OdinEditor.OnInspectorGUI () (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Drawers/OdinEditor.cs:79)
UnityEditor.UIElements.InspectorElement+<>c__DisplayClass72_0.<CreateInspectorElementUsingIMGUI>b__0 () (at /Users/bokken/build/output/unity/unity/Editor/Mono/UIElements/Inspector/InspectorElement.cs:713)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&) (at /Users/bokken/build/output/unity/unity/Modules/IMGUI/GUIUtility.cs:190)
This is my code causing the problem:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using CharacterLogic.StatCalculations;
using Core;
using Core.EngineExtentions;
using DG.Tweening;
using Items.Inventory;
using Pathfinding;
using RPGLogic;
using RPGLogic.StatBases;
using RPGLogic.StatEffects;
using Sirenix.OdinInspector;
using UI;
using UnityEngine;
using VisualEffects;
namespace CharacterLogic
{
[Searchable]
public class CharacterStats : MonoBehaviour
{
[SerializeField] private ComboResolver comboResolver;
public ComboResolver ComboResolver => comboResolver;
[SerializeField] private CharacterStatsDataSO characterStatsDataSo;
//attributes, stats etc, including properties
[SerializeField] private Progression soul;
[ListDrawerSettings(ShowIndexLabels = true, ListElementLabelName = "statType")] [SerializeField]
private List<Stat> stats = new List<Stat>();
[ListDrawerSettings(ShowIndexLabels = true, ListElementLabelName = "statType")] [SerializeField]
private List<Progression> attributes = new List<Progression>();
[ListDrawerSettings(ShowIndexLabels = true, ListElementLabelName = "statType")] [SerializeField]
private List<Skill> skills = new List<Skill>();
public Progression Soul
{
get => soul;
set
{
soul = value;
RecalculateStatInfluencing();
}
}
public List<Stat> Stats
{
get => stats;
set
{
stats = value;
RecalculateStatInfluencing();
}
}
public List<Progression> Attributes
{
get => attributes;
set
{
attributes = value;
RecalculateStatInfluencing();
}
}
public List<Skill> Skills
{
get => skills;
set
{
skills = value;
RecalculateStatInfluencing();
}
}
public int ViewDistance
{
get
{
var lightRadiusStat = StatOperations.GetStatTypeFromEnum(StatTypeEnum.LightRadius);
return (int)GetStat(lightRadiusStat).ActualValue;
}
}
private bool isCalculatingStatInfluencing = false;
public bool IsCalculatingStatInfluencing => isCalculatingStatInfluencing;
[SerializeField] private bool aliveState = true;
public bool IsAlive
{
get => aliveState;
set => aliveState = value;
}
public CharacterCombatActions characterCombatActions;
public Action<CharacterStats> OnKilled;
private MainAnimationWrapper mainAnimationWrapper;
private DamageTextSpawner damageTextSpawnerSpawner;
private SpawnGore goreSpawner;
[SerializeField] private SkillGoverningAttributes SkillGoverningAttributes;
private InventoryEquipController inventoryEquipController;
[SerializeField]private CharacterAbilities characterAbilities;
public CharacterAbilities CharacterAbilities => characterAbilities;
public InventoryEquipController InventoryEquipController => inventoryEquipController;
private AppliedStatusEffectList appliedStatusEffects = new AppliedStatusEffectList();
public AppliedStatusEffectList AppliedStatusEffects => appliedStatusEffects;
[SerializeField] private GameObject bodyGfx;
[HideInEditorMode] [ShowInInspector]
public bool IsOnTurn
{
get
{
if (!Application.isPlaying) return false;
return GameComponentAggregator.Instance.TurnController.IsCharaterOnTurn(this);
}
}
private void Awake()
{
characterCombatActions = transform.root.GetComponentInChildren<CharacterCombatActions>();
var troot = transform.root;
damageTextSpawnerSpawner = troot.GetComponentInChildren<DamageTextSpawner>();
goreSpawner = EngineExtention.FindInActiveObjectByName("GoreSpawner").GetComponent<SpawnGore>();
characterStatsDataSo = troot.GetComponentInChildren<CharacterDataWrapper>().CharacterStatsDataSo;
SetAllStatsFromSOs();
mainAnimationWrapper = troot.GetComponentInChildren<MainAnimationWrapper>();
}
//this should be in characted data wrapper but I was not lucky putting it there, too much work...
private void SetAllStatsFromSOs()
{
soul = characterStatsDataSo.soul.GetDeepCopyValues();
soul.ValueChangedExcludingModifications += RecalculateStatInfluencing;
foreach (var stat in characterStatsDataSo.stats)
{
var statToAdd = stat.GetDeepCopyValues();
statToAdd.owner = this;
stats.Add(statToAdd);
statToAdd.ValueChangedExcludingModifications += RecalculateStatInfluencing;
}
foreach (var attribute in characterStatsDataSo.attributes)
{
var attributeToAdd = attribute.GetDeepCopyValues();
attributeToAdd.owner = this;
attributes.Add(attributeToAdd);
attributeToAdd.ValueChangedExcludingModifications += RecalculateStatInfluencing;
}
foreach (var skill in characterStatsDataSo.skills)
{
var skillToAdd = skill.GetDeepCopyValues();
skillToAdd.owner = this;
skills.Add(skillToAdd);
skillToAdd.ValueChangedExcludingModifications += RecalculateStatInfluencing;
}
}
private void Start()
{
inventoryEquipController = transform.root.GetComponentInChildren<InventoryEquipController>();
inventoryEquipController.InventoriesLoaded += RecalculateStatInfluencing;
inventoryEquipController.AnyItemEquiped += RecalculateStatInfluencing;
characterCombatActions.OnTurnStarted += OnCharacterTurnStarted;
}
// private void OnDestroy()
// {
// inventoryEquipController.InventoriesLoaded -= InitiateAllItemEquipEventHandlers;
// characterTurnController.OnTurnStarted -= StartTurn;
// }
#region GetStat and other
public Stat GetStatOrAttributeOrSkill(StatType item)
{
Stat itemToReturn = null;
if (item == StatOperations.GetStatTypeFromEnum(StatTypeEnum.Soul))
itemToReturn = Soul;
foreach (Stat i in Stats)
{
if (i.statType == item)
return i;
}
foreach (Progression i in Attributes)
{
if (i.statType == item)
return i;
}
foreach (Skill i in Skills)
{
if (i.statType == item)
return i;
}
if (itemToReturn == null)
new Exception(this.gameObject.name + " - " +
string.Concat("Critical error: Stat or Attribute or Skill not found: ", item));
return itemToReturn;
}
public Stat GetStat(StatType statType)
{
Stat itemToReturn = null;
foreach (Stat i in Stats)
{
if (i.statType == statType)
return i;
}
new Exception(string.Concat("Critical error: Stat not found: ", statType));
return itemToReturn;
}
public Progression GetAttribute(StatType item)
{
Progression itemToReturn = null;
foreach (Progression i in Attributes)
{
if (i.statType == item)
return i;
}
new Exception(string.Concat("Critical error: Attribute not found: ", item));
return itemToReturn;
}
public Skill GetSkill(StatType item)
{
Skill itemToReturn = null;
foreach (Skill i in Skills)
{
if (i.statType == item)
return i;
}
new Exception(string.Concat("Critical error: Skill not found: ", item));
return itemToReturn;
}
public SkillCheckResult PerformSkillCheck(StatType stat, int difficulty)
{
return new SkillCheckResult(GetStatOrAttributeOrSkill(stat), difficulty);
}
#endregion
#region Fighting and defence //to be moved to other file/class
// private ActionOutcomeAndXPInfo actualActionOutcomeAndXpBeingReceived; // damage done over more instances
public void DefendOrApplyHeal(ActionOutcomeAndXPInfo thisActionOutcomeAndXp)
{
var asf = thisActionOutcomeAndXp.AppliedStatusEffect;
if (asf != null && asf.StatusEffectData.hasRegeneration)
GetStatOrAttributeOrSkill(asf.StatusEffectData.regeneration.InfluencedStat)
.ConsumeValue(thisActionOutcomeAndXp.HealAppliedFromAllRanges);
if (thisActionOutcomeAndXp.DamageDone > 0)
{
foreach (var dmg in thisActionOutcomeAndXp.AppliedDamages)
{
GetStatOrAttributeOrSkill(dmg.damage.influencedStat).ConsumeValue(dmg.ReducedDamage);
}
}
}
public void DoAllPostDamageProcessing(ActionOutcomeAndXPInfo actualDamage)
{
//apply damage, display it and spawn blood
if (actualDamage.DamageDone != 0)
goreSpawner.Spawn(transform.position,
actualDamage.DamageDonePercentageOfMax);
damageTextSpawnerSpawner.Spawn(actualDamage);
//todo maybe just add xpinfo to some logger or something
if (GetStat(StatOperations.GetStatTypeFromEnum(StatTypeEnum.Health)).ActualValue < 0)
KillChracter();
}
public void KillChracter()
{
// do not destroy player object
if (!transform.root.CompareTag("Player"))
{
aliveState = false;
goreSpawner.Spawn(transform.position, 1);
transform.root.GetComponent<SingleNodeBlocker>().Unblock();
StartCoroutine(DoDeathAnimation());
OnKilled?.Invoke(this);
}
else
{
aliveState = false;
OnKilled?.Invoke(this);
//TODO: missing player death - see on gui
}
}
private IEnumerator DoDeathAnimation()
{
mainAnimationWrapper.SetDisplayPriorityToCompleteChar("MapObjects");
Tween animationTween = bodyGfx.transform.DORotate(new Vector3(0, 0, -90), 0.5f).SetEase(Ease.InCirc);
yield return animationTween.WaitForCompletion();
}
#endregion
#region Exp Aggregation
public void GainAndDistributeXPFromAttack(List<ActionOutcomeAndXPInfo> gainedXPs)
{
foreach (var gainedXP in gainedXPs)
{
float experience = 0f;
experience += gainedXP.xpGivenToAttackerFromGivenDamage;
var ability = gainedXP.ability.AbilityContainer;
var item = gainedXP.ability.item;
ability.Progression.GainXpAndDistributeToParent(experience);
if (item != null)
{
item.progression.GainXpAndDistributeToParent(experience);
GetSkill(item.governingSkill).GainXpAndDistributeToParent(experience);
SkillGoverningAttributes.MultiplyAndDistributeExpForAttributes(
this, experience, item.governingSkill);
}
else
{
GetSkill(ability.governingSkill).GainXpAndDistributeToParent(experience);
SkillGoverningAttributes.MultiplyAndDistributeExpForAttributes(
this, experience, ability.governingSkill);
}
soul.GainXpAndDistributeToParent(experience);
}
}
public void GainAndDistributeXPFromDefence(ActionOutcomeAndXPInfo gainedXP)
{
float experience = 0f;
experience += gainedXP.xpGivenToDefenderFromReceivedDamage;
var itemsEquiped = inventoryEquipController.Equips.Count(x => x.EquipedItem != null);
if (itemsEquiped > 0)
{
var expForItems = experience / itemsEquiped;
foreach (var item in inventoryEquipController.Equips.ConvertAll(x => x.EquipedItem))
{
if (item == null) continue;
var count = gainedXP.AppliedDamages.Count(dmg =>
item.modifiers.Any(x => x.modifiedStat == dmg.DamageType.ReductionDefender));
item.progression.GainXpAndDistributeToParent(expForItems*count);
if (item.governingSkill == null) continue;
GetSkill(item.governingSkill).GainXpAndDistributeToParent(expForItems*count);
SkillGoverningAttributes.MultiplyAndDistributeExpForAttributes(
this, expForItems*count, item.governingSkill);
}
}
soul.GainXpAndDistributeToParent(experience);
}
public float GiveXPFromThisCharacter()
{
//TODO:missing the summarization and main level
float returnXP = 0;
foreach (var stat in Stats)
returnXP += stat.ActualValue;
foreach (var item in inventoryEquipController.Equips)
{
if (item.EquipedItem != null)
returnXP += item.EquipedItem.GiveXP();
}
return returnXP;
}
#endregion
#region Stat influencing and status effects
public Action StatInfluencingRecalculated;
private void RecalculateStatInfluencing()
{
if (!isCalculatingStatInfluencing)
{
isCalculatingStatInfluencing = true;
foreach (var stat in stats)
{
stat.ResetValues();
}
GameComponentAggregator.Instance.statInfluencing.ApplyStatModifications(this);
isCalculatingStatInfluencing = false;
characterAbilities.RecalculateAbilityStatInfluencing(this);
StatInfluencingRecalculated?.Invoke();
}
}
public AppliedStatusEffect AddStatusEffect(AppliedStatusEffect asfToAdd)
{
//todo prevent effects multiplication
appliedStatusEffects.Add(asfToAdd);
return asfToAdd;
}
private void MakeStatusEffectTicks()
{
RecalculateStatInfluencing();
foreach (var asf in appliedStatusEffects)
{
DoSingleStatusEffectTick(asf);
}
appliedStatusEffects.RemoveAll(x => !x.IsDurationValid);
}
public void DoSingleStatusEffectTick(AppliedStatusEffect asf)
{
var xpInfo = asf.MakeDurationTickAndApplySE(this);
xpInfo.CalculateXP();
GainAndDistributeXPFromDefence(xpInfo);
xpInfo.Attacker.GainAndDistributeXPFromAttack(xpInfo.ToOneObjectList());
DefendOrApplyHeal(xpInfo);
DoAllPostDamageProcessing(xpInfo);
}
#endregion
//this is public only because of testing
public void OnCharacterTurnStarted()
{
GetStat(StatOperations.GetStatTypeFromEnum(StatTypeEnum.ActionPoints)).FullHealValue();
MakeStatusEffectTicks();
}
}
}
Comments (4)
-
-
Thank you for reply @TorVestergaard.
I wanted to go with Unity 2022 LTS and was waiting for this 'releasing very shortly patch' :). 2 days have been passed and still no patch released.
Could you please tell us exact date for this patch?
-
Should be today - we typically do not do patch releases in the weekend.
-
Thanks again 🙏
- Log in to comment
This issue has been resolved in a patch that will be releasing very shortly :)