I am trying to change game objects, specifically Cubes, to change material during run-time.
Rather then trying to search for material by name, I decided to try a different approach, assign a few game objects with pre-set materials, then when I want to change material of something, I am trying to get it from those pre-made objects.
I already spent some time trying to fix that, found a few possible mistakes, fixed them, but I still don't get the result I need.
And I also use debugging, will give more information below.
So, I first created a few Materials as global variables.
public Material MaterialGrass;
public Material MaterialRock;
public Material MaterialSand;
public Material MaterialWater;
Then, I am trying to load actual materials from pre-set game objects.
GameObject MainVariablesObject = GameObject.Find("MainVariablesObject");
GlobalVariablesScript thisGlobalVariablesScript = MainVariablesObject.GetComponent<GlobalVariablesScript>();
thisGlobalVariablesScript.MaterialGrass = GameObject.Find("GrassTexture").GetComponent<MeshRenderer>().materials[0];
thisGlobalVariablesScript.MaterialRock = GameObject.Find("RockTexture").GetComponent<MeshRenderer>().materials[0];
thisGlobalVariablesScript.MaterialSand = GameObject.Find("SandTexture").GetComponent<MeshRenderer>().materials[0];
thisGlobalVariablesScript.MaterialWater = GameObject.Find("WaterTexture").GetComponent<MeshRenderer>().materials[0];
The game view is divided into 9 squares.
GameObject CubeFront0 = GameObject.Find("CubeFront0");
GameObject CubeFront1 = GameObject.Find("CubeFront1");
GameObject CubeFront2 = GameObject.Find("CubeFront2");
GameObject CubeLeft0 = GameObject.Find("CubeLeft0");
GameObject CubeLeft1 = GameObject.Find("CubeLeft1");
GameObject CubeLeft2 = GameObject.Find("CubeLeft2");
GameObject CubeRight0 = GameObject.Find("CubeRight0");
GameObject CubeRight1 = GameObject.Find("CubeRight1");
GameObject CubeRight2 = GameObject.Find("CubeRight2");
And now the part that I still didn't manage to solve:
public static void DisplayPlayerView()
{
GameObject MainVariablesObject = GameObject.Find("MainVariablesObject");
GlobalVariablesScript thisGlobalVariablesScript = MainVariablesObject.GetComponent<GlobalVariablesScript>();
DefineTexturesSources();
GetSurroundingCells();
GameObject CubeFront0 = GameObject.Find("CubeFront0");
GameObject CubeFront1 = GameObject.Find("CubeFront1");
GameObject CubeFront2 = GameObject.Find("CubeFront2");
GameObject CubeLeft0 = GameObject.Find("CubeLeft0");
GameObject CubeLeft1 = GameObject.Find("CubeLeft1");
GameObject CubeLeft2 = GameObject.Find("CubeLeft2");
GameObject CubeRight0 = GameObject.Find("CubeRight0");
GameObject CubeRight1 = GameObject.Find("CubeRight1");
GameObject CubeRight2 = GameObject.Find("CubeRight2");
GameObject[] Cubes = new GameObject[] { CubeFront0, CubeFront1, CubeFront2,
CubeLeft0, CubeLeft1, CubeLeft2,
CubeRight0, CubeRight1, CubeRight2};
char[] Map = new char[] { thisGlobalVariablesScript.Front0, thisGlobalVariablesScript.Front1, thisGlobalVariablesScript.Front2,
thisGlobalVariablesScript.Left0, thisGlobalVariablesScript.Left1, thisGlobalVariablesScript.Left2,
thisGlobalVariablesScript.Right0, thisGlobalVariablesScript.Right1, thisGlobalVariablesScript.Right2};
for (int i = 0; i < 9; i++)
{
Material thisMaterial = Cubes[i].GetComponent<MeshRenderer>().materials[0];
//MeshRenderer thisMaterial = Cubes[i].GetComponent<MeshRenderer>();
if (Map[i] == 'G')
thisMaterial = thisGlobalVariablesScript.MaterialGrass;
if (Map[i] == 'R')
thisMaterial = thisGlobalVariablesScript.MaterialRock;
if (Map[i] == 'S')
thisMaterial = thisGlobalVariablesScript.MaterialSand;
if (Map[i] == 'W')
thisMaterial = thisGlobalVariablesScript.MaterialWater;
Cubes[i].GetComponent<MeshRenderer>().materials[0] = thisMaterial;
Debug.Log(Cubes[i].name + " " + Cubes[i].GetComponent<MeshRenderer>().materials[0]);
}
What I found by placing those Debug.Log commands:
1) All game objects I try to "Find" exist, there is no error here.
2) "thisMaterial" gets the correct material, so I think there is no problem with defining materials as variables, and getting correct data.
3) I no longer get error that material isn't attached.
4) Inside the "for" loop, the "thisMaterial" shows new and correct data, and "Cubes[i].GetComponent().materials[0]" shows old data - materials I set by hand in the beginning.
5) The data in array "Map" is correct.
What I am missing?
Related
I'm stuck with this one for quite some time now. I am trying to create spheres via script and update their position based on the position of points. Their position is updating on the Debug.Log() but they are not moving in Game View.
Here is my code:
void createSpheres(int objCount, float xPointsPos, float yPointsPos){
var sphereCreator = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphereCreator.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
sphereCreator.transform.position = new Vector3(xPointsPos, yPointsPos, 0);
sphereCreator.AddComponent<Rigidbody>();
sphereCreator.GetComponent<Rigidbody>().useGravity = false;
sphereCreator.AddComponent<SphereCollider>();
//ADD THE SPHERES TO THE SPHERELIST
sphereList = new List<Sphere>();
for(int loop = 0; loop < objCount-1; loop++){
Sphere temp = new Sphere();
temp.sphereName = "sphere"+sphereNameCount;
temp.sphereObj = sphereCreator;
temp.sphereXPos = xPointsPos;
temp.sphereYPos = yPointsPos;
sphereList.Add(temp);
}
sphereNameCount++;
}
void UpdateSpheres()
{
for(int i = 0; i < sphereList.Count - 1; i++){
sphereList[i].sphereXPos = points[i].position.x;
sphereList[i].sphereYPos = points[i].position.y;
Debug.Log($"{sphereList[i].sphereXPos}" + " -- " + $"{points[i].position.x}");
}
}
public class Sphere{
public string sphereName;
public float sphereXPos;
public float sphereYPos;
public GameObject sphereObj;
}
The createSpheres() method is called inside a loop containing how many points are spawned to match it.
I also tried checking if the ArrayList is empty or not using Debug.Log() and it returned all the Sphere gameObjects that I added.
Any help or hint will be highly appreciated. Thanks!
You do not create a number of spheres according to the code. You create just a single sphere and assign it to all your Sphere instances. To create and move your spheres:
create GameObject object for every object instead of assigning the same object for each Sphere class instance
use .transform.position of the created object, assigned to Sphere class instance to move the corresponding GameObject instance
I'm making a save system for my game, and have run into a NullReferenceException while loading the keybindings for one of the player objects (player 2). Since I used identical code to load the keybinds for player 1, and that one works fine, I believe that the problem comes from player 2 being inactive when the load script is run.
Here's most of the relevant code, based on Brackey's Save/Load tutorial:
public string[] player1;
public string[] player2;
public GameData(AchievementsManager am, GameObject play2)
{
var p1 = play1.GetComponent<playerMovement>();
var p2 = play2.GetComponent<playerMovement>();
player1 = new string[4] { p1.up, p1.down, p1.left, p1.right};
player2 = new string[4] { p2.up, p2.down, p2.left, p2.right};
}
public class SaveManager : MonoBehaviour
{
public GameObject play2;
void Start()
{
GameData data = SaveSystem.loadGame();
if (data != null)
{
var p1 = play1.GetComponent<playerMovement>();
var p2 = play2.GetComponent<playerMovement>();
string[] p1_controls = data.player1;
string[] p2_controls = data.player2;
p1.up = p1_controls[0]; p2.up = p2_controls[0];
p1.down = p1_controls[1]; p2.down = p2_controls[1];
p1.left = p1_controls[2]; p2.left = p2_controls[2];
p1.right = p1_controls[3]; p2.right = p2_controls[3];
}
}
}
The error is on the line p2.up = p2_controls[0];, but I think that it's because there's a problem with play2.GetComponent<playerMovement>(); related to player 2 being inactive at startup. Is there a good way to fix this without making player 2 active at startup, or at least a way to better understand what exactly is causing the problem?
play2 is a GameObject referenced in the class header of SaveManager. play2 does have a playerMovement component that runs properly. I think that the NullReferenceException comes from the SaveManager not properly loading the information that it contains.
You should use in the GameData() an If(play2 != null) before trying to access to the P2 variable and assign it.
And you can do the same thing in the Start () when you assign the P2 variables
i am creating a video game like Crypt of the necrodancer and i am blocked when i want to generate a map
my idea was simple :
make a grid system and replace some tiles to have specific tiles (obstacle, wall...) but i don't know how i should store the information to replace my tile.
I based my grid generation map from Sebastian Lague tutorial
i would like to replace in the inspector my tiles
as far as i am, i've found a script for replacing some tiles by another but i can't save it...
Here is the script for replacing stuff
using UnityEngine;
using UnityEditor;
public class ReplaceWithPrefab : EditorWindow
{
[SerializeField] private GameObject prefab;
[MenuItem("Tools/Replace With Prefab")]
static void CreateReplaceWithPrefab()
{
EditorWindow.GetWindow<ReplaceWithPrefab>();
}
private void OnGUI()
{
prefab = (GameObject)EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), false);
if (GUILayout.Button("Replace"))
{
var selection = Selection.gameObjects;
for (var i = selection.Length - 1; i >= 0; --i)
{
var selected = selection[i];
var prefabType = PrefabUtility.GetPrefabType(prefab);
GameObject newObject;
if (prefabType == PrefabType.Prefab)
{
newObject = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
}
else
{
newObject = Instantiate(prefab);
newObject.name = prefab.name;
}
if (newObject == null)
{
Debug.LogError("Error instantiating prefab");
break;
}
Undo.RegisterCreatedObjectUndo(newObject, "Replace With Prefabs");
newObject.transform.parent = selected.transform.parent;
newObject.transform.localPosition = selected.transform.localPosition;
newObject.transform.localRotation = selected.transform.localRotation;
newObject.transform.localScale = selected.transform.localScale;
newObject.transform.SetSiblingIndex(selected.transform.GetSiblingIndex());
Undo.DestroyObjectImmediate(selected);
}
}
GUI.enabled = false;
EditorGUILayout.LabelField("Selection count: " + Selection.objects.Length);
}
}
any ideas for how i could save the informations?
Unity recently added a tilemap system a few versions ago. It seems to be what you're looking for and it already handles the grids for you.
You can read more about it here:
https://docs.unity3d.com/Manual/class-Tilemap.html
You can edit the tilemap with the SetTile method and a Tile object, rather than instanciating a bunch of prefabs. The Tiles themselves are created with a tile palette which you can also read more about in the page above.
This may seem like a stupid question but I'm stuck with it. I have GameObjects in a list (List<GameObject>) and I want to add them on the scene runtime, prefarbly on predefined places (like placeholders or something). What would be a good way to do it? I've been searching the net but can't really find anything that would solve this. This is my code so far:
public static List<GameObject> imglist = new List<GameObject>();
private Vector3 newposition;
public static GameObject firstGO;
public GameObject frame1;//added line
void Start (){
newposition = transform.position;
firstGO = GameObject.Find ("pic1");
frame1 = GameObject.Find ("Placeholder1");//added line
//this happens when a button is pressed
imglist.Add(firstGO);
foreach(GameObject gos in imglist ){
if(gos != null){
print("List: " + gos.name);
try{
//Vector3 temp = new Vector3 (0f, 0f, -5f);
Vector3 temp = new Vector3( frame1.transform.position.x, frame1.transform.position.y, -1f);//added line
newposition = temp;
gos.transform.position += newposition;
print ("position: " + gos.transform.position);
}catch(System.NullReferenceException e){}
}
}
}
How can I place the pics (5) on the predefined spots?
//----------------
EDIT: Now I can place 1 image to a placeholder (transparent png). For some reason z-value goes all over the place so it needs to be forced to -1f but that's OK. I add the images to the list from other scenes and there can be 1-5 of them. Do I need to put the placeholders in another list or array? I'm a bit lost here.
If you've already created 5 new objects you can just do like they do here:
http://unity3d.com/learn/tutorials/modules/beginner/scripting/invoke under the InvokeScript
foreach(GameObject gos in imglist)
{
Instantiate(gos, new Vector3(0, 2, 0), Quaternion.identity);
}
I don't really understand what you're trying to do, but if I'm correct and you have a list of objects, and you know where you want to move them at runtime, just make two lists,
one containing the objects and
one containing transforms of empty game-objects in the scene placed at those predefined positions, and match them at runtime.
Populate both lists from the inspector.
public List<GameObject> imglist = new List<GameObject>();
public List<Transform> imgPositions = new List<Transform>();
void Start()
{
for(var i = 0 i < imglist.Count; ++i)
{
imglist[i].transform.position = imgPositions[i].position
}
}
The general best way is to create prefabs for your objects, passing them as a parameter and instantiate when needed (Start in your case). That's the common case, but maybe yours is slightly different.
This is an example of passing a prefabs array and to instantiate one object for each one in the array:
public GameObject prefabs[];
List<GameObject> objects = new List<GameObject>();
void Start() {
for(GameObject prefab in prefabs) {
GameObject go = Instantiate(prefab, Vector3.zero, Quaternion.identity) as GameObject; // Replace Vector3.zero by actual position
objects.Add(go); // Store objects to access them later: total enemies count, restart game, etc.
}
}
In case you need several instances for the same prefab (multiple enemies or items, for instance) just adapt code above.
I am new to Unity3D and C# and have been struggling to find a convenient way to reuse the same prefab, with unique name & tag, and Add it to a List.
I have the following test code:
public class testScript : MonoBehaviour {
public static List<string> cardDeckBackListNames = new List<string> {
"HAB", "H2B", "H3B", "H4B", "H5B", "H6B", "H7B", "H8B", "H9B", "H10B", "HJB", "HQB", "HKB",};
private GameObject temp_GameObject;
public static List<GameObject> master_GameObject_List = new List<GameObject> ();
// Use this for initialization
void Start () {
temp_GameObject = Resources.Load ("BackSide") as GameObject;
temp_GameObject.name = cardDeckBackListNames [0];
temp_GameObject.tag = cardDeckBackListNames [0];
master_GameObject_List.Add (temp_GameObject);
temp_GameObject = Resources.Load ("BackSide") as GameObject;
temp_GameObject.name = cardDeckBackListNames [1];
temp_GameObject.tag = cardDeckBackListNames [1];
master_GameObject_List.Add (temp_GameObject);
int yy = 0;
foreach (GameObject aGO in master_GameObject_List) {
print ("After loading: " + aGO.tag + " #"+ yy);
yy++;
}
print ("master_GameObject_List.Count: " + master_GameObject_List.Count);
}
}
Get the following result:
After loading: H2B #0
After loading: H2B #1
master_GameObject_List.Count: 2
My conclusion is that I cannot use the same GameObject and "only" modify the name & tag as it seems like when i do that it also modify all objects that i have loaded into the List.
As i am very new at C# i would like to ask what would be the correct way of doing what i am trying to do? 1.) reusing the same prefab with unique name & tag, 2.) load the "unique" objects into a List
ps. I initially used a "foreach" loop but removed that to be able to do a clean and simple test.
Problem
When you use Resources.Load() you loading the prefab, but every time you make a change you are editing the same prefab.
Here is a picture of the editor showing you what you are pointing to:
Solution
Since you want unique GameObjects the first thing you need to do is Instantiate them. After that you can edit each individual GameObject as you like.
void Start ()
{
temp_GameObject = Instantiate(Resources.Load("BackSide")) as GameObject;
temp_GameObject.name = cardDeckBackListNames[0];
temp_GameObject.tag = cardDeckBackListNames[0];
master_GameObject_List.Add(temp_GameObject);
temp_GameObject = Instantiate(Resources.Load("BackSide")) as GameObject;
temp_GameObject.name = cardDeckBackListNames[1];
temp_GameObject.tag = cardDeckBackListNames[1];
master_GameObject_List.Add(temp_GameObject);
}
These are the results: