Unity Referencing an Instantiated (cloned) object on a Button via Script - c#

My Problem:
I cant get it to work, that every Building has its own SpawnPoint.
I'm working on an RTS like build mechanism. I have set it up that after pressing a key I can place buildings in the scene. The buildings get instantiated from a prefab and after they get instantiated they get a script called "BuildingScript" attached to them. Now i want to implement, so that i get a individual spawn point for every building (for now just next to it). I got a UI set up with a button, which by pressing spawns a unit at the building.
I had the "BuildingScript" attached to the prefab, but when i set the spawn point for one Building, it set it for every Building in the Scene. So a Unit was Spawned always at the same Buidling.
I want to set it up, that every Building has its own spawn point. Thats why I want to give every Building the script "BuildingScript" when Instatiated, because I hope, that this way every script gets handled individually. Is that right? Or will it still set the same point for every building, because the script is still the same?
Also I wanna reference the current placed building to the button, so when its clicked, it will run only the code of the last placed building (for now). I think I cant do this by using "On Click()" Of the Button, because my clone isnt Instatiated yet, so I have to reference the clone to the button somehow via Script, so the button works with the clone.So my problem is, that I need to set a reference from my cloned Building to the Button, after I placed the clone.
I googled a lot on how to do this, but didnt found any answers to my problem besides this https://forum.unity.com/threads/controlling-instantiated-game-objects-from-ui-buttons.332005/.
But I cant get it to work and I think it will not work because my clone is an Object and not a GameObject, so I could never set reference to it to call the funktion SpawnUnit(), because GetComponent only works for a GameObject.
Now I'm really at a point where I just don't know how Unity handles these kind of things.
BuildingScript on the Instantiated Building
public class BuildingScript : MonoBehaviour
{
public bool SavePos = false;
public Vector3 SpawnPoint;
public Vector3 BuildingPos;
public GameObject Unit;
void Start()
{
FindObjectOfType<SpawnButtonReference>().GiveReference(this);
}
public void SpawnUnit()
{
//I did this because if a building gets instatiated i wanted it to save its
Position to Spawn Units from it (doesnt really work though).
"MousePos" ist the last Position of the Mouse in the PlacementScript, before klicking to place the building.
if (SavePos == false)
{
BuildingPos = GameObject.FindObjectOfType<GroundPlacementController>().GetComponent<GroundPlacementController>().MousePos;
Debug.Log(BuildingPos);
SavePos = true;
}
float PosX = BuildingPos.x + 2;
float PosZ = BuildingPos.z + 2;
SpawnPoint = new Vector3(PosX, 0, PosZ);
Debug.Log("Spawn" + SpawnPoint);
Instantiate(Unit, SpawnPoint, Quaternion.identity);
}
}
Script on the Button
public class SpawnButtonReference : MonoBehaviour
{
public GameObject objectReference = null;
internal void GiveReference(BuildingScript Object)
{
objectReference = Object;
}
void OnButtonPress()
{
if (objectReference != null)
{
objectReference.GetComponent<BuildingScript>().SpawnUnit();
}
}
}

So I solved it myself with a little workaround. Instead of trying to reference the clone, I wrote a script on the Spawn Button which searches all Objects with the "BildingScript" then if they are Selected (which can only be one Building) it spawns a Unit at its Spawn point.
The Building itself saves his spawn point when being placed (so when Input.GetMouseButtonUp(0))
works very well for me :)

Related

How do I make an Object active on key down?

I'm using Unity's new input system and I'm trying to set up an input that places a shield in front of you as long as you hold the key down.
At the moment I can make the shield appear in the world but it's fixed in the world and locked to one direction.
Right now this is the code.
private void ShieldUp() {
GameObject shield = GameObject.Instantiate(shieldPrefab, playerFront.position, Quaternion.identity, bulletParent);
}
I created a prefab for the "shield", playerFront being the location where I want it spawn. I think all I've done is make it so every time I hit the shield key it just makes a prefab.
But, like stated before, I want it to only appear while the key is held and deleted when the key is released.
I'd also want it to face the same direction as the character, but one step at a time.
Now the problem with the new input system is there so many ways that you can use it.
so to actually help we need a lot more information about how you use the new input system.
about the code you were showing you are instantiated entire new game object each time you click the Shield button, I would recommend using a GameObject that is a child of the player GameObject and simply activating and deactivating it.
I can give you a example with the old input system.
public class PlayerShield : MonoBehaviour
{
[SerializeField] private GameObject shield;
private void Awake()
{
shield.SetActive(false);
}
void Update()
{
UseShield();
}
private void UseShield()
{
if (Input.GetKeyDown(KeyCode.E))
{
shield.SetActive(true);
}
if (Input.GetKeyUp(KeyCode.E))
{
shield.SetActive(false);
}
}
}

Photon in Unity is instantiating two players for seemingly no reason

I've been trying to implement multiplayer in my game for the past day now with little success. First UNET had no native way to sync scale between clients and now after I've changed over to Photon I've got another problem I can't seem to fix and it is happening for seemingly no reason at all.
When I click play, I wait for a bit and two players are instantiated. I only have one Unity instance open, only one instance of my script that instantiates it and I can't think of anything else in my current testing area that would cause two players to spawn.
Here's the code inside my 'NetworkManager'
const string VERS = "V.0.1";
public GameObject player;
void Start () {
PhotonNetwork.ConnectUsingSettings(VERS);
}
void OnJoinedLobby()
{
RoomOptions roomOptions = new RoomOptions() {IsVisible=false,MaxPlayers=1};
PhotonNetwork.JoinOrCreateRoom("Room1", roomOptions, TypedLobby.Default);
}
void OnJoinedRoom()
{
PhotonNetwork.Instantiate("Player",GameObject.Find("SpawnPoint").transform.position,Quaternion.Euler(0,0,0),0);
}
void Update () {
}
I've made it specifically look for 'Player' instead of using the GameObject variable's transform name incase it was finding two prefabs for some strange reason but it wasn't. A print statement shows that OnJoinedRoom is being called twice which I don't understand why.
I have auto-join lobby switched to true in the photon settings object that's created when you start up. Setting it to false results in nothing being instantiated.
Turns out I also had the script attached to another empty called SpawnPoint and didn't realise!

How to differentiate between an old instantiated object and a new one?

public void PlaceIconToSlot() //gets called by a button
{
GameObject IconClone = Instantiate(Icons[properIconIndex], Slots[properSlotIndex].transform.position, Quaternion.identity);
}
Icons and Slots are Arrays. The first one tells the program what to instantiate and the second one where to instantiate. Quaternion.identity just means no rotation.
What I am trying to do: Duplicate an image and place it in a slot, then if another image is placed on top of the old one, the old one should be destroyed.
What is happening: Everything works, except that the old one doesn't get destroyed and the new one sits on top of the old. I mean of course it doesn't get destroyed since I didn't program that, but this is my question. How can I Destroy(OldClone) when there is only an IconClone? How can I introduce to the function the concept of an OldClone?
Since you call the function PlaceIconToSlot I'd guess, you might have a Slot component. If so, you can add a member variable to it that holds the current icon (assuming it's one icon per slot) and just work with that.
Something like this:
public class Slot
{
public GameObject Icon;
public void PlaceIconToSlot()
{
// If you overwrite it, the garbage collector will destroy it a some point anyways,
// but it doesn't hurt to do this destroy call
Destroy(Icon);
Icon = Instantiate(...);
}
}
Potentially pass the parameters (the new icon to instantiate) to this function if you above function at some centralized spot. Something like SpotXYZ.PlaceIcon(icon) or SpotXYZGameObject.GetComponent<Slot>().PlaceIcon(icon).
An idea would be to set a tag (let's say oldImage) to your original image. When you instantiate, destroy the object with that tag and then add the oldImage tag to the new image so that it will then be destroyed when another image is instantiated.
public void PlaceIconToSlot() //gets called by a button
{
GameObject IconClone = Instantiate(Icons[properIconIndex], Slots[properSlotIndex].transform.position, Quaternion.identity);
Destroy(GameObject.FindWithTag("oldImage"));
IconClone.gameObject.tag="oldImage";
}
I haven't tried this but it's worth a go!

Unity - how can I show / hide 3D Text

how can I display 3D text after certain time then hide it after certain time
My tries
public Text text_tap;
GameObject.Find("3dtext").active = true; // first try but it dosnt work
if (Time.time > 5) {
// second try but it I cant attach my 3d text to my script
text_tap.gameObject.SetActive(true);
}
I cant find any thing in 3D documentation
I don't know the exactly problem, but there you have some hints:
If you search in the scene for a GameObject that is deactivated it won't find it. The Gameobject MUST be active for the GameObject.Find() function to work. The easiest thing you can do is to keep the GameObject activated, and if the initial state is for it to stay hidden just hide it in the Awake().
Secondly, seems that you are trying to access a TextMesh object
but you reference in your code a Text object.
If you find a GameObject and request a Component that the GO does not contains, it returns null.
Finally The api to Activate/Deactivate a GameObject (GO) is
myGameobject.SetActive(true)
The one you are using (myGameobject.active = true) is deprecated
Try this example, it should work:
public YourMonoBehaviour : MonoBehaviour
{
public TextMesh text_tap;
float awakeTime;
void Awake()
{
// Remember to activate the GO 3dtext in the scene!
text_tap = GameObject.Find("3dtext").GetComponent<TextMesh>():
awakeTime = Time.time
}
void Update()
{
if ((Time.time - awakeTime) > 5)
{
// second try but it I cant attach my 3d text to my script
text_tap.gameObject.SetActive(true);
}
}
}
If you need to "do something after a delay" you're talking about Coroutines.
Checking Time.time will only check if the game has been running for x time, and using Thread.Sleep in Unity will cause it to delay since you're causing an Update or similar to lock and not return.
Instead, use
yield return WaitForSeconds(5);
text_tap.gameObject.SetActive(false);
As another warning, this code assumes that the target object is not the same gameObject as the one hosting this script, since coroutines do not execute on inactive objects. Similarly, disabling an ancestor (via the scene hierarchy, or transofrm.parent) of a gameObject disables the gameObject itself.
If this is the case, get the component that renders 3d text and disable it instead of the whole gameObject via the enabled field.
you can also use Invoke() to achieve. as explained above note that the text would have to be set by other means than Find cause if it is not active it will not find it.
void Start() //or any event
{
Invoke("ShowTextTap", 5f);//invoke after 5 seconds
}
void ShowTextTap()
{
text_tap.gameObject.SetActive(true);
//then remove it
Invoke("DisableTextTap", 5f);
}
void DisableTextTap()
{
text_tap.gameObject.SetActive(false);
}

Spawning Player at certain Point in Unity

I am making a small 2d click'n'point in Unity, and what I want to do is: I want to move towards the door and when my Player steps on a game Object with an attached SceneSwitcher Script he shall go through the door, into another scene. That works fine so far. Now I don't want him to appear in the middle of the room, but on the door, where he entered the room.
using UnityEngine;
using System.Collections;
using PixelCrushers.DialogueSystem;
public class ScenSwitcher : MonoBehaviour {
public string SceneName = "";
void OnTriggerEnter2D(Collider2D other) {
SwitchScene();
}
void SwitchScene(){
LevelManager levelManager = DialogueManager.Instance.GetComponent<LevelManager>();
levelManager.LoadLevel (SceneName);
changePosition ();
Debug.Log ("Scene Wechseln nach: " + SceneName);
}
void changePosition(){
GameObject player = GameObject.Find("Player");
player.transform.position = new Vector3(12,12,0);
}
}
That is my code, it does change Scenes, but not change the position. I would appreciate any help :)
On your ChangePosition() method you are passing hardcoded values to player position and it will assume always (12,12,0) on your scene space.
You need to define a spawn manager where you will get dynamically witch spawn point in your scene you want to use.
edited:
1: Try to create a singleton GameManager ( you can find singleton pattern examples here ) (IMPORTANT: Add DontDestroyOnLoad on your GameManager Awake).
2: In your GameManager define a Vector3 NextPosition property or something like this.
3: Declare a public Vector3 Destination on your "teleport" script to set it per teleport on inspector/editor.
4: Before this line levelManager.LoadLevel (SceneName) of code set GameManager.NextPosition = this.Destination;
5: If you are not persisting your character between scenes just call on one of hes behaviours Awake() or, if he persists create a method void OnLevelWasLoaded(int level) and chage players position setting GameManager.NextPosition ( wisely testing if it is valid for the current level before ;) ).
I cant try or do better coding now because I don't have access to unity editor so I hope it helps at last start a good research to solve your problem =/.
I would think the loadLevel function destroys the current script so changePosition does not get executed? I could be wrong.
Even if it is getting executed, there is a good chance it is executed before the level load and the properties for the next scene override where it got moved to.
I forget the exact syntax but look into getting GameObjects to not be destroyed on scene change.
EDIT
Object.DontDestroyOnLoad

Categories