Accesing the position of a prefab (Photon Prefab)

Hello everyone, Im trying to make a online multiplayer game with photon networking and i have the player prefab. But at the same time im trying to make infinte, procedural world generation. In order to do that i need to get the transform of the player. But it is a prefab and i can’t get the transform of the player. Please help me. Kind Regards, Cesurix Here is my code:

using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using UnityEngine;
 
 public class TerrainController : MonoBehaviour {
 
     [SerializeField]
     private GameObject terrainTilePrefab = null;
     [SerializeField]
     private Vector3 terrainSize = new Vector3(20, 1, 20);
     public Vector3 TerrainSize { get { return terrainSize; } }
     [SerializeField]
     private Gradient gradient;
     [SerializeField]
     private float noiseScale = 3, cellSize = 1;
     [SerializeField]
     private int radiusToRender = 5;
     [SerializeField]
     private Transform[] gameTransforms;
     [SerializeField]
     private Transform playerTransform;
     [SerializeField]
     private Transform water;
     [SerializeField]
     private float uvScale = 1;
 
     public Transform Water { get { return water; } }
     [SerializeField]
     private int seed;
     [SerializeField]
     private GameObject[] placeableObjects;
     public GameObject[] PlaceableObjects { get { return placeableObjects; } }
     [SerializeField]
     private Vector3[] placeableObjectSizes;//the sizes of placeableObjects, in matching order
     public Vector3[] PlaceableObjectSizes { get { return placeableObjectSizes; } }
     [SerializeField]
     private int minObjectsPerTile = 0, maxObjectsPerTile = 20;
     public int MinObjectsPerTile { get { return minObjectsPerTile; } }
     public int MaxObjectsPerTile { get { return maxObjectsPerTile; } }
     [SerializeField]
     private float destroyDistance = 1000;
     [SerializeField]
     private bool usePerlinNoise = true;
     [SerializeField]
     private Texture2D noise;
     public static float[][] noisePixels;
 
     private Vector2 startOffset;
 
     private Dictionary<Vector2, GameObject> terrainTiles = new Dictionary<Vector2, GameObject>();
 
     private Vector2[] previousCenterTiles;
     private List<GameObject> previousTileObjects = new List<GameObject>();
     public Transform Level { get; set; }
     private Vector2 noiseRange;
 
     private void Awake() {
         if (noise)
             noisePixels = GetGrayScalePixels(noise);
         GenerateMesh.UsePerlinNoise = usePerlinNoise;
         noiseRange = usePerlinNoise ? Vector2.one * 256 : new Vector2(noisePixels.Length, noisePixels[0].Length);
     }
 
     private void Start() {
         InitialLoad();
     }
 
     public void InitialLoad() {
         DestroyTerrain();
 
         Level = new GameObject("Level").transform;
         water.parent = Level;
         playerTransform.parent = Level;
         foreach (Transform t in gameTransforms)
             t.parent = Level;
 
         float waterSideLength = radiusToRender * 2 + 1;
         water.localScale = new Vector3(terrainSize.x / 10 * waterSideLength, 1, terrainSize.z / 10 * waterSideLength);
 
         Random.InitState(seed);
         //choose a random place on perlin noise
         startOffset = new Vector2(Random.Range(0f, noiseRange.x), Random.Range(0f, noiseRange.y));
         RandomizeInitState();
     }
 
     private void Update() {
         //save the tile the player is on
         Vector2 playerTile = TileFromPosition(playerTransform.localPosition);
         //save the tiles of all tracked objects in gameTransforms (including the player)
         List<Vector2> centerTiles = new List<Vector2>();
         centerTiles.Add(playerTile);
         foreach (Transform t in gameTransforms)
             centerTiles.Add(TileFromPosition(t.localPosition));
 
         //if no tiles exist yet or tiles should change
         if (previousCenterTiles == null || HaveTilesChanged(centerTiles)) {
             List<GameObject> tileObjects = new List<GameObject>();
             //activate new tiles
             foreach (Vector2 tile in centerTiles) {
                 bool isPlayerTile = tile == playerTile;
                 int radius = isPlayerTile ? radiusToRender : 1;
                 for (int i = -radius; i <= radius; i++)
                     for (int j = -radius; j <= radius; j++)
                         ActivateOrCreateTile((int)tile.x + i, (int)tile.y + j, tileObjects);
                 if (isPlayerTile)
                     water.localPosition = new Vector3(tile.x * terrainSize.x, water.localPosition.y, tile.y * terrainSize.z);
             }
             //deactivate old tiles
             foreach (GameObject g in previousTileObjects)
                 if (!tileObjects.Contains(g))
                     g.SetActive(false);
 
             //destroy inactive tiles if they're too far away
             List<Vector2> keysToRemove = new List<Vector2>();//can't remove item when inside a foreach loop
             foreach (KeyValuePair<Vector2, GameObject> kv in terrainTiles) {
                 if (Vector3.Distance(playerTransform.position, kv.Value.transform.position) > destroyDistance && !kv.Value.activeSelf) {
                     keysToRemove.Add(kv.Key);
                     Destroy(kv.Value);
                 }
             }
             foreach (Vector2 key in keysToRemove)
                 terrainTiles.Remove(key);
 
             previousTileObjects = new List<GameObject>(tileObjects);
         }
 
         previousCenterTiles = centerTiles.ToArray();
     }
 
     //Helper methods below
 
     private void ActivateOrCreateTile(int xIndex, int yIndex, List<GameObject> tileObjects) {
         if (!terrainTiles.ContainsKey(new Vector2(xIndex, yIndex))) {
             tileObjects.Add(CreateTile(xIndex, yIndex));
         } else {
             GameObject t = terrainTiles[new Vector2(xIndex, yIndex)];
             tileObjects.Add(t);
             if (!t.activeSelf)
                 t.SetActive(true);
         }
     }
 
     private GameObject CreateTile(int xIndex, int yIndex) {
         GameObject terrain = Instantiate(
             terrainTilePrefab,
             Vector3.zero,
             Quaternion.identity,
             Level
         );
         //had to move outside of instantiate because it's a local position
         terrain.transform.localPosition = new Vector3(terrainSize.x * xIndex, terrainSize.y, terrainSize.z * yIndex);
         terrain.name = TrimEnd(terrain.name, "(Clone)") + " [" + xIndex + " , " + yIndex + "]";
 
         terrainTiles.Add(new Vector2(xIndex, yIndex), terrain);
 
         GenerateMesh gm = terrain.GetComponent<GenerateMesh>();
         gm.TerrainSize = terrainSize;
         gm.Gradient = gradient;
         gm.NoiseScale = noiseScale;
         gm.CellSize = cellSize;
         gm.UVScale = uvScale;
         gm.NoiseOffset = NoiseOffset(xIndex, yIndex);
         gm.Generate();
 
         Random.InitState((int)(seed + (long)xIndex * 100 + yIndex));//so it doesn't form a (noticable) pattern of similar tiles
         PlaceObjects po = gm.GetComponent<PlaceObjects>();
         po.TerrainController = this;
         po.Place();
         RandomizeInitState();
 
         return terrain;
     }
 
     private Vector2 NoiseOffset(int xIndex, int yIndex) {
         Vector2 noiseOffset = new Vector2(
             (xIndex * noiseScale + startOffset.x) % noiseRange.x,
             (yIndex * noiseScale + startOffset.y) % noiseRange.y
         );
         //account for negatives (ex. -1 % 256 = -1, needs to loop around to 255)
         if (noiseOffset.x < 0)
             noiseOffset = new Vector2(noiseOffset.x + noiseRange.x, noiseOffset.y);
         if (noiseOffset.y < 0)
             noiseOffset = new Vector2(noiseOffset.x, noiseOffset.y + noiseRange.y);
         return noiseOffset;
     }
 
     private Vector2 TileFromPosition(Vector3 position) {
         return new Vector2(Mathf.FloorToInt(position.x / terrainSize.x + .5f), Mathf.FloorToInt(position.z / terrainSize.z + .5f));
     }
 
     private void RandomizeInitState() {
         Random.InitState((int)System.DateTime.UtcNow.Ticks);//casting a long to an int "loops" it (like modulo)
     }
 
     private bool HaveTilesChanged(List<Vector2> centerTiles) {
         if (previousCenterTiles.Length != centerTiles.Count)
             return true;
         for (int i = 0; i < previousCenterTiles.Length; i++)
             if (previousCenterTiles _!= centerTiles*)*_

return true;
return false;
}

public void DestroyTerrain() {
water.parent = null;
playerTransform.parent = null;
foreach (Transform t in gameTransforms)
t.parent = Level;
Destroy(Level);
terrainTiles.Clear();
}

private static string TrimEnd(string str, string end) {
if (str.EndsWith(end))
return str.Substring(0, str.LastIndexOf(end));
return str;
}

public static float[][] GetGrayScalePixels(Texture2D texture2D) {
List grayscale = texture2D.GetPixels().Select(c => c.grayscale).ToList();

List<List> grayscale2d = new List<List>();
for (int i = 0; i < grayscale.Count; i += texture2D.width)
grayscale2d.Add(grayscale.GetRange(i, texture2D.width));

return grayscale2d.Select(a => a.ToArray()).ToArray();
}

}

remove the InitialLoad() call from your Start()

then add this to your Update()

private void Update() {
if (!playerTransform)
{
    playerTransform = GameObject.FindWithTag("Player");
    if (playerTransform)
        InitialUpdate();

    if (!playerTransform)
        return;
}

… then the rest of your Update code.

Basically this will wait until the player is in the scene to start it’s processing, you can use this trick any time you have something that is waiting on something else. This assumes your Player is never destroyed otherwise the InitialPlayer would be called more than once. If you allow the local player to get destroyed then you will want to add a boolean and set it, so you also check something like if (playerTransform && !doneInitialLoad). Also, the you will need to change the Tags on the non-local players to be something else like RemotePlayer at the point that non-local player are spawned in via Photon.