I'm making a solitaire game and I strugle with one functionality.
In time of player is clicking on deck cards appears in discard pile. They are created in factory :
[SerializeField]private GameObject prefab;
public void Create(Transform parent, float zOffset, float yOffset, CardModel model, bool isCovered, bool isLast, string pileName, GameObject curretPile, ActionsManager manager)
{
GameObject instance = Instantiate(prefab, new Vector3(parent.position.x, parent.position.y - yOffset, parent.position.z - zOffset), Quaternion.identity, parent);
this.model = model;
this.view = instance.GetComponent<CardView>();
this.controller = GetComponent<CardController>();
instance.GetComponent<CardController>().SetModel(this.model);
instance.GetComponent<CardController>().SetView(view);
string spriteName = (instance.GetComponent<CardController>().ReturnValue()).ToString() + instance.GetComponent<CardController>().ReturnSuit();
Sprite faceingSprite = uncoverSprite.Find(item => item.name == spriteName);
instance.GetComponent<CardController>().SetUncoveredSprite(faceingSprite);
instance.GetComponent<CardController>().SetPileName(pileName);
instance.GetComponent<CardController>().SetIsCoveredBool(isCovered);
instance.GetComponent<CardController>().SetIsLastInPile(isLast);
instance.GetComponent<CardController>().SetActionsManager(manager);
instance.GetComponent<CardController>().SetCurrentPile(curretPile);
When all cards are created to discard pile and player click again on deck it sends action with demand of the list of card from discard pile, they are returning to deck and they are destroy in discard pile.
public void OnCardDemand()
{
List<CardModel> list = ReturnList();
RemoveAllElementsFromList();
if(OnDeckSend != null)
{
OnDeckSend(list);
}
CardController[] cards = gameObject.GetComponentsInChildren<CardController>();
foreach(CardController card in cards)
{
Destroy(card.gameObject);
}
}
Cards should be instatiated again after clicking on deck pile but there is an error :
MissingReferenceException: The object of type 'SpriteRenderer' has been destroyed but you are still trying to access it.
How can I destroy cards without any error?
It's a deck of cards - I wouldn't try to continuously create and destroy them, just assign new transform parents and set local positions to zero if you want to move them. I'd also recommend using SetActive(false) instead of destroying, then you can use SetActive(true) to "create" them back - this is along the lines of an object pool.
Ultimately the problem is that you've cached a reference to a Component on the GameObject that you're deleting, Wherever you're using the SpriteRenderer you need a null check, or else you need a more sophisticated way to unsubscribe that reference in OnDestroy for the GameObject your destroying.
Related
I use Playfab and Photon for my Unity project. I added some weapons, shields and body types to my player prefab and disabled all of them (except the head). I store which weapon, shield and body type the user have, and pull them from playfab when the player logs in. But the problem is, I can see my own weapon, shield and body but cannot see other player's. I can only see their heads! I assume that it is because when other user joins the scene, their prefab instantiates with PhotonNetwork but SetActive method is working for individuals, not for the server. Here is my code:
public override void OnJoinedRoom()
{
s = "PlayerArmature(Clone)/";
if(PFLogin.prefabName=="Female"){
gameObject = PhotonNetwork.Instantiate(playerPrefabFemale.name, new Vector3(73, 22, 34), Quaternion.identity, 0,null);
s = "PlayerArmatureF(Clone)/";
s += "FemaleCharacterPolyart/";
view = playerPrefabFemale.GetComponent<PhotonView>();
}else{
gameObject = PhotonNetwork.Instantiate(playerPrefab.name, new Vector3(73, 22, 34), Quaternion.identity, 0,null);
s += "MaleCharacterPolyart/";
view = playerPrefab.GetComponent<PhotonView>();
}
GameObject body = GameObject.Find(s+PFLogin.body);
GameObject cloak = GameObject.Find(s+PFLogin.cloak);
GameObject shield = GameObject.Find(s+"root/pelvis/spine_01/spine_02/spine_03/clavicle_l/upperarm_l/lowerarm_l/hand_l/weapon_l/"+PFLogin.shield);
GameObject weapon = GameObject.Find(s+"root/pelvis/spine_01/spine_02/spine_03/clavicle_r/upperarm_r/lowerarm_r/hand_r/weapon_r/"+PFLogin.weapon);
body.SetActive(true);
cloak.SetActive(true);
shield.SetActive(true);
weapon.SetActive(true);
if (view.IsMine)
{
view.RPC("ShowMesh", RpcTarget.AllBuffered, s);
}
}
void ShowMesh(string s)
{
GameObject body = GameObject.Find(s+PFLogin.body);
GameObject cloak = GameObject.Find(s+PFLogin.cloak);
GameObject shield = GameObject.Find(s+"root/pelvis/spine_01/spine_02/spine_03/clavicle_l/upperarm_l/lowerarm_l/hand_l/weapon_l/"+PFLogin.shield);
GameObject weapon = GameObject.Find(s+"root/pelvis/spine_01/spine_02/spine_03/clavicle_r/upperarm_r/lowerarm_r/hand_r/weapon_r/"+PFLogin.weapon);
body.SetActive(true);
cloak.SetActive(true);
shield.SetActive(true);
weapon.SetActive(true);
}
PFLogin is a static class which stores static variables(body&weapon etc) that are assigned with playfab responses.
So my question is how can I call SetActive(true) such that everyone see others bodys(or other things).
At first I've tried without RPC and all I see was heads without bodies in the screen, now I've tried with RPC but nothing changed. It could be my lack because I am very new to all of this.
I would try to use the isMine to make every prefab active:
//at the beginning public PhotonView PV;
public void Awake()
{
PV = Gameobject.GetComponent<PhotonView>();
}
if (!PV.isMine)
{ Prefab.gameobject.SetActive(true) }
I hope it helps, I have never done it like you did, so I am
im trying to make a card game. There is something I can't understand:
I have a class named HandManagement that has a list of GameObject and a method AddToHand that add to a list (the hand) a gameobject passed through variable (bringing in hand)
I have a Tile GameObject on the screen with a script that, when collides with mousedown:
the variable hand of HandManagement type is called with method AddToHand to add that gameobject to hand (AddToHand(this.gameobject)
When I try unity tells me that the object doesnt exist..
Why?
Thank you
Tile Class:
{
m_ObjectCollider.isTrigger = true;
tileInHand.PickTile (tileName);
HandManagement Class:
public GameObject tile;
public List<GameObject> hand = new List<GameObject>();
public void PickTile(string pickedTile)
{
Debug.Log ("picked");
tile = Instantiate (tile);
tile.transform.SetParent (transform, false); //i put it in a grid of hand card
hand.Add (tile); //I add it to the list of card in hand
}}
first of all, change the string to a GameObject, then Instantiate it
public void PickTile(GameObject pickedTile)//change it from string to gameobject
{
Debug.Log ("picked");
tile = Instantiate (pickedTile);//instantiate the picked tile.
tile.transform.SetParent (transform, false);
hand.Add (tile);
}}
now tile holds the GameObject "pickedTile" so it will be added to the grid and to the hand.
Next, you need to change the referenced object from a string to a GameObject
tileInHand.PickTile(tileGameObject);
The Instantiate function copies the object tile into tile, this is empty to begin with. Try instantiating a prefab gameobject into the tile, or make sure gameobject tile is never empty (eg. by setting it in the Start()).
https://docs.unity3d.com/ScriptReference/Object.Instantiate.html
This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 1 year ago.
I'm new to working in unity and i don't quite understand how to transfer data between scenes. In my case it's the sprites for my inventory when the player picks up an item. here is the code for the items sprites etc.
public class Items : MonoBehaviour
{
public string ItemName;
public string ItemText;
public int itemInList;
public Sprite itemSprite;
public InventorySlot inventorySlot;
public ItemSprites itemSprites;
public Sprite blueberrySprite;
public Sprite cheesecakeSprite;
public Sprite whitebreadSprite;
public Sprite cookiesSprite;
public void Awake()
{
//DontDestroyOnLoad(this);
}
public void gettingID()
{
GameObject Go = new GameObject();
Go.AddComponent<InventorySlot>();
inventorySlot = Go.GetComponent<InventorySlot>();
inventorySlot.itemID = itemInList;
}
public void ItemList()
{
// Cheesecake
if(itemInList == 1)
{
ItemName = "Cheesecake";
ItemText = "A delishious Juicy Cheesecake";
itemSprite = cheesecakeSprite;
}
// White Bread
if (itemInList == 2)
{
ItemName = "White Bread";
ItemText = "a basic white loaf that smells amazing";
itemSprite = whitebreadSprite;
}
// Cookies
if (itemInList == 3)
{
ItemName = "Cookies";
ItemText = "just plain Chocolatechip cookies, still delishious though";
itemSprite = cookiesSprite;
}
// Blueberries
if (itemInList == 4)
{
ItemName = "Blueberries";
ItemText = "Big juicy yummy blueberries!";
itemSprite = blueberrySprite;
Debug.Log(ItemText);
}
}
it works just fine when I'm in the scene where the script exist but when I try to use it in my new scene the image will turn out white(blank). I guess that is because the gameobject is not set anymore in the new scene, but how do i transfer the set gameobject sprite to a new scene? I now that you can make stuff static but if I read correctly then it doesn't work on gameobjects. and Dontdestroyonload doen't seem to work either cus the script is not attached to a gameobject in the scene.
a) You can make your sprite a static property in your class and you should be able to access it from anywhere in unity. You can make a simple DataHolder class to hold this value and other static values in your Game (It need not be a MonoBehaviour). However, you will need to load them somehow (From resources or AssetBundles) into your static Array
b) Check my Answer here on how to properly persist a GameObject in the Game if you are using DontDestroyOnLoad and have it attached to a GameObject in the scene. This should be a good option to use, considering you already have a MonoBehaviour class. Just attach it to a GameObject and Follow the answer in the link to maintain one correct instance of the Component.
c) Consider using a simple ScriptableObject if you want to save these values across multiple Game Sessions. It is a simple data container that helps you save data Independent of Classes.
Follow this link for Unity's Documentation on ScriptableObjects
I'm currently experiencing a coder's block right now as I'm trying to instantiate text on a game object, specifically a Zombie prefab. I've gotten down the enemy's position for the text to be spawned but can't seem to make it follow the zombie's movement as it walks towards the player.
This is my 'Word Spawner' script.
public GameObject wordpb; // my word prefab
public Transform canvas; // connected to a canvas ui that has world camera set
public EnemyOne enemy;
public DisplayWord Spawn()
{
Vector2 targetPos = new Vector2(enemy.transform.position.x, enemy.transform.position.y);
GameObject wordobj = Instantiate(wordpb, targetPos, Quaternion.identity, canvas);
DisplayWord displayWord = wordobj.GetComponent<DisplayWord>();
return displayWord;
}
and this is where the DisplayWord class is derived from.
public Text text;
public void SetWord(string word)
{
text.text = word;
}
public void ThrowLetter()
{
text.text = text.text.Remove(0, 1);
text.color = Color.red;
}
public void ThrowWord()
{
Destroy(gameObject);
}
My best guess is that I should be implementing a void Update method in which I use transform.Translate? Or should I put a placeholder that acts as a child class to my Zombie prefab and attach the DisplayWord script there? Please help a poor soul out.
How does the text relate to the Zombie game object? Knowing what the text is for in relation to the zombie might inform the best way to make it follow the game object.
If the text is something like a worldspace nametag, instead of translating in Update you could make the text a child of the game object it needs to follow. It might not be the most elegant solution but if you give each text its own worldspace canvas you could assign the text as a child of the zombie, or, if you know you'll always have text following a zombie, you could just add a worldspace canvas and text to the zombie prefab...
To instantiate as a child you'd need to rework your word prefab to be its own worldspace canvas with your text as its child.
You can assign a parent when you instantiate:
wordobj = Instantiate(wordpb, enemy);
Or set the parent after instantiation:
wordobj.transform.SetParent(enemy.gameObject.transform, true);
The second parameter in SetParent is 'worldPositionStays', and controls whether the child object keeps its world position (true) or whether it's transform is evaluated relative to its new parent's transform. You could make it work either way: if you leave it 'true' you don't need to change the rest of your code, but I think if you make it false you don't need to get the enemy GameObject's position... The same is true for Instantiate when a parent is assigned but no transform position/rotation. So I think you could skip the step of finding the enemy's position and rewrite your Spawn code to:
public GameObject wordpb; // my word prefab
public EnemyOne enemy;
public DisplayWord Spawn()
{
GameObject wordobj = Instantiate(wordpb, enemy, false);
DisplayWord displayWord = wordobj.GetComponent<DisplayWord>();
return displayWord;
}
You'd need to assign the newly instantiated canvas's worldspace camera to make this work...
I have set up a random spawner that creates new game objects but they are being created outside of my canvas and therefore can't be seen when play the game. Is there any way to fix this? The objects 'neg thoughts' are UI Buttons and are being created outside of the canvas even though I need them to appear on screen so they can be used.
I did see a similar question but the suggestions didn't work for my problem.
this is very frustrating for me and any help would be awesome!
You can simply pass the parent into Instantiate
parent Parent that will be assigned to the new object
var newObj = Instantiate(prefab, parentTransform);
or with additional transforms
var newObj = Instantiate (prefab, position, rotation, parentTransform);
Or as others already said you can still do it afterwards at any time either by simply assigning a new transform.parent
newObj.transform.parent = parentTransform;
or using transform.SetParent
newObj.transform.SetParent(parentTransform, worldPositionStays);
The advantage of the later is that you have an optional parameter worldPositionStays
worldPositionStays If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.
assigning a new
transform.parent = parentTransform;`
will always act the same way as
transform.SetParent(parentTransform);
since the default value for worldPositionStays (so if you don't explicitly pass it) is true.
So for the specific case of UI in a Canvas you could use
public TheSpawnComponent : MonoBehaviour
{
// Via the Inspector drag&drop any object here that is inside the canvas
// or the canvas itself
[SerializeField] private GameObject parentInCanvas;
[SerializeField] private Button buttonPrefab;
public void DoInstantiate()
{
var newButton = Instantiate (buttonPrefab, parentInCanvas);
//Todo Position and callback
}
}
Or if the spawner script is attached to an object inside the canvas anyway you could also spawn as child of this one directly using
var newButton = Instantiate(buttonPrefab, transform);
You can specify the transforms parent after you have created the game object.
spawnedObject.transform.parent = canvas.transform;
You have to use below code
public GameObject Prefab; // Object to Create
public Transform ParentOfObj;// Must Be inside the canvas or canvas it self
void Start()
{
GameObject g = Instantiate(Prefab) as GameObject;
g.transform.SetParent(ParentOfObj);
g.transform.localScale = Vector3.one;
}