UNET Change Texture of child object for clients - c#

I have tried multiple techniques, but still no result. The host is able to change the color, but not for the client players. The code looks like this.
public List<GameObject> bases = new List<GameObject>();
[SyncVar]
public Texture redTexture;
[SyncVar]
public Texture blueTexture;
[SyncVar]
GameObject objID;
NetworkIdentity objNetID;
public void CheckTexture()
{
if (isLocalPlayer)
{
for (int i = 0; i < bases.Count; ++i)
{
var tempScript = bases[i].GetComponent<BaseScoreScript>();
if (tempScript.owner == "blue")
{
objID = bases[i];
CmdCheckText(objID, blueTexture);
}
}
}
}
[ClientRpc]
void RpcChangeTexture(GameObject obj, Texture text)
{
obj.GetComponentInChildren<MeshRenderer>().material.mainTexture = text;
}
[Command]
void CmdCheckText(GameObject obj, Texture texture)
{
objNetID = obj.GetComponent<NetworkIdentity>();
objNetID.AssignClientAuthority(connectionToClient);
RpcChangeTexture(obj, texture);
objNetID.RemoveClientAuthority(connectionToClient);
}

Thanks for any who look at this. I solved the issue. Probably not the most efficient, I can work on that later. But I added a [Command] to the non player object (which does have a NetworkIdentidy and set to local player), in this command I reference a list of players in the scene and set a public variable in the playersscript (which is a SyncVar <- that what did it). And in the playerscript I reference that non Player object and set its texture to the one I wanted. Not worried if it isLocalPlayer or not, because I want all players to see the change.
[Command]
void CmdSyncOwner(string own)
{
//List<GameObject> players = new List<GameObject>();
foreach (GameObject obj in GameObject.FindObjectsOfType<GameObject>())
{
players.Remove(obj);
players.Add(obj);
}
for (int i = 0; i < players.Count; ++i)
{
if (players[i].tag.Contains("Ply"))
{
players[i].GetComponent<FPSPlayerFunction>().baseOwner = own;
}
}
}
that's the code in the non-player script and:
[SyncVar]
public string baseOwner;
void BaseStatus()
{
var blue = Color.blue;
var red = Color.red;
foreach(GameObject obj in bases)
{
if (baseOwner == "blue")
{
centBaseStatus.image.color = blue;
centBaseStatus.GetComponentInChildren<Text>().text = "Center Base : "+ baseOwner;
GameObject.Find("CenterBase").GetComponentInChildren<Renderer>().material.mainTexture = BlueBase;
}
}
}
for the playerscrip.
I know its not pretty. But yea, if you have any suggestions on better ways to do this, please let me know. And if this is helpful to someone... Awesome Sauce!

Related

deactivate and activate an object in a list

I am a beginner. I want to create a game where there are six different positions and objects that spawn from them everytime player collides one of them and then they respawn. it s a loop until game finish.
I am having a problem with positions; if player stays at a position it just collides continually. I need to deactivate the position that player collides and reactivate when player collides other position. Position were made with empthy object and its collider. I dont know where to add what.
Thanks for your help
public class NewSpawnScript : MonoBehaviour{
public GameObject[] SpawnPoints;
public float spawnTime;
public GameObject[] threeD;
public List<GameObject> possibleSpawn = new List<GameObject>();
public List<GameObject> possibleThreeD = new List<GameObject>();
void Start()
{
//to avoid spawn more then one object at a position
for(int i=0;i<SpawnPoints.Length;i++)
{
possibleSpawn.Add(SpawnPoints[i]);
}
// to avoid to spawn same objects at different positions
for (int l=0;l<threeD.Length;l++)
{
possibleThreeD.Add(threeD[l]);
}
InvokeRepeating("SpawnItems", spawnTime, spawnTime);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag=="Obj")
{
//to avoid spawn more then one object at a position
for (int i = 0; i < SpawnPoints.Length; i++)
{
possibleSpawn.Add(SpawnPoints[i]);
}
// to avoid to spawn same objects at different positions
for (int l = 0; l<threeD.Length; l++)
{
possibleThreeD.Add(threeD[l]);
}
InvokeRepeating("SpawnItems", 2.5f, 2.5f);
}
}
void Update()
{
}
void SpawnItems()
{
if(possibleSpawn.Count>0 && possibleThreeD.Count>0)
{
for(int i=0;i<12;i++)
{
int spawnIndex = Random.Range(0, possibleSpawn.Count);
int spawnObject = Random.Range(0, possibleThreeD.Count);
GameObject newObj = Instantiate(possibleThreeD[spawnObject], possibleSpawn[spawnIndex].transform.position, possibleSpawn[spawnIndex].transform.rotation) as GameObject;
newObj.GetComponent<Destroyer>().mySpawnPoint = possibleSpawn[spawnIndex];
newObj.transform.parent = newObj.GetComponent<Destroyer>().mySpawnPoint.transform;
possibleSpawn.RemoveAt(spawnIndex);
possibleThreeD.RemoveAt(spawnObject);
i++;
}
}
}
}

Unity3D - Cant attach more then one body limb/mesh

I am trying to assemble together human from 3D model that have all limbs split. Like Head,Torso,Arms,Legs etc. And in runtime I would like to build a whole human with all those limbs assembled together. Here is what I do:
private void Start()
{
Debug.Log("Trying to build a Race!");
RaceModelSO firstRace = _races[0];
GameObject stiches = Instantiate(firstRace.GetRaceBodySlots()[0].mesh, new Vector3(773,0.83F,778), Quaternion.identity) as GameObject;
stiches.name = "HumanMale";
for (int i = 0; i < firstRace.GetRaceBodySlots().Length; i++)
{
if (i != 0)
{
GameObject bodyPart = Instantiate(firstRace.GetRaceBodySlots()[i].mesh) as GameObject;
_stitcher.Stitch(bodyPart, stiches);
}
}
}
Here is the actual Stitcher class that is assembling the limbs together.
public class Stitcher
{
public GameObject Stitch(GameObject sourceClothing, GameObject targetAvatar)
{
var boneCatalog = new TransformCatalog(targetAvatar.transform);
var skinnedMeshRenderers = sourceClothing.GetComponentsInChildren<SkinnedMeshRenderer>();
var targetClothing = AddChild(sourceClothing, targetAvatar.transform);
foreach (var sourceRenderer in skinnedMeshRenderers)
{
var targetRenderer = AddSkinnedMeshRenderer(sourceRenderer, targetClothing);
targetRenderer.bones = TranslateTransforms(sourceRenderer.bones, boneCatalog);
}
return targetClothing;
}
private GameObject AddChild(GameObject source, Transform parent)
{
source.transform.parent = parent;
foreach (Transform child in source.transform)
{
Object.Destroy(child.gameObject);
}
return source;
}
private SkinnedMeshRenderer AddSkinnedMeshRenderer(SkinnedMeshRenderer source, GameObject parent)
{
GameObject meshObject = new GameObject(source.name);
meshObject.transform.parent = parent.transform;
var target = meshObject.AddComponent<SkinnedMeshRenderer>();
target.sharedMesh = source.sharedMesh;
target.materials = source.materials;
return target;
}
private Transform[] TranslateTransforms(Transform[] sources, TransformCatalog transformCatalog)
{
var targets = new Transform[sources.Length];
for (var index = 0; index < sources.Length; index++)
targets[index] = DictionaryExtensions.Find(transformCatalog, sources[index].name);
return targets;
}
#region TransformCatalog
private class TransformCatalog : Dictionary<string, Transform>
{
#region Constructors
public TransformCatalog(Transform transform)
{
Catalog(transform);
}
#endregion
#region Catalog
private void Catalog(Transform transform)
{
if (ContainsKey(transform.name))
{
Remove(transform.name);
Add(transform.name, transform);
}
else
Add(transform.name, transform);
foreach (Transform child in transform)
Catalog(child);
}
#endregion
}
#endregion
#region DictionaryExtensions
private class DictionaryExtensions
{
public static TValue Find<TKey, TValue>(Dictionary<TKey, TValue> source, TKey key)
{
TValue value;
source.TryGetValue(key, out value);
return value;
}
}
#endregion
}
So let me explain the issue. In firstRace.GetRaceBodySlots() I have all the prefabs with all limbs of the body. I get all of them and I call _stitcher.Stitch() for each of them.
The problem is that the first limb is the one created before the :
for (int i = 0; i < firstRace.GetRaceBodySlots().Length; i++)
{
if (i != 0)
{
GameObject bodyPart = Instantiate(firstRace.GetRaceBodySlots()[i].mesh) as GameObject;
_stitcher.Stitch(bodyPart, stiches);
}
}
And that only the second limb gets attached properly all the others are there but not visible. Take a look:
So SOH_HM_1_Head is the first limb that was created before the foreach than is the second one SOH_HM_1_Body which is the first one inside the loop and only that one was created visible from all inside the foreach loop. All others are created but invisible. Take a look:
Do you have any idea why only the first limb from inside the foreach loop gets created visible? Any idea how can I solve this issue.
P.S.
Looks like the exact same issue was described here: Here
Can you suggest any other method rather than coroutine ?
For all people searching for a way to stitch many meshes into one all using same rig, I've come up with some small script: Unity-3D-MeshStitcher

Unity How to GetCompoent<Outline>() & GetCompoent<Shadow>() with a GameObject

public class ABCD : MonoBehaviour
{
[SerializeField]
Outline outline;
[SerializeField]
Shadow shadow;
private void Start()
{
outline = GetComponent<Outline>();
shadow = GetComponent<Shadow>();
}
}
we knew Outline is Implementation Shadow like this.
So When I call
shadow = GetCompoent<Shadow>();
It Possible find out the Outline Compoent , because Ouline is also a Shadow.
My question is How can I get the right Compoent?
And I can't drop the compoent to keep reference.
Because My code exactly like this
//this not a monobehavior
class MyText
{
Shadow shadow;
Outline outline;
public MyText(Transfrom transfrom)
{
outline = transfrom.GetComponent<Outline>();
shadow = transfrom.GetComponent<Shadow>();
}
}
If I create Compoent to keep reference, it will use more cost.
Use GetCompoents Can slove that , Anyone have better solution?
foreach (Shadow s in GetComponents<Shadow>())
{
if (s is Outline)
{
outline = s as Outline;
}
else
{
shadow = s;
}
}
The GetComponent will always return the first available member of asked type.
You need to use Get Components to get more than one. Here's an example from https://unitygem.wordpress.com/getcomponent-in-c/
using UnityEngine;
using System.Collections;
public class Health : MonoBehaviour
{
private Force [] scripts = null;
[SerializeField] private int health = 5;
private void Start()
{
this.scripts = this.gameObject.GetComponents<Force>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
for (int i = 0; i < this.scripts.Length; i++)
{
if(this.scripts[i].ReportForce())
{
this.health += 5;
}
}
}
}
}

How to reference a class from child to parent?

It may sound stupid but how can i reference a class from one script in child in another script in parent? I cant find anything on google. Note: There are couple errors in my script, that's not the point of the post.
//Public
//Private
private Rigidbody myRigidbody;
private Renderer myRenderer;
private Material tileDefaultMaterial;
private Material tileSelectedMaterial;
private Material tileSameGroupMaterial;
void Start () {
myRigidbody = GetComponent<Rigidbody> ();
myRenderer = GetComponent<Renderer> ();
tileDefaultMaterial = Resources.Load ("TileDefault", typeof(Material)) as Material;
tileSelectedMaterial = Resources.Load ("TileSelected", typeof(Material)) as Material;
tileSameGroupMaterial = Resources.Load ("TileSameGroup", typeof(Material)) as Material;
}
void Update () {
}
public class TileClass {
public int tileGroup = 0; //Indicates the Tile Group Number.
}
//Public
public GameObject[] allTiles; //Aray of all Tile GameObject.
public bool tileIsSelected = false; //If a Tile is Selected.
public int selectedTileGroup = 0; //Indicates the Selected Tile Group Number.
public int tileGroup = 0; //Indicates the Tile Group Number.
//Private
void Start () {
allTiles = new GameObject[transform.childCount];
for(int i = 0; i < transform.childCount; i++){
allTiles [i] = transform.GetChild (i).gameObject;
}
}
void Update () {
}
void OnMouseDown (){
RaycastHit hitInfo = new RaycastHit ();
bool hit = Physics.Raycast (Camera.main.ScreenPointToRay (Input.mousePosition), out hitInfo);
if (hitInfo.transform.gameObject.tag == "Tile" && tileIsSelected == false) {
Debug.Log ("A Tile is Selected!");
tileIsSelected = true;
selectedTileGroup = ;
for(int i = 0; i < allTiles.Length; i++){
if (this.tileGroup == selectedTileGroup) {
allTiles [i].GetComponent<Renderer> ().material = tileSameGroupMaterial;
}
}
myRenderer.material = tileSelectedMaterial;
} else if (hitInfo.transform.gameObject.tag == "Tile" && tileIsSelected == true) {
Debug.Log ("Second Tile is Clicked! (Should Swap them!)");
myRenderer.material = tileDefaultMaterial;
}
}
There is a famous saying :
var allTiles = transform.GetComponentsInChildren<Tile>();
And as I told you yesterday, Add OnMouseDown() in Tile.cs and write myRenderer.material = tileDefaultMaterial; there. No need to write this in TileManager.cs. And NO need to use Raycast when using OnMouseDown().
I can't read your image code, so I'll make up my own class names for the example. I'll call the parent class TileManager and the child class Tile.
Say in Tile you want access to the array of tiles in the TileManager. If allTiles is declared as public, you'd do in Tile somewhere.
TileManager tileManager = transform.parent.GetComponent<TileManager>();
var allTiles = tileManager.allTiles;
Now the Tile child has a reference to the array. Was this what you were wanting?
how about base.Method?
That should do it

How to manage variables in child script from another script in parent? [duplicate]

This question already has answers here:
How to reference a class from child to parent?
(3 answers)
Closed 6 years ago.
I am trying to manage some variables in TileScript like TileGroupID, TileIsSelected from the TilesManager script in parent but i cant find a way to do it. For example i want to search all the Tiles if one of them have TileIsSelected True and then do something in all Tiles that have TileGroupID 1. I created tilesArray to keep track all the game objects but i don't know how i can use that to do what i said earlier.
TileScript:
//Public
public int tileGroupID = 0; //Indicates the Tile Group ID.
public bool tileIsSelected = false; //If this Tile is Selected.
public GameObject[] tilesArray; //Reference to Tiles Array.
//Private
private Rigidbody myRigidbody;
private Renderer myRenderer;
private Material tileDefaultMaterial;
private Material tileSelectedMaterial;
private Material tileSameGroupMaterial;
void Start () {
tilesArray = GetComponentInParent<TilesManager> ().tilesArray;
myRigidbody = GetComponent<Rigidbody> ();
myRenderer = GetComponent<Renderer> ();
tileDefaultMaterial = Resources.Load ("TileDefault", typeof(Material)) as Material;
tileSelectedMaterial = Resources.Load ("TileSelected", typeof(Material)) as Material;
tileSameGroupMaterial = Resources.Load ("TileSameGroup", typeof(Material)) as Material;
}
TilesManager:
//Public
public GameObject[] tilesArray; //Aray of all Tiles GameObject.
//Private
void Start () {
tilesArray = new GameObject[transform.childCount];
for(int i = 0; i < transform.childCount; i++){
tilesArray [i] = transform.GetChild (i).gameObject;
}
}
void OnMouseDown (){
//Do stuff with each tile.
}
The OnMouseDown function should be moved from the TilesManager script to the TileScript script. It will be called when each Tile is clicked on. When this happens, you can call a function on the TilesManager function and pass it the current GameObject that was clicked on.
In your TileScript script:
void OnMouseDown()
{
//Let Tiles Manager know that there was a press on (this) Tile
tileManagerInstance.OnObjectSelection(this.gameObject);
}
In your TilesManager script:
public void OnObjectSelection(GameObject selectedObj)
{
//Change Material of the Selected GameObject
selectedObj.GetComponent<MeshRenderer>().material = tileSelectedMaterial;
}
when i click on a tile TileIsSelected change to true, change his
material and change all the other tile material that have same
TileGroupID as the one i clicked.
All you need now is a basic for loop. Loop through the tilesArray array and check which one has the-same tileGroupID as the GameObject that was clicked.
void findTheSameTileGroupIDAndChangeColor(GameObject selectedObj)
{
//Get the TileScript attached to the selectedObj
TileScript selectedTileScript = selectedObj.GetComponent<TileScript>();
//Loop through all GameObject in the array
for (int i = 0; i < tilesArray.Length; i++)
{
/*
Make sure this is NOT selectedObj since we've arleady
changed its Material in OnObjectSelection function
*/
if (selectedObj != tilesArray[i])
{
//Get TileScript attached to the current Tile loop
TileScript tileLoop = tilesArray[i].GetComponent<TileScript>();
//Check if selectedObj and the current loop tileGroupID matches
if (selectedTileScript.tileGroupID == tileLoop.tileGroupID)
{
//It matches! Now, change it's color
tileLoop.GetComponent<MeshRenderer>().material = tileSelectedMaterial;
}
}
}
}
Finally the Whole thing I said put together:
TileScript:
public class TileScript : MonoBehaviour
{
public int tileGroupID = 0; //Indicates the Tile Group ID.
public bool tileIsSelected = false; //If this Tile is Selected.
public GameObject[] tilesArray; //Reference to Tiles Array.
//Private
private Rigidbody myRigidbody;
private Renderer myRenderer;
private Material tileDefaultMaterial;
private Material tileSelectedMaterial;
private Material tileSameGroupMaterial;
private TilesManager tileManager;
void Start()
{
tilesArray = GetComponentInParent<TilesManager>().tilesArray;
myRigidbody = GetComponent<Rigidbody>();
myRenderer = GetComponent<Renderer>();
tileDefaultMaterial = Resources.Load("TileDefault", typeof(Material)) as Material;
tileSelectedMaterial = Resources.Load("TileSelected", typeof(Material)) as Material;
tileSameGroupMaterial = Resources.Load("TileSameGroup", typeof(Material)) as Material;
//Get Tiles Manager
GameObject tileManagerObj = GameObject.Find("Tiles");
tileManager = tileManagerObj.GetComponent<TilesManager>();
}
void OnMouseDown()
{
//Let Tiles Manager know that there was a press on (this) Tile
tileManager.OnObjectSelection(this.gameObject);
}
}
TilesManager:
public class TilesManager : MonoBehaviour
{
public GameObject[] tilesArray; //Aray of all Tiles GameObject.
public Material tileSelectedMaterial;
void Start()
{
tilesArray = new GameObject[transform.childCount];
for (int i = 0; i < transform.childCount; i++)
{
tilesArray[i] = transform.GetChild(i).gameObject;
}
}
//Will be called from the TileScript(Receives any tile that is selected)
public void OnObjectSelection(GameObject selectedObj)
{
//Change Material of the Selected GameObject
selectedObj.GetComponent<MeshRenderer>().material = tileSelectedMaterial;
//Change Mateial of other GameObjects that matches the tileGroupID of the selectedObj
findTheSameTileGroupIDAndChangeColor(selectedObj);
}
void findTheSameTileGroupIDAndChangeColor(GameObject selectedObj)
{
//Get the TileScript attached to the selectedObj
TileScript selectedTileScript = selectedObj.GetComponent<TileScript>();
//Loop through all GameObject in the array
for (int i = 0; i < tilesArray.Length; i++)
{
/*
Make sure this is NOT selectedObj since we've arleady
changed its Material in OnObjectSelection function
*/
if (selectedObj != tilesArray[i])
{
//Get TileScript attached to the current Tile loop
TileScript tileLoop = tilesArray[i].GetComponent<TileScript>();
//Check if selectedObj and the current loop tileGroupID matches
if (selectedTileScript.tileGroupID == tileLoop.tileGroupID)
{
//It matches! Now, change it's color
tileLoop.GetComponent<MeshRenderer>().material = tileSelectedMaterial;
}
}
}
}
}

Categories