Dynamically creating a CustomEditor class in Unity / C# - c#

I am trying to implement edit-mode scripts in my Unity project. The idea is that a script can be attached to an object and as it's edited, attributes like scale/position/rotation can be seen updating.
Without writing an edit mode script, all this stuff is possible, but the changes are not visible until the play button is pressed. That's the difference here.
I'm using a variation of the script provided here to do this.
First of all I have a MonoBehavior script attached to my game object - it looks like so:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class FlippersEditMode : MonoBehaviour {
public float extraScale = 1f;
public void updateEditorState() {
transform.localScale = (Vector3.up * extraScale);
}
}
Second I have a script in the Editor folder that calls this updateEditorState method whenever a "Update" button is pressed:
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(FlippersEditMode))]
public class FlipperEditModeMeta: Editor {
public override void OnInspectorGUI() {
base.OnInspectorGUI();
if (GUILayout.Button("Update")) {
if (target.GetType() == typeof(FlippersEditMode)) {
FlippersEditMode flippers = (FlippersEditMode)target;
flippers.updateEditorState();
}
}
}
}
This works fine and I'm thankful for that, but this last chunk of code is pure boilerplate. I will need to have a nearly-identical one of these files for each script that I want to effect edit mode.
So I started on trying to make a "meta" editor script. The idea is that this script would have an array of slots that the developer can drag scripts into. Each script dragged here will get a CustomEditor class defined like the one shown above. Of course the classes being dragged here would each need to implement updateEditorState but that's the only requirement of the protocol.
So if I make another editor script that looks like this:
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(EditModeScripts))]
public class EditModeScriptsMeta : Editor {
public override void OnInspectorGUI() {
base.OnInspectorGUI();
if (GUILayout.Button("Update")) {
if (target.GetType() == typeof(EditModeScripts)) {
EditModeScripts edit_mode_scripts = (EditModeScripts)target;
edit_mode_scripts.updateEditorState()
}
}
}
}
Then I can trigger the updateEditorState function in the following:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class EditModeScripts : MonoBehaviour {
public MonoBehaviour[] scripts = {};
public void updateEditorState () {
for (int i = 0; i < scripts.Length; i++) {
MonoBehaviour script = scripts[i];
// How to generate an editor class here for each script?
}
}
}
But as you can see I don't know how to dynamically generate the classes.

I don't think your approach will work purely dynamically. Unity compiles your code everytime you change something and come back to unity. After compiling it Unity uses Reflection to determine which editors are available.
You could theoretically create new source files which contain the editor code. I don't know though whether it will automagically reimport the scripts or you need to manually trigger the reimport.
For manually reimporting you can use a combination of AssetDatabase.Refresh() and if that is not enough, select the script asset via the Selection class and trigger the reimport menu entry using EditorApplication.ExecuteMenuItem.

Related

"The type or namespace name 'SceneAsset' could not be found" error when trying to build the game

So in this script below, I used the SceneAsset reference in the C# script in Unity and it worked well when playing from the editor.
However, when I tried to build my game, it wouldn't work as I got errors from the console, saying
The type or namespace name 'SceneAsset' could not be found".
Is it a bug or it's something I'm doing wrong?
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ProvincePinScript : MonoBehaviour
{
public int ActiveWorldProvinceID;
public SceneAsset[] WorldProvinces;
// We can set up a pin that can take bananaman to certain provinces or countries! :)
public void Begin()
{
Invoke("Travel", 4f);
}
public void Travel()
{
SceneManager.LoadScene(WorldProvinces[ActiveWorldProvinceID].name);
}
}
To reference SceneAsset you added a using reference to UnityEditor. This will work fine in the editor, but once you build the final game, all references to UnityEditor are removed.
You normally add scripts intended to run in the editor to a folder in your project named Editor. When building, Unity won’t include anything in that folder. But you’ve circumvented that mechanism, and you’re left with a UnityEditor stub that doesn’t contain anything.
The documentation here goes in to a little more detail about the SceneAsset.
In general, you would point to a scene by either its build index or scene name, using the SceneManager.LoadSceneAsync, as an example, which is detailed here.
Here's an example of a hack that I mocked up (written, untested, but should do the job). This should allow you to use the UnityEditor.SceneAsset in the Inspector and will serialise the scene names to a List<string> that you can use in the runtime script. As mentioned, this is just a hack I mocked up, for the exercise. I make no assertion that this is indeed a good idea, but in theory it will allow you to drag and drop scene asset objects into the Inspector, instead of manually writing out the scene names as strings.
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
public class ProvincePinScript : MonoBehaviour
#if UNITY_EDITOR
, ISerializationCallbackReceiver
#endif
{
public int ActiveWorldProvinceID;
#if UNITY_EDITOR
public SceneAsset[] WorldProvinces;
#endif
[HideInInspector, SerializeField]
private List<string> _worldProvinces;
// We can set up a pin that can take bananaman to certain provinces or countries! :)
public void Begin ( )
{
Invoke ( "Travel", 4f );
}
public void Travel ( )
{
SceneManager.LoadScene ( _worldProvinces [ ActiveWorldProvinceID ] );
}
#if UNITY_EDITOR
public void OnAfterDeserialize ( ) => FillScenes ( );
public void OnBeforeSerialize ( ) => FillScenes ( );
public void OnValidate ( ) => FillScenes ( );
private void FillScenes ( )
{
if ( _worldProvinces is null )
_worldProvinces = new ( );
_worldProvinces.Clear ( );
foreach ( var s in WorldProvinces )
_worldProvinces.Add ( s.name );
}
#endif
}

Assign a tag to an object in a unity via code permanently

I'm making a subway simulator
I made a lot of empty blocks, they all have the Uncreated tag by default and they do not have a name, when you click on any of them, a window appears, if the station is with the Uncreated tag, then the station creation window, if with the Created tag , then the edit window. The Created tag is assigned via the script below
I want the StationBox to always have the Created tag after pressing a button, but it goes back to the previous value when the game is restarted, how can I solve this?
I would like to make a universal script for all stations
using System;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class AddStation : MonoBehaviour
{
public InputField Input;
public Text StationName;
public GameObject Button;
public GameObject CreateStation;
public GameObject StationBox;
public void OnMouseDown(){
if (Input.text != ""){
CreateStation.SetActive(false);
StationName.text = Input.text.ToString();
StationBox.tag = "Created";
}
}
}

When generating automatic new guid by script why the new guid is one long 0 digits?

The original goal is to make an editor script and only when attaching the mono script to an object to make that it will generate the new guid but not sure how to do it. So for now I'm using the ExecuteInEditMode attribute.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class GenerateAutomaticGuid : MonoBehaviour
{
public string guid;
private Guid uniqueID;
// Start is called before the first frame update
void Start()
{
uniqueID = new System.Guid();
guid = uniqueID.ToString();
}
// Update is called once per frame
void Update()
{
}
}
The result in the string is : 00000000-0000-0000-0000-000000000000
and it's executing when getting back to the editor and not only when attaching the script to a gameobject.
The main goal is to generate a unique id to each gameobject the script will be attach to.
I need it since the gameobjects instance id's change each time I'm loading a saved game. The unity generate new id's for the objects and the id/s on the saved game file is not the same.
That's what new Guid() does; use Guid.NewGuid() instead

How to change the Unity Post Processing Layer Anti Aliasing Type Through C#

Im wondering How to change the Unity Post Processing Layer Anti Aliasing Type Through C#, I have tried everything but can't figure it out.
I looked through the code for the layer script but still cant figure it out.
I have tried:
MainL.antialiasingMode = Antialiasing.[Setting];
[Setting] = An Antialiasing setting.
But that hasn't worked.
code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.PostProcessing;
public class GameQuality : MonoBehaviour
{
public Camera Main;
public PostProcessVolume MainV;
public PostProcessProfile Less;
public PostProcessProfile High;
public PostProcessProfile Ultra;
public PostProcessLayer MainL;
// Use this for initialization
void Awake()
{
QualitySettings.GetQualityLevel();
if(QualitySettings.GetQualityLevel() > 2)
{
MainV.profile = Less;
}
if(QualitySettings.GetQualityLevel() == 3)
{
MainV.profile = High;
}
if(QualitySettings.GetQualityLevel() == 4)
{
MainV.profile = Ultra;
}
}
// Update is called once per frame
void Update ()
{
}
}
You can use Post​Process​Layer.antialiasingmode with a value of PostProcessLayer.Antialiasing (make sure to use the full type name including the PostProcessingLayer.)
MainL.antialiasingMode = PostProcessingLayer.Antialiasing.XY;
where XY can be one out of None, FastApproximateAntialiasing, SubpixelMorphologicalAntialiasing or TemporalAntialiasing.
Note that this allone might not be enough. You might also have to set the values of the according antialiasing MainL.temporalAntialiasing, MainL.fastApproximateAntialiasing or MainL.subpixelMorphologicalAntialiasing.
Btw you should remove the first call of
QualitySettings.GetQualityLevel();
it just does nothing

Unity translate rotate an imported object

i can't translate.rotate (0,5,5) in unity on an imported model
what should i do to make it rotate i can't find a good source.
my code is here: i put script under gear or main camera but it doesn't rotate i also selected model name on script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour {
public GameObject gear;
// Use this for initialization
// Update is called once per frame
void Update () {
gear.transform.Rotate (100, 10, 10);
}
}
According to your image, you don't do it corretly. Follow these steps :
Drag the Gear model from the Assets and drop it into your scene
Select the Gear GameObject in your scene
Drag the NewBehaviourScript from your Assets and drop it to the inspector of the Gear Gameobject
Drag the Gear Gameobject in your scene to the public field gear of the NewBehaviourScript attached to the gear gameobject
If you feel the 4th step is useless change the script as follow :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour {
// Update is called once per frame
void Update () {
transform.Rotate (100, 10, 10);
}
}

Categories

Resources