I have an AR app that displays objects when it detects a QR code. The way I do it is with an empty object called model caller that has an script that instantiates a model, this is the script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Android;
using UnityEngine.Networking;
using UnityEngine.UI;
public class Load_Models: MonoBehaviour
{
// Start is called before the first frame update
[Serializable]
public class PlaceModel
{
public string Clave = "";
}
public GameObject[] model;
public string[] clave_modelos;
public string URL;
public GameObject ModeloUI;
public PlaceModel placeModel;
Dictionary<string, GameObject> strGO = new Dictionary<string, GameObject>();
public void Start()
{
StartCoroutine(GetRequest(URL));
for (int i = 0; i < model.Length; i++)
{
strGO.Add(clave_modelos[i], model[i]);
}
}
IEnumerator GetRequest(string uri)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
// Request and wait for the desired page.
yield return webRequest.SendWebRequest();
string jsonForm = uri;
if (webRequest.isNetworkError)
{
Debug.Log("Error loading");
}
else
{
try
{
PlaceModel model_1 = JsonUtility.FromJson<PlaceModel>(webRequest.downloadHandler.text);
Instantiate(strGO[model_1.Clave], new Vector3(0, 0, 0), Quaternion.identity, transform); //instantiates the model
Debug.Log("Loaded");
}
catch
{
Debug.Log("Error in connection");
}
}
}
}
}
and this is what happens when I detect more than 1 QR (it also happens with only one, just without the models "fusing" with each other):
Explanation: It should display 3 models, 1 simple street (the white block) and 2 "no model" 3d texts, but, the idea is for the models to appear "attached" (I don't know how to word it) to the QR code. And I tried to do it by having model caller as a child of the ImageTarget and with model caller being in the dead center of the imagetarget, also with new Vector3(0, 0, 0).
Is there a way to do this?
I know that I can do it by simply using the prefab by itself instead of a script, but I need for the models to change depending on a website (which I already did).
I'm using EasyAR 3 for this
If I got you right, try to change Instantiate line this way:
Instantiate(strGO[model_1.Clave], transform.position, Quaternion.identity, transform);
Also, maybe, you dont need to set parent transform, it depends from your implementation.
Related
I'm trying to make a ui that has a video shown and after that video is done, it recommends 3 different videos, these videos are dependent on the video that was just watched.
Now did I manage to get the buttons to display different text with a list system.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.UI;
// using UnityEngine.Video;
public class Recommendations : MonoBehaviour
{
public Button Rec1;
public Button Rec2;
public Button Rec3;
private int[] recommendedButtonValues;
private float[] recommendedSliderValues;
public List<DescriptionContainer> descriptionContainer = new List<DescriptionContainer>();
public List<ImageContainer> ImageContainer = new List<ImageContainer>();
public VideoplayerScript VideoPlayer;
public void SetRecommendationButtons(int buttonValue, float sliderValue) {
CheckRecommendations(buttonValue, sliderValue);
Rec1.GetComponentInChildren<TextMeshProUGUI>().text = descriptionContainer[recommendedButtonValues[0]].descriptions[(int)recommendedSliderValues[0]];
Rec2.GetComponentInChildren<TextMeshProUGUI>().text = descriptionContainer[recommendedButtonValues[1]].descriptions[(int)recommendedSliderValues[1]];
Rec3.GetComponentInChildren<TextMeshProUGUI>().text = descriptionContainer[recommendedButtonValues[2]].descriptions[(int)recommendedSliderValues[2]];
private void CheckRecommendations(int buttonValue, float sliderValue) {
if (buttonValue == 0) {
if (sliderValue == 0) {
recommendedButtonValues = new[] { 2, 0, 0 };
recommendedSliderValues = new[] { 0f, 2f, 4f };
//currentInterviewPhoto = ImageContainer[recommendedButtonValues].images[(int)recommendedSliderValues];
}
else if (sliderValue == 1) {
recommendedButtonValues = new[] { 1, 0, 3 };
recommendedSliderValues = new[] { 1f, 4f, 0f };
}
}
//this is repeated multiple times but I dont think its needed to show to get the point across, both for multiple button as slider values.
public void OnClickButton1() {
VideoPlayer.buttonValue = recommendedButtonValues[0];
VideoPlayer.sliderValue = recommendedSliderValues[0];
Debug.Log("Recommendation pressed for the following video, Button value: " + recommendedButtonValues[0] + ". Slider value: " + recommendedSliderValues[0]);
//repeated for all three buttons
[System.Serializable]
public class DescriptionContainer {
[TextArea(10, 10)]
public List<string> descriptions;
}
All this works but I'm now trying to do something similar with an images but I cant really get a list to work with an images. The thing I want to do is have 3 different images, depending on the
recommendedButtonValues[0,1 or 2] and the recommendedSliderValues[0,1 or 2] it decides which one to show. I've tried creating a list with textures like this:
[System.Serializable]
public class ImageContainer
{
public List<Texture> images;
}
public void WhatImag1e()
{
Image1 = ImageContainer[recommendedButtonValues[0]].images[(int)recommendedSliderValues[0]];
}
But I got the error that Unity cant convert UnityEngine.Texture to UnityEngine.UI.Image.
So my questions is what kind of list can I make which does work? Or is this not possible with a list and does I need to do.
I havent copied my entire code since that seems like an overkill but if I missed something I can show it. I'm using Unity version 2019.4.18f1 for this.
A UI.Image is a sprite container, it has a property 'sprite' where you can set sprites and it will change the texture of the widget to the sprite passed.
In your case, if you have textures, you need to change your UI.Image to UI.RawImage (change component in the editor), which accepts textures in its property 'texture'.
I have been working on a dialogue system for my game and I was wondering if anyone knows how to keep the system between different scenes. I know you can use things such as Player Prefs but for one, I do not understand it and upon research, people do not generally recommend it for storing large complicated things. I managed to get close to doing so by using dontDestroy just as you would with a character, however, it did not work completely as the button to switch to the next line of text, of course, broke along with the singleton I created for my system. What would be the best way for me to go about this?
Here is all of my code just in case it is needed:
Making the scriptable object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Dialogue", menuName = "Dialogues")]
public class Dialogue : ScriptableObject
{
[System.Serializable]
public class Info
{
public string myName;
public Sprite portrait;
[TextArea(4, 8)]
public string mytext;
}
[Header("Insert Dialogue Info Below")]
public Info[] dialogueInfoSection;
}
Main code for system (sigleton breaks here while switching scenes):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MainDialogueManager : MonoBehaviour
{
public static MainDialogueManager instance;
private void Awake()
{
if(instance != null)
{
Debug.LogWarning("FIX THIS" + gameObject.name);
}
else
{
instance = this;
}
}
public GameObject DialogueBoX;
public Text dialogueNameofChar;
public Text characterSays;
public Image characterPortrait;
private float textDelay = 0.005f;
public Queue<Dialogue.Info> dialogueInfoSection = new Queue<Dialogue.Info>();
public void EnqueueDialogue(Dialogue db)
{
DialogueBoX.SetActive(true);
dialogueInfoSection.Clear();
foreach(Dialogue.Info info in db.dialogueInfoSection)
{
dialogueInfoSection.Enqueue(info);
}
DequeueDialogue();
}
public void DequeueDialogue()
{
if (dialogueInfoSection.Count==0)
{
ReachedEndOfDialogue();
return; /////
}
Dialogue.Info info = dialogueInfoSection.Dequeue();
dialogueNameofChar.text = info.myName;
characterSays.text = info.mytext;
characterPortrait.sprite = info.portrait;
StartCoroutine(TypeText(info));
}
IEnumerator TypeText(Dialogue.Info info)
{
characterSays.text= "";
foreach(char c in info.mytext.ToCharArray())
{
yield return new WaitForSeconds(textDelay);
characterSays.text += c;
yield return null;
}
}
public void ReachedEndOfDialogue()
{
DialogueBoX.SetActive(false);
}
}
Dialogue Activation:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MainDialogueActivation : MonoBehaviour
{
public Dialogue dialogue;
public void startActivationofDialogue()
{
MainDialogueManager.instance.EnqueueDialogue(dialogue);
}
private void Start()
{
startActivationofDialogue();
}
}
Go to next dialogue line:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MainDialogueButtons : MonoBehaviour
{
public void GoToNextDialogueLine()
{
MainDialogueManager.instance.DequeueDialogue();
}
}
How about something like this?
The idea is pretty similar to what you're doing, with a few tweaks:
I'm storing the active dialog in a scriptable object (DialogueSystem) so that it can persist between scenes. Each time I load a new scene, I check if there's an active dialog, and if I so I show the dialog popup in Start().
Whereas you remove the dialog section that you're currently showing to the player from the current dialog, I don't remove the current section until the player clicks to the next section. That's necessary because you may need to re-show the same section if you move to a new scene.
Make sure to create an instance of the DialogueSystem scriptable object and assign it to MainDialogueActivation and MainDialogManager
MainDialogActiviation has some testing code in it so you can hit a key to start a new dialog or switch between scenes.
MainDialogueActiviation.cs
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainDialogueActivation : MonoBehaviour
{
public Dialogue dialogue;
// This scriptable object stores the active dialog so that you
// can persist it between scenes
public DialogueSystem dialogSystem;
private void Start()
{
// If we had an active dialog from the previous scene, resume that dialog
if (dialogSystem?.dialogInfoSections.Count > 0)
{
GetComponent<MainDialogueManager>().ShowDialog();
}
}
private void Update()
{
// Pressing D queues and shows a new dialog
if (Input.GetKeyDown(KeyCode.D))
{
GetComponent<MainDialogueManager>().EnqueueDialogue(this.dialogue);
}
// Pressing C ends the current dialog
if (Input.GetKeyDown(KeyCode.C))
{
this.dialogSystem.dialogInfoSections.Clear();
GetComponent<MainDialogueManager>().ReachedEndOfDialogue();
}
// Pressing S swaps between two scenes so you can see the dialog
// persisting
if (Input.GetKeyDown(KeyCode.S))
{
if (SceneManager.GetActiveScene().name == "Scene 1")
{
SceneManager.LoadScene("Scene 2");
}
else if (SceneManager.GetActiveScene().name == "Scene 2")
{
SceneManager.LoadScene("Scene 1");
}
}
}
}
MainDialogueManager.cs
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class MainDialogueManager : MonoBehaviour
{
// This scriptable object stores the active dialog
public DialogueSystem dialogSystem;
public GameObject DialogueBox;
public Text dialogueNameofChar;
public Text characterSays;
public Image characterPortrait;
private float textDelay = 0.005f;
// The game object for the dialog box that is instantiated in this
// scene
private GameObject dialogBoxGameObject;
/// <summary>
/// Shows the dialog window for the dialog that is in this object's
/// dialogSystem property.
/// </summary>
public void ShowDialog()
{
// Instantiate the dialog box prefab
this.dialogBoxGameObject = Instantiate(this.DialogueBox);
// I'd recommend putting a script on your "dialog box" prefab to
// handle this stuff, so that this script doesn't need to get a
// reference to each text element within the dialog prefab. But
// this is just a quick and dirty example for this answer
this.dialogueNameofChar = GameObject.Find("Character Name").GetComponent<Text>();
this.characterSays = GameObject.Find("Character Text").GetComponent<Text>();
this.characterPortrait = GameObject.Find("Character Image").GetComponent<Image>();
// If you have multiple response options, you'd wire them up here.
// Again; I recommend putting this into a script on your dialog box
GameObject.Find("Response Button 1").GetComponent<Button>().onClick.AddListener(ShowNextDialogSection);
GameObject.Find("Response Button 2").GetComponent<Button>().onClick.AddListener(ShowNextDialogSection);
ShowDialogSection(this.dialogSystem.dialogInfoSections.Peek());
}
/// <summary>
/// Puts a dialog into this object's dialogSystem property and
/// opens a dialog window that will show that dialog.
/// </summary>
public void EnqueueDialogue(Dialogue db)
{
foreach (Dialogue.Info info in db.dialogueInfoSection)
{
this.dialogSystem.dialogInfoSections.Enqueue(info);
}
ShowDialog();
}
/// <summary>
/// Removes the dialog section at the head of the dialog queue,
/// and shows the following dialog statement to the player. This
/// is a difference in the overall logic, because now the dialog
/// section at the head of the queue is the dialog that's currently
/// being show, rather than the previous one that was shown
/// </summary>
public void ShowNextDialogSection()
{
this.dialogSystem.dialogInfoSections.Dequeue();
if (this.dialogSystem.dialogInfoSections.Count == 0)
{
ReachedEndOfDialogue();
return;
}
Dialogue.Info dialogSection = this.dialogSystem.dialogInfoSections.Peek();
ShowDialogSection(dialogSection);
}
/// <summary>
/// Shows the specified dialog statement to the player.
/// </summary>
public void ShowDialogSection(Dialogue.Info dialogSection)
{
dialogueNameofChar.text = dialogSection.myName;
characterSays.text = dialogSection.mytext;
characterPortrait.sprite = dialogSection.portrait;
StartCoroutine(TypeText(dialogSection));
}
IEnumerator TypeText(Dialogue.Info info)
{
characterSays.text = "";
foreach (char c in info.mytext.ToCharArray())
{
yield return new WaitForSeconds(textDelay);
characterSays.text += c;
yield return null;
}
}
public void ReachedEndOfDialogue()
{
// Destroy the dialog box
Destroy(this.dialogBoxGameObject);
}
}
DialogSystem.cs
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Dialogues/Dialog System")]
public class DialogueSystem : ScriptableObject
{
public Queue<Dialogue.Info> dialogInfoSections = new Queue<Dialogue.Info>();
}
Here's what my dialog box prefab looks like
Every scene needs an object (presumably a prefab to make it easy to add to every scene) that has MainDialogActiviation and MainDialogManager on it. Mine looks like this:
This might be a bit of an unpopular opinion but using Singleton's are fine. It's just that MonoBehaviour singletons are tricky, you can use Object.DontDestroyOnLoad(instance). But things get ugly because it doesn't get destroyed when the scene changes (good) but if you go back to the scene it will load another one (bad). There's a few ways to get around that like having the object destroy itself if there's already an instance or having a subscene.
I would suggest not using MonoBehaviour singletons and use ScriptableObject singletons. You can lazy instantiate by putting the asset in a resource folder and use Resource.Load like this.
public class ScriptableSingleton<T> : ScriptableObject where T : ScriptableSingleton<T> {
private static string ResourcePath {
get {
return typeof(T).Name;
}
}
public static T Instance {
get {
if (instance == null) {
instance = Resources.Load(ResourcePath) as T;
}
return instance;
}
}
private static T instance;
}
With this code you create a Singleton class say DialogueManager you create a DialogueManager.asset for it and put it in a "Resources" folder.
I am creating a tool that helps the artist in my studio design their UI easily, and then give them the ability to export the UI to the developers to use in their projects.
Long story short, I followed this tutorial: What is the best way to save game state?
and it worked correctly when I wrote everything in the same script.
However when I opened another unity project to import the data, it stopped working correctly. It would read the file, but then the spawnObject variable which is supposed to be instantiated stays null.
The class I use here is called UIdata, and it has only one gameobject variable which is inputObject, and the class is exactly the same as the one I am exporting from the other project
I am not quite sure why it is not working, can deserialization work if its importing something from another project?
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Runtime.Serialization;
using System.Xml.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Xml;
using System;
public class DataSaver
{
public static void saveData<T>(T dataToSave, string dataFileName)
{
string tempPath = Application.streamingAssetsPath + "/newUICorrect.txt";
string jsonData = JsonUtility.ToJson(dataToSave, true);
byte[] jsonByte = Encoding.ASCII.GetBytes(jsonData);
if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
}
try
{
File.WriteAllBytes(tempPath, jsonByte);
Debug.Log("Saved Data to: " + tempPath.Replace("/", "\\"));
}
catch (Exception e)
{
Debug.LogWarning("Failed To PlayerInfo Data to: " + tempPath.Replace("/", "\\"));
Debug.LogWarning("Error: " + e.Message);
}
}
public static T loadData<T>(string dataFileName)
{
string tempPath = Application.streamingAssetsPath + "/newUICorrect.txt";
if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
{
Debug.LogWarning("Directory does not exist");
return default(T);
}
if (!File.Exists(tempPath))
{
Debug.Log("File does not exist");
return default(T);
}
else
{
Debug.Log("found file");
}
byte[] jsonByte = null;
try
{
jsonByte = File.ReadAllBytes(tempPath);
Debug.Log("Loaded Data from: " + tempPath.Replace("/", "\\"));
}
catch (Exception e)
{
Debug.LogWarning("Failed To Load Data from: " + tempPath.Replace("/", "\\"));
Debug.LogWarning("Error: " + e.Message);
}
string jsonData = Encoding.ASCII.GetString(jsonByte);
object resultValue = JsonUtility.FromJson<T>(jsonData);
return (T)Convert.ChangeType(resultValue, typeof(T));
}
}
[System.Serializable]
public class UIData
{
public GameObject inputObject;
}
public class LoadingScript : MonoBehaviour
{
private GameObject objectToSpawn;
void Start()
{
}
void Update()
{
if (Input.GetKeyDown(KeyCode.I))
{
importObject();
}
}
public void importObject()
{
UIData loadedData = new UIData();
loadedData = DataSaver.loadData<UIData>("UI");
objectToSpawn = loadedData.inputObject;
if (loadedData == null)
{
return;
}
print(objectToSpawn);
}
}
#Programmer is correct, you cannot serialize engine types. There are several ways to go about resolving your issue. The first is to rebuild your gameobject by serializing its MonoBehaviour components in the following manner (pseudocode):
public GameObject TestSaveAndLoadComponent(MyMonoBehaviour myComponent, GameObject objectToLoadComponentOnto){
// convert component to Json
string jsonString = JsonUtility.ToJson(myComponent);
// Pretend to save the json string to File
string directory = "TestDirectory";
string file = "objectFile";
SaveLoadData.SaveString(directory,file,objectJson);
// load string from file
string loadString = SaveLoadData.LoadString(directory,file);
// add a MonoBehaviour component to the object so that it can be
// overwritten by the JsonUtility
MyMonoBehaviour componentToOverwrite = objectToLoadComponentOnto.AddComponent<MyMonoBehaviour>();
JsonUtility.FromJsonOverwrite(loadString, componentToOverwrite);
return newObject;
}
JsonUtility may not provide a deep copy of your your monoBehaviour class, I don't use it much. It's pretty bare bones, and if it doesn't meet your needs, there's always Json.Net for unity.
All that being said, you shouldn't need to serialize gameObjects or monobehaviours if you structure your code in the proper way, using an Model-view-controller pattern. In such a case you would only need to serialize very simple classes that define the Model for the UI, and then the your view classes would rebuild the ui in a separate scene or project from the given model.
This is related to happyfuntimes plugin if you have used it .
I am trying to make a game on it and stuck at a point of displaying score along with name to display on large screen while user is playing on his mobile.(i have already tried to display name and score on mobile screens have seen in sample do not need that ). Please suggest how can this be done if you have used happyfuntimes plugin.
I could see the HFTgamepad input having public GameObject player name which I am trying to access ,do I have to make array ?
public string playerName;
I am trying to put these name on array.
Displaying anything in unity is really normal unity issue and not special to happyfuntimes. Games display highscore lists, item lists, inventory lists, etc... A list of players is no different
Use the GUI system display text.
According to the docs if you want to generate UI dynamically you should make a prefab
So basically you'd make a UI hierarchy in the scene. When a player is added to the game Instantiate your name-score prefab, search for the UI hierarchy gameObject (do that at init time), then set the parent of the preafab you just instantiated so it's in the UI hierarchy.
Look up the UI.Text components that represent name and score and set their text properties.
There's various auto layout components to help layout your scores
When a player disconnects Destroy the prefab you instantiated above.
OR ...
Conversely you can use the old immediate mode GUI. You'd make a GameObject, give it a component that has a OnGUI function. Somewhere keep a list of players. Add players to the list as they connect and remove them from the list as they disconnect. In your OnGUI function loop over the list of players and call the GUI.Label function or similar to draw each name and score
Here's a hacked up example.
Assuming you have a PlayerScript script component that's on each player that has a public accessor PlayerName for getting the player's name then you can make a GameObject and put a script like this on it.
using UnityEngine;
using System.Collections.Generic;
public class ExamplePlayerListGUI : MonoBehaviour
{
static ExamplePlayerListGUI s_instance;
List<PlayerScript> m_players = new List<PlayerScript>();
static public ExamplePlayerListGUI GetInstance()
{
return s_instance;
}
void Awake()
{
if (s_instance != null)
{
Debug.LogError("there should only be one ExamplePlayerListGUI");
}
s_instance = this;
}
public void AddPlayer(PlayerScript bs)
{
m_players.Add(bs);
}
public void RemovePlayer(PlayerScript bs)
{
m_players.Remove(bs);
}
public void OnGUI()
{
Rect r = new Rect(0, 0, 100, m_players.Count * 20);
GUI.Box(r, "");
r.height = 20;
foreach(var player in m_players)
{
GUI.Label(r, player.PlayerName);
r.y += 20;
}
}
}
PlayerScript can then call ExamplePlayerListGUI.GetInstance().AddPlayer in its Start function and ExamplePlayerListGUI.GetInstance().RemovePlayer in it's OnDestroy function
public PlayerScript : MonoBehaviour
{
private string m_playerName;
...
public string PlayerName
{
get
{
return m_playerName;
}
}
void Start()
{
...
ExamplePlayerListGUI playerListGUI = ExamplePlayerListGUI.GetInstance();
// Check if it exists so we can use it with or without the list
if (playerListGUI != null)
{
playerListGUI.AddPlayer(this);
}
}
void OnDestroy()
{
...
ExamplePlayerListGUI playerListGUI = ExamplePlayerListGUI.GetInstance();
if (playerListGUI != null)
{
playerListGUI.RemovePlayer(this);
}
}
}
Has anyone got multiple player prefabs working (eg different character classes with different prefabs) on the new Unity UNET Networking?
Finally got it working!
Massive thank you to #ClausKleber for their answer at
http://forum.unity3d.com/threads/how-to-set-individual-playerprefab-form-client-in-the-networkmanger.348337/#post-2256378
Edited working version below, Works with the Network Manager HUD to create and join.
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;
public class NetManagerCustom : NetworkManager
{
// in the Network Manager component, you must put your player prefabs
// in the Spawn Info -> Registered Spawnable Prefabs section
public short playerPrefabIndex;
public override void OnStartServer()
{
NetworkServer.RegisterHandler(MsgTypes.PlayerPrefab, OnResponsePrefab);
base.OnStartServer();
}
public override void OnClientConnect(NetworkConnection conn)
{
client.RegisterHandler(MsgTypes.PlayerPrefab, OnRequestPrefab);
base.OnClientConnect(conn);
}
private void OnRequestPrefab(NetworkMessage netMsg)
{
MsgTypes.PlayerPrefabMsg msg = new MsgTypes.PlayerPrefabMsg();
msg.controllerID = netMsg.ReadMessage<MsgTypes.PlayerPrefabMsg>().controllerID;
msg.prefabIndex = playerPrefabIndex;
client.Send(MsgTypes.PlayerPrefab, msg);
}
private void OnResponsePrefab(NetworkMessage netMsg)
{
MsgTypes.PlayerPrefabMsg msg = netMsg.ReadMessage<MsgTypes.PlayerPrefabMsg>();
playerPrefab = spawnPrefabs[msg.prefabIndex];
base.OnServerAddPlayer(netMsg.conn, msg.controllerID);
Debug.Log(playerPrefab.name + " spawned!");
}
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
MsgTypes.PlayerPrefabMsg msg = new MsgTypes.PlayerPrefabMsg();
msg.controllerID = playerControllerId;
NetworkServer.SendToClient(conn.connectionId, MsgTypes.PlayerPrefab, msg);
}
// I have put a toggle UI on gameObjects called PC1 and PC2 to select two different character types.
// on toggle, this function is called, which updates the playerPrefabIndex
// The index will be the number from the registered spawnable prefabs that
// you want for your player
public void UpdatePC ()
{
if (GameObject.Find("PC1").GetComponent<Toggle>().isOn)
{
playerPrefabIndex = 3;
}
else if (GameObject.Find("PC2").GetComponent<Toggle>().isOn)
{
playerPrefabIndex= 4;
}
}
}
Create a new class that derives from the built-in NetworkManager script. In there, just add a few supporting fields and an override for OnServerAddPlayer().
[SerializeField] Vector3 playerSpawnPos;
[SerializeField] GameObject character1;
[SerializeField] GameObject character2;
// etc.
GameObject chosenCharacter; // character1, character2, etc.
// Instantiate whichever character the player chose and was assigned to chosenCharacter
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId) {
var player = (GameObject)GameObject.Instantiate(chosenCharacter, playerSpawnPos, Quaternion.identity);
NetworkServer.AddPlayer(conn, player, playerControllerId);
}
Reference: http://docs.unity3d.com/Manual/UNetManager.html