Camera stops following after setting .Character = newCharacter

bbjones

Member
I've got a basic test going where I spawn in a character prefab that was made as a simple UCC TPS character with no items.

I then have 3 buttons to test with being:
  1. Spawn new playable character
    • delete the old character if it exists
    • GameObject.Instantiate(...) the character prefab
    • Enable the Opsive camera and set CameraController.Character = spawnedCharacter
    • I can now play/move the character around as expected
  2. Change playable to AI
    • Uses CharacterBuilder methods to remove Input and Add AI components
    • disables opsive camera
    • enables a different non-opsive scene level camera
    • I can no longer control the character, the scene level camera is now active as expected
  3. Change AI back to playable
    1. Uses CharacterBuilder methods to add Input and remove AI components
    2. disables scene camera
    3. Enable the Opsive camera and set CameraController.Character = spawnedCharacter
    4. I can control the player again, opsive cam is active as expected, but it no longer follows the player in scene
  4. (Repeat) Spawn a new playable character
    1. this destroys the previous characters and starts things over again with the Opsive camera back to working as expected and under player control

Part of this question is if I'm swapping between playable and AI correctly, and if so, what might be the issue with re-attaching the camera after swapping back to playable?

Spawing character based on this code:
C#:
                spawnedCharacter = GameObject.Instantiate(characterDefaultKits[c].CharacterPrefab);

                // attach new character to Opsive camera
                opsiveCamera.SetActive(true);
                nonOpsiveCamera.SetActive(false);
                uccCam.Character = spawnedCharacter;

Swapping is then done with this code:
C#:
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Keypad1))
        {
            print("Spawn Character...");
            SpawnACharacter();
        }
        if (Input.GetKeyDown(KeyCode.Keypad2))
        {
            print("Convert To AI...");
            ConvertPlaybleToAiAgent();
        }
        if (Input.GetKeyDown(KeyCode.Keypad3))
        {
            print("Convert To Playable...");
            ConvertAiAgentToPlayable();
        }
    }

    public void ConvertPlaybleToAiAgent()
    {
        CharacterBuilder.AddAIAgent(this.spawnedCharacter);
        CharacterBuilder.RemoveUnityInput(this.spawnedCharacter);

        Opsive.UltimateCharacterController.Camera.CameraController uccCam = opsiveCamera.GetComponent<Opsive.UltimateCharacterController.Camera.CameraController>();
        opsiveCamera.SetActive(false);
        nonOpsiveCamera.SetActive(true);
        //uccCam.Character = null;
    }
    public void ConvertAiAgentToPlayable()
    {
        CharacterBuilder.RemoveAIAgent(this.spawnedCharacter);
        CharacterBuilder.AddUnityInput(this.spawnedCharacter);

        Opsive.UltimateCharacterController.Camera.CameraController uccCam = opsiveCamera.GetComponent<Opsive.UltimateCharacterController.Camera.CameraController>();
        opsiveCamera.SetActive(true);
        nonOpsiveCamera.SetActive(false);
        uccCam.Character = this.spawnedCharacter;
    }

Note that the character prefab I'm using is made from the Demo Nolan model as a simple UCC TPS character, demo item collection and no inventory items.
No items are added during spawn either.
 
Last edited:
2 other things...

1 - In one test scenario I only spawn in a single character, change it to AI, then back to playable and the camera still loses attachment.
Event if i comment out the line uccCam.Character = this.spawnedCharacter; in ConvertAiToPlayable().

2 - If I include the commented out line //uccCam.Character = null; in ConvertPlayableToAiAgent() I get an error when trying to re-attach the camera to the spawnedCharacter, so I leave it commented out.

ArgumentException: An item with the same key has already been added. Key: Default-Material (Instance) (UnityEngine.Material)
System.Collections.Generic.Dictionary`2[TKey,TValue].TryInsert (TKey key, TValue value, System.Collections.Generic.InsertionBehavior behavior) (at <599589bf4ce248909b8a14cbe4a2034e>:0)
System.Collections.Generic.Dictionary`2[TKey,TValue].Add (TKey key, TValue value) (at <599589bf4ce248909b8a14cbe4a2034e>:0)
Opsive.UltimateCharacterController.ThirdPersonController.Camera.ObjectFader.OnAttachCharacter (UnityEngine.GameObject character) (at Assets/Opsive/UltimateCharacterController/Scripts/ThirdPersonController/Camera/ObjectFader.cs:243)
Opsive.UltimateCharacterController.Events.InvokableAction`1[T1].Invoke (T1 arg1) (at Assets/Opsive/UltimateCharacterController/Scripts/Events/InvokableAction.cs:87)
Opsive.UltimateCharacterController.Events.EventHandler.ExecuteEvent[T1] (System.Object obj, System.String eventName, T1 arg1) (at Assets/Opsive/UltimateCharacterController/Scripts/Events/EventHandler.cs:417)
Opsive.UltimateCharacterController.Camera.CameraController.InitializeCharacter (UnityEngine.GameObject character) (at Assets/Opsive/UltimateCharacterController/Scripts/Camera/CameraController.cs:468)
Opsive.UltimateCharacterController.Camera.CameraController.set_Character (UnityEngine.GameObject value) (at Assets/Opsive/UltimateCharacterController/Scripts/Camera/CameraController.cs:62)
SpawnCharacter.ConvertAiAgentToPlayable () (at Assets/_PlayerControlTest/SpawnCharacter.cs:66)
SpawnCharacter.Update () (at Assets/_PlayerControlTest/SpawnCharacter.cs:44)
 
Last edited:
I can control the player again, opsive cam is active as expected, but it no longer follows the player in scene
What does it follow? Does it act as though it is disabled? You can see what character it thinks that it is following by placing a breakpoint within CameraController.Move and looking at the m_Character variable.

SpawnCharacter.Update () (at Assets/_PlayerControlTest/SpawnCharacter.cs:44)

What is line 44 of SpawnCharacter.cs? You should be able to set uccCam.Character to null and not get any errors.

On line 182 of ObjectFader try adding the following:

Code:
                if (m_Character != null && m_Character != character) {
                    m_OriginalMaterialValuesMap.Clear(); // here
 
The camera is placed back on the player like it will follow them, it just doesn't move, only the player moves around.

Line 44 of SpawnCharacter.cs is the method call ConvertAiAgentToPlayable();
(Full script below)

Changes I made now are:
  • When swapping to AI, set cam.Character = null
  • When swapping back to playable, set cam.Character = spawnedCharacter
  • Applied code change to ObjectShader as you suggested
No more error and I can swap in any combination between new spawn, AI, and playable, and the camera now follows the player.
However, when swapping from AI back to Playable, the right click will only orbit the camera around the player, it no longer controls the player rotation like it does when you spawn a new character.

I have a repro package using all Opsive Demo assets here:
See the README.txt for steps to reproduce.

Same package used for the other forum post about inventory issues.

Full script - SpawnCharacter.cs

C#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Opsive.UltimateCharacterController.Inventory;
using Opsive.UltimateCharacterController.Objects.CharacterAssist;
using Opsive.UltimateCharacterController.Utility.Builders;

[System.Serializable]
public class SpawnCharacter : MonoBehaviour
{
    [Tooltip("At run-time, select the type of character you want to spawn. Then call the SpawnACharacter() method, easy way is to wire that up to a UI Button click event.")]
    [Header("Testing")]
    public ECharacterType characterTypeToSpawn;
    public bool makeAiAgent;
    public bool doNotDestroyAiAgents;
    [Tooltip("Drag in the Opsive camera you created in your scene with the Opsive Setup Manager.  Be sure to uncheck Init Character On Awake.")]
    public GameObject opsiveCamera;
    public GameObject nonOpsiveCamera;
    //[Tooltip("If false the Opsive camera will not reference the newly spawned UCC character.")]
    //public bool assignSpawnToCamera;

    [Tooltip("One entry for every combination of character type and default loadout")]
    [Header("Default Kits")]
    public CharacterDefaultKits[] characterDefaultKits;

    private GameObject spawnedCharacter;
    private bool spawnIsAi;

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Keypad1))
        {
            print("Spawn Character...");
            SpawnACharacter();
        }
        if (Input.GetKeyDown(KeyCode.Keypad2))
        {
            print("Convert To AI...");
            ConvertPlaybleToAiAgent();
        }
        if (Input.GetKeyDown(KeyCode.Keypad3))
        {
            print("Convert To Playable...");
            ConvertAiAgentToPlayable();
        }
    }

    public void ConvertPlaybleToAiAgent()
    {
        CharacterBuilder.AddAIAgent(this.spawnedCharacter);
        CharacterBuilder.RemoveUnityInput(this.spawnedCharacter);

        Opsive.UltimateCharacterController.Camera.CameraController uccCam = opsiveCamera.GetComponent<Opsive.UltimateCharacterController.Camera.CameraController>();
        opsiveCamera.SetActive(false);
        nonOpsiveCamera.SetActive(true);
        uccCam.Character = null;
    }
    public void ConvertAiAgentToPlayable()
    {
        CharacterBuilder.RemoveAIAgent(this.spawnedCharacter);
        CharacterBuilder.AddUnityInput(this.spawnedCharacter);

        Opsive.UltimateCharacterController.Camera.CameraController uccCam = opsiveCamera.GetComponent<Opsive.UltimateCharacterController.Camera.CameraController>();
        opsiveCamera.SetActive(true);
        nonOpsiveCamera.SetActive(false);
        uccCam.Character = this.spawnedCharacter;
    }

    // The code for adding ItemPickups is taken from the UMA Integration asset scripts found here https://opsive.com/downloads/?pid=923
    // Note that I'm not using anything to do with UMA, and does not require the UMA asset to function.
    // Spawns the character with the typical Unity.GameObject.Instantiate() method.
    public void SpawnACharacter()
    {
        print("Attempt to spawn character...");

        // destroy currently spawned character if exists
        //if (spawnedCharacter != null)
        if (spawnedCharacter != null)
        {
            Destroy(spawnedCharacter);
        }

        Opsive.UltimateCharacterController.Camera.CameraController uccCam = opsiveCamera.GetComponent<Opsive.UltimateCharacterController.Camera.CameraController>();

        // loop through character kits to find the type that matches variable characterTypeToSpawn
        for (int c = 0; c < characterDefaultKits.Length; ++c)
        {
            if (characterDefaultKits[c].ECharacterType == characterTypeToSpawn)
            {
                // find the matching default kit by character type
                spawnedCharacter = GameObject.Instantiate(characterDefaultKits[c].CharacterPrefab);

                // attach new character to Opsive camera
                opsiveCamera.SetActive(true);
                nonOpsiveCamera.SetActive(false);
                uccCam.Character = spawnedCharacter;


                // get UCC inventory from new character
                var inventory = spawnedCharacter.GetComponent<InventoryBase>();
                if (inventory == null)
                {
                    return;
                }

                // add all ItemPickups for the new character type
                for (int i = 0; i < characterDefaultKits[c].ItemPickups.Length; ++i)
                {
                    if (characterDefaultKits[c].ItemPickups[i] == null)
                    {
                        continue;
                    }

                    characterDefaultKits[c].ItemPickups[i].DoItemPickup(spawnedCharacter, inventory, -1, true, true);
                }
            }
        }
    }
}

// this class represents all the default items for a single character type
[System.Serializable]
public class CharacterDefaultKits
{
    public ECharacterType ECharacterType;
    public GameObject CharacterPrefab;
    public ItemPickup[] ItemPickups;
}

// this class represents the different types of characters
[System.Serializable]
public enum ECharacterType
{
    CharacterType1,
    CharacterType2,
    CharacterType3
}
 
After more help from Justin sorting out the repro package via email, this issue was resolved.

Justin provided some updated UCC scripts to fix the issue as well as changes for my script (updated version attached).

A complete test is demonstrated here including the updated scripts from Justin.
 

Attachments

  • SpawnCharacter.cs
    5.6 KB · Views: 4
Top