Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Problems Instantiating player characters and setting up their variables and controls.

Discussion in 'Getting Started' started by Tset_Tsyung, Feb 18, 2016.

  1. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    406
    Hey all,

    It's me again, lol. Sorry.

    I'll post the code from the game controller, and then pose the problem:

    Code (CSharp):
    1. void SpawnPlayers(int numberOfPlayers)
    2.     {
    3.         for (int x = 0; x < numberOfPlayers; x++)
    4.         {
    5.             // get a random start point from the list of spare spaw points
    6.             bool spare = false;
    7.             int y = 0;
    8.             while (spare == false)
    9.             {
    10.                 y = Random.Range(0, spareSpawnPoints.Length);
    11.                 spare = spareSpawnPoints [y];
    12.             }
    13. // now instantiate the player at the point y in the spawnpoints array
    14.             players [x] = Instantiate (playerPrefabs [Random.Range (0, playerPrefabs.Length)], spawnPoints [y].position,Quaternion.identity) as GameObject;
    15.  
    16. // state that this spawn point is no longer available
    17.             spareSpawnPoints [y] = false;
    18.  
    19.             Debug.Log ("Setting up player " + (x+1) + " with name Player" + (x+1));
    20.  
    21. // set up the players name - This part works FINE!
    22.             players [x].name = "Player" + (x + 1);
    23.  
    24. // get the player controller script from the instantiated object
    25.             PlayerController playercontroller = players [0].GetComponent<PlayerController> ();
    26.  
    27.             if (!playercontroller)
    28.                 Debug.Log ("Unable to get player controller script reference for player ");
    29.  
    30. // set that scripts playernumber variable. - This part works fine initially, but after second
    31. // character is done, player1's variable is reset to 0...?
    32.             playercontroller.SetPlayerNumber (x+1);
    33.         }
    34.     }
    I'm trying to instantiate 2 player models (just trying to get it local multiplayer atmo), then set up their names and variables. As you can see I have an array called players, which is declared at the start as follow:
    Code (CSharp):
    1. private GameObject[] players;
    . Now that ACTUAL instantiating is fine - I get 2 models at different spawn points. Their names are set correctly as "Player1" and "Player2" accordingly. And INITIALLY, the variables for BOTH players that holds the int representing their player number takes the correct value. So Player1 has a playernumber of 1, Player 2 has 2. Brilliant! This works fine!

    However, as soon as the next frame starts I'm getting a message stating that it can't find 'Player0's' controls. Basically, Player1 get's it's player number reset to 0, whereas as Player2's player number stays as 2...

    Here's the player contoller script (it's been edited for brevity - can include full script if required)

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [System.Serializable]
    5. public class PlayerController : MonoBehaviour {
    6.  
    7. // public movement variables - speed, jump power etc.
    8. // component variables
    9. //booleans for movement modes
    10.  
    11.     private int playerNumber;
    12.  
    13.       // Use this for initialization
    14.     void Start () {
    15. //get rigidody and animator references.
    16. // set initial facing direction
    17.  
    18.     }
    19.  
    20.     // Update is called once per frame
    21.     void Update () {
    22.  
    23.         //////////////////
    24.         ///
    25.         /// Check for ALL inputs
    26.         /// Use the string "Player" plus player number to find correct input
    27.         //////////////////
    28.  
    29.         // check for input.
    30.         hMove = Input.GetAxis("HorizontalP" + playerNumber);
    31.  
    32.         if(hMove != 0f)
    33.             isWalking = true;
    34.  
    35.         // check that the player is on ground for jumping
    36.         if (Input.GetButton ("JumpP" + playerNumber))
    37.         {
    38.             RaycastHit2D hit = Physics2D.Raycast (transform.position, -transform.up, jumpCheckDistance);
    39.             if (hit.transform.CompareTag ("Ground"))
    40.                 jump = true;
    41.  
    42.         }
    43.  
    44.         // now make sure that the character is looking in the right direction
    45.         // if the player is not touching the axis then leave the player as they are
    46.  
    47.         if(Input.GetAxisRaw ("HorizontalP" + playerNumber) != 0f)
    48.             facingRight = Input.GetAxisRaw("HorizontalP" + playerNumber);
    49.          
    50.         if (Input.GetButton ("RunP" + playerNumber))
    51.             isRunning = true;
    52.      
    53.         ///////////////////////////
    54.         //
    55.         // animation adjustment!
    56.         //
    57.         ///////////////////////////
    58.  
    59.         ////////////////////////////
    60.         ///
    61.         ///     Stairsmode placement
    62.         ///
    63.         ////////////////////////////
    64.  
    65.         }
    66.  
    67.      
    68.     }
    69.  
    70.     void FixedUpdate()
    71.     {
    72. //apply forces to character
    73.     }
    74.  
    75.     public void StairsModeActivate(Transform stairsBottom, Transform stairsTop)
    76.     {
    77. //get on stairs    }
    78.  
    79.     public void StairsModeDeactivate()
    80.     {
    81. // get off stairs    }
    82.  
    83.     void Animate ()
    84.     {
    85.         // This function sets the animation for the character based on boolean triggers
    86.     }
    87.  
    88.      
    89.     public void SetPlayerNumber(int numberSet)
    90.     {
    91.         Debug.Log ("In SetPlayerNumber.  Setting up this player as number " + numberSet + " at time " + Time.time);
    92.         playerNumber = numberSet;
    93.         Debug.Log ("Player is now Player " + playerNumber + "with an id of " + gameObject.GetInstanceID());
    94.     }
    I understand this may be a tricky one, but any help would be greatly appreciated. I'm not exactly 100% when it comes to instantiating characters and the like. I went through the ENTIRE tanks tutorial - which is where I got the idea of using a string comprised of a keyword and a variable to grab the correct input (Player2's input works fine FYI), but I noticed that they use an array of scripts and attached instantiated objects to them. I instead are instantiating the objects WITH the scripts already attached - have I gone down the wrong methodology?

    Many regards, Mike
     
  2. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    406
    I should also point out that both scripts have the same instance ID, according to the last line in the playercontroller script... no idea why - I'm going to get coffee, lol.
     
  3. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    406
    okay, I was wrong...

    Player2's player number is resetting to 0, whilst player1's player number is getting turned into 2...

    Guess: Not separating the instantiated scripts properly... or not even instantiating properly...
     
  4. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    Why are you using 0 instead of x here?
    Code (csharp):
    1.  
    2. // get the player controller script from the instantiated object
    3.             PlayerController playercontroller = players [0].GetComponent<PlayerController> ();
     
    Schneider21 likes this.
  5. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    406
    Yes... I realised that... I believe I'd changed that TO 0 for testing purposes. But I can't be 100% as, well, I'm an idiot... lol

    But I've just finished some testing. I decided, after reading another post or 2, to ADD the scripts after the instatiating, by using .AddComponent. This seems to have solved it. And I now have to character able to run round the test scene! XD

    Thank you for the reply Joe, I appreciate the post ;)
     
  6. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    I'm glad you got it working, but I would caution you against drawing the wrong lesson here.

    The "standard" way to do this sort of thing, is to have all the scripts you need already on the prefab, and then just instantiate the prefab. This brings a lot of advantages, such as allowing you to easily tweak the properties of those scripts in the editor.

    The problem you observed, I believe, was caused by a simple bug in your code — you got the PlayerController of instance 0 in both cases, and since that's what you used to assign the player number, of course they ended up with the wrong player numbers. By rewriting your code to add the scripts after, you eliminated the bug... but of course you could have very simply fixed the bug with the first approach, too.

    A better lesson to draw here is: simplify your code. Don't repeat yourself. Your code above uses player[x] multiple times, and in one place, you messed up, introducing a bug. If you had designed your code under the KISS and DRY (Don't Repeat Yourself) principles, this would have been avoided. It would look something like this:

    Code (CSharp):
    1.     void SpawnPlayers(int numberOfPlayers) {
    2.         for (int x = 0; x < numberOfPlayers; x++) {
    3.             players[x] = SpawnPlayer(x+1);
    4.         }
    5.     }
    6.  
    7.     GameObject SpawnPlayer(int playerNum) {
    8.         Vector3 where = FindSpawnPoint();
    9.         GameObject prefab = playerPrefabs[Random.Range(0, playerPrefabs.Length)];
    10.         GameObject noob = Instantiate(prefab, where, Quaternion.identity) as GameObject;
    11.         noob.GetComponent<PlayerController>().SetPlayerNumber(playerNum);
    12.         return noob;
    13.     }
    14.  
    15.     Vector3 FindSpawnPoint() {
    16.         // get a random start point from the list of spare spawn points
    17.         while (true) {
    18.             i = Random.Range(0, spareSpawnPoints.Length);
    19.             if (spareSpawnPoints[i]) {
    20.                 spareSpawnPoints[i] = false;    // this one is no longer available
    21.                 return spawnPoints[i];
    22.             }
    23.         }
    24.     }
    See how simple it is this way — SpawnPlayers is only 3 lines long! Hard to screw that up. And each of the helper functions is similarly short and sweet, with only one job to do. And with this approach, the original bug could not have occurred — players[x] only appears in exactly one place, where you assign to it. SpawnPlayer, which actually spawns and configures the player, refers only to its new instance ("noob") and so can't get confused and refer to something else.
     
    Tset_Tsyung and Schneider21 like this.
  7. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    Listen to @JoeStrout, of course. He'll never steer you wrong.

    But also, understand that you have to write thousands of lines of crappy code before you'll start to write "good code". It comes with experience. Like Joe said, make sure you're seeing the actual mistake and not just putting a Band-Aid on it and learning bad habits, but don't let a fear of making mistakes hold you up from writing functional code. As you progress, that dirty and functional code will become a huge PITA to manage, and you'll learn tons as you refactor it to be cleaner, more concise, and reusable.
     
    Tset_Tsyung and JoeStrout like this.
  8. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    406
    Wow, thank you guys!

    I did find a tutorial video on 'Good Coding Practices' in the learn section - not gotten round to checking that our yet, but will do.

    Yeah, I noticed that in the tutorials videos (Tanks, etc.) they liked splitting up their code with functions a lot - I didn't understand what the point was... until now. Lol

    Again, thanks guys. Much appreciated!
     
    JoeStrout likes this.
  9. Deleted User

    Deleted User

    Guest

    I wrote this down physically in a notebook.