Start() being called 2 times with Photon - c#

I'm making a multiplayer system. I have a strange situation about Start(). When the first player joins there is 2 logs "player joined". So, when second player joins, their model instantiates twice (there is PhotonNetwork.Instantiate in Start()). I guess Start() is being called 2 times somehow?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using TMPro;
public class MPRoomServer : MonoBehaviour
{
public GameObject rocket;
public TMP_Text title;
// Start is called before the first frame update
void Start()
{
PhotonNetwork.Instantiate(rocket.name, new Vector3(20.4928417f,4.3900001f,-10.116004f), Quaternion.identity);
title.text = "Room " + PhotonNetwork.CurrentRoom.Name;
Debug.Log("New player joined");
}
// Update is called once per frame
void Update()
{
}
}

OnJoinedRoom() works even when you create a room. So, my scene was loaded 2 times, and the script was used 2 times.

Related

Accessing centerEyeRotation (face tracking) from TrackedPoseDriver in Unity

I am using ARKit within Unity to implement face tracking on an iOS device. Face tracking is working fine, and I want to access the real-time centerEyeRotation measurements produced by the face tracking. Following this helpful post
I used the code below to try and at least access the TrackedPoseDriver, but I keep getting back the data from the Main Camera (the game object that this script is attached to). Any idea how I can access the centerEyeRotation data?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class getTrackedPoseDriver : MonoBehaviour
{
bool found;
MonoBehaviour mbTrackingPose;
MonoBehaviour mb;
// Start is called before the first frame update
void Start()
{
Component[] components = Camera.main.GetComponents(typeof(MonoBehaviour));
found = false;
foreach (Component component in components)
{
string name = component.ToString();
mb = (MonoBehaviour)component;
if (name.Contains("UnityEngine.InputSystem.XR.TrackedPoseDriver"))
{
mbTrackingPose = mb;
found = true;
Debug.Log(found);
Debug.Log(mbTrackingPose.name);
break;
}
}
}
// Update is called once per frame
void Update()
{
Debug.Log(mbTrackingPose.transform.rotation.y);
}
}
I also attempted a different approach (see below). This script (attached to the Main Camera) does confirm XRNode.CenterEye as valid, but device.TryGetFeatureValue fails to get centerEyePosition.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
public class centerEyeRotation : MonoBehaviour
{
Quaternion centEyeRot;
Vector3 centEyePos;
//Quaternion rotation;
bool tryGet;
// Start is called before the first frame update
void Start()
{
tryGet = false;
InputDevice device = InputDevices.GetDeviceAtXRNode(XRNode.CenterEye);
if (device.isValid)
{
Debug.Log("Device is valid");
if (device.TryGetFeatureValue(CommonUsages.centerEyePosition, out centEyePos))
tryGet = true;
Debug.Log("centerEyePosition found");
}
}
}
I may have solved the problem via a different route; the project instantiates an AR face prefab object at runtime. By attaching a script to this prefab I was able to report the transform of the prefab, which seems to be giving me the orientation data I need.

Trying to add a delay to an gameobject spawner [duplicate]

This question already has answers here:
How to make the script wait/sleep in a simple way in unity
(7 answers)
Closed 4 months ago.
I'm a new C# programmer here in the early stages of creating a project in Unity where you play as a microbe that needs to eat blue food pellets to grow and survive. I've got the blue food pellets to spawn randomly across the map but I want to add a delay because too much is spawning at once. This is what I've attempted to do so far. Any help would be appreciated!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks;
public class Spawner : MonoBehaviour
{
public GameObject food;
public async void Wait(float duration)
{
Vector3 randomSpawnPosition = new Vector3(Random.Range(-50, 50), 1, Random.Range(-50, 50));
Instantiate(food, randomSpawnPosition, Quaternion.identity);
await Task.Delay((int)duration * 1000);
}
// Update is called once per frame
void Update()
{
async void Wait(float duration);
}
}
What I've tried:
Putting the delay function in the update function. The program just gets confused and thinks I'm trying to call the function.
Calling the function after combining all my code into the one function, the program rejects this.
Like the default code snippet says, Update runs every frame. Using Task.Delay to delay events would still spawn objects with the same frequency, they would only start spawning delayed.
The typical way to do this in Unity is to use a coroutine.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour
{
[SerializeField] GameObject food;
protected void OnEnable()
{
StartCoroutine(SpawnFoodRoutine());
}
IEnumerator SpawnFoodRoutine()
{
while(enabled)
{
SpawnFood();
var waitTime = Random.Range(1f, 5f);
yield return new WaitForSeconds(waitTime);
}
}
void SpawnFood()
{
Vector3 randomSpawnPosition = new Vector3(
Random.Range(-50f, 50f),
1f,
Random.Range(-50f, 50f));
Instantiate(food, randomSpawnPosition, Quaternion.identity);
}
}
I have made this prefab called “pellet”, then I have created a new gameObject called “SpawnManager” and, finally, added this script to SpawnManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnPellet : MonoBehaviour
{
public GameObject prefab;
public bool canSpawnNewPellet = true;
public float delay;
void Update(){
if(canSpawnNewPellet){
Invoke("SpawnNewPellet", delay);
canSpawnNewPellet = false;
}
}
void SpawnNewPellet()
{
GameObject instance = Instantiate(prefab);
instance.transform.position = new Vector3(Random.Range(0,10), Random.Range(0,10), 0);
canSpawnNewPellet = true;
}
}
The I have dragged and dropped/edited values on the fields in the inspector (Prefab, canSpawnNewPellet and Delay). Those are public fields inside the script, so you can populate them drag and dropping directly from your assets folder.
Hit play and you have a new spawn every X seconds where X is the value of delay in seconds.
Screenshot of the game executing after 21 seconds.
What does it do?
Every frame it evaluates if can spawn a new pellet (bool canSpawnNewPellet). If it can, then it starts an invocation of another method with X seconds (and mark the bool as false since we don’t want to call more invocations during de instantiation of our first sample).
For more references: https://docs.unity3d.com/ScriptReference/MonoBehaviour.Invoke.html
Edited: typo.

Make the CC Collider follow the camera (UNITY)

So I made a script that should In theory make the Character Controller's Collider follow the Player camera. Here is the Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
public class CCCameraFollower : MonoBehaviour
{
public GameObject Camera;
public CharacterController character;
// Start is called before the first frame update
void Start()
{
character = GetComponent<CharacterController>();
}
// Update is called once per frame
void LateUpdate()
{
character.center = Camera.transform.position;
}
}
This works Fine/Okay when I try it out, however as soon as I enter Climb() In my Climber script:
void Climb()
{
InputDevices.GetDeviceAtXRNode(climbingHand.controllerNode)
.TryGetFeatureValue(CommonUsages.deviceVelocity, out Vector3 velocity);
character.Move(transform.rotation * -velocity * Time.fixedDeltaTime);
cachedVelocity = -velocity;
Debug.Log(cachedVelocity);
}
When this is Climb() Runs, this happens:
Image that Shows The Issue
I don't see a reason for this to happen, maybe its very obvious. I don't know... Anyways, my question is: "How do I make the Collider of the CC Follow the Player Camera?".
In the CCCameraFollower.LateUpdate() method you are actually moving the center of the character controller and not the position of the Camera. Change to this and it will probably work as you want.
void LateUpdate()
{
Camera.transform.position = character.center;
}

Unity, why so many prefabs are getting generated?

The code is here:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnManager : MonoBehaviour
{
public GameObject[] animalprefabs;
public float spawninterval = 2.5f;
public float spawnDelay = 2.0f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
InvokeRepeating("spawnRandomAnimal",spawnDelay, spawninterval);
}
void spawnRandomAnimal()
{
int animalIndex = Random.Range(0, animalprefabs.Length);
int SpawnIndex = 20;
Vector3 spawnPosition = new Vector3(Random.Range(-SpawnIndex, SpawnIndex), 0, 25);
Instantiate(animalprefabs[animalIndex], spawnPosition ,animalprefabs[animalIndex].transform.rotation);
}
}
I want to instantiate the prefab after certain time interval randomly but somehow tonnes of prefabs are getting generated .I want one prefab to be instantiated at a random place after the time interval ... Someone plzzzz help
You are re-calling invokeRepeating every single time Update() is called. Everytime it gets called it adds another task to repeat.
Move it to start to fix your issue.
void Start()
{
InvokeRepeating("spawnRandomAnimal",spawnDelay, spawninterval);
}
you are using invokerepeating() function in update(). that's why too many prefabs are generated. remove invokerepeating() from update() and try it in start() or onenable().

Still pausing/resuming and start a new game is not working good. Can'r figure what should I do?

First a screenshot of my Hierarchy :
Everything is inside a one scene name The :
The game start when Main Game is disabled. Inside Main Game sit all the game objects. The Main Menu and Game Manager are enabled.
When running the game first time, When the game start there is short animation of the player for 5 seconds. The player start from some rotating degrees on Z. Z = 50 when x and y both are 0. Then the player is rotating slowly over the Z to 0.
It's like the player is sleeping and awake up.
I'm using post processing stack by unity.
And here is the first problem. While the player is rotating on the Z and post processing effect is working if I press the Escape key it will bring me back to the Main Menu but then if I press the Escape key again it will start the game over again from the begin.
But if I'm waiting in my game for the player to finish rotating on the Z and the post processing effect is finished and then pressing on Escape it will bring the main menu and second time will resume the game from the same point.
I can't figure out why when the player is rotating and the post process is working the escape key make it start the game over again from the being ?
This is a screenshot of the game when start and after finish the rotating and the process stack :
Another problem I noticed now. After the game start using the post process and the player rotating finished if I press Escape it will go to main menu and escape again will be back to the game but for example in the second screenshot the conversation is not continue. It will return to the same point in the game but things not seems to continue like the conversation.
On the Back to main menu object I have a script attached to it :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class BackToMainMenu : MonoBehaviour
{
private bool _isInMainMenu = false;
public GameObject mainGame;
public GameObject mainMenu;
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (!_isInMainMenu)
{
// -- Code to freeze the game
mainGame.SetActive(false);
mainMenu.SetActive(true);
}
else
{
// -- Code to unfreeze the game
mainGame.SetActive(true);
mainMenu.SetActive(false);
}
_isInMainMenu = !_isInMainMenu;
}
}
}
On the Main Menu object under Main Menu I have attached this script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainMenu : MonoBehaviour
{
public GameObject mainGame;
public GameObject mainMenu;
public void PlayGame()
{
mainGame.SetActive(true);
mainMenu.SetActive(false);
}
public void QuitGame()
{
Application.Quit();
}
}
On the PLAY button I'm using this script method PlayGame from the MainMenu script.
In the Main Game object on the Player object attached to thew Player I have some scripts the controller :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float speed = 10.0f;
// Update is called once per frame
void Update()
{
float translatioin = Input.GetAxis("Vertical") * speed;
float straffe = Input.GetAxis("Horizontal") * speed;
translatioin *= Time.deltaTime;
straffe *= Time.deltaTime;
transform.Translate(straffe, 0, translatioin);
}
}
Player Lock Manager :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerLockManager : MonoBehaviour
{
public PlayerCameraMouseLook playerCameraMouseLook;
public PlayerController playerController;
// Start is called before the first frame update
public void PlayerLockState(bool LockPlayer, bool LockPlayerCamera)
{
if (LockPlayer == true)
{
playerController.enabled = false;
}
else
{
playerController.enabled = true;
}
if (LockPlayerCamera == true)
{
playerCameraMouseLook.enabled = false;
}
else
{
playerCameraMouseLook.enabled = true;
}
}
}
And Mouse Lock State :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MouseLockState : MonoBehaviour
{
public bool lockState = true;
private void Start()
{
LockState(lockState);
}
private void Update()
{
}
public void LockState(bool lockState)
{
if (lockState == false)
{
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
else
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
}
}
}
Under the Player as child I have the Player Camera object and attached to the Player Camera also some scripts :
Player Camera Mouse Look :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCameraMouseLook : MonoBehaviour
{
public float sensitivity = 5.0f;
public float smoothing = 2.0f;
private GameObject player;
private Vector2 mouseLook;
private Vector2 smoothV;
// Use this for initialization
void Start()
{
player = this.transform.parent.gameObject;
}
// Update is called once per frame
void Update()
{
var md = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
md = Vector2.Scale(md, new Vector2(sensitivity * smoothing, sensitivity * smoothing));
smoothV.x = Mathf.Lerp(smoothV.x, md.x, 1f / smoothing);
smoothV.y = Mathf.Lerp(smoothV.y, md.y, 1f / smoothing);
mouseLook += smoothV;
mouseLook.y = Mathf.Clamp(mouseLook.y, -90f, 90f);
transform.localRotation = Quaternion.AngleAxis(-mouseLook.y, Vector3.right);
player.transform.localRotation = Quaternion.AngleAxis(mouseLook.x, Vector3.up);
}
}
And Depth Of Field script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.PostProcessing;
public class DepthOfField : MonoBehaviour
{
public GameObject player;
public PostProcessingProfile postProcessingProfile;
public bool dephOfFieldFinished = false;
public PlayerLockManager playerLockManager;
private Animator playerAnimator;
private float clipLength;
// Start is called before the first frame update
void Start()
{
playerAnimator = player.GetComponent<Animator>();
AnimationClip[] clips = playerAnimator.runtimeAnimatorController.animationClips;
foreach (AnimationClip clip in clips)
{
clipLength = clip.length;
}
var depthOfField = postProcessingProfile.depthOfField.settings;
depthOfField.focalLength = 300;
StartCoroutine(changeValueOverTime(depthOfField.focalLength, 1, clipLength));
postProcessingProfile.depthOfField.settings = depthOfField;
}
IEnumerator changeValueOverTime(float fromVal, float toVal, float duration)
{
playerLockManager.PlayerLockState(true, true);
float counter = 0f;
while (counter < duration)
{
var dof = postProcessingProfile.depthOfField.settings;
if (Time.timeScale == 0)
counter += Time.unscaledDeltaTime;
else
counter += Time.deltaTime;
float val = Mathf.Lerp(fromVal, toVal, counter / duration);
dof.focalLength = val;
postProcessingProfile.depthOfField.settings = dof;
yield return null;
}
playerAnimator.enabled = false;
dephOfFieldFinished = true;
}
}
I have under Main Game also a object name Openning Scene and attached to it :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OpeningCutscene : MonoBehaviour
{
public NaviConversations naviConversation;
public DepthOfField dephOfField;
// Update is called once per frame
void Update()
{
if (dephOfField.dephOfFieldFinished == true)
{
naviConversation.PlayNaviConversation(0);
dephOfField.dephOfFieldFinished = false;
}
}
}
It's a bit long but everything is connected.
The game start with the main menu.
When clicking the PLAY button a new game start.
When a new game start the script Depth Of Field is in action using the post processing. Also I'm locking the player so the player will not be able to move either the mouse or the player it self.
When the depth of field script finished his work a conversation between Navi and the Player start. When a conversation is in action the player can move the mouse 360 degrees but cant move the player to any direction.
When the conversation ended the player can also move to any direction.
Problems :
When start a new game while the depth of field script is in work pressing escape will bring the main menu but pressing escape again will not resume the depth of field script but will start it all over again.
When waiting the conversation to end if not pressing escape until the conversation ended then when moving around and pressing escape it will bring the main menu and again will resume the game from the same point.
The problems is when the game is doing something like the depth of field it will start the game over again instead resuming or when in conversation in the middle the conversation will never continue.
The ides with the escape key is once to get to main menu and second to resume the game.
The PLAY button is what should only start a new game and not the escape key.
It's a bit long but everything is connected.
I can't see which objects you hooked up to mainGame and mainMenu in BackToMainMenu from your hierarchy, so I have some conjecture in here.
Sounds like you want Escape key to pause, and unpause, as well as bring up a menu, you want Play button in the menu to restart your game.
However, in both MainMenu and BackToMainMenu you use the same code:
mainGame.SetActive(true);
mainMenu.SetActive(false);
This just turns the objects on and off in the hierarchy. This means the first time you do one of these things, all objects turned on under the gameobject referenced by mainGame will run their Awake and Start methods, but not the second time. Also depending which objects are active (like I said I can't fully see what component is on what object and which objects are referenced by which serialized field) you might be able to introduce a state error on BackToMainMenu._isInMainMenu because that field isn't changed when you hit play. Here is a fantastic image showing execution timeline in Unity:
Monobehavior Timeline
In summary:
The second time you run the game DepthOfField won't call Start a second time. Instead try OnEnable.
The conversation and game logic is not shown in your post, but likely it also is initializing in Start and won't run a second time.
A bit of trickiness around Coroutines is likely to blame here. When DepthOfField starts it also starts a coroutine. That coroutine keeps going even if you set the gameobject inactive. So you run the game once, that coroutine starts, you turn the object off when you hit escape, the coroutine finishes, you hit play again, but DepthOfField.dephOfFieldFinished == true and your playerAnimator is disabled and won't enable again.
Also 5. This kind of behavior can be tricky and is usually dependent on what else you have going on in your scene. Since you have one scene with everything at once, you need to watch out for putting stuff in Awake and Start since this will only run once. Instead you can try a number of things, my favorite is usually to set up a singleton or static class that works as a state machine for the whole scene. It will call custom Initialize functions in your behaviors instead of using Awake and Start.
Keep references to your coroutines by doing things like:
private Coroutine depthOfFieldRoutineRef;
private OnEnable()
{
if (depthOfFieldRoutineRef != null)
{
StopCoroutine(depthOfFieldRoutineRef);
}
depthOfFieldRoutineRef =
StartCoroutine(changeValueOverTime
(depthOfField.focalLength, 1, clipLength));
// Don't forget to set depthOfFieldRoutineRef to null again at the end of routine!
}
For simple pausing behavior you can also try setting Time.timescale = 0; and back to 1 when you want play to resume.
Less a question than a set of problems, hopefully this set of solutions helps!
Tutorial on Awake and Start
Docs on Coroutines
Docs on Time.timescale

Categories

Resources