[C#] Affichage et sélection des bones dans l'éditeur

Cette section est destinée aux scripts partagés par la communauté. Chaque post est destiné à un script. Suivez bien les recommandations.
Avatar de l’utilisateur
Alesk
Messages : 2303
Inscription : 13 Mars 2012 09:09
Localisation : Bordeaux - France
Contact :

[C#] Affichage et sélection des bones dans l'éditeur

Message par Alesk » 18 Fév 2014 13:01

Salut,

N'ayant rien trouvé de concluant sur glouglou, je me suis attelé à la réalisation d'un petit script permettant de visualiser et sélectionner directement les bones d'un gameobject depuis la vue de l'éditeur.
Voici le script à coller sur l'objet au sommet de la hiérarchie :

Code : Tout sélectionner

using UnityEngine;
using System.Collections;
using UnityEditor;

[ExecuteInEditMode]
public class DrawBones : MonoBehaviour
{

	public bool drawAxis = true;
	public bool drawJoints = true;

	private GameObject selectedBone = null;
	private bool mouseup = false;

	void Start(){
		if(Application.isPlaying) this.enabled = false;

	}

	void drawbone(Transform t)
	{
				
		foreach ( Transform child in t){
			
			float len = 0.05f;

			if(selectedBone == t.gameObject){
				Gizmos.color = Color.magenta;
			}else{
				Gizmos.color = Color.white;
			}

			Gizmos.DrawLine(t.position,  child.position);

			if(drawJoints){
				if(selectedBone == child.gameObject){
					Gizmos.color = Color.magenta;
				}else{
					Gizmos.color = Color.white;
				}
				Gizmos.DrawSphere(child.position,Mathf.Min (0.02f,Vector3.Distance(child.position,t.position)*0.2f));
			}
			if(drawAxis){
				Gizmos.color = Color.red;
				Gizmos.DrawLine(child.position,  child.position+child.right*len);

				Gizmos.color = Color.green;
				Gizmos.DrawLine(child.position,  child.position+child.up*len);

				Gizmos.color = Color.blue;
				Gizmos.DrawLine(child.position,  child.position+child.forward*len);
			}
			drawbone(child);
			
		}

		
	}

	void OnDrawGizmos(){

		if (Event.current.type == EventType.MouseDown){
			Ray r = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
			Gizmos.DrawLine(r.origin,r.origin + r.direction*10);
			getNearestBone (r,transform);
		}

		if (Event.current.type == EventType.MouseUp && selectedBone!=null){
			mouseup = true;
		}

		if(Event.current.type == EventType.Repaint && mouseup){
			Selection.activeGameObject = selectedBone;
			mouseup = false;
		}

		drawbone(transform);
	}

	void getNearestBone(Ray r,Transform t){

		Transform nearest = null;
		float distance = 9999f;
		float d = 0f;
		Component[] transforms = t.gameObject.GetComponentsInChildren(typeof(Transform), true);

		foreach ( Transform child in transforms){
			if(child.parent != null){

				d = dist_Point_to_Segment(child.position,r.origin,r.origin+r.direction*100);

				if(d < distance && d < 0.1f){
					distance = d;
					nearest = child;
				}
			}
		}
		selectedBone = null;

		if(nearest != null) selectedBone = nearest.gameObject;
	}

	float dist_Point_to_Segment( Vector3 P, Vector3 S0, Vector3 S1){
		Vector3 v = S1 - S0;
		Vector3 w = P - S0;
		
		float c1 = Vector3.Dot(w,v);
		if ( c1 <= 0 )
			return Vector3.Distance(P, S0);
		
		float c2 = Vector3.Dot(v,v);
		if ( c2 <= c1 )
			return Vector3.Distance(P, S1);
		
		float b = c1 / c2;
		Vector3 Pb = S0 + b * v;
		return Vector3.Distance(P, Pb);
	}
}
Je suis pas encore super satisfait du résultat car le clic est perturbé par la sélection native de l'éditeur.
Du coup je doit attendre l'event Repaint qui suit l'event mouseUp pour effectuer la sélection du bone le plus proche de l'endroit où l'on vient de cliquer
Mais entretemps la sélection du gameobject contenant les bones s'est opérée, donc dans l'ordre on a :
- mousedown -> détection du bone le plus proche du point cliqué
- mouseup
- unity détecte le clic et procède à la sélection du gameobject du mesh
- Repaint -> sélection du bone que le script a détecté

Du coup, à chaque clic ça procède à deux sélections consécutives, ce qui est plutôt désagréable... si quelqu'un a une idée pour régler ça, je suis preneur :)
... Ou si un autre script mieux foutu existe aussi ;)

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: Affichage et sélection des bones dans l'éditeur

Message par artemisart » 21 Fév 2014 19:41

Salut,

Tu as essayé Event.current.Use ? logiquement ça devrais le faire.
(j'ai pas pu tester, j'ai pas de rig sous la main ^^).

Avatar de l’utilisateur
Alesk
Messages : 2303
Inscription : 13 Mars 2012 09:09
Localisation : Bordeaux - France
Contact :

Re: Affichage et sélection des bones dans l'éditeur

Message par Alesk » 21 Fév 2014 23:31

oué, j'ai trouvé un bout d'exemple
maintenant ça donne ça :

Code : Tout sélectionner

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

[ExecuteInEditMode]
public class DrawBones : MonoBehaviour
{

	public bool drawAxis = false;
	public bool drawJoints = true;

	private GameObject selectedBone = null;
	private bool mouseup = false;



	void Start(){
		if(Application.isPlaying) this.enabled = false;

	}

	void drawbone(Transform t)
	{
				
		foreach ( Transform child in t){
			
			float len = 0.05f;

			if(selectedBone == t.gameObject){
				Gizmos.color = Color.magenta;
			}else{
				Gizmos.color = Color.white;
			}

			Gizmos.DrawLine(t.position,  child.position);

			if(drawJoints){
				if(selectedBone == child.gameObject){
					Gizmos.color = Color.magenta;
				}else{
					Gizmos.color = Color.white;
				}
				Gizmos.DrawSphere(child.position,Mathf.Min (0.02f,Vector3.Distance(child.position,t.position)*0.2f));
			}
			if(drawAxis){
				Gizmos.color = Color.red;
				Gizmos.DrawLine(child.position,  child.position+child.right*len);

				Gizmos.color = Color.green;
				Gizmos.DrawLine(child.position,  child.position+child.up*len);

				Gizmos.color = Color.blue;
				Gizmos.DrawLine(child.position,  child.position+child.forward*len);
			}
			drawbone(child);
			
		}

		
	}

	void OnDrawGizmos(){

		int controlID = GUIUtility.GetControlID(FocusType.Passive);

		switch(Event.current.GetTypeForControl(controlID)){
			case EventType.MouseDown:
				
				Ray r = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
				Gizmos.DrawLine(r.origin,r.origin + r.direction*10);
				getNearestBone (r,transform);

				if(selectedBone != null){
					GUIUtility.hotControl = controlID;
					Event.current.Use();
				}
				
			break;

			case EventType.MouseUp:
				if(selectedBone != null){
					//Selection.activeGameObject = selectedBone;

					GUIUtility.hotControl = 0;
					Event.current.Use();
					mouseup = true;
				}
			break;
		}

		if(Event.current.type == EventType.Repaint && mouseup){
			/*
			List<GameObject> select = new List<GameObject>();
			select.Add(selectedBone);
			Selection.objects = select.ToArray();
			*/
			Selection.activeGameObject = selectedBone;
			mouseup = false;
		}

		drawbone(transform);
	}

	void getNearestBone(Ray r,Transform t){

		Transform nearest = null;
		float distance = 9999f;
		float d = 0f;
		Component[] transforms = t.gameObject.GetComponentsInChildren(typeof(Transform), true);

		foreach ( Transform child in transforms){
			if(child.parent != null){

				d = dist_Point_to_Segment(child.position,r.origin,r.origin+r.direction*100);

				if(d < distance && d < 0.1f){
					distance = d;
					nearest = child;
				}
			}
		}
		selectedBone = null;

		if(nearest != null) selectedBone = nearest.gameObject;
	}

	float dist_Point_to_Segment( Vector3 P, Vector3 S0, Vector3 S1){
		Vector3 v = S1 - S0;
		Vector3 w = P - S0;
		
		float c1 = Vector3.Dot(w,v);
		if ( c1 <= 0 )
			return Vector3.Distance(P, S0);
		
		float c2 = Vector3.Dot(v,v);
		if ( c2 <= c1 )
			return Vector3.Distance(P, S1);
		
		float b = c1 / c2;
		Vector3 Pb = S0 + b * v;
		return Vector3.Distance(P, Pb);
	}
}
c'est pas encore parfait car on voit flasher le mesh, mais ça semble bon pour le reste :)
(sauf que ça ne récupère pas la coloration du bone si jamais on le sélectionne depuis la hiérarchie, mais bon, c'est pas trop grave)

Répondre

Revenir vers « Scripts »