Can't instantiate "scene object" [Unity Mirror Multiplayer] - unity3d

I am trying to instantiate an object that already exists in the scene so that I can enlarge it and "zoom" in on it, but when I play the game and hover over an object, instead of showing the zoomed card, this error shows up:
Card1(Clone)(Clone) has already spawned. Don't call Instantiate for NetworkIdentities that were in the scene since the beginning (aka scene objects). Otherwise the client won't know which object to use for a SpawnSceneObject message.
NOTE: I am following a tutorial for a 2D Unity Card game with mirror (2019.2.15f1), and I am using a different version of Unity (2020.1.0f1).
First, I am instantiating objects into my game with this script, which never throws any errors.
[Command]
public void CmdDealCards()
{
for (int i = 0; i < 4; i++)
{
GameObject card = Instantiate(playerDeck[Random.Range(0, playerDeck.Count)], new Vector2(0, 0), Quaternion.identity);
NetworkServer.Spawn(card, connectionToClient);
RpcShowCard(card, "Dealt");
}
}
The problem comes when I try to instantiate these objects again later, here:
public void OnHoverEnter()
{
zoomCard = Instantiate(gameObject, new Vector2(Input.mousePosition.x, 540), Quaternion.identity);
zoomCard.transform.SetParent(Canvas.transform, true);
zoomCard.layer = LayerMask.NameToLayer("Zoom");
RectTransform rect = zoomCard.GetComponent<RectTransform>();
rect.sizeDelta = new Vector2(240, 354);
}

I know this is a really late answer, but I came across it while running into the same issue, and maybe in the future, someone will do the same as me, so:
What you've done is attached the wrong prefab to your CardZoom script in the Inspector.
Image of Inspector with Zoom Card attached
What I did, and presumably you also, was to attach Card1 here, which is where it was failing to duplicate the NetworkIdentity

Related

Unity: How to switch scenes and when returning to the original scene, return player to the place they switched their scene

I have two scenes: Main Street & Building Scene
When the player is Main Street, if the player's trigger box touches the building and the player presses "q", the scene would switch to the Building Scene.
I want it so that when the player exits the Building Scene and returns to the Main Street Scene, the player is back to the position they entered the Building Scene that they entered from. Apologies in advance if this doesn't make sense.
sceneSwitchingScript:
public int buildingToLoad;
public Text InputText;
public movement player;
public Vector3 playerPrevPos;
void OnTriggerEnter2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("[Q] to enter");
if(Input.GetKeyDown("q")){
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
//Debug.Log(gameObject.tag);
Application.LoadLevel(buildingToLoad);
}
}
}
void OnTriggerStay2D(Collider2D col){
if(col.CompareTag("Player")){
if(Input.GetKeyDown("q")){
//spawn = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z)
Application.LoadLevel(buildingToLoad);
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
}
}
}
void OnTriggerExit2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("");
}
}
Setting the player's position when they exit the building
public switchScene ss;
void OnTriggerStay2D(Collider2D col){
if(Input.GetKeyDown("q")){
if(col.gameObject.CompareTag("ExitPoint")){
transform.position = ss.playerPrevPos;
}
}
}
However, these two scrips do not work and I'm not sure if this is related but when I make the player do the switch scene thing in-game, this error pops up:
NullReferenceException: Object reference not set to an instance of an object
movement.OnTriggerStay2D (UnityEngine.Collider2D col)
This error message mentions the error on this line:
transform.position = ss.playerPrevPos;
With Unity, the traditional loading of a scene was fairly destructive. There was the concept of setting a GameObject as safe by setting it as DontDestroyOnLoad, which, in a way, removed the object from the scene altogether, which protected it from scene loads.
Unity finally properly implemented multiscene editing and loading. It's essentially DDOL, but done properly.
Now you can actually have multiple scenes loaded at the same time. Now, what that allows you to do is to have a "Manager" scene that handles all of the objects that are common between scenes, and only load (and unload) the specific objects required for that individual scene.
You'd use it like this:
SceneManager.LoadScene("YourScene", LoadSceneMode.Additive);
That would load "YourScene" in ADDITION to the currently loaded scene. Likewise, removing a scene is:
SceneManager.UnloadScene("YourScene");
Now, if you have a Manager scene, you can include in your manager scene a script that holds data for each individual scene. As a hacky example, you might have:
public Vector3 InsideSceneLastPosition { get; set; }
Which you then assign to before loading your outside scene. When you load your inside scene again, you can read InsideSceneLastPosition again to reposition your character.
Here's the link to the LoadSceneAsync page at Unity.
There's more to it than that, for instance, you have to listen for the SceneManager.sceneLoaded event to know when you've actually loaded the next scene, so that you can reposition your GameObejcts. You can find information about that here.
You can see multiscene editing my simply dragging multiple scenes from the Project window into you Hierarchy window. All the scenes listed will additively work together. But you'll have to be careful though, as there's another "gotcha" in that you cant reference objects from one scene to another. You can call scripts cross scene, but you won't be able to drag a game object from one scene, into the object field of a component on a different scene. Don't worry, you'll get the hang on it =)

How to make Unity UI object draggable when the objects are instantiating at runtime?

I am working on a management game where every user has some specific characters in save files. I am instantiating these characters inside a panel, I want the user to choose one of the cards and drag it to some specific point. I am able to make a drag script for the object that is already in a scene. But how to achieve the same thing if objects are generating at runtime?
I just need some idea how to do it.
here's my current code to drag UI object.
public void OnDrag(){
btn.transform.position = Input.mousePosition;
}
public void EndDrag(){
if (btn.transform.position.x -500 <50 || btn.transform.position.x -500 > -50) {
//btn.transform.position = new Vector3 (-10, 10);
rt.anchoredPosition = new Vector3 (500, 100, 0);
}
else{
rt.anchoredPosition = new Vector3 (-10, -10, 0);
}
}
At the time you Instantiate your GameObject obj, do:
obj.addComponent("YourScript");
where YourScript is your script, which you have written, that attached to a GameObjects makes it draggable..
Alternatively, you instantiate a prefab, attach the script to the prefab through the editor.
This is preferrable because addComponent() will get deprecated in the next versions.
I also recommend to keep a List<YourInstantiatedObjectType> in the parent.

How can I add a scriptable tile at runtime to a Unity Tilemap?

I'm trying to add one of my scriptable tiles to my Unity Tilemap during runtime. I can paint the tile in the editor and everything looks fine but I would like to add it during runtime.
I tried instantiating the ScriptableObject asset itself, but it does not have the correct sprites it needs that the generated tile asset has in the editor. Also, I don't believe I should be instantiating the ScriptableObject asset for each tile since one asset is meant to be shared for all of the same tiles on the map.
The Tilemap API provides a way to add a tile, but no instructions or example of how to create the tile.
This is kinda off the top of my head, but I was messing around with something similar yesterday, instantiating a tile on click.
Vector3Int tilePos = map.WorldToCell(mousePos);
YourTileClass tileInstance = ScriptableObject.CreateInstance<YourTileClass>();
Texture2D tex = Resources.Load<Texture2D>("tileTexture") as Texture2D;
Sprite sprite = new Sprite();
sprite = Sprite.Create(tex, new Rect(0, 0, 400, 400), new Vector2(0.5f, 0.5f));
Tile tile = Resources.Load<Tile>("tileInstance") as Tile;
tileInstance .sprite = sprite;
yourTileMap.SetTile(tilePos , tile);
I am not 100% if that is the best way to do it, but it worked for me. Let me know if that does not work and I will double check the actual file.
Sorry, I can't comment, but not knowing what you've tried so far, have you tried assigning the ruletile in the inspector and then just assigning it with SetTile at runtime whenever you need to? From the sounds of it this should achieve what you're trying to do
public Tilemap tilemap;
public MyRuleTile myRuleTile;
tilemap.SetTile(coordinate, myRuleTile);
Assuming your ScriptableObject is inheriting from TileBase object you can do the following:
public Tilemap tilemap;
public TileBase tileToDraw; //Object to draw at runtime
//Get tilemap position from the worldposition
Vector3Int tilemapReferencePosition = tilemap.WorldToCell(reference.transform.position);//this can be the position of another gameObject e.g your Player or MousePosition
roadTileMap.SetTile(tilemapReferencePosition, tileToDraw);
I recently used something similar for a Fire Emblem style RPG where I wanted to draw all the potential directions a spell can be cast before casting it and I ended up with the following function
public void DrawPreAttack(GameObject reference, Spell spell) {
Vector3Int gridReferencePos = preAttackTileMap.WorldToCell(reference.transform.position);
foreach (Vector2 direction in spell.validDirections) {
for (int i = 0; i <= spell.maxDistance; i++) {
preAttackTileMap.SetTile(new Vector3Int(gridReferencePos.x + (i * (int)direction.x) , gridReferencePos.y + (i * (int)direction.y) , 0), preAttackTile);
}
}
}
I have provided two small scripts for you that show you exactly how to do this:
1) The ScriptableObject
After placing this script in the Assets/??? folder, you can create new ScriptableObjects (or rather "ScriptableTiles") by right-clicking in the folder -> ScriptableObjects/ScriptableTile. After you have created such an asset, you have to "fill" it with what you want to have in it. In this case: a Tile. Just drag any Tile from your folder into the "m_Tile" slot that the ScriptableTile provides for you.
using UnityEngine;
using UnityEngine.Tilemaps;
[CreateAssetMenu(fileName = "NewScriptableTile", menuName = "ScriptableObjects/ScriptableTile")]
public class ScriptableTile : ScriptableObject
{
public Tile m_Tile;// Fill this with a Tile
public bool m_IsCollider;// This does nothing, it's just a showcase for what else could be stored in here
}
2) A simple "Placer"
Attach the "Placer" MonoBehaviour to a GameObject and give it a reference to a TileMap and your ScriptableTile. On Start() it will place the Tile in the middle of the chosen TileMap.
using UnityEngine;
using UnityEngine.Tilemaps;
public class ScriptableTilePlacer : MonoBehaviour
{
public Tilemap m_ExampleTileMap;
public ScriptableTile m_ExampleTile;
private void Start()
{
m_ExampleTileMap.SetTile(Vector3Int.zero, m_ExampleTile.m_Tile);
}
}
I've played around with creating TileMaps at runtime myself - you can get even more out of ScriptableTiles by adding more variables. For example, if you have a separate Collider TileMap, you could use a bool to mark certain Tiles as "IsCollider" or "IsNotCollider". Another idea would be to not store Tiles inside the ScriptableTile, but Sprites. This allows you to completely bypass the TilePalette - which is pretty much useless when it comes to runtime creation. Just write a short public Tile GetTileFromSprite() method inside the ScriptableTile. With a few lines of code you can create Tiles from Sprites and return them.

Unity: GameObject always at center regardless of position changes

I am working on a 2D game and have created a game object using C# script as below. I also set my camera to orthogonal and have adjusted my sprite based on the width of the screen. Regardless of the position I set, the object is always at the center of the screen. How can I solve this?
using UnityEngine;
using System.Collections;
public class TestingPositions : MonoBehaviour {
GameObject hero;
Sprite heroSprite;
Vector3 heroPosition;
// Use this for initialization
void Start () {
hero = new GameObject ();
Instantiate (hero, heroPosition, Quaternion.identity);
Camera camera = GameObject.FindGameObjectWithTag ("MainCamera").GetComponent<Camera> ();
heroPosition = camera.ScreenToWorldPoint(new Vector3(Screen.width/4, Screen.height/4, camera.nearClipPlane));
heroSprite = Resources.Load <Sprite> ("Sprites/heroImage");
SpriteRenderer renderer = hero.AddComponent<SpriteRenderer>(); renderer.sprite = heroSprite;
}
}
when you use Instantiate you have to use it on
an existing model.
Instantiate means "duplicate this model" or "copy this model", or "make a new one, using this model as an example".
What you are doing, is creating a brand new empty "hero" game object - and then "instantiating" it. That is meaningless and does nothing.
What you must do whenever you want to use "Instantiate" is this:
public GameObject modelPerson;
Note that the name must be "modelSomething".
first put that in your code. LOOK at the Inspector. MAKE your actual model hero (or whatever it is)
Sit it somewhere off camera where it is not seen.
Now, drag that thing to the "modelPerson" slot in the Inspector.
If you are not familiar with the basics of using Inspector-dragging in Unity, review basic Unity tutorials https://unity3d.com/learn/tutorials/topics/scripting
Next in your code, perhaps in Start, try this
GameObject newHero = Instantiate( modelPerson );
newHero.transform.position = .. whatever you want
newHero.transform.rotation = .. whatever you want
newHero.name = "Dynamically created";
newHero.transform.parent = .. whatever you want
once you understand these basics, there is very much more to learn about Instantiate. You can ask that in separate questions. Good luck.
Your need to save the reference to your gameObject that is created with Instantiate, because Instantiate makes a copy not modifies the original.
To modify a gameobjects position after instantiation, you need to use gameobject.transform.position = newPosition; To modify it before instantiation, you would need to do the "heroPosition" line before using heroPosition in Instantiate.
So like this:
using UnityEngine;
using System.Collections;
public class TestingPositions : MonoBehaviour
{
GameObject hero;
SpriteRenderer heroSprite;
// Use this for initialization
void Start()
{
Camera camera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
//Save the reference to the instantiated object into a variable
//Since you are creating an object from scratch, you don't even need Instantiate, which means copy - not create.
hero = new GameObject();
//Set its position
hero.transform.position = camera.ScreenToWorldPoint(new Vector3(Screen.width / 4, Screen.height / 4, camera.nearClipPlane));
//Set its rotation
hero.transform.rotation = Quaternion.identity;
//Add sprite renderer, save the reference
heroSprite = hero.AddComponent<SpriteRenderer>();
//Assign the sprite
heroSprite.sprite = Resources.Load<Sprite>("Sprites/heroImage");
}
}

Set transform from another GameObject's script

I'm trying to make a script to set an object when is being instantiated. The problem is, I don't clearly know how to do it. I have this function..
function spawnPlayer()
{
var CameraScript = GameObject.Find(PlayerPrefab.name).GetComponent("Camera Control");
Network.Instantiate(PlayerPrefab, spawnObject.position, Quaternion.identity, 0);
}
Where PlayerPrefab is going to be the Prefab that's going to be instantiated. When this happens, I need to set the instantiated gameObject on another GameObject which is camera and has a script called "Camera Control" and inside there's a transform Target which I'm trying to set. How to do this?
The code you posted can't be right. You are using the PlayerPrefab's name to to find the Camera Control script attached to the camera? By that logic then the moment you instantiate PlayerPrefab, on the second line, you will have a second camera.
I think what you want to do is this: Instantiate the player prefab and make the camera point to the player.
So I am assuming the CameraControl script is created. You need the following before we start to code.
Attach CameraControl script to the camera in the scene.
Make sure the Player script is attached to the Player Prefab.
Have a third script that will instantiate the PlayerPrefab. I will call it Instantiator. Attach it to an empty GameObject in the scene, think of it as the world GameObject. We will call it World.
Make sure the Instantiator script is attached to the World GameObject and that it is pointing to the PlayerPrefab.
Code: Instantiator
The Instantiator script will spawn and create things we will use in the scene.
#pragma strict
var PlayerPrefab : GameObject;
function Start ()
{
// You can add position and rotation to the function call if you like.
var p = Instantiate(PlayerPrefab) as GameObject;
// Find the camera script and point to Player's transform.
Camera.main.GetComponent("CameraControl").SendMessage("setTarget", p.transform);
}
Notice I used the fact that the MainCamera in the scene is marked by Unity for you so it is easy to find.
Code: CameraControl
The CameraControl will have the logic to follow the Player as you see fit. Notice that target will point to what the camera will focus on. Of course following the Player around you will have to write.
var target : Transform;
function setTarget(t : Transform)
{
target = t;
}
I just taught myself a bit of JavaScript. I had never used it before.
I found my solution.
What I was meaning on my question was to set my camera's script the transform of the instantiated object.
I did not have to make many empty objects with scripts value of each object; it took me hours to find it because I did not know how unity handles the scripts objects calls.
This is how I made it:
var PlayerPrefab : GameObject;
var spawnObject : Transform;
private var MainCamera : GameObject;
function spawnPlayer()
{
var player = Instantiate(PlayerPrefab, spawnObject.position, Quaternion.identity);
MainCamera = GameObject.Find("MainCamera");
player.name = "Ball";
if(MainCamera)
{
MainCamera.GetComponent.<CameraControl>().target = player.transform;
Debug.Log("Succeed.");
}
}
Like this, my camera will have the transform properties of the new instantiated object automatically.