Jump to content
Unity Insider Forum

Raycast in Editor Modus - fertig


Quellcode

Recommended Posts

Guten Abend,

ich habe die letzten 2 Tage schon das Internet durchforstet, aber entweder scheinen die Scriptbeispiele veraltet zu sein oder ich stehe einfach auf dem Schlauch :-(

Ein bisheriges Ingame Script soll nun für den Editor fit gemacht werden, nur verzweifle ich schon an einem Raycast aus der Editor "Kamera".

Dieses Script habe ich bisher:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[ExecuteInEditMode]
public class MyClass : Editor {

	void OnSceneGUI()
    {
		Debug.Log("OnSceneGUI");

		if( Event.current.type == EventType.MouseMove ){
			Debug.Log("Raycast");
            //HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
		}
	}

	void Update()
	{	
		Debug.Log("Update in Edit Mode - nur 1x nach Änderungen!");
	}
}

Leider klappt diese Variante so gar nicht... ich habe nun nichtmehr die Möglichkeit das Script einem Object anzuhängen, habe aber keine Fehlermeldungen im Log.

Und als dies noch ging, wurde kein Debug.Log ausgeführt... mein Raycast ist noch auskommentiert, da ich soweit noch nicht bin.

 

Ich habe davon auch mehrere Abwandlungen ausprobiert... MonoBehaviour Klasse statt die Editor und auch diese Punkte:

https://answers.unity.com/questions/62655/raycasting-in-unity-editor.html

 

Kennt Jemand ein einfaches Beispiel?

Ein Video / Tutorial welches mit der 5er Unity Version läuft?

 

Vielen Dank im Voraus für eure Unterstützung!

 

====================

Okay, ich habe soeben parallel noch einen Link gefunden. Dieser war sehr aufschlussreich.

Für alle die vllt. das selbe Problem haben:

EditorMono an GameObject "hängen", dann wird EditorRaycast geladen (wenn man das GameObject auch ausgewählt hat)

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(EditorMono))]
public class EditorRaycast : Editor {

	void OnSceneGUI()
    {
		if( Event.current.type == EventType.MouseMove ){
			
			Ray ray = HandleUtility.GUIPointToWorldRay( Event.current.mousePosition );
         
			RaycastHit hit;
			if( Physics.Raycast( ray, out hit ) ){
				Debug.Log("Raycast? "+hit.transform.name);
			}
		}
	}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EditorMono : MonoBehaviour {

	void Start()
	{
	
	}
}

 

Link: http://josbalcaen.com/unity-editor-place-objects-by-raycast/

Link zu diesem Kommentar
Auf anderen Seiten teilen

Editorskripte müssen in ein Verzeichnis mit dem Namen "Editor", dann werden sie ausgeführt.
Zudem muss beim Editor angegeben werden, für welchen Typ er aktiviert werden soll.

Dieses Skript in einen Folder namens "Editor" packen und dann ein Transform in der Scene anwählen. Wenn du die Maus bewegst und ein Objekt mit Collider in der Szene triffst, dann sollte eine entsprechende Ausgabe kommen:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Transform))]
public class MyClass : Editor
{

    void OnEnable()
    {
        // init stuff
    }

    void OnSceneGUI()
    {
        Debug.Log("OnSceneGUI");

        if (Event.current.type == EventType.MouseMove)
        {
            Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);

            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                Debug.Log("We hit something: " + hit.collider.gameObject.name);
            }
        }
    }

}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hier ein Mini-Helfer-Skript, welches ein Transform in der Scene auf dem (Terrain-)Boden platziert, wenn man "Strg+Y" drückt, einfacher gehts nicht. Ich denke man kann es sehr leicht für andere Funktionen modifizieren.

Das GameObjekt in der Szene erst leicht verschieben und dann "Strg+Y" drücken ;) (man kann auch die Sceneview leicht verändern, dann klappt es auch)
Das Skript muss in einen Folder namens Editor gelegt werden.

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Transform))]
public class Placer : Editor
{

    void OnEnable()
    {
        // do init stuff
    }

    void OnSceneGUI()
    {
        Transform selectedObject = (target as Transform);

        if ((Event.current.keyCode == KeyCode.Y) && Event.current.control)
        {
            Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);

            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                // Place the transform at the hit position
                selectedObject.position = hit.point;
            }
        }
    }

}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 19 Stunden schrieb Zer0Cool:

Editorskripte müssen in ein Verzeichnis mit dem Namen "Editor", dann werden sie ausgeführt.

Da muss ich kurz widersprechen. Der Editor-Ordner macht exakt eine Sache: Dass der Inhalt nicht im Build landet. Ob und wann das Script ausgeführt wird, wird davon nicht beeinflusst.

Naja, abgesehen von "nicht im Build".

Und sonst... Begrenzt man sich mit dem CustomEditor nicht auf ein einzelnes Script, das da ausgeführt werden kann? Wie wäre es stattdessen mit Registrieren in einem der Editor-Callbacks in einer InitializeOnLoadMethod?

[InitializeOnLoadMethod]
private static void Initialize()
{
  EditorApplication.update -= MyStuff;
  EditorApplication.update += MyStuff;
  // oder
  SceneView.onSceneGUIDelegate -= MyStuff;
  SceneView.onSceneGUIDelegate += MyStuff;
}

private static void MyStuff()
{
  // ab dafür
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Sascha danke, schaue ich mir auch an!

 

Eine andere Frage ergibt sich gerade und ich wollte kein 2tes Thema eröffnen.

Das Editor Script läuft soweit gut, nur wenn ich dann ins Spiel gehe und wieder raus fehlt mir eine Information. Im Internet habe ich bereits die Doku zur "Serialize" gefunden, aber dennoch wird die Info gelöscht.

So sieht die Class aus, welche offensichtlich verloren geht bzw die Class (unter ####) welche die Verbindung speichern soll:

using UnityEngine;
using System.Collections;

public class MySaveBox {

	[SerializeField]
    public Vector3[] points;

}

####

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyClass : MonoBehaviour {
	
	[SerializeField]
	public MySaveBox MySaveBox;

}

 

 

Das [SerializeField] war ein Zusatz, auf Grund der Doku. Dies hat jedoch das Problem nicht behoben...

Was mache ich falsch bzw habe ich vergessen? :-(

Link zu diesem Kommentar
Auf anderen Seiten teilen

"public" Felder werden von Haus aus serialisiert. Man muss aber bei Klassen die man im Inspektor einbetten möchte folgendes machen
(https://docs.unity3d.com/ScriptReference/Serializable.html)

using UnityEngine;
using System.Collections;

[System.Serializable]
public class MySaveBox : System.Object {
    public Vector3[] points;
}

####

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyClass : MonoBehaviour {	
	public MySaveBox MySaveBox;
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Zer0Cool das hat mir schon weiter geholfen! Danke :)

Ich hatte außerdem bei allen Scripten [ExecuteInEditMode] angehängt, welche vom Ablauf betroffen waren... dies hat mich nun auch etwas weiter gebracht.

Nun konnte ich den aktuellen Fehler auf eine Zeile eingrenzen, dort wird eine Dictionary ausgelesen.

Diese ist zwar auch "public" und die Class ist ebenfalls mit [ExecuteInEditMode] sowie [System.Serializable] gekennzeichnet, aber es scheint als würde er diese Daten verlieren.

Gibt es bei einer Dictionary  weiter Dinge zu beachten oder suche ich an der falschen Stelle?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich würde mal [ExecuteInEditMode] entfernen, vermutlich brauchst du dies gar nicht und es kann zu seltsamen Effekten führen wie ein Überschreiben von Werten... Ich würde es nur drin lassen, wenn du es unbedingt brauchst... aber wie gesagt, ich habe damit nur schlechte Erfahrungen gemacht... 
Na in ein Dictionary steckst du etwas über einen Schlüssel hinein und holst den Wert über den Schlüssel wieder heraus, ansonsten wie eine normale Variable zu verwenden.
Aber du musst ein DIctionary initialisieren mit  new ...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Habe ich testweise entfernt, aber dies hat damit nichts zu tun. Habe 4 Scripte, eines davon hat ein Update, dort habe ich nun den "Execute" noch drin, da ich ihn benötige.

Die anderen Scripte brauchten den Wert nicht, das stimmt.

 

Aber ich habe eine Dictionary mit einem ENUM

// Aufruf aus anderer Class
public Dictionary<MyClass.types, MyClass> fields;

//ENUM in "MyClass"
public enum types { Top, Left, Right};

 

Dies scheint ein Problem zu sein, alles andere wird gespeichert.

Auch wenn ich nicht in den InGame Modus gehe, genügt der folgende Test:

Ich setzte Objekte (A) mit meinem Script (diese sind verschachtelt und verbunden) und kopiere nun diesen Block (B = Kopie von A)

Bei A kann ich nun noch alle Funktionen ausführen, bei B kommt die Meldung des fehlenden Index... und dies in der Zeile folgenden Zeile:

foreach(KeyValuePair<MyClass.types, MyClass> field in fields)

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Doch, soweit stimmt das schon. Es sind 3 Mesh Quader, welche pro "MyClass" existieren... eine "Decke" und 2 Seitenteile.

 

EDIT:

Thema hat sich akut erledigt, dennoch wäre es interessant für die Zukunft wie es mit einer Dictonary eines Custom Types funktioniert.

Da es nur 3 Elemente sind (in meinem speziellen Fall), habe ich diese einfach als 3 separate MyClass Objekte eingebunden und arbeite mit einem Index.

Dies löst mein momentanes Problem.

 

Danke für deine Hilfe @Zer0Cool

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...