- Home /
Switching between first-person Character Controllers
I have a scene with multiple Character Controllers, all first-person. They are all based on the same Character Controller, but each one hase different children with different scripts attached. (One carries a flashlight and has a HUD, another cannot move, etc). I need to:
Activate the correct Character Controller at the start of the scene (and deactivate the rest).
At triggered moments, swap to another Character Controller.
There are many similar questions here in unityAnswers, but I am having a hard time coming up with a solution that caters to this specific circumstance. One idea seems to be:
Find all scripts attached to Character Controllers + all children and disable all. Disable cameras. Then, enable all scripts on the desired character controller plus its camera.
However, I do no know how to find all scripts in all children, so I haven't been able to test this yet.
Thank you.
I started a snippet of code for initialization that might be on the right track. I set up an array to hold references to all character controllers, and playerControllerList[0] will be the starting character. This codes runs on game start:
public GameObject[] playerControllerList;
void Start () {
for( int i = 1; i < playerControllerList.Length; i++ ){
playerControllerList[i].GetComponent<$$anonymous$$onoBehaviour>().enabled = false;
}
}
However, the actual .enabled = false; does not work. Supposedly this .enabled = false line needs to be applied to all scripts attached to all children.
Answer by aldonaletto · May 17, 2013 at 11:32 PM
Just disabling the scripts may not be enough: a disabled script still can process events like OnTriggerEnter, OnCollisionEnter etc. A better and simpler alternative would be to deactivate all game objects except the one currently selected - for instance:
public int curPlayer = 0;
public GameObject[] playerControllerList;
void Start () {
for( int i = 0; i < playerControllerList.Length; i++ ){
// activate only curPlayer, and deactivate the others:
playerControllerList[i].SetActive(i == curPlayer);
}
}
EDITED:
The alternative above is ideal for cases where only a single player may exist at any time. If the other players must stay alive but simply don't answer to commands, maybe the script disabling approach is better. A reasonably way to do that is to keep in each player an enabling script: this script gets at Start an array of all scripts attached to the player instance (including itself), and enable/disable them with a dedicated function. You should then have an empty object with the players list and the selection function, which should call the enabling function in all players when selecting one of them.
The enabling script:
using UnityEngine;
using System.Collections;
public class EnablePlayer: MonoBehaviour {
MonoBehaviour[] scripts; // list of all scripts in this player
void Start(){ // get the scripts list at Start:
scripts = GetComponentsInChildren<MonoBehaviour>();
}
// enabling function:
public void EnableMe(bool onOff){
foreach (MonoBehaviour script in scripts){
script.enabled = onOff;
}
}
}
The control script:
using UnityEngine;
using System.Collections;
public class SelectPlayer: MonoBehaviour {
public EnablePlayer[] playerList; // drag the players here
void SelPlayer(int whichOne) {
for( int i = 0; i < playerList.Length; i++ ){
// activate only whichOne, and deactivate the others:
playerList[i].EnableMe(i == whichOne);
}
}
}
Disabling a script makes Unity stop calling its Update, FixedUpdate and LateUpdate functions - but the script still exists, so you can directly call any function, read/write its variables etc. This is good, but may be bad too: if an enemy is shooting at the player, for instance, it will keep hurting the unlucky guy even after you've switched to another player. If you want to avoid this, use the enabled script property to "disable" any function that you don't want to be executed when the player isn't selected - for instance:
public void SomeFunctionThatMustAlsoBeDisabled(){
// abort this function when the script is disabled:
if (!enabled) return;
...
}
Thank you, this is perfect! the "SetActive" function is exactly what I was missing. I also never thought do a Boolean comparison with an object flag, so thanks for the other tip!
The one downside is that the other Controllers, renderer and all, will be disabled. However, it does not affect me, as in this project, none of the character will be able to see each other anyway.
Once I get the entire system working, I will edit my original post for anyone that my stumble across this question in the future.
Anyway, I included a script disabling alternative in my answer - from your comment above, this approach may be better for your game. It's a little slower, but this may make no difference at all since players aren't supposed to be switched very often.
Nice, thank you for the extra information in the edit. I have a prototype working with the initial SetActive() method, but I plan to update to this new script-disabling method.
I do not actually need to see the other Character Controllers, though keeping them all active but with disabled scripts will be nice - sometimes a character swap may happen while the player is falling through the air, and this approach will allow the Character controller to naturally come to rest on the ground even as the player is controlling another character controller.
Very clever coding. GetComponentsInChildren() is something else that I was missing out on - I am definitely learning a lot.
For the moment, I have my code set up with a an extra layer of functions to swap the player, for example:
void setController0() {
helmetCamera.SetActive(true);
currentController = 0;
setController();
}
void setController1() {
helmetCamera.SetActive(false);
currentController = 1;
setController();
}
void setController() {
for( int i = 0; i < playerControllerList.Length; i++ ){
playerControllerList[i].SetActive(i == currentController);
}
}
This allows me to put in extra toggles and actions in the initial "setControllerX" function (like enabling or disabling the "helmetCamera" as above, etc). However, with your script, I will be able to put these Character-specific toggles within Controller's Enable$$anonymous$$e() function, and have each on/off property controlled by Boolean variables within each Controller.
Your solution may prove to be more useful, as each Controller's options could be easily ticked on and off in the inspector.
Your answer
Follow this Question
Related Questions
Switching from GameObject to GameObject by using a list 1 Answer
Changing player model with keypress. 3 Answers
Switch turns between the Player and AI 0 Answers
View and control script broken? 1 Answer
Need help with fixing a code 1 Answer