Topic: [Tutorial] 3D Character Select Screen (Basic/Pro/Source)
Here's a basic implementation of how to get 3D characters on your select screen. There's two parts to this, the prefab creation part and the coding part. All coding is done to the CharacterSelectionScript.cs file, which is available in all versions. So this should be possible on Basic and Pro versions as well as Source.
This method works for both Mecanim and Legacy, and they can even work together (I've tested with all 4 default characters).
Prefab Creation
Before coding anything, you'll need to create new prefabs for each of your characters. We'll be instantiating these when a character is highlighted. We are using our own prefabs as it will be more effort/work to deal with the HitBoxesScript.cs file attached to the default prefabs. Also, in order to get the 2P character to mirror properly, we'll need to use our own Animation Controller (if using Mecanim).
Create a duplicate of the character's prefab.
Rename the duplicate to something appropriate (e.g. Mike_Select).
On this new prefab, remove the HitBoxesScript component.
Next step is dependent on animation type (NOTE: you're checking for either Animation or Animator components).
If Legacy (prefab has Animation component)
Drag the idle clip into the Animation and Animations fields. Ensure Player Automatically is ticked.
Ensure you're selecting the correct idle clip by looking for it in the Character's Basic Moves list.
If Mecanim (prefab has Animator component)
Ensure the Avatar is set correctly (look for it in the Character's MoveSet).
In your Project, create an Animator Controller, rename to something appropriate (e.g. Mike Controller) and drag it into the Controller slot.
Highlight the Animator Controller, then open the Animator Unity window. Double check you are looking at the right Controller by looking at the bottom right corner of this window (it should say something like UFE/Characters/Mike Controller.controller)
Drag into the Animator window the idle clip for this character. To ensure you're using the right clip, select it from the Character's Basic Moves. This idle clip should be orange to signify it's the default clip.
In the Animator window, right click the clip and select copy. Then right click anywhere in the window and choose paste.
Select the copied clip, and in the Inspector rename to Idle Mirror (or similar) and check Mirror.
Back in the Animator window, create a new Parameter and call it "mirror" (all lowercase and no quotes).
Finally, right click Any State and select Make Transition and then click the Idle Mirror clip. This creates a transition to the Mirror clip from any point.
Click the transition arrow, and in the Inspector change the condition to be mirror == true.
Then adjust the blend time to be 0 (drag the blue bits until there's no angled blue bit between the clips.
That's the prefab creation step. You'll later need to add these to the CharacterSelectionScript component, but we'll need to create the variable for that first.
Coding CharacterSelectionScript.cs
Open CharacterSelectionScript.cs, and add the following variables to the beginning of the class:
// 3D Select stuff
// Our reference to the current 3D instance; we'll be destroy the previous as we create a new one
private GameObject p1SelectInstance;
private GameObject p2SelectInstance;
public Vector3 p1SelectPosition = new Vector3(-6,0,0);
public Vector3 p2SelectPosition = new Vector3(6,0,0);
public GameObject[] characterPrefabs;
// this is our last known hover, when it's changed we update the 3D instance
private int p1LastHover;
private int p2LastHover;
Then add in this function:
// Show 3D instance of character when highlighted
public void OnCharacterHighlighted (int player, int characterIndex) {
if (player == 1) {
if (p1SelectInstance != null) {
Destroy(p1SelectInstance);
}
p1SelectInstance = (GameObject) Instantiate(characterPrefabs[characterIndex]);
p1SelectInstance.transform.position = p1SelectPosition;
} else {
if (p2SelectInstance != null) {
Destroy(p2SelectInstance);
}
p2SelectInstance = (GameObject) Instantiate(characterPrefabs[characterIndex]);
p2SelectInstance.transform.position = p2SelectPosition;
Animator myAnimator = p2SelectInstance.GetComponent<Animator>();
if (myAnimator != null) {
// Mecanim, mirror via Animator...
myAnimator.SetBool("mirror", true);
} else {
p2SelectInstance.transform.localScale = new Vector3(p2SelectInstance.transform.localScale.x * -1, p2SelectInstance.transform.localScale.y, p2SelectInstance.transform.localScale.z);
}
p2SelectInstance.transform.localEulerAngles = new Vector3(p2SelectInstance.transform.localRotation.x, -90, p2SelectInstance.transform.localRotation.z);
}
}
Then inside the Start() function, after this line:
if (UFE.config.characters.Length >= 4) p2HoverIndex = 3;
add:
// highlight 1P
OnCharacterHighlighted(1, p1HoverIndex);
Then inside DoFixedUpdate(), look for this:
// Select Character
if (!p1AxisHeld && UFE.config.player1Character == null){
p1HoverIndex = CharacterMenuSelection(p1HoverIndex, p1InputController, p1InputController.horizontalAxis, p1InputController.verticalAxis);
and below add:
if (p1HoverIndex != p1LastHover) {
OnCharacterHighlighted(1, p1HoverIndex);
p1LastHover = p1HoverIndex;
}
Then look for:
if (!p2AxisHeld && UFE.config.player2Character == null){
p2HoverIndex = CharacterMenuSelection(p2HoverIndex, p2InputController, p2InputController.horizontalAxis, p2InputController.verticalAxis);
and below add:
if (p2HoverIndex != p2LastHover) {
OnCharacterHighlighted(2, p2HoverIndex);
p2LastHover = p2HoverIndex;
}
Then look for the function StageStageSelect() and change to the below:
void StartStageSelect(){
Destroy(p1SelectInstance);
Destroy(p2SelectInstance);
UFE.StartStageSelect(0);
}
Finally, look for this:
if (p1SelectedBig != null) {
GUI.DrawTexture(SetResolution(new Rect(79, 112, 311, 496)), p1SelectedBig);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUI.skin.label.fontSize = 30 * (Screen.height/720);
GUI.Label(SetResolution(new Rect(178, 597, 250, 50)), UFE.config.characters[p1HoverIndex].characterName);
}
if (p2SelectedBig != null) {
GUI.DrawTextureWithTexCoords(SetResolution(new Rect(902, 113, 311, 496)), p2SelectedBig, new Rect(0, 0, -1, 1));
GUI.skin.label.alignment = TextAnchor.UpperRight;
GUI.skin.label.fontSize = 30 * (Screen.height/720);
GUI.Label(SetResolution(new Rect(860, 597, 250, 50)), UFE.config.characters[p2HoverIndex].characterName);
}
and comment out that whole section since we don't need it now. There's more you could comment out too, but this is the minimum required to get it all working.
Integration
With coding and prefab creation out of the way, you need to put them together. First, we need to set things up:
In the Hierarchy, select the Main Camera and change the position to (0,4,-10) and zero out the rotation. Apply this to the prefab too, to avoid accidental resets.
In the Project view, find the CharacterSelectScreen prefab, and remove the background texture from the GUITexture.
Then add the Prefabs you created earlier into the Character Prefabs slots. Ensure the order is the same as you have in Global Options | Characters.
That should work now. Let me know if you get stuck.
Notes:
You can create your own 3D "stage" to place your characters on. Adjust the p1SelectPosition p2SelectPosition accordingly.
If the idle animation doesn't loop, you need to check the loop option in the Inspector for the clip (either in Project view or Animator).
Try adding a selected animation to your character (like a win pose) and triggering that when the character is selected.