您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
185 行
5.9 KiB
185 行
5.9 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Unity.Robotics.PickAndPlace
|
|
{
|
|
|
|
[RequireComponent(typeof(MeshRenderer))]
|
|
[RequireComponent(typeof(BoxCollider))]
|
|
public class TargetPlacement : MonoBehaviour
|
|
{
|
|
const string k_NameExpectedTarget = "Target";
|
|
static readonly int k_ShaderColorId = Shader.PropertyToID("_Color");
|
|
// The threshold that the Target's speed must be under to be considered "placed" in the target area
|
|
const float k_MaximumSpeedForStopped = 0.01f;
|
|
|
|
[SerializeField]
|
|
[Tooltip("Target object expected by this placement area. Can be left blank if only one Target in scene")]
|
|
GameObject m_Target;
|
|
[SerializeField]
|
|
[Range(0, 255)]
|
|
[Tooltip("Alpha value for any color set during state changes.")]
|
|
int m_ColorAlpha = 100;
|
|
|
|
MeshRenderer m_TargetMeshRenderer;
|
|
|
|
float m_ColorAlpha01 => m_ColorAlpha / 255f;
|
|
MeshRenderer m_MeshRenderer;
|
|
BoxCollider m_BoxCollider;
|
|
PlacementState m_CurrentState;
|
|
PlacementState m_LastColoredState;
|
|
|
|
public PlacementState CurrentState
|
|
{
|
|
get => m_CurrentState;
|
|
private set
|
|
{
|
|
m_CurrentState = value;
|
|
UpdateStateColor();
|
|
}
|
|
}
|
|
|
|
public enum PlacementState
|
|
{
|
|
Outside,
|
|
InsideFloating,
|
|
InsidePlaced
|
|
}
|
|
|
|
// Start is called before the first frame update
|
|
void Start()
|
|
{
|
|
// Check for mis-configurations and disable if something has changed without this script being updated
|
|
// These are warnings because this script does not contain critical functionality
|
|
if (m_Target == null)
|
|
{
|
|
m_Target = GameObject.Find(k_NameExpectedTarget);
|
|
}
|
|
|
|
if (m_Target == null)
|
|
{
|
|
Debug.LogWarning($"{nameof(TargetPlacement)} expects to find a GameObject named " +
|
|
$"{k_NameExpectedTarget} to track, but did not. Can't track placement state.");
|
|
enabled = false;
|
|
return;
|
|
}
|
|
|
|
if (!TrySetComponentReferences())
|
|
{
|
|
enabled = false;
|
|
return;
|
|
}
|
|
InitializeState();
|
|
}
|
|
|
|
bool TrySetComponentReferences()
|
|
{
|
|
m_TargetMeshRenderer = m_Target.GetComponent<MeshRenderer>();
|
|
if (m_TargetMeshRenderer == null)
|
|
{
|
|
Debug.LogWarning($"{nameof(TargetPlacement)} expects a {nameof(MeshRenderer)} to be attached " +
|
|
$"to {k_NameExpectedTarget}. Cannot check bounds without it, so cannot track placement state.");
|
|
return false;
|
|
}
|
|
|
|
// Assume these are here because they are RequiredComponent components
|
|
m_MeshRenderer = GetComponent<MeshRenderer>();
|
|
m_BoxCollider = GetComponent<BoxCollider>();
|
|
return true;
|
|
}
|
|
|
|
void OnValidate()
|
|
{
|
|
// Useful for visualizing state in editor, but doesn't wholly guarantee accurate coloring in EditMode
|
|
// Enter PlayMode to see color update correctly
|
|
if (m_Target != null)
|
|
{
|
|
if (TrySetComponentReferences())
|
|
{
|
|
InitializeState();
|
|
}
|
|
}
|
|
}
|
|
|
|
void InitializeState()
|
|
{
|
|
if (m_Target.GetComponent<BoxCollider>().bounds.Intersects(m_BoxCollider.bounds))
|
|
{
|
|
CurrentState = IsTargetStoppedInsideBounds() ?
|
|
PlacementState.InsidePlaced : PlacementState.InsideFloating;
|
|
}
|
|
else
|
|
{
|
|
CurrentState = PlacementState.Outside;
|
|
}
|
|
}
|
|
|
|
void OnTriggerEnter(Collider other)
|
|
{
|
|
if (other.gameObject.name == m_Target.name)
|
|
{
|
|
CurrentState = PlacementState.InsideFloating;
|
|
}
|
|
}
|
|
|
|
void OnTriggerExit(Collider other)
|
|
{
|
|
if (other.gameObject.name == m_Target.name)
|
|
{
|
|
CurrentState = PlacementState.Outside;
|
|
}
|
|
}
|
|
|
|
bool IsTargetStoppedInsideBounds()
|
|
{
|
|
var targetIsStopped = m_Target.GetComponent<Rigidbody>().velocity.magnitude < k_MaximumSpeedForStopped;
|
|
var targetIsInBounds = m_BoxCollider.bounds.Contains(m_TargetMeshRenderer.bounds.center);
|
|
|
|
return targetIsStopped && targetIsInBounds;
|
|
}
|
|
|
|
// Update is called once per frame
|
|
void Update()
|
|
{
|
|
if (CurrentState != PlacementState.Outside)
|
|
{
|
|
CurrentState = IsTargetStoppedInsideBounds() ?
|
|
PlacementState.InsidePlaced : PlacementState.InsideFloating;
|
|
}
|
|
}
|
|
|
|
void UpdateStateColor()
|
|
{
|
|
if (m_CurrentState == m_LastColoredState)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var mpb = new MaterialPropertyBlock();
|
|
Color stateColor;
|
|
switch (m_CurrentState)
|
|
{
|
|
case PlacementState.Outside:
|
|
stateColor = Color.red;
|
|
break;
|
|
case PlacementState.InsideFloating:
|
|
stateColor = Color.yellow;
|
|
break;
|
|
case PlacementState.InsidePlaced:
|
|
stateColor = Color.green;
|
|
break;
|
|
default:
|
|
Debug.LogError($"No state handling implemented for {m_CurrentState}");
|
|
stateColor = Color.magenta;
|
|
break;
|
|
}
|
|
|
|
stateColor.a = m_ColorAlpha01;
|
|
mpb.SetColor(k_ShaderColorId, stateColor);
|
|
m_MeshRenderer.SetPropertyBlock(mpb);
|
|
m_LastColoredState = m_CurrentState;
|
|
}
|
|
}
|
|
}
|