Jump to content
Unity Insider Forum
Xurium

Rotierbare Objektansicht

Recommended Posts

Liebe Community,

ich arbeite an einem virtuellen Museum und hätte gern ein Script, dass es erlaubt, als auswählbar definierte Objekte mit einem Mausklick an sich heranzuholen
und dann vor sich mit Ziehen der Maus zu rotieren. Klickt man abermals, soll das Objekt in seiner ursprünglichen Ausrichtung an seinen Ausgangsort.

Aktuell bin ich soweit, dass vom Zentrum des Bildschirms einen Ray nach vorne schießt und getroffene Objekte, die über eine Tag als auswählbar definiert wurden,
über ein Material hervorgehoben werden (Das habe ich aus einem Tutorial zu Raycast übernommen).
Allerdings bin ich jetzt etwas ratlos, wie ich da weiter machen soll...ich poste mal mein Script, darin hab ich auch in Etappen reingeschrieben, was ich gerne weiter tun würde.
Es wäre wirklich klasse, wenn mir jemand ein bisschen unter die Arme greift. Ich will so viel wie möglich selbst machen, aber mir fehlt da einfach noch das Hintergrundwissen zu.

Liebe Grüße,
Chris

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

public class SelectionManager : MonoBehaviour
{
    [SerializeField] private string selectableTag = "Selectable"; //Auswählbare Objekte werden als solche definiert
    [SerializeField] private Material highlightMaterial;
    [SerializeField] private Material defaultMaterial;  //Das will ich irgendwie durch das ursprüngliche Material des jeweiligen Objektes ersetzen

    private Transform _selection;
    // Update is called once per frame
    private void Update()
    {
            //Wenn ein Objekt nicht vom Ray getroffen wird, hat es das default Material
            if (_selection != null)
            {
            var selectionRenderer = _selection.GetComponent<Renderer>();
            selectionRenderer.material = defaultMaterial;
            _selection = null;
             }

        //Wirf einen Strahl vom Zentrum des Bildschirms aus
        var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;

        //Wenn der Strahl etwas trifft
        if (Physics.Raycast(ray, out hit))
        {
            var selection = hit.transform;
                //Ist das Objekt auswählbar?
            if (selection.CompareTag(selectableTag))
            {
                //Wenn es noch nicht gehighlighted ist, highlighte es!
                var selectionRenderer = selection.GetComponent<Renderer>();
                if (selectionRenderer  != null)
                {
                    selectionRenderer.material = highlightMaterial;
                }

                _selection = selection;

    //Mit Linker Maustaste kann ein hervorgehobenes Objekt ausgewählt werden
     //           if (Input.GetMouseButtonDown(0))
     //          {
      //              SelectableObject obj = hit.collider.gameObject.GetComponent<SelecableObject>();
       //             obj.Select(transform.position + transform.forward * 0.5f, transform.position);
       //         }

            }
           
        }
    }

    //Ich will, dass das default Material immer das Ursprüngliche Material des Ausgewählten Objekts ist

    //Die Reichweite des Rays soll auf einen näheren Bereich begrenzt sein

    //Ein ausgewähltes Objekt wird nahe an die Kamera gezogen (Vielleicht zu einem Gameobjekt,
    //das vor der Kamera positioniert wird und sich mit dem Rigidbody bewegt?)

    //Es wird ein UI aktiviert, dass Informationen zu dem Objekt anzeigt

    //Dort kann es mit der Bewegung der Maus rotiert werden.
     //Transform.Rotate(0, 10 + Time.deltaTime, 0);
    
    //Dafür muss natürlich für die Zeit, in der ein Objekt ausgewählt ist,
    //die Maussteuerung der Kamera deaktiviert werden


    //Klickt man abermals Linke Maustaste oder ESC, wird das Objekt wieder unausgewählt

    //es wird zu seiner Ursprungsposition bewegt in seiner ursprünglichen Ausrichtung

}

 

Share this post


Link to post
Share on other sites

Etwas kann ich schon helfen, hab die letzten Tage ja viel hier gelernt ^^.

Beim Ray kannst du eine LayerMask mitgeben, die er dann auch nur trifft, da kannst du dir die ganze Abfrage sparen. Die Länge des Rays kannst du auch im Ray festlegen und zwar vor der LayerMask ^^! Das hatte ich vergessen und dafür Stunden geopfert...

Das Material würde ich einfach in eine Variable OldMaterial oder so packen, wenn ich selektieren kann und danach einfach wieder zuordnen. Das löst jetzt zwar nicht alles, aber ich hoffe, es hilft etwas.

Gruß

Uwe

  • Like 1

Share this post


Link to post
Share on other sites

So ich hab mich mal dran gemacht, das Rotieren war der "schwerste Part":
- es gibt nun eine neue Klasse und die muss an jedes GO gehangen werden welches man "aufheben" können soll (die GOs heißen bei dir "default")
- jedes Selectable GO braucht einen Collider (z.b. Box-Collider)
- der "SelectionManager" ist nun nur noch für die Steuerung zuständig, detektiert nicht mehr und ändert auch den Renderer der Selectable GOs nicht mehr.
- mit ALT kann der Mauscursor eingeschaltet werden und damit kann der Spieler nun das Objekt mit dem Mauszeiger anklicken oder markieren
- bei LMB wird das Objekt an den Spieler herangezogen (in das Pivot GO das unter dem Rigidbody liegen sollte - siehe unten)
- mit der Maus kann das Object nun rotiert werden, dabei werden die Kamera-Achsen als Rotations-Achsen genommen
- drückt der Spieler LMB noch einmal wird das Objekt zurückgelegt und der Spieler kann sich wieder frei bewegen
- der Controller wird während der Aktionen entsprechend deaktiviert
- die Kameradistanz bis zu welcher die Objekte selektierbar sein dürfen kann eingestellt werden

Wichtig ist noch folgendes:
Alle GOs die man aufheben können soll müssen nun die Selectable Klasse bekommen!
Alle GOs die Selectable sein sollen dürfen nicht mehr static sein!

1j1VZoB.png


Unter dem Rigidbody liegt nun ein Pivot-Objekt, in welches die Selectable-Objekte gezogen werden.
Dieses GO muss dem  SelectionManager Skript zugewiesen werden (siehe entsprechender Slot).


qRWUDLZ.png

QHaWKQl.png

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

public class Selectable : MonoBehaviour
{

    public Material defaultMaterial;
    public Material highlightMaterial;
    public float maxDistanceCamera = 10f;

    private Camera m_Camera;
    private SelectionManager m_SelectionManager;
    private Renderer m_Renderer;

    private Transform originalParent;
    private Vector3 originalPosition;
    private Quaternion originalRotation;
    private float distanceToCamera;

    // Start is called before the first frame update
    void Start()
    {
        originalParent = this.transform.parent;
        originalPosition = this.transform.position;
        originalRotation = this.transform.rotation;
        m_SelectionManager = GameObject.FindObjectOfType<SelectionManager>();
        m_Renderer = this.GetComponentInChildren<Renderer>();
        m_Camera = GameObject.FindObjectOfType<Camera>();
    }

    public void Reset()
    {
        this.transform.parent = originalParent;
        this.transform.position = originalPosition;
        this.transform.rotation = originalRotation;
        m_Renderer.material = defaultMaterial;
        m_SelectionManager.CurrentSelection = null;
    }

    void OnMouseDown()
    {
        distanceToCamera = (transform.position - m_Camera.transform.position).magnitude;
        if (distanceToCamera < maxDistanceCamera)
        {
            m_SelectionManager.CurrentSelection = this.gameObject;
            this.transform.parent = m_SelectionManager.SelectableHandle;
            this.transform.localPosition = Vector3.zero;
        }
    }

    //If your mouse hovers over the GameObject
    void OnMouseOver()
    {
        distanceToCamera = (transform.position - m_Camera.transform.position).magnitude;
        if (distanceToCamera < maxDistanceCamera)
        {
            m_Renderer.material = highlightMaterial;
        }
    }

    //The mouse is no longer hovering over the GameObject
    void OnMouseExit()
    {
        m_Renderer.material = defaultMaterial;
    }

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

public class SelectionManager : MonoBehaviour
{
    public Transform SelectableHandle;

    [HideInInspector]
    public GameObject CurrentSelection;

    private FirstPersonController firstPersonController;
    private Camera m_Camera;
    private int leftMBCounter = 0;

    private void Start()
    {
        m_Camera = GameObject.FindObjectOfType<Camera>();
        firstPersonController = GameObject.FindObjectOfType<FirstPersonController>();
        if (SelectableHandle == null)
        {
            Debug.LogWarning("Bitte ein leeres Container-Gameobjekt für die Positionierung eines Selectable unter dem Rigidbody anlegen.");
            Debug.LogWarning("Es wird ein default Container-Gameobjekt erzeugt.");
            GameObject selectableHandle = new GameObject();
            selectableHandle.name = "SelectableHandle";
            selectableHandle.transform.parent = firstPersonController.transform;
            selectableHandle.transform.localPosition = new Vector3(0, 1.3f, 1.3f);
            SelectableHandle = selectableHandle.transform;
        }
    }

    // Update is called once per frame
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.LeftAlt) && CurrentSelection == null)
        {
            firstPersonController.enabled = false;
            Cursor.visible = true;
            //Cursor.lockState = CursorLockMode.Confined;
            Cursor.lockState = CursorLockMode.None;
        }

        if (Input.GetKeyUp(KeyCode.LeftAlt) && CurrentSelection == null)
        {
            firstPersonController.enabled = true;
        }

        if (Input.GetMouseButtonDown(0) && CurrentSelection!= null)
        {
            leftMBCounter++;
            // Hide mouse cursor
            Cursor.visible = false;
            Cursor.lockState = CursorLockMode.Locked;

            // Prevent player from moving around
            firstPersonController.enabled = false;

            if (leftMBCounter > 1)
            {
                CurrentSelection.GetComponent<Selectable>().Reset();

                // Unlock the player so he can move around again
                firstPersonController.enabled = true;
                leftMBCounter = 0;

                // Clear the selection
                CurrentSelection = null;
            }
        }

        if (CurrentSelection != null)
        {
            RotateObject();
        }
    }

    public void RotateObject()
    {
        float InputX = Input.GetAxis("Mouse X");
        float InputY = Input.GetAxis("Mouse Y");
        CurrentSelection.transform.RotateAround(CurrentSelection.transform.position, m_Camera.transform.up, InputX * 550f * Time.deltaTime);
        CurrentSelection.transform.RotateAround(CurrentSelection.transform.position, m_Camera.transform.right, InputY * 550f * Time.deltaTime);
    }
}

Bekannte Probleme:
Wenn der Spieler sehr dicht an einem Tisch steht, dann collidiert der Collider des Objektes mit dem Collider des Tisches.

  • Like 1

Share this post


Link to post
Share on other sites

Ich hab mal noch einen Highlight-Shader eingebunden und damit jeder was davon hat kommt es hier in den Thread. Der Shader ist nicht ganz perfekt aber dafür kostenlos und man muss kein Postprocessing betreiben (was mehr Performance kostet) um das Gleiche zu erreichen.

Damit man nicht den Unity-Standardshader anpassen muss verwende ich hier einfach 2 Materials für den gleichen Mesh. Das 1. Material rendert den Mesh und das 2. Material rendert die Outline um den Mesh. Man könnte das ganze auch in einen Shader packen, aber sobald sich der Unity-Standardshader ändert muss man diese Code wieder nachziehen.

Die Klasse Selectable muss entsprechend angepasst werden um diesen Shader zu verwenden:

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

public class Selectable : MonoBehaviour
{

    public Material defaultMaterial;
    public Material highlightMaterial;
    public float maxDistanceCamera = 10f;
    public float outLineWitdh = 0.0005f;

    private Camera m_Camera;
    private SelectionManager m_SelectionManager;
    private Renderer m_Renderer;

    private Transform originalParent;
    private Vector3 originalPosition;
    private Quaternion originalRotation;
    private Material originalMaterial;
    private float distanceToCamera;

    // Start is called before the first frame update
    void Start()
    {
        originalParent = this.transform.parent;
        originalPosition = this.transform.position;
        originalRotation = this.transform.rotation;
        m_SelectionManager = GameObject.FindObjectOfType<SelectionManager>();
        m_Renderer = this.GetComponentInChildren<Renderer>();
        m_Camera = GameObject.FindObjectOfType<Camera>();
        originalMaterial = m_Renderer.material;
    }

    public void Reset()
    {
        this.transform.parent = originalParent;
        this.transform.position = originalPosition;
        this.transform.rotation = originalRotation;
        m_Renderer.material = defaultMaterial;
        m_SelectionManager.CurrentSelection = null;
    }

    void OnMouseDown()
    {
        distanceToCamera = (transform.position - m_Camera.transform.position).magnitude;
        if (distanceToCamera < maxDistanceCamera)
        {
            m_SelectionManager.CurrentSelection = this.gameObject;
            this.transform.parent = m_SelectionManager.SelectableHandle;
            this.transform.localPosition = Vector3.zero;
        }
    }

    //If your mouse hovers over the GameObject
    void OnMouseOver()
    {
        distanceToCamera = (transform.position - m_Camera.transform.position).magnitude;
        if (distanceToCamera < maxDistanceCamera)
        {
            //m_Renderer.material = highlightMaterial;
            Material[] materials = m_Renderer.materials;
            materials = new Material[2];
            materials[0] = highlightMaterial;
            materials[0].SetFloat("_Outline", outLineWitdh);
            materials[1] = originalMaterial;
            m_Renderer.materials = materials;
        }
    }

    //The mouse is no longer hovering over the GameObject
    void OnMouseExit()
    {
        //m_Renderer.material = defaultMaterial;
        Material[] materials = new Material[1];
        materials[0] = originalMaterial;
        m_Renderer.materials = materials;
    }

}

Das Selectable-Skript bekommt nun ein Material mit diesem Shader zugewiesen:
PISkkid.png

Das Ergebnis sieht dann so aus:
6xzatIc.png

Für jedes Mesh muss leider separat die Dicke der Outline eingestellt werden, so hat z.B. die Vase eine Dicke von 0.01 und die Figur rechts daneben 0.0005.

Der Shader ist im Anhang.

Silhouette Only.shader

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...