- Home /
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[i] != centerTiles[i])
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<float> grayscale = texture2D.GetPixels().Select(c => c.grayscale).ToList();
List<List<float>> grayscale2d = new List<List<float>>();
for (int i = 0; i < grayscale.Count; i += texture2D.width)
grayscale2d.Add(grayscale.GetRange(i, texture2D.width));
return grayscale2d.Select(a => a.ToArray()).ToArray();
}
}
Answer by GetLitGames · Jun 20, 2021 at 03:22 PM
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.
I tried to put your code to the update but it says that GameObject can not be a transform.
then the one line should be GameObject.FindWithTag("Player").transform instead. You probably need more time to learn more about C# and Unity but I hope that I helped. Keep at it and you will learn.
Thank you very much that worked just like a charm. I can't find a proper way to thank you. You just saved my life(probably 10 days from my life) THANK YOU :)
Your answer
Follow this Question
Related Questions
Photon Pun 2 "using photon.pun" not working. 8 Answers
[PHOTON PUN] Players cant shoot each other? 1 Answer
How do I set up multiplayer? 0 Answers
Join Error photon 0 Answers