Drop currently held item on death

Cheo

Active member
Hello, unless I'm mistaken it looks like we can't get a character to only drop the item he's currently holding, ticking Remove All On Death and Auto Remove Character Items on Inventory causes to drop the entire inventory when a character dies, but so far it looks like it's all or nothing out of the box.

I can use this simple line of code to make an enemy drop his current weapon while he's alive :

C#:
itemSetManager.GetActiveItemSet(0).GetCharacterItem(0).Drop(1, true) ;

But when called from CharacterHealth's OnDeath event it doesn't drop the weapon and throws this error :

Code:
[Exception] NullReferenceException: Object reference not set to an instance of an object
DropTestScript.DropTest() at /Scripts/DropTestScript.cs:17
  15:  public void DropTest()
  16:  {
-->  17:      itemSetManager.GetActiveItemSet(0).GetCharacterItem(0).Drop(1, true) ;
  18:      Debug.Log("DropTest called");
  19:  }

InvokableCall.Invoke() at <56073df97ede4f769c2cc45d546d986d>:0

UnityEngine.Events.UnityEvent`3[T0,T1,T2].Invoke() at <56073df97ede4f769c2cc45d546d986d>:0

Health.Die() at /Opsive/UltimateCharacterController/Scripts/Traits/Health.cs:500
 498:      EventHandler.ExecuteEvent<Vector3, Vector3, GameObject>(m_GameObject, "OnDeath", position, force, attacker);
 499:      if (m_OnDeathEvent != null) {
--> 500:          m_OnDeathEvent.Invoke(position, force, attacker);
 501:      }
 502:  }

CharacterHealth.Die() at /Opsive/UltimateCharacterController/Scripts/Traits/CharacterHealth.cs:132
 130:  {
 131:      m_CharacterLayer = m_GameObject.layer;
--> 132:      base.Die(position, force, attacker);
 134:      if (m_DeathLayer != 0) {

Health.OnDamage() at /Opsive/UltimateCharacterController/Scripts/Traits/Health.cs:415
 413:                  if (m_NetworkInfo == null || m_NetworkInfo.HasAuthority()) {
 414:  #endif
--> 415:                      Die(damageData.Position, force, attacker);
 416:  #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
 417:                  }

CharacterHealth.OnDamage() at /Opsive/UltimateCharacterController/Scripts/Traits/CharacterHealth.cs:116
 114:  public override void OnDamage(DamageData damageData)
 115:  {
--> 116:      base.OnDamage(damageData);
 118:      if (m_DamagedEffect != null) {

Health.Damage() at /Opsive/UltimateCharacterController/Scripts/Traits/Health.cs:323
 321:  #endif
--> 323:              OnDamage(damageData);
 324:          }

MyPenetrationDamageProcessor.Process() at /Scripts/MyUCCImpactActions/MyPenetrationDamageProcessor.cs:19
  17:     {
  18:        damageData.Amount *= m_Multiplier;
-->  19:        target.Damage(damageData);
  20:     }
  21:  }

SimpleDamage.OnImpactInternal() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Impact/SimpleDamage.cs:220
 218:  } else {
 219:      if (damageProcessor == null) { damageProcessor = DamageProcessor.Default; }
--> 220:      damageProcessor.Process(damageTarget, pooledDamageData);
 221:  }

ImpactAction.OnImpact() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Impact/ImpactAction.cs:279
 277:  {
 278:      if (m_Delay <= 0) {
--> 279:          OnImpactInternal(ctx);
 280:      } else {

ImpactAction.TryInvokeOnImpact() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Impact/ImpactAction.cs:269
 267:      }
--> 269:      OnImpact(ctx);
 270:  }

ImpactActionGroup.OnImpactInternal() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Impact/ImpactAction.cs:142
 140:          if (m_ImpactActions[i] == null) { continue; }
--> 142:          m_ImpactActions[i].TryInvokeOnImpact(impactCallbackContext, forceImpact);
 143:      }
 144:  }

ImpactActionGroup.OnImpact() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Impact/ImpactAction.cs:129
 127:      if (m_ImpactActions == null) { return; }
--> 129:      OnImpactInternal(impactCallbackContext, forceImpact);
 130:  }

GenericShootableImpactModule.OnImpact() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Modules/Shootable/ImpactModule.cs:117
 115:  {
 116:      if (m_Conditions.CanImpact(impactCallbackContext)) {
--> 117:          m_ImpactActions.OnImpact(impactCallbackContext, true);
 118:      } else {
 119:          m_FailImpactActions.OnImpact(impactCallbackContext, true);

ShootableAction.OnFireImpact() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/ShootableAction.cs:346
 344:  #endif
 345:              for (int i = 0; i < m_ImpactModuleGroup.EnabledModules.Count; i++) {
--> 346:                  m_ImpactModuleGroup.EnabledModules[i].OnImpact(shootableImpactCallbackContext);
 347:  #if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
 348:                  invokedBitmask |= 1 << m_ImpactModuleGroup.EnabledModules[i].ID;

MyHitscanShooter.HitscanFire() at /Scripts/MyUCCImpactActions/MyHitscanShooter.cs:334
--> 334:  ShootableAction.OnFireImpact(m_ShootableImpactCallbackContext);
 336:  // Spawn a tracer which moves to the hit point.

SchedulerBase.AddEventInternal[T,U]() at <5be52286c54c47958ace04af73911405>:0

SchedulerBase.Schedule[T,U]() at <5be52286c54c47958ace04af73911405>:0

MyHitscanShooter.Fire() at /Scripts/MyUCCImpactActions/MyHitscanShooter.cs:214
 212:  for (int i = 0; i < m_FireCount; ++i)
 213:  {
--> 214:      Scheduler.Schedule(m_HitscanFireDelay, HitscanFire, dataStream, ammoData);
 215:  }

ShootableAction.TriggerItemAction() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/ShootableAction.cs:266
 264:  // The shooter will take care of removing the ammo when getting the projectile data.
 265:  // It will also call OnFire when it has done firing.
--> 266:  m_ShooterModuleGroup.FirstEnabledModule.Fire(m_ShootableUseDataStream);
 268:  // The item can complete its use.

SimpleBaseTrigger.UseItemTrigger() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Modules/TriggerModule.cs:368
 366:  {
 367:      m_TriggerData.Force = 1;
--> 368:      UsableAction.TriggerItemAction(m_TriggerData);
 369:      m_WasTriggered = true;
 370:  }

UsableAction.UseItem() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/UsableAction.cs:521
 519:      }
--> 521:      m_TriggerActionModuleGroup.FirstEnabledModule.UseItemTrigger();
 522:  }

ShootableAction.UseItem() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/ShootableAction.cs:245
 243:      }
--> 245:      base.UseItem();
 246:  }

UsableAction.UseItemUpdateInternal() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/UsableAction.cs:665
 663:  // The item may not be able to be used.
 664:  if (CanUseItem()) {
--> 665:      UseItem();
 666:      EventHandler.ExecuteEvent<IUsableItem>(m_Character, "OnUseAbilityUsedItem", this);

UsableAction.UseItemUpdate() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/UsableAction.cs:633
 631:  public virtual void UseItemUpdate(Use useItemAbility)
 632:  {
--> 633:      var isNotTryingToStop = UseItemUpdateInternal(useItemAbility);
 634:      if (IsDebugging) {
 635:          DebugLogger.SetInfo(InfoKey_IsTryingToStop, (!isNotTryingToStop).ToString());

Use.LateUpdate() at /Opsive/UltimateCharacterController/Scripts/Character/Abilities/Items/Use.cs:459
 458:          // Allow the items currently in use to be updated.
--> 459:          m_UsableItems[i].UseItemUpdate(this);
 460:      }
 461:  }

UltimateCharacterLocomotion.LateUpdateActiveAbilities() at /Opsive/UltimateCharacterController/Scripts/Character/UltimateCharacterLocomotion.cs:672
 670:  if (abilities != null) {
 671:      for (int i = 0; i < abilityCount; ++i) {
--> 672:          abilities[i].LateUpdate();
 673:      }
 674:  }

UltimateCharacterLocomotion.UpdateCharacter() at /Opsive/UltimateCharacterController/Scripts/Character/UltimateCharacterLocomotion.cs:598
 596:      // Allow the abilities to update after the character has been moved.
 597:      LateUpdateActiveAbilities(m_ActiveAbilities, ref m_ActiveAbilityCount);
--> 598:      LateUpdateActiveAbilities(m_ActiveItemAbilities, ref m_ActiveItemAbilityCount);
 599:  }

CharacterLocomotion.Move() at /Opsive/UltimateCharacterController/Scripts/Character/CharacterLocomotion.cs:561
 559:  EnableColliderCollisionLayer(false);
--> 561:  UpdateCharacter();
 563:  EnableColliderCollisionLayer(true);

SimulationManager+SmoothedCharacter.Move() at /Opsive/UltimateCharacterController/Scripts/Game/SimulationManager.cs:169
 167:      }
--> 169:      m_Locomotion.Move(horizontalMovement, forwardMovement, deltaYaw);
 170:      AssignFixedLocation();
 171:  }

SimulationManager.MoveCharacters() at /Opsive/UltimateCharacterController/Scripts/Game/SimulationManager.cs:723
 721:  for (int i = 0; i < m_Characters.Count; ++i) {
 722:      if (interpAmount == -1) {
--> 723:          m_Characters[i].Move(preMove);
 724:      } else {
 725:          if (!m_Characters[i].Locomotion.Interpolate) {

SimulationManager.FixedUpdate() at /Opsive/UltimateCharacterController/Scripts/Game/SimulationManager.cs:671
 669:  MoveCharacters(true, -1);
 670:  RotateCameras();
--> 671:  MoveCharacters(false, -1);
 672:  MoveCameras(-1);

I'm not sure how to get handle this one, if someone has a fix please let me know, I'd be much obliged ! However if this cannot be done out of the box without a custom script then I'd request this feature be added, enemies dropping the weapon they're currently holding when dying is something you'd expect from any shooter nowadays. Thanks in advance.
 
Last edited:
The built in inventory is pretty limited and for the large number of use cases we rely on the Ultimate Inventory System integration. The asset is focused on controller features rather than inventory features.

The exception that you are getting is within your custom script so I would set a breakpoint to debug what is null. With that said, this is the type of feature that would require you to subclass the inventory component.
 
Hello, allow me to come back to this issue. I haven't experimented much with UIS yet nor actually tried subclassing the Inventory script, but one thing caught my attention tonight : it seems that I can't even get a character to just keep his weapon in his hands when dying, even if Remove All On Death and Unequip All On Death are set to false. This issue can be seen in the ucc demo scene in the Die and Revive section. Is this even expected ?
In any case, before getting the character to actually drop the weapon, getting him to just keep his weapon will be a necessary first step. I'll take another look at this tomorrow, hope you can help with this. Thanks.
 
The EquipUnequip ability automatically unequips the item when the character dies.
C#:
        /// <summary>
        /// The character has died.
        /// </summary>
        /// <param name="position">The position of the force.</param>
        /// <param name="force">The amount of force which killed the character.</param>
        /// <param name="attacker">The GameObject that killed the character.</param>
        private void OnDeath(Vector3 position, Vector3 force, GameObject attacker)
        {
            m_PrevActiveItemSetIndex = m_ActiveItemSetIndex;
            // Don't immediately unequip if in first person view to allow the arms move off the screen.
            StartEquipUnequip(-1, false, !m_CharacterLocomotion.FirstPersonPerspective);

            if (m_Inventory.UnequipAllOnDeath) {
                m_InventoryAmount.Clear();
            }
        }

If you comment out the StartEquipUnequip function above it will keep your item equipped.

The item might still disapear because of the ThirdPersonPerspectiveItem:
Code:
/// <summary>
/// The character has died.
/// </summary>
/// <param name="position">The position of the force.</param>
/// <param name="force">The amount of force which killed the character.</param>
/// <param name="attacker">The GameObject that killed the character.</param>
private void OnDeath(Vector3 position, Vector3 force, GameObject attacker)
{
    // When the character dies disable the holster. Do not disable the item's object because that may not be activated again
    // when the character respawns, whereas the holster target should always be activated since it's an empty GameObject.
    if (HolsterTarget != null && m_PickedUp) {
        SetActive(false, true);
        HolsterTarget.gameObject.SetActive(false);
    }
}

So if your weapon has a holster you might need to change that function too.

Result you die with your item still equipped:
1699608884104.png

@Justin let me know if you want me to add some booleans to make those things optional, or at least make the functions virtual.

As for adding an option to drop only the active items, I could add that as a feature request, it would take a bit longer to implement as it would need to work in different scenarios.
 
Ah I forgot to mention.
It's not exactly what you want, but it might be a workaround for now.
Don't forget about the RemoveException list. This allows you to specify the items you do not want to remove/drop
1699609289180.png
 
Thanks for this amazing support @Sangemdoko ! I commented these OnDeath functions out and the character is now keeping his weapon when dying.

Capture d’écran (2187).png

The only issue right now is that the weapon is clipping through the ground, it should have a collider attached, perhaps even a rigidbody and joint !

I'm not sure this is super helpful but I still wanted to share this video from 2020, I don't have that project anymore but if I remember correctly these weapons were manually unparented, and given a collider and rigidbody when their holder died.


Once again, perhaps not the greatest example, I was not very familiar with UCC back then.

If you are able to implement a clean and relatively realistic way to get currently held items dropped on death, it would be much, much appreciated ! It really is a feature you'd expect from any shooter game nowadays. If I may offer one last suggestion, we may take example on the throwing system and deactivate the currently held item, spawn the Drop Prefab at the exact same position and rotation it was, and give it a tiny bit of force. This way, the drop prefab would not be the exact same object and could for example be an item pickup.

Lastly I'm not Justin but as a fellow game dev I'd totally ask for some booleans to make things optional in the Equip Unequip and Third Person Perpective Item scripts, it would make much more sense.

Lemme know if you need anything from me to help on this. Thanks
 
Top