Author Topic: First person controller not working as expected  (Read 10627 times)

toreau

  • Newbie
  • *
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 23
    • View Profile
First person controller not working as expected
« on: May 10, 2014, 11:48:21 AM »
I almost feel ashamed of posting this, but here goes.

I'm in the process of converting my code from Photon to TNet, and I instantly encountered a problem with my first person player. The problem itself is well-known; I do a build, connect, the player works just fine. I go back to Unity and connect from there, everything works fine. Back to the build, and now I control the other player.

I have searched this forum for any clues, but every working piece of code "logic" I find, it makes me just think that my code should work.

Anyway, here it is:

  1. using UnityEngine;
  2. using TNet;
  3.  
  4. public class PlayerController : TNBehaviour {
  5.  
  6.         static public PlayerController instance;
  7.  
  8.         #region MouseLook
  9.  
  10.         private float mouseSensitivity  =   2.0f;
  11.         private float upDownRotation    =   0.0f;
  12.         private float minUpDownRotation = -70.0f;
  13.         private float maxUpDownRotation =  70.0f;
  14.    
  15.     #endregion
  16.  
  17.         #region Movement
  18.  
  19.         private float walkingSpeed   =   6.0f;
  20.         private float runningSpeed   =  12.0f;
  21.         private float crouchingSpeed =   2.0f;
  22.         private float currentSpeed   =   0.0f;
  23.  
  24.         private float jumpHeight    = 5.0f;
  25.         private float gravityForce  = 9.8f;
  26.         private float verticalSpeed = 0.0f;
  27.  
  28.         private bool isRunning   = false;
  29.         private bool isCrouching = false;
  30.  
  31.         #endregion
  32.  
  33.         private CharacterController characterController;
  34.         private Camera              playerCamera;
  35.  
  36.         void Awake () {
  37.                 if ( TNManager.isThisMyObject ) {
  38.                         instance = this;
  39.                 }
  40.     }
  41.  
  42.         void Start () {
  43.                 if ( !tno.isMine ) {
  44.                         this.enabled = false;
  45.         }
  46.  
  47.                 characterController = GetComponent<CharacterController>();
  48.                 playerCamera        = GetComponentInChildren<Camera>();
  49.     }
  50.  
  51.         void Update () {
  52.                 if ( !tno.isMine ) {
  53.                         return;
  54.                 }
  55.  
  56.                 UpdateMouseLook();
  57.                 UpdateMovement();
  58.     }
  59.  
  60.         void UpdateMouseLook () {
  61.                 float leftRightRotation = Input.GetAxis ("Mouse X") * mouseSensitivity;
  62.                 characterController.transform.Rotate ( 0, leftRightRotation, 0 );
  63.                
  64.                 upDownRotation -= Input.GetAxis ("Mouse Y") * mouseSensitivity;
  65.                 upDownRotation  = Mathf.Clamp ( upDownRotation, minUpDownRotation, maxUpDownRotation );
  66.                
  67.                 playerCamera.transform.localRotation = Quaternion.Euler ( upDownRotation, 0, 0 );
  68.     }
  69.  
  70.         void UpdateMovement () {
  71.  
  72.                 // Decide speed
  73.                 isRunning    = ( Input.GetKey(KeyCode.LeftShift) ) ? true : false;
  74.                 currentSpeed = ( isRunning ) ? runningSpeed : walkingSpeed;
  75.                
  76.                 // Crouching speed?
  77.                 if ( isCrouching ) {
  78.                         currentSpeed = crouchingSpeed;
  79.                 }
  80.                
  81.                 // Gravity
  82.                 verticalSpeed -= gravityForce * Time.deltaTime;
  83.                 verticalSpeed = Mathf.Clamp ( verticalSpeed, -3000, jumpHeight ); // TODO: Do this in a better way
  84.  
  85.                 // Things that can only be done while on the ground
  86.                 if ( characterController.isGrounded ) {
  87.                        
  88.                         // Are we jumping?
  89.                         if ( Input.GetButtonDown ("Jump") ) {
  90.                                 verticalSpeed = jumpHeight;
  91.                         }
  92.                        
  93.                         // Are we crouching?
  94.                         if ( Input.GetKey (KeyCode.LeftControl) ) {
  95.                                 if ( !isCrouching ) {
  96.                                         characterController.height /= 2;
  97.                                        
  98.                                         isCrouching = true;
  99.                                 }
  100.                         }
  101.                         else {
  102.                                 if ( isCrouching ) {
  103.                                         characterController.height *= 2;
  104.                                        
  105.                                         Transform characterTransform = characterController.transform;
  106.                                         characterTransform.position = new Vector3( characterTransform.position.x, characterTransform.position.y + (characterController.height / 2), characterTransform.position.z );
  107.                                        
  108.                                         isCrouching = false;
  109.                                 }
  110.                         }
  111.                        
  112.                 }
  113.         // Things that can only be done while NOT on the ground
  114.         else {
  115.             // TODO
  116.         }
  117.        
  118.         // Calculate speed
  119.         Vector3 motion = new Vector3 (Input.GetAxis ("Horizontal") * currentSpeed, verticalSpeed, Input.GetAxis ("Vertical") * currentSpeed);
  120.        
  121.         motion = characterController.transform.rotation * motion;
  122.        
  123.         // Do the actual movement
  124.         characterController.Move ( motion * Time.deltaTime );
  125.  
  126.         }
  127.  
  128. }
  129.  

Everything is AutoSync'ed at the moment (I will convert to RFCs when I'm sure things are working as expected). I'm sure I'm overlooking something super-obvious, but I can't for the life of it see what.
« Last Edit: May 10, 2014, 12:12:13 PM by toreau »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: First person controller not working as expected
« Reply #1 on: May 10, 2014, 11:34:45 PM »
Is AutoSync set to "only owner can sync"?

The script itself seems fine, but you do have some redundancy in there. You seem to be checking for ownership in 3 places, when I'd suggest doing it only in one place (Update).

Also all this will only work if you are instantiating the object that has this script via TNManager.Create. Otherwise all those checks you have are not going to give the right values.

TNAutoCreate would do this correctly, for example.

toreau

  • Newbie
  • *
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 23
    • View Profile
Re: First person controller not working as expected
« Reply #2 on: May 11, 2014, 05:14:14 AM »
The redundant checks are merely proofs of my desperation. ;D

The AutoSync is set to true, and the scene has a PlayerSpawn gameobject with a TNAutoCreate component that is set to create a Player prefab. Persistence is turned off.

The player prefab itself has a CharacterController, a TNObject, a TNAutoSync (which syncs position and rotation), and it has this PlayerController attached to it. I've also tried commenting out parts in case I mess with all the auto-stuff going on, and keep the tno.isMine check only in Update(), but it doesn't make any difference;

  1.         void Awake () {
  2. //              if ( TNManager.isThisMyObject ) {
  3. //                      instance = this;
  4. //              }
  5.     }
  6.  
  7.         void Start () {
  8. //              if ( !tno.isMine ) {
  9. //                      this.enabled = false;
  10. //        }
  11.  
  12.                 characterController = GetComponent<CharacterController>();
  13.                 playerCamera        = GetComponentInChildren<Camera>();
  14.     }
  15.  
  16.         void Update () {
  17.                 if ( !tno.isMine ) {
  18.                         return;
  19.                 }
  20.  
  21.                 UpdateMouseLook();
  22.                 UpdateMovement();
  23.     }
  24.  

Any ideas?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: First person controller not working as expected
« Reply #3 on: May 11, 2014, 08:40:06 AM »
My guess... is that you never checked "run in background" option in build settings, so when you tab to Unity, you effectively disconnect from the server, so you end up controlling both objects. Ownership is transferred to the remaining player. However, thinking about it... you said persistence is off, so technically the object should be destroyed when the player gets DC'd. Have you tried keeping visual track of who's currently in the game? If you start up TNServer.exe and connect to that, you should have a clear idea of when people connect and leave. You can also NGUIDebug.Log values to the screen if you need.

One more thing... what IDs do you get for your two created objects, did you check that? If the IDs are static, then that may explain the issue as well as two objects would share the same ID. All prefabs that you instantiate should start with the ID of 0, letting TNet choose a dynamic value.

toreau

  • Newbie
  • *
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 23
    • View Profile
Re: First person controller not working as expected
« Reply #4 on: May 11, 2014, 09:14:37 AM »
"Run in Background" is checked, AND the players are assigned different IDs (because the prefab is set to ID = 0), AND I can see the other player(s) without any problems. :-\

toreau

  • Newbie
  • *
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 23
    • View Profile
Re: First person controller not working as expected
« Reply #5 on: May 11, 2014, 09:35:08 AM »
The only interesting (?) thing I can see going, is that Awake() and Start() is called twice when I play the game in Unity3D, ie. with only ONE player. I assume that's not normal?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: First person controller not working as expected
« Reply #6 on: May 11, 2014, 02:02:06 PM »
That can only happen if you have two objects, or the script was added twice.

toreau

  • Newbie
  • *
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 23
    • View Profile
Re: First person controller not working as expected
« Reply #7 on: May 12, 2014, 12:42:38 AM »
...and it seems to be because I have a GameObject with a TNManager component with the player prefab attached to it, AND a GameObject with a TNAutoCreate component with the player prefab attached to it. If I disable one or the another, things stops working of course.

This approach is what I got from the Youtube tutorial, but it's clearly not 100% correct...?

djray2k

  • Jr. Member
  • **
  • Thank You
  • -Given: 4
  • -Receive: 4
  • Posts: 74
  • Blockey Hockey!
    • View Profile
    • Walking Talking Grilling Bear Studios
Re: First person controller not working as expected
« Reply #8 on: May 12, 2014, 09:25:06 AM »
There has to be something else with your approach, because that's a correct way to get an object created at the launch of the scene.

What script is calling the awake function twice? Could you right click the script in the project tab a click Find References in Scene and confirm that it's only added once?

I'd also try creating a temporary scene with only the prefab that gets autosync'd to try to isolate the problem and make sure it's not something else going wrong.

toreau

  • Newbie
  • *
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 23
    • View Profile
Re: First person controller not working as expected
« Reply #9 on: May 12, 2014, 10:17:17 AM »
There are only two objects that references the Player;

* TNJoin, which has TNManager with the Player prefab attached to it, and TNAutoJoin.
* PlayerSpawn which has TNAutoCreate with the Player prefab attached to it.

When I watched the Youtube-tutorial, I immediately thought it was strange that the prefab is referenced two places, but what do I know. :) I can also tell you that - for som unknown reason - Awake() is suddenly only called once. So there is some progress, at least. :)

But! Whenever I fire up a build, and then play from Unity3D, both clients control the LAST player joined the "Unity Player" works as it should, while the "Build Player" controls the "Unity Player".

I just want to make sure that this in my PlayerController (attached to the Player prefab) is the proper way:

  1. static public PlayerController instance;
  2.  
  3. void Awake () {
  4.     if ( TNManager.isThisMyObject ) {
  5.         instance = this;
  6.     }
  7. }
  8.  
  9. void Update () {
  10.     if ( !tno.isMine ) {
  11.         return;
  12.     }
  13. }
  14.  

Why are there two very similar attributes here, actually? Or are they the same? Should I use both etc.?

Thanks again!
« Last Edit: May 12, 2014, 10:56:43 AM by toreau »

djray2k

  • Jr. Member
  • **
  • Thank You
  • -Given: 4
  • -Receive: 4
  • Posts: 74
  • Blockey Hockey!
    • View Profile
    • Walking Talking Grilling Bear Studios
Re: First person controller not working as expected
« Reply #10 on: May 12, 2014, 12:00:56 PM »
I don't use tno.isMine personally. At least from what I've learned (mostly from the Aren's youtube video) I create a property called "IsLocal."  I check this to make sure each client is only updating his own object.

  1. public class MyPlayer
  2. {
  3.  
  4. public static MyPlayer localPlayer;
  5.  
  6. ...
  7.  
  8. public bool IsLocal
  9. {
  10.         get
  11.         {
  12.             return localPlayer == this;
  13.         }
  14. }
  15.  
  16. ...
  17.  
  18. }
  19.  

I know that TNManager.IsThisMyObject should only be used in the awake call to determine if it's been created by the TNManager. Quickly checking the source for tno.IsMine, it actually uses TNManager.IsThisMyObject to determine if it is true or not, so I'm guessing that it should only be used within the awake call as well. I'm not sure about that though.

I wish I could help more, that's all I know right now.

toreau

  • Newbie
  • *
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 23
    • View Profile
Re: First person controller not working as expected
« Reply #11 on: May 12, 2014, 12:43:33 PM »
As it is right now, I think I have to give up on TNet. :(

I got rid of all the auto-stuff and wrote everything myself, ie. connect to server, create player etc. etc., but the behaviour is exactly the same as before.

TNet's lack of documentation - and lack of proper examples - makes it far more convenient to go for one of the more mature networking libraries out there, but I really want TNet to work because it looks so good.

So - unless anyone else have some brilliant ideas, or a simple example of an FPS controller, then I need to move on. Until TNet 2.0 is out, of course. ;)

djray2k

  • Jr. Member
  • **
  • Thank You
  • -Given: 4
  • -Receive: 4
  • Posts: 74
  • Blockey Hockey!
    • View Profile
    • Walking Talking Grilling Bear Studios
Re: First person controller not working as expected
« Reply #12 on: May 12, 2014, 01:29:50 PM »
Would you be willing to send me your project to see if I could find what's going on?

djray2k

  • Jr. Member
  • **
  • Thank You
  • -Given: 4
  • -Receive: 4
  • Posts: 74
  • Blockey Hockey!
    • View Profile
    • Walking Talking Grilling Bear Studios
Re: First person controller not working as expected
« Reply #13 on: May 12, 2014, 03:12:00 PM »
Got the files, but I couldn't actually run it. I think that's probably because I don't have the Daikon Forge stuff.

Anyways, I noticed you have two TNManagers. One in Connection, and one in TNJoin, and I assume the one in Connection is the intended manager to have because you placed your prefab inside of that one. I suggest you remove the one in the TNJoin object.

There's also a PlayerSpawn object with a TNAuto Create script, but you didn't place a prefab in the script for it to use (The Player prefab, presumably). That script will automatically create the prefab once the game is set up.

It looks like you're a little confused by how TNet works. I don't know if you tried the youtube tutorial Aren made, but it's very helpful and explains everything.

toreau

  • Newbie
  • *
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 23
    • View Profile
Re: First person controller not working as expected
« Reply #14 on: May 12, 2014, 03:23:48 PM »
Keep in mind that both the TNJoin and the PlayerSpawn objects are disabled; I started out with them from the tutorial, and they hold the AutoConnect stuff. All of that logic has been moved to the Connection object instead.