Dynamic Player Creation

saboehnke

New member
I just started using the Third Person Controller asset. Going through the setup videos I have to setup the camera, managers, and characters all through the character manager. Is this asset setup to specifically do this all at editor time? I need to be able to do this all at runtime. I tried just creating a prefab of the managers that are created and of a default player that I setup in the character manager, but I end up with errors because the camera (which is just added to the main camera that exists before the player exists) tries to find an object with the player tag immediately before the player has been created. Does this asset support this type of use? Or am I going to have to make a bunch of changes to the code to mimic what the editor tools do? I have gone through the DemoManager for the asset I purchased as well and even there the player that is used is already setup ahead of time.
 
I recommend that you take a look at the UMA integration. This shows how you can use the CharacterBuilder to create the character at runtime. The UMA integration is useful even if you don't plan on using UMA since it shows you the process.
 
@Justin Ok I've been checking that integration out and it looks like that I just need to do the same thing as the UMACharacterBuilder once a character is created. So for a basic implementation, it looks like I just need to call BuildCharacter() and BuildCharacterComponents() with the corresponding params and then setup the camera association. Does that sound about right? Because going that route, and using the Demo animation controller from the Third Person Controller asset, I end up with some nulls throughout the AnimationMonitor and other places that all seem to be related to the auto initialization being set to false in the AddEssentials() function (which is called in the BuildCharacter() function). Basically, AwakeInternal() doesn't get called a few things because the AutoInitialization isn't turned on so I'm not sure how things are supposed to get initialized. m_CharacterLocomotion in the AnimatorMonitor.cs for example.

I'm basically wanting to be able to make this fully dynamic so that I can bring in any rigged player (humanoid or non-humanoid) and get them setup and moving.
 
Last edited:
Does that sound about right?

Yes, that's correct. Have you tried stepping through the UMA Character Builder code to see what is different with your code? In that situation at runtime AutoInitialization is false as well so it will provide a good comparison for what is different with your setup.
 
Ah I got it working. The model itself was loaded without having the animation type or avatar set. Fixing that gets it to work. But what happens when I try using non-humanoid characters? Will this same build structure still work?
 
Yes, the Character Builder doesn't differentiate between humanoids and non-humanoids.
 
Does the character builder support character rigs that have a generic animation type instead of humanoid? I end up with the UltimateCharacterLocomotion.cs trying to access m_GameObject but that isn't initialized unless CharacterInitializer.AutoInitialization is turned on. I'm not sure how it was working before but now regardless of animation type it runs into this issue.

Looking into it further, I have no idea how it would work. AwakeInternal() won't get called while AutoInitialization if off, which it gets turned off. Then the CharacterInitializer's OnAwake action should get called in the CharacterBuilder.cs but the code that throws an error happens before the code can make it that far because it is after the UltimateCharacterLocomation component is added to the player. So, the Start() function of the UltimateCharacterLocomotion is called it errors out because of this line:

EventHandler.ExecuteEvent(m_GameObject, "OnCharacterSnapAnimator", true);

because m_GameObject is null.
 
Last edited:
Yes, it does. It's the same code that is running by the Character Manager for generic characters. If it was working before I recommend reverting to that version and comparing to what you currently have.
 
Alright so I found the issue. I'm not really sure how to resolve it though.

When a character with an animation type of generic has the BuildCharacter() function called, it calls AddEssentials() which will add the UltimateCharacterLocomotion component to the character gameobject. After that, the logic adds colliders. In the AddCollider() function lies my issue:

C#:
if (collider != null) {
    var positioner = collider.AddComponent<CapsuleColliderPositioner>();
    positioner.FirstEndCapTarget = character.transform;

    var animator = character.GetComponent<Animator>();
    if (animator != null) {
        // The CapsuleColliderPositioner should follow the character's movements.
        var head = animator.GetBoneTransform(HumanBodyBones.Head); // <---- THIS LINE CAUSES AN ERROR
        if (head != null) {
            positioner.SecondEndCapTarget = head;
            positioner.RotationBone = positioner.PositionBone = animator.GetBoneTransform(HumanBodyBones.Hips);
        }
    }
}

The line that I have commented about the error above will throw an error if the character is not humanoid. This causes the logic to kick out of where it is at and it goes straight into the Start() function of the UltimateCharacterLocomotion.cs where most variables have not yet been initialized because the error being thrown skipped it. So, that's why m_GameObject ends up being null in the event I mentioned previously.

Is this an issue you've seen before? I'm not sure how I can use Generic animation types if humanoid is a requirement. Hopefully I'm just missing something and having a bone structure that matches that of a humanoid (regardless of animation type of the model) is not a requirement.
 
Last edited:
For generic characters the Capsule Collider Positioner should not be added. Try changing that logic to:

Code:
            if (collider != null) {
                var animator = character.GetComponent<Animator>();
                if (animator == null || animator.isHuman) {
                    var positioner = collider.AddComponent<CapsuleColliderPositioner>();
                    positioner.FirstEndCapTarget = character.transform;

                    // The CapsuleColliderPositioner should follow the character's movements.
                    var head = animator.GetBoneTransform(HumanBodyBones.Head);
                    if (head != null) {
                        positioner.SecondEndCapTarget = head;
                        positioner.RotationBone = positioner.PositionBone = animator.GetBoneTransform(HumanBodyBones.Hips);
                    }
                }
            }
 
Will that change be included in the next update? Otherwise the change on my side will be overwritten when I pull updates in the future. That logic is in the CharacterBuilder.cs.

Also, with that change I get a similar issue when the LookAt viewtype is being added to the character, so the same sort of change is necessary:

C#:
public override void AttachCharacter(GameObject character)
{
    base.AttachCharacter(character);

    if (m_Character != null && m_Target == null) {
        Animator characterAnimator = null;
        var modelManager = m_Character.GetCachedComponent<Opsive.UltimateCharacterController.Character.ModelManager>();
        if (modelManager != null) {
            characterAnimator = modelManager.ActiveModel.GetComponent<Animator>();
        } else {
            var animatorMonitor = m_Character.GetComponentInChildren<Opsive.UltimateCharacterController.Character.AnimatorMonitor>();
            if (animatorMonitor != null) {
                characterAnimator = animatorMonitor.GetComponent<Animator>();
            }
        }
        if (characterAnimator != null) { // ADD && characterAnimator.isHuman
            m_Target = characterAnimator.GetBoneTransform(HumanBodyBones.Head); // THIS LINE WILL THROW THE ERROR
        } else {
            m_Target = m_Transform;
        }
    }
}
 
Top