I've used Unity for quite some time, but never tried the editor. Today I decided to try to make an EditorWindow to help me with shortcuts with my development.
What I want is to click on a button on my Window to turn on and off a specific light in my scene.
Added the code and an image of the Window below.
I am clueless on how to proceed. Any help would be appreciated a lot!
public class DevTools : EditorWindow
{
public Object workLight;
[MenuItem("Window/DevTools")]
public static void ShowWindow()
{
GetWindow<DevTools>("Development Tools");
}
private void OnGUI()
{
GUILayout.Label("This is the development tools.", EditorStyles.boldLabel);
workLight = EditorGUILayout.ObjectField(workLight, typeof(object), true);
if (GUILayout.Button("Working Light"))
{
if(workLight == null)
{
ShowNotification(new GUIContent("No light selected"));
}
else
{
}
}
}
}
This is my current Window
Well, you need a reference to the object to proceed with turning it on and off. You can either add a field to the editor and manually choose the object from the scene or dynamically find it through GameObject.Find.
I don't really know Unity (never used it) but it looks pretty straightforward.
The EditorFieldForObject method seems to take a second type param that denotes which types you can select using the field in the GUI - I'm not sure if you can make this a GameObject or not, but there are basically two approaches that should work:
Leave your code as is and cast the object (in OnGUI method):
var gameObject = workLight as GameObject;
if(gameObject == null)
{
ShowNotification(new GUIContent("No light selected"));
}
else
{
var light = gameObject.GetComponent<Light>();
if(light == null)
{
ShowNotification(new GUIContent("Selected object is not a light"));
return;
}
// Not sure if this is the correct way to disable a light but you can figure it out
light.enabled = false;
// or light.enabled = !light.enabled to toggle state
}
The other way is to make the editor GUI box look for GameObject instances in the first place:
public GameObject workLight;
....
workLight = EditorGUILayout.ObjectField(workLight, typeof(GameObject), true);
But I'm not sure if this is possible or not (as I've said, I don't know Unity)
Edit: apparently you need to use GameObject.SetActive(boolean) to toggle the
state of an object
Since you want to toggle, you can use the GameObject.activeSelf field which gives you the active true/false flag on the current object
So this should work:
light.SetActive(!light.activeSelf);
Thanks a lot Charleh!
With a little bit of rewriting, this seem to work. Not sure if it is optimized perfectly, but it works without a hitch (atm) :p.
public class DevTools : EditorWindow
{
Object workLight = default;
[MenuItem("Window/DevTools")]
public static void ShowWindow()
{
GetWindow<DevTools>("Development Tools");
}
private void OnGUI()
{
GUILayout.Label("This is the development tools.", EditorStyles.boldLabel);
if (GUILayout.Button("Working Light"))
{
if (workLight == null)
{
ShowNotification(new GUIContent("No light selected"));
}
else
{
var light = GameObject.Find("Work Light");
if (light == null)
{
ShowNotification(new GUIContent("Selected object is not a light"));
return;
}
else
{
light.gameObject.GetComponent<Light>().enabled = !light.gameObject.GetComponent<Light>().enabled;
}
}
}
}
}
Related
I am trying to make a list of gameObjects disappear when the player enters a room, the best way I could think about doing it, was changing the material alpha frame by frame. If there is an alternative to this method that is more performatic, please tell! (not that I noticed any bad performance as this seems simple enough)
I expect the player to enter a room and all of the gameObjects in that list to disappear at the same time and at the same rate (as if they were all just one object having their transparency changed in runtime) but what happens is (I've made a video: https://youtu.be/z1pz2Te_hDg ) that some gameObjects change before others, some disappear way faster than expected and just in the end after the others, it all looks weird.
Changing the alpha of only one object at a time seems fine, but since I need to loop through all of the materials, I can't simply put all objects as a child of one gameObject.
I've tried to change the alpha without making a custom shader with an alpha property by accessing the default alpha in the default "HDPR/Lit" shader and it behaves the same.
This problem seems easy, I feel like I am doing something stupidly wrong but I've been at this for a couple of weeks.
Here is my "RoomTransparencyController" script:
bool transparent = false;
public float enhance = 5;
private struct ShaderPropertyIDs {
public int AlphaRef;
}
private ShaderPropertyIDs shaderProps;
public List<GameObject> gameObjectList;
public List<Material> materialList;
void Start() {
foreach(GameObject obj in gameObjectList) {
foreach(Material mat in obj.GetComponent<MeshRenderer>().materials) {
materialList.Add(mat);
}
}
// Cache property IDs
shaderProps = new ShaderPropertyIDs() {
AlphaRef = Shader.PropertyToID("AlphaRef"),
};
}
void Update() {
UpdateChildsAlpha3(transparent);
}
private void OnTriggerEnter(Collider col) {
if(col.tag == "Player") {
transparent = true;
}
}
private void OnTriggerExit(Collider col) {
if(col.tag == "Player") {
transparent = false;
}
}
void UpdateChildsAlpha3(bool transparent) {
foreach(Material mat in materialList) {
mat.SetFloat("AlphaRef", Mathf.Clamp(mat.GetFloat("AlphaRef") + Time.deltaTime * enhance * (transparent ? -1 : 1), 0, 1));
}
}
PS: This script should mainly contain props and specific walls from a room so there is a script in every room with its gameObject list for better organization. So I thought that each room having its script was the best way to organize and replicate.
If you want the objects to be gone, then use a for loop with Destroy() on all the objects, and if you want them to stop being visible, just use SetActive(). If you want them to work, but just stop being visible, you can disable the renderer component.
It will be much better if you try to use some tweens to solve this problem. Like doTween or LeanTween, where they have some better-optimized way to reduce alpha or anything. Try to play around with it.
Recently started studying the book "Mobile Game Development with Unity" by John Manning and there was a problem with the code.
Unity keeps throwing this error when trying to add a component.
"The script dont inherit a native class that can manage"
Here is the code itself. The name of the class and the code are the same. I've been digging the Internet for several hours and just can't find a solution.
The code was attached to the book and it also gives an error when added.
using UnityEngine;
[RequireComponent (typeof(SpriteRenderer))]
public class BodyPart: MonoBehaviour
{
public Sprite detachedSprite;
public Sprite burnedSprite;
public Transform bloodFountainOrigin;
bool detached = false;
public void Detach()
{
detached = true;
this.tag = "Untagged";
transform.SetParent(null, true);
}
public void Update()
{
if (detached == false)
{
return;
}
var rigidbody = GetComponent<Rigidbody2D>();
if (rigidbody.IsSleeping())
{
foreach (Joint2D joint in GetComponentsInChildren<Joint2D>())
{
Destroy(joint);
}
foreach (Rigidbody2D body in GetComponentsInChildren<Rigidbody2D>())
{
Destroy(body);
}
foreach (Collider2D collider in GetComponentsInChildren<Collider2D>())
{
Destroy(collider);
}
Destroy(this);
}
}
public void ApplyDamageSprite(Gnome.DamageType damageType)
{
Sprite spriteToUse = null;
switch (damageType)
{
case Gnome.DamageType.Burning:
spriteToUse = burnedSprite;
break;
case Gnome.DamageType.Slicing:
spriteToUse = detachedSprite;
break;
}
if (spriteToUse != null)
{
GetComponent<SpriteRenderer>().sprite = spriteToUse;
}
}
}
There are a number of other forum posts on this error including a Unity community post, a Unity Answers post, and another StackOverflow post
Most of them concur that it is an issue with Unity, not the script itself. I would recommend starting with the suggestions in these posts
Restart Unity
If you added the script while running the game, stop and recompile
Confirm that the script name matches exactly
Remove any spaces from script name
Update to the latest release of your version of Unity
Reinstall Unity
If you've tried any of these already, please let us know what you've done to fix the problem and we can try to assist more
Anyone know how i can stop this from flicking basically I'm doing a raycast and if it hits the crafting table it it shows up a text saying press e to craft but it is flickering cause its in the void update function but if anyone knows a work around that would be nice thanks!
if(hit.collider.tag == "CraftingTable")
{
if(textIsOn)
{
noshowcraftingtext();
}
else
{
showcraftingtext();
}
}
void showcraftingtext()
{
textIsOn = true;
pressEToShowCraftingTableUI.SetActive(true);
}
void noshowcraftingtext()
{
textIsOn = false;
pressEToShowCraftingTableUI.SetActive(false);
}
The main problem of your code is that you are trying to change the state after state was changed.
That's why updated isn't the case in a big project, because all such things will lead to unpredictable or logically conflicted behavior, to avoid this you should use Data driven approach instead.
But to fix exactly your issue, you need to have a function, which isn't based on the state of the object, but returns what should happen right now.
There isn't much about what's going on inside your game, but I can guess that you are firing a raycast with mouse and then checking is it craftable or not.
public void Raycaster : MonoBehavior {
public void YourClickMethod()
{
if(hit.collider.tag == "CraftingTable")
{
hit.collider.GetComponent<UserInputReceiver>().SetClicked(this);
}
}
}
And on the object which is receiving your raycast you should add this:
public class UserInputReceiver : MonoBehavior {
private bool _isEnabled = false;
//set this inside inspector
public GameObject ObjectToEnable;
private Raycaster _currentSender = null
public void SetClicked(Raycaster sender){
_isEnabled = !_isEnabled;
_currentSender = sender;
}
public void Update(){
if(_currentSender != null && _isEnabled) {
_isEnabled = Vector3.Distance(transform.position, _currentSender.transform.position) < 1f; //set the threshold based on your unit system
}
//or another object, you can put it inside public field and enable him
//gameObject.SetActive(_isEnabled);
objectToEnable.SetActive(_isEnabled); //here you can pass a reference through inspector for your canvas
}
}
This is how you can outstand this issue with a short way.
This code will set the flag based on the received input and the distance. If you need to keep enabled text forever, than remove distance check than it will enable only with a second click, without distance dependency.
I need a way to check all scripts if boolean is true and then a way in this script to see in the if statement of the current light the character is standing next to has the boolean true to activate and only then should the Instantiate function be triggered.
private bool Once = true;
public Transform Spawnpoint1;
public Transform Spawnpoint2;
public GameObject Prefab1;
public GameObject Prefab2;
//Something like this, but I don't know where to go after that
GameObject[] Lights = GameObject.FindGameObjectsWithTag("lightSwitch");
//foreach(GameObject Light in Lights)
void OnTriggerEnter2D(Collider2D collision)
{
if (Once == true)
{
Debug.Log("It's true");
if (LightOnOff.isON == true) // It needs to check this constantly
{
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
}
}
here is the Light script as well
public static bool isON;
public void lightOn()
{
this.GetComponent<Light>().enabled = true;
isON = true;
}
First of all you shouldn't use static
public static bool isON;
for an individual value! static makes the value a "non instanced" value, meaning it belongs to the entire class instead of instances => To say it in simple words this variable is shared between all your components (see this post for more information). Instead use
public bool isON;
Than access the insteance variable of the component like
Update: From the comments I now know that actually the components are not on the collider object but rather on a child of the object this script is attached to
void OnTriggerEnter2D(Collider2D collision)
{
// Update. TODO check if the correct Object collided
//if(!collision.gameObject == <your player>)
if (!Once) return;
// Update this now searches in its own children instead of children of collision
var lightOnOff = GetComponentInChildren<LightOnOff>(true);
if(!lightOnOff)
{
Debug.Log("No LightOnOff found on collision" + collision.name, this);
return;
}
Debug.Log("It's true");
if (!LightOnOff.isON) return;
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
But instead of using your LightOnOff component you could also simply acces the Light component and do something like
Update: From the comments I now know that actually the components are not on the collider object but rather on a child of the object this script is attached to
void OnTriggerEnter2D(Collider2D collision)
{
if (!Once) return;
// directly access the Light
// Update this now searches in its own children instead of children of collision
var light = GetComponentInChildren<Light>(true);
if(!light)
{
Debug.Log("No Light found on collision" + collision.name, this);
return;
}
Debug.Log("It's true");
// Directly check if the Light component is enabled
if (!light.enabled) return;
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
You don't need to keep track of all the lights just for that.
Something you do need to change is that you should make isON not static. This is because an actual light might be on or not, not that the concept of lights are on or not.
public bool isON;
Check the Collider2D for the associated object you're colliding with, and look for a light there. The following code assumes any light will be on the same GameObject as a Trigger or one of its children.
void OnCollisionEnter2D(Collider2D col) {
// only activate once
if (once) {
// Get light if exists
GameObject collidedObject = col.gameObject;
Light light = collidedObject.GetComponentInChildren<Light>();
if (light != null) {
// We have a light, check if it's on. We only care about the collided light
if (light.isON) {
Debug.Log("It's Lit fam");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
// Note that if we run into another lit light nothing will happen,
// even if its the first time running into that particular light
Once = false;
}
}
}
}
Also, you can just use if(something) instead of if(something == true)
I have a game mechanic where the player can toggle the car headlights with the L keypress and the backlights witht he S key, which also controls backwards movement.
This is shown in the code below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class headLights : MonoBehaviour {
private Light frontLight;
private Light backLight;
// Use this for initialization
void Start () {
frontLight = GetComponent<Light>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.L))
{
frontLight.enabled = !frontLight.enabled;
}
if (Input.GetKeyDown(KeyCode.S))
{
backLight = GetComponent<Light>();
backLight.enabled = !backLight.enabled;
}
}
}
The problem is when I press L or S, both the front and back lights turn on because which I assume, the GetComponent refers to all extra Light components in the Scene and generalizes them as one.
I want to get the S key to only turn on the "backLights" while it is pressed and the L key to only toggle the "frontLights".
METHODS I HAVE USED TO TRY FIX THE PROBLEM
frontLight = GameObject.Find("Player").GetComponent<Light>();
This code just gives me errors like "the gameobject player does not have any light components attached to it(although it clearly does) blah blah blah.
I have also tried using tags but they confuse me a lot and seem like the easy way out. I know in the future I will have to learn how to do object orientated syntax and coding so I would very much like to learn how to reference it!
Please help me if you can, it would make my day~~ :)
Please note, you do not have to solve the whole problem for me if you are short on time, just giving general syntax that I can just swap out would help me a great deal!
Its really easy to disambiguate the two lights. Just make the variables public and set them in the inspector. Be sure to null check them before you use them to make sure they are set.
I just realized that probably didn't make sense.
Lets say your hierarchy is set up like this with the light objects as children to the car:
car
+-FrontLight
+-RearLight
Instead of putting the custom behavior on the light gameobjects, you should put it on the car.
Then, the behavior would look like this:
public class headLights : MonoBehaviour {
public Light frontLight;
public Light backLight;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.L))
{
if(frontLight != null) {
frontLight.enabled = !frontLight.enabled;
}
}
if (Input.GetKeyDown(KeyCode.S))
{
if(backLight != null) {
backLight.enabled = !backLight.enabled;
}
}
}
This is because the lights are not Components of the car, they are Children of it.