Create material from code and assign it to an object - c#

I am very new to Unity3d. I have a prefab which contains 6 quads making it a cube. I want to add image textures to different faces of cube.
I am getting images from a web service, so I have to add or change material in the script.
The problem I am facing is, I am not able to find material property in gameObject.
I have tried below code:
using UnityEngine;
using System.Collections;
public class shelfRuntime : MonoBehaviour {
public GameObject bottle;
public GameObject newBottle;
// Use this for initialization
void Start () {
iterateChildren(newBottle.transform.root);
GameObject rocketClone = (GameObject)Instantiate(bottle, bottle.transform.position, bottle.transform.rotation);
rocketClone.transform.localScale += new Vector3(1, 1, 1);
GameObject newBottleInMaking = (GameObject)Instantiate(newBottle, newBottle.transform.position, newBottle.transform.rotation);
Transform[] allChildren = newBottleInMaking.GetComponentsInChildren<Transform>();
foreach (Transform child in allChildren)
{
// do whatever with child transform here
}
}
void iterateChildren(Transform trans)
{
Debug.Log(trans.name);
if (trans.name == "Back") {
var ting = trans.gameObject.GetComponent<Renderer>();
// trans.renderer.material // there is no material property here
}
// Do whatever logic you want on child objects here
if (trans.childCount == 0) return;
foreach (Transform tran in trans)
{
iterateChildren(tran);
}
}
// Update is called once per frame
void Update () {
}
}
How to set material to quads? There are 6 quads inside my prefab.

You can no longer access some components directly in Unity. You must use GetComponent to get the component(Renderer) then access the material from it.
trans.renderer.material = ....
should be changed to
trans.GetComponent<Renderer>().material = yourNewMaterial;
Finally, when Cube or Quad is created in Unity, MeshRenderer is automtatically attached to them not Renderer. So, you might get run-time error with GetComponent<Renderer>(). Use MeshRenderer instead.
trans.GetComponent<MeshRenderer>().material = yourNewMaterial;
To create Material during run-time:
Material myNewMaterial = new Material(Shader.Find("Standard"));
The example below will create a Material, assign standard shader to it then change the texture to the texture from the myTexture variable before applying it to a GameObject.
public Texture myTexture;
void Start()
{
//Find the Standard Shader
Material myNewMaterial = new Material(Shader.Find("Standard"));
//Set Texture on the material
myNewMaterial.SetTexture("_MainTex", myTexture);
//Apply to GameObject
trans.GetComponent<MeshRenderer>().material = myNewMaterial;
}

Related

How would I edit an attribute of a component of a game object within Unity through a C# file?

I'm trying to make a script that will dynamically change the near clip plane's distance based on how close the camera is to "nodes" that I place down. The only issue is that I am not sure how to refer to the near clip plane field on the camera component in the C# script in order to change it during runtime. Image of the camera component for reference.
Also, here is the script so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NodeManager : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
List<Transform> nodeArray = new List<Transform>;
var parentNode = transform;
Transform[] children = GetComponentsOnChildren<Transform>();
foreach (Transform child in children)
{
nodeArray.Add(child);
}
List<float> distanceMagnitudes = new List<float>;
}
// Update is called once per frame
void Update()
{
for(var i = 0; i < parentNode.childCount; i++)
{
Vector3 offset = nodeArray[i].position - this.position;
distanceMagnitudes[i] = offset.sqrMagnitude;
}
float distanceToClosest = sqrt(distanceMagnitudes.min());
if(distanceToClosest < 10)
{
parentNode.Find("Camera Offset").Find("Main Camera").
}
}
}
First of all try not to use Find("Main Camera") to get camera in your scene instead define the camera before and set it's value in the Start() method like this or make it public and set it in the inspector
Camera mainCamera;
private void Start()
{
// find by type if you will only have one camera always
mainCamera = FindObjectOfType<Camera>();
// find by tag if you might have several cameras
mainCamera = GameObject.FindWithTag("Your Camera Tag").GetComponent<Camera>();
}
You can get the near clip wherever by..
mainCamera.nearClipPlane = 1.0f; //Your Value

How do I find the location of an object through code

I'm trying to attach the camera to a character's xPos without grouping the camera to the player
The thing to be careful about is whether you mean you want the camera to be linked to the character's local x-position or if you want the camera to be linked to the character's world x-position. The two will vary based on the transforms of the character's parent GameObjects.
You can either tag the character and find it by doing a lookup in Start() or you can make the character reference a public GameObject targetCharacter in your camera script and set the reference manually in the editor.
Once you determine how you're going to do it:
public class CameraLocator : MonoBehaviour
{
public GameObject targetCharacter;
void Start()
{
if(targetCharacter == null)
{
targetCharacter = GameObject.FindGameObjectWithTag("YourCharacterTag");
if(targetCharacter == null)
{
Debug.LogError("targetCharacter is not set and could not be found!");
}
}
}
void Update()
{
if(targetCharacter != null)
{
var camPosition = this.gameObject.transform.position;
var targetPosition = targetCharacter.transform.position;
camPosition.x = targetPosition.x;
this.gameObject.transform.position = camPosition;
}
}

Unity Changing Material Tiling per Gameobject

I've written a script that is supposed to make tiling materials easier. I was tired of constantly re-adjusting the tiling settings of materials on my walls as I resized them.
[ExecuteInEditMode]
public class AutoTiler : MonoBehaviour
{
public float xScaleMult = 1;
public float yScaleMult = 1;
private Material texture;
// Update is called once per frame
void Update()
{
if (!texture)
{
texture = GetComponent<MeshRenderer>().material; //get the material if it is null
}
//set the material scale based on the size of the object
texture.mainTextureScale = new Vector2(Mathf.Max(transform.localScale.x, transform.localScale.z) * xScaleMult, transform.localScale.y * yScaleMult);
}
}
However, every time I reload the scene, I get an error message saying:
Instantiating material due to calling renderer.material during edit mode. This will leak materials into the scene. You most likely want to use renderer.sharedMaterial instead.
I've looked up this error but I can't quite figure out how to solve it. I understand that I'm copying the material in order to change it, but that is the desired result, as it needs different tiling settings for every wall, so that it does not appear stretched. Currently the code works, but fills the console with this error message. Is there a better way to do this?
in editor you can use sharedmaterial instead material property, something like this:
using UnityEngine;
[ExecuteInEditMode]
public class AutoTiler : MonoBehaviour
{
public float xScaleMult = 1;
public float yScaleMult = 1;
private Material material;
private Material Material
{
get
{
if (material == null)
{
MeshRenderer render = GetComponent<MeshRenderer>();
#if UNITY_EDITOR
material = render.sharedMaterial;
#else
material = render.material;
#endif
}
return material;
}
}
// Update is called once per frame
void Update()
{
//set the material scale based on the size of the object
Material.mainTextureScale = new Vector2(Mathf.Max(transform.localScale.x, transform.localScale.z) * xScaleMult, transform.localScale.y * yScaleMult);
}
}
for more details about difference between material and sharedmaterial see :
http://gyanendushekhar.com/2018/09/23/difference-material-shared-material-unity-tutorial/
https://docs.unity3d.com/ScriptReference/Renderer-sharedMaterial.html

Change the color of an instantiated prefab Unity

so the player hits a button to create a box. I need this box to be randomly varying between a few colors. I also need this box to have a tag corresponding to said color. Green box - "greenBlock" tag etc.
I have instantiated the box and then try to change it's material with material.color. It doesn't do anything. I've seen suggestions of sharedMaterial but having tried that found it just ends up changing the color of every game object in the scene. I think I'm fetching the box prefabs renderer correctly? Any help would be appreciated!
Here's what I have so far:
public class ButtonScript : MonoBehaviour
{
public GameObject Box;
public Transform spawnpoint1;
public Transform spawnpoint2;
public Rigidbody2D player;
public Renderer boxRenderer;
[SerializeField]
private Color boxColor;
[SerializeField]
private AudioSource actionSound;
// Update is called once per frame
private void Start()
{
//boxRenderer = Box.gameObject.GetComponent<Renderer>();
boxRenderer = GameObject.Find("Box").GetComponent<Renderer>(); // Find the renderer of the box prefab
}
public void OnTriggerStay2D(Collider2D col)
{
if (player) // If it's the player in the collider trigger
{
if (Input.GetKeyDown(KeyCode.E))
{
Instantiate(Box, spawnpoint1.position, Quaternion.identity);
boxRenderer.material.color = boxColor; // change the color after it is instantiated
actionSound.Play();
}
}
}
}
boxRenderer.material.SetColor("_Color", boxColor);
or
boxRenderer.material.color = new Color(0.5, 0.5, 0.0, 1.0);
And when you instantiate the box, you need to get the render for the box at this point as it is a new object. So:
if (Input.GetKeyDown(KeyCode.E))
{
Box = Instantiate(Box, spawnpoint1.position, Quaternion.identity);
boxRenderer = Box.transform.GetComponent<Renderer>();
boxRenderer.material.color = boxColor; // change the color after it is instantiated
actionSound.Play();
}
Note you have creating a New color and assigning it, you can't modify the color there as it may be used on other objects that are using the same material.
Check out SetColor in the docs, that is setting the shader property called _Color which is the default color element in shaders, you can of course have more depending on the shader.

Attaching dynamic Line Renderer to GameObject

I've created a (3D)-canvas which includes a list. The canvas is located in the world space. It also faces to the camera all the time when it's activated by selecting a GameObject. Now I'd like the add a dynamic Line Renderer between the upper left corner of the canvas and the point of the RaycastHit / selected object (see sketch). The position of the LR on the GO-side should be static, the LR on the list-side should be dynamic. Thus, it should stay attached at the corner even the canvas starts rotating in all axis (x,y,z).
I already stuck at the positioning of the LR on the list-side. I don't get the corner-coordinates to attach the LR there. I've already tried the bounds-method and some others but I'm not getting ahead. My code so far:
//GUI elements
public Canvas canvas_list;
public GameObject content_list;
LineRenderer lr_list;
// Use this for initialization
void Start ()
{
Setup_LR();
}
// Update is called once per frame
void Update ()
{
// Canvas facing to the camera
canvas_list.transform.rotation = ar_camera.transform.rotation;
// LineRenderer is always attachted to canvas (Part 1)
lr_list.SetPosition(1, new Vector3(canvas_list.GetComponent<Collider>().bounds.min.x, canvas_list.GetComponent<Collider>().bounds.max.y, canvas_list.GetComponent<Collider>().bounds.min.z));
// Selecting objects
if (Input.GetMouseButtonDown(0))
{
Selecting_Objects();
}
}
void Selecting_Objects()
{
raycastHit = new RaycastHit();
if (Physics.Raycast(ar_camera.ScreenPointToRay(Input.mousePosition), out raycastHit))
{
if (!EventSystem.current.IsPointerOverGameObject())
{
GameObject selected_object = raycastHit.collider.gameObject;
// LineRenderer is always attachted to building component (Part 2) - avoids that the line renderer gets attached to GUI element
lr_list.SetPosition(0, raycastHit.point);
}
}
}
void Setup_LR()
{
canvas_list.transform.gameObject.AddComponent<BoxCollider>();
lr_list = new GameObject().AddComponent<LineRenderer>();
lr_list.material.color = Color.red;
lr_list.startWidth = 0.01f;
lr_list.endWidth = 0.01f;
}
That's how it looks now:
Thanks!

Categories