I need to have an object in my scene change between two different materials at run time, when ever a button is pressed in my Unity project. However, I have never done this before and I'm having an issue getting my head around how to do this.
In my scene I have one game object I've called my controller. This script holds my material switching class and is looking like this:
public GameObject cupMesh;
bool isOn = true;
// Use this for initialization
void Start ()
{
cupMesh = GameObject.Find("CupMesh");
}
// Update is called once per frame
void Update ()
{
}
void OnGUI()
{
if(GUI.Button(new Rect(10,10, 100, 40), "Show mesh"))
{
renderer.enabled = false;
}
}
I know this doesn't change the material, but the above code does nothing. I've never modified anything on the mesh renderer before but I know there is a list of materials on it.
How can I access that list so I can have my program switch between the two materials found there?
To show or hide a gameObject, rather than using render.enabled property, you should use this:
// Unactivates the game object.
gameObject.SetActive (false);
However it's not clear from the code if you want to adjust the material of the object the script is attached to, or the cupMesh game object.
If you wanted to make the cupMesh disappear, you would use:
cupMesh.SetActive (false);
Or if you wanted to access the material component of the cupMesh, this is
cupMesh.renderer.material
Related
I am making a little game and I want my character to disappear until you press space, but I don't want to destroy it since it has a script inside. I tried what I wrote below, but it didn't work.
void Start () {
gameObject.SetActive(false);
}
void Update () {
if (Input.GetKeyDown(Keycode.Space)) {
gameObject.SetActive(true);
}
Have any clues to fix it or replacement code?
You set the GameObject to disabled with SetActive(false). As a result, the Update() method is no longer executing. Because of this, the code that will unhide your character can never run.
There are many ways to get the behavior you want, but the simplest I can think of is this: Instead of disabling the entire gameobject, just disable the renderer (whether it's a sprite renderer or mesh renderer). This will make your character disappear, but this script will keep running.
Example:
public Renderer renderer; // drag your renderer component here in the Unity inspector
void Start ()
{
renderer.enabled = false;
}
void Update ()
{
if (Input.GetKeyDown(Keycode.Space))
renderer.enabled = true;
}
Another approach (and I think, better) is to create a child object of your character's GameObject, call it "Body", and place everything that deals with rendering your character in there. Then disable/enable that child gameobject as desired.
Using gameObject.SetActive(false) disables the gameObject and all its components, including the script you're running so it can't turn itself on if the script driving it is off.
If you add the script to a parent object of the object you're trying to disable you can get the effect you're looking for like this:
public gameObejct childObject;
void Start()
{
childObject.SetActive(false);
}
void Update()
{
if (Input.GetKeyDown(Keycode.Space))
{
childObject.SetActive(true);
}
}
I have a game wehre you are a cube and you dodge obstacles, I just implemented the ability to change the color of the cube like changing skin. I did that by assigning a different material to the player when he presses "2".
Here is the script:
void Update()
{
if (Input.GetKeyDown("2"))
{
Object.GetComponent<MeshRenderer>().material = Material1;
}
}
When you die the scene resets and when you win a new scene is loaded, I would like the game to remember the material change even after the scene is reset or a new scene is loaded. I have done some research and found something called "PlayerPrefs" and I have been playing around with it but nothing even got close to working and I didn't really understand what i was doing.
I really want to understand how this works becuase I know i will be using it alot when making games. Can someone help me understand?
Thanks.
Create one gameobject and apply this script
public class SavingMaterial : MonoBehaviour
{
public static SavingMaterial instance;
public Material mat;
void Awake()
{
if(instance == null)
{
instance = this;
DontDestroyOnLoad(base.gameObject);
}
else
{
Destroy(base.gameObject);
}
}
public void StoreMaterial(MeshRenderer mesh)
{
mat = mesh.material;
}
}
When you want to store material call this function like this
SavingMaterial.instance.StoreMaterial(you meshrenderer component);
and when you need this material just get from this class like this
material = SavingMaterial.instance.mat;
and note that if you quit the game you loose saved material as it is store in variable otherwise scene change and reset won't affect it.
you only save int,string and bool in playerprefs. you have to store values of materials into string and then save this string into playerprefs.
alternatively you can assign this material to local variable and set that script on Dontdestroyonload so it will not reset as scene is destroy or reset.
I want to make a robot on unity3D. I want to make the gripper of the robot when collide with object attach the gripper. So the object will follow the gripper.
What will be added to this script in order to make some thing like this?
private Rigidbody gripper;
void Start()
{
gripper_part01 = GetComponent<Rigidbody>();
}
void Update()
{
if (Input.GetKey("a"))
gripper.AddForce(transform.forward * 100);
}
void OnCollisionEnter(Collider obj1)
{
// how to make obj1 follow the gripper
}
It can be done by various methods. But the simplest would be making the obj1 child of gripper as soon it collides.
code will look some thing like this
void OnCollisionEnter(Collider obj1)
{
// how to make obj1 follow the gripper
obj1.transform.parent = gripper.transform;
}
Look into the state machine pattern
http://gameprogrammingpatterns.com/state.html
You'll want to create 2 states:
Not Following(Default)
Following
When the player enters the collider, switch the state to following. I'm not going to write you an entire state machine/state switching architecture for you to copy & paste, but I can tell you that you're going to want to make the states themselves a MonoBehavior class, and the actual StateMachine will not have any parent classes and will be attached to the object's controllers.
Then, you'll want to keep track of the player's coordinates like so
Vector3 playerPosition;
void OnCollisionEnter(Collider obj1)
{
if(obj1.gameObject.tag == "Player"){
playerPosition = obj1.transform.position;
myStateMachine.switchState("Follow"); //obviously replace this with your own state machine code
}
}
Make sure the player's gameObject actually has the "Player" tag in the Unity editor.
I've been working on a simple 2D game in unity and it just has three scenes, the start scene, the game scene, and the game over scene. I want to display the score from the game in the game over screen. I created a score manager game object in the game scene that uses the DontDestroyOnLoad() function to carry it over into the game over screen and I gave it access to the score which is managed by the game manager. I've been debugging my code and the score is translated over into the score manager and is maintained when the game over screen loads, but for some reason it won't let me update the score text object. Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour {
public static ScoreManager Instance;
private GameController gameController;
private int scoreInstance;
private Text scoreText;
// When scene is first loaded
void Awake() {
this.InstantiateController();
}
// Use this for initialization
void Start () {
GameObject gameControllerObject = GameObject.FindWithTag("GameController");
if (gameControllerObject != null)
{
gameController = gameControllerObject.GetComponent<GameController>();
}
GameObject scoreTextObject = GameObject.FindWithTag("ScoreText");
if (scoreTextObject != null)
{
scoreText = scoreTextObject.GetComponent<Text>();
}
scoreInstance = 0;
scoreText.text = "";
}
// Update is called once per frame
void Update () {
scoreInstance = gameController.score;
Debug.Log("Score: " + scoreInstance.ToString());
scoreText.text = scoreInstance.ToString();
}
private void InstantiateController ()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(this);
}
else if (this != Instance)
{
Destroy(this.gameObject);
}
}
}
So I tried to programmatically gather the "score text" ui component in the start function because I figured I can't just make it public and drag in the text component because the score manager is actually in a different scene than the score text object. I also tried adding this whole bit of code to gather the text component into the update function so that it can do that when the score manager is actually a part of game over screen. Nothing seems to work and I have no idea why. Can anybody please help me with this? Also I keep getting a "NullReferenceException: Object reference not set to an instance of an object" error. Thanks in advance for any help.
Unity Start function is only called the first time the script is enabled, i.e. not every time the scene changes for a DontDestroyOnLoad object.
So if you need to wire up some changes after a scene change, you need to either detect the scene change, or have an object that starts in that scene trigger the code you want to run.
Having another object on the new scene trigger things is easy and pretty fool-proof, but there's a builtin function you can add to your other objects:
void OnLevelWasLoaded(int currentLevel)
{
}
This will be called on level changes, and give you the level's number (not name sadly). However, the above is deprecated and they want you to use Unity's SceneManager, so the proper way to set this up is now:
Unity 5 OnLevelWasLoaded?
Start()
{
SceneManager.sceneLoaded += this.OnLoadCallback;
}
void OnLoadCallback(Scene scene, LoadSceneMode sceneMode)
{
// you can query the name of the loaded scene here
}
I am having an issue respawning a prefab after it has been destroyed. I can't seem to get it to respawn back at its original start position after a second of being destroyed. I have created an empty game object and attached the SpawnTargets.cs script to it. I'm not sure of what the best methodology to approach this situation. Another object with a script attached to it does the actual destroy of the prefab. BulletCollisionHandler.cs works fine though. Thanks for any help. Code is below:
SpawnTargets.cs:
using UnityEngine;
using System.Collections;
public class SpawnTargets : MonoBehaviour
{
public GameObject targetCircle;
public GameObject targetSquare;
public GameObject targetStar;
private Vector3 circleSpawnPosition = new Vector3(0.0f, 1.227389f, -7.5f);
private Vector3 squareSpawnPosition = new Vector3(0.0f, 1.027975f, -7.993299f);
private Vector3 starSpawnPosition = new Vector3(0.0f, 1.8f, -7f);
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
SpawnTarget ();
}
void SpawnTarget()
{
}
}
BulletCollisionHandler.cs:
using UnityEngine;
using System.Collections;
public class BulletCollisionHandler : MonoBehaviour
{
public GameObject targetCircle;
// Use this for initialization
void Start ()
{
Destroy (gameObject, 2);
}
// Update is called once per frame
void Update ()
{
}
void OnCollisionEnter(Collision other)
{
if(other.gameObject.name == "TargetSquare")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit square");
}
else if(other.gameObject.name == "TargetCircle")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit circle");
}
else if(other.gameObject.name == "TargetStar")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
((TargetMovementVertical)other.gameObject.GetComponent<TargetMovementVertical>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit star");
}
}
}
You're not calling Instantiate() anywhere, so it's hard to see where the new object would come from in the code you've supplied.
In any case, it might be better not to use Destroy. If you want to immediately reset the object, why not simply recycle it back to the start position? It's a good idea to avoid instantiating and destroying lots of objects, it's better to hide/disable the ones your don't need and unhide/re-enable them.
Here's a tutorial on the general idea. The tutorial is about groups of objects but the same trick would work for recycling single objects too.
You are better of using gameObject.SetActive( true/false ); for activating / deactivating the gameObject instead of just using Destroy.
Then if you are using Destroy you have 3 options that comes to mind for getting it into the desire position before the Player sees it.
1) You enable the game object after disabling its Renderer component. Then you equalize the transform's position / rotation the one you need. After this you re-enable the Renderer component. It should be placed where you want it.
2) You Instantiate the gameObject, but first making sure the Renderer component is disabled on its Prefab, by default, so you can re-assign its Transform values then - re-enable the Renderer again.
3) You make an invisible gameObject (an Empty gameObject) and Instantiate the wanted gameObject, you then make the Empty to be the parent of the newly created gameObject.. Provided that the parent Empty is exactly where you want it to be, when you instantiate and reset the child's position it should jump off right on top the the Empty parent.
I'm not giving code since you haven't and I don't have no idea of which method you might end up liking more. In terms of performance the Enable/Disable are the best option.
And as theodox says Object Pooling is your best friend for things like bullets, although it might be applied to many other gameObjects that might work as 'collections of objects' on your game's logic. It's totally worth learning.