using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
[ExecuteAlways]
public class DrawOnBoxCollider : MonoBehaviour
{
public enum DrawStates
{
DrawCube,
DrawWireCube
}
public Transform boxColliderToDrawOn;
[Range(0f, 1f)]
public float colorTransparency;
public bool draw = false;
public DrawStates drawStates;
private BoxCollider boxCollider = null;
// Start is called before the first frame update
void Start()
{
if (boxColliderToDrawOn != null)
{
boxCollider = boxColliderToDrawOn.GetComponent<BoxCollider>();
}
drawStates = DrawStates.DrawCube;
}
// Update is called once per frame
void Update()
{
}
private void DrawGizmosOnRunTime(Color color)
{
if (boxCollider != null && draw)
{
Gizmos.color = color;
Matrix4x4 rotationMatrix = Matrix4x4.TRS(boxCollider.transform.position, boxCollider.transform.rotation, boxCollider.transform.lossyScale);
Gizmos.matrix = rotationMatrix;
switch (drawStates)
{
case DrawStates.DrawCube:
Gizmos.DrawCube(boxCollider.center, boxCollider.size);
break;
case DrawStates.DrawWireCube:
Gizmos.DrawWireCube(boxCollider.center, boxCollider.size);
break;
}
}
}
private void OnDrawGizmos()
{
Color c = Color.red;
c.a = colorTransparency;
DrawGizmosOnRunTime(c);
}
}
Stop Offset을 선택해 콜러이더 위쪽 상단으로 옮겨줍니다.
이것에 마춰 Plate도 이동 시킵니다.
Push Button 자식으로 디버깅용 Sphere를 만들어 콜라이더 상단으로 위치 시킵니다.
호버 및 선택과 같은 상호 작용 상태에 대한 이벤트를 제공하는 가장 간단한 Interactable 개체입니다.
XRPokeFilter
기본 찌르기 기능을 허용하고 상호 작용 가능 항목이 선택되는 시기에 대한 제약 조건을 정의하는 필터 구성 요소입니다.
XRPokeFollowAffordance
XRPokeFilter와 같은 IPokeStateDataProvider에 대한 애니메이션 어포던스를 따릅니다. 찌른 위치를 따르는 버튼과 같은 누른 변환을 애니메이션하는 데 사용됩니다.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
public class PushButtonController : MonoBehaviour
{
public enum HoverType
{
NONE, ON, OFF
}
private XRSimpleInteractable _interactable;
private XRPokeFollowAffordance _affordance;
private bool isOn = false;
private BoxCollider _boxCollider;
private Vector3 initBoxColliderCenter;
private HoverType _hoverType;
private XRPokeFilter filter;
void Start()
{
_interactable = GetComponent<XRSimpleInteractable>();
_affordance = GetComponent<XRPokeFollowAffordance>();
_boxCollider = GetComponent<BoxCollider>();
filter = GetComponent<XRPokeFilter>();
initBoxColliderCenter = _boxCollider.center;
_interactable.hoverEntered.AddListener(arg0 =>
{
if (isOn)
{
_hoverType = HoverType.OFF;
}
else
{
_hoverType = HoverType.ON;
_affordance.returnToInitialPosition = false;
}
});
_interactable.hoverExited.AddListener((arg0) =>
{
if (isOn == false)
{
_affordance.returnToInitialPosition = true;
_boxCollider.center = initBoxColliderCenter;
_hoverType = HoverType.NONE;
}
});
}
private void Update()
{
if (_interactable.isHovered && isOn == false)
{
//-0.045+0.0398
_boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0);
}
if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero)
{
if (_hoverType == HoverType.ON)
{
isOn = true;
Debug.Log("On");
}
else if(_hoverType == HoverType.OFF)
{
isOn = false;
Debug.Log("Off");
// if(coroutine != null) StopCoroutine(coroutine);
// coroutine= StartCoroutine(ReturnToInitPosition());
_hoverType = HoverType.NONE;
}
}
}
// private Coroutine coroutine;
//
// private IEnumerator ReturnToInitPosition()
// {
// while (true)
// {
// _affordance.pokeFollowTransform.localPosition = Vector3.Lerp(_affordance.pokeFollowTransform.localPosition, new Vector3(0, 0.045f, 0), Time.deltaTime);
// if (_affordance.pokeFollowTransform.localPosition == new Vector3(0, 0.045f, 0))
// break;
// yield return null;
// }
// Debug.LogFormat("move complete");
// _boxCollider.center = initBoxColliderCenter;
// }
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
public class PushButtonController : MonoBehaviour
{
public enum HoverType
{
NONE, ON, OFF
}
private XRSimpleInteractable _interactable;
private XRPokeFollowAffordance _affordance;
private BoxCollider _boxCollider;
private Vector3 initBoxColliderCenter;
private HoverType _hoverType;
private bool _isOn;
void Start()
{
_interactable = GetComponent<XRSimpleInteractable>();
_affordance = GetComponent<XRPokeFollowAffordance>();
_boxCollider = GetComponent<BoxCollider>();
initBoxColliderCenter = _boxCollider.center;
_interactable.hoverEntered.AddListener(arg0 =>
{
if (_isOn)
{
_hoverType = HoverType.OFF;
}
else
{
_hoverType = HoverType.ON;
_affordance.returnToInitialPosition = false;
}
});
_interactable.hoverExited.AddListener((arg0) =>
{
if (_isOn == false)
{
_affordance.returnToInitialPosition = true;
_boxCollider.center = initBoxColliderCenter;
_hoverType = HoverType.NONE;
}
});
}
private void Update()
{
if (_interactable.isHovered && _isOn == false)
{
//-0.045+0.0398
_boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0);
}
if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero)
{
if (_hoverType == HoverType.ON)
{
_isOn = true;
Debug.Log("On");
}
else if(_hoverType == HoverType.OFF)
{
_isOn = false;
Debug.Log("Off");
_hoverType = HoverType.NONE;
}
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
public class PushButtonController : MonoBehaviour
{
public enum HoverType
{
NONE, ON, OFF
}
private XRSimpleInteractable _interactable;
private XRPokeFollowAffordance _affordance;
private BoxCollider _boxCollider;
private Vector3 initBoxColliderCenter;
private HoverType _hoverType;
private bool _isOn;
void Start()
{
_interactable = GetComponent<XRSimpleInteractable>();
_affordance = GetComponent<XRPokeFollowAffordance>();
_boxCollider = GetComponent<BoxCollider>();
initBoxColliderCenter = _boxCollider.center;
_interactable.hoverEntered.AddListener(arg0 =>
{
var poke = arg0.interactorObject as XRPokeInteractor;
var ray = new Ray(poke.attachTransform.position, poke.attachTransform.forward);
//Debug.DrawRay(ray.origin, ray.direction, Color.red, 5f);
var dot = Vector3.Dot(poke.attachTransform.forward, -_boxCollider.transform.up);
Debug.LogFormat("{0}", dot);
var ray2 = new Ray(_boxCollider.transform.position, -_boxCollider.transform.up);
//Debug.DrawRay(ray2.origin, ray2.direction, Color.green, 5f);
// 코사인 값으로부터 각도(라디안)를 계산한 후, 라디안을 도로 변환
var angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
Debug.LogFormat("Dot: {0}, Angle: {1} degrees", dot, angle);
if (angle > 45)
{
Debug.LogFormat("45보다 큽니다.");
}
else
{
Debug.LogFormat("45보다 작습니다.");
if (_isOn)
{
_hoverType = HoverType.OFF;
}
else
{
_hoverType = HoverType.ON;
_affordance.returnToInitialPosition = false;
}
}
//EditorApplication.isPaused = true;
});
_interactable.hoverExited.AddListener((arg0) =>
{
if (_isOn == false)
{
_affordance.returnToInitialPosition = true;
_boxCollider.center = initBoxColliderCenter;
_hoverType = HoverType.NONE;
}
});
}
private void Update()
{
if (_interactable.isHovered && _isOn == false)
{
//-0.045+0.0398
_boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0);
}
if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero)
{
if (_hoverType == HoverType.ON)
{
_isOn = true;
Debug.Log("On");
}
else if(_hoverType == HoverType.OFF)
{
_isOn = false;
Debug.Log("Off");
_hoverType = HoverType.NONE;
}
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
public class PushButtonController : MonoBehaviour
{
public enum HoverType
{
NONE, ON, OFF
}
private XRSimpleInteractable _interactable;
private XRPokeFollowAffordance _affordance;
private BoxCollider _boxCollider;
private Vector3 initBoxColliderCenter;
private HoverType _hoverType;
private bool _isOn;
private Vector3 previousPosition;
[SerializeField] private Transform attachTransform;
void Start()
{
_interactable = GetComponent<XRSimpleInteractable>();
_affordance = GetComponent<XRPokeFollowAffordance>();
_boxCollider = GetComponent<BoxCollider>();
initBoxColliderCenter = _boxCollider.center;
// 초기 위치 설정
previousPosition = attachTransform.position;
_interactable.hoverEntered.AddListener(arg0 =>
{
var poke = arg0.interactorObject as XRPokeInteractor;
// 콜리전 발생 시 이전 위치와 현재 위치를 비교하여 이동 방향 계산
Debug.LogFormat("previousPosition: {0}", previousPosition);
Debug.LogFormat("poke.attachTransform.position: {0}", poke.attachTransform.position);
Vector3 direction = previousPosition - poke.attachTransform.position;// - previousPosition;
var ray = new Ray(poke.attachTransform.position, direction);
//Debug.DrawRay(ray.origin, ray.direction, Color.red, 5f);
DrawArrow.ForDebug(ray.origin, ray.direction, 5f, Color.red, ArrowType.Solid);
var dot = Vector3.Dot(poke.attachTransform.forward, -_boxCollider.transform.up);
//Debug.LogFormat("{0}", dot);
var ray2 = new Ray(_boxCollider.transform.position, -_boxCollider.transform.up);
Debug.DrawRay(ray2.origin, ray2.direction, Color.green, 5f);
// 코사인 값으로부터 각도(라디안)를 계산한 후, 라디안을 도로 변환
var angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
Debug.LogFormat("Dot: {0}, Angle: {1} degrees", dot, angle);
if (angle > 45)
{
Debug.LogFormat("45보다 큽니다.");
}
else
{
Debug.LogFormat("45보다 작습니다.");
if (_isOn)
{
_hoverType = HoverType.OFF;
}
else
{
_hoverType = HoverType.ON;
_affordance.returnToInitialPosition = false;
}
}
EditorApplication.isPaused = true;
// 다시 이전 위치를 현재 위치로 업데이트
//previousPosition = transform.position;
});
_interactable.hoverExited.AddListener((arg0) =>
{
if (_isOn == false)
{
_affordance.returnToInitialPosition = true;
_boxCollider.center = initBoxColliderCenter;
_hoverType = HoverType.NONE;
}
});
}
private void Update()
{
// 매 프레임마다 현재 위치를 업데이트
previousPosition = transform.position;
if (_interactable.isHovered && _isOn == false)
{
//-0.045+0.0398
_boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0);
}
if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero)
{
if (_hoverType == HoverType.ON)
{
_isOn = true;
Debug.Log("On");
}
else if(_hoverType == HoverType.OFF)
{
_isOn = false;
Debug.Log("Off");
_hoverType = HoverType.NONE;
}
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
public class PushButtonController : MonoBehaviour
{
public enum HoverType
{
NONE, ON, OFF
}
private XRSimpleInteractable _interactable;
private XRPokeFollowAffordance _affordance;
private BoxCollider _boxCollider;
private Vector3 initBoxColliderCenter;
private HoverType _hoverType;
private bool _isOn;
private Vector3 previousPosition;
[SerializeField] private Transform attachTransform;
void Start()
{
_interactable = GetComponent<XRSimpleInteractable>();
_affordance = GetComponent<XRPokeFollowAffordance>();
_boxCollider = GetComponent<BoxCollider>();
initBoxColliderCenter = _boxCollider.center;
// 초기 위치 설정
previousPosition = attachTransform.position;
_interactable.hoverEntered.AddListener(arg0 =>
{
var poke = arg0.interactorObject as XRPokeInteractor;
// 콜리전 발생 시 이전 위치와 현재 위치를 비교하여 이동 방향 계산
Debug.LogFormat("previousPosition: {0}", previousPosition);
Debug.LogFormat("poke.attachTransform.position: {0}", poke.attachTransform.position);
Vector3 direction = (previousPosition - poke.attachTransform.position).normalized;// - previousPosition;
var ray = new Ray(_boxCollider.transform.position, direction);
//Debug.DrawRay(ray.origin, ray.direction, Color.red, 5f);
DrawArrow.ForDebug(ray.origin, ray.direction, 5f, Color.red, ArrowType.Solid);
var dot = Vector3.Dot(direction, -_boxCollider.transform.up);
//Debug.LogFormat("{0}", dot);
var ray2 = new Ray(_boxCollider.transform.position, -_boxCollider.transform.up);
Debug.DrawRay(ray2.origin, ray2.direction, Color.green, 5f);
// 코사인 값으로부터 각도(라디안)를 계산한 후, 라디안을 도로 변환
var angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
Debug.LogFormat("Dot: {0}, Angle: {1} degrees", dot, angle);
if (angle > 45)
{
Debug.LogFormat("45보다 큽니다.");
}
else
{
Debug.LogFormat("45보다 작습니다.");
if (_isOn)
{
_hoverType = HoverType.OFF;
}
else
{
_hoverType = HoverType.ON;
_affordance.returnToInitialPosition = false;
}
}
//EditorApplication.isPaused = true;
// 다시 이전 위치를 현재 위치로 업데이트
previousPosition = transform.position;
});
_interactable.hoverExited.AddListener((arg0) =>
{
if (_isOn == false)
{
_affordance.returnToInitialPosition = true;
_boxCollider.center = initBoxColliderCenter;
_hoverType = HoverType.NONE;
}
});
}
private void Update()
{
// 매 프레임마다 현재 위치를 업데이트
previousPosition = transform.position;
if (_interactable.isHovered && _isOn == false)
{
//-0.045+0.0398
_boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0);
}
if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero)
{
if (_hoverType == HoverType.ON)
{
_isOn = true;
Debug.Log("On");
}
else if(_hoverType == HoverType.OFF)
{
_isOn = false;
Debug.Log("Off");
_hoverType = HoverType.NONE;
}
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
public class PushButtonController : MonoBehaviour
{
public enum HoverType
{
NONE, ON, OFF
}
private XRSimpleInteractable _interactable;
private XRPokeFollowAffordance _affordance;
private BoxCollider _boxCollider;
private Vector3 initBoxColliderCenter;
private HoverType _hoverType;
private bool _isOn;
private Vector3 previousPosition;
[SerializeField] private Transform attachTransform;
private bool isHoverEntered = false;
void Start()
{
_interactable = GetComponent<XRSimpleInteractable>();
_affordance = GetComponent<XRPokeFollowAffordance>();
_boxCollider = GetComponent<BoxCollider>();
initBoxColliderCenter = _boxCollider.center;
// 초기 위치 설정
previousPosition = attachTransform.position;
_interactable.hoverEntered.AddListener(arg0 =>
{
var poke = arg0.interactorObject as XRPokeInteractor;
if (isHoverEntered == false)
{
isHoverEntered = true;
if (_hoverType == HoverType.NONE)
{
// 콜리전 발생 시 이전 위치와 현재 위치를 비교하여 이동 방향 계산
Debug.LogFormat("previousPosition: {0}", previousPosition);
Debug.LogFormat("poke.attachTransform.position: {0}", poke.attachTransform.position);
Vector3 direction = (previousPosition - poke.attachTransform.position).normalized;// - previousPosition;
var ray = new Ray(_boxCollider.transform.position, direction);
//Debug.DrawRay(ray.origin, ray.direction, Color.red, 5f);
DrawArrow.ForDebug(ray.origin, ray.direction, 5f, Color.red, ArrowType.Solid);
var dot = Vector3.Dot(direction, -_boxCollider.transform.up);
//Debug.LogFormat("{0}", dot);
var ray2 = new Ray(_boxCollider.transform.position, -_boxCollider.transform.up);
Debug.DrawRay(ray2.origin, ray2.direction, Color.green, 5f);
// 코사인 값으로부터 각도(라디안)를 계산한 후, 라디안을 도로 변환
var angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
Debug.LogFormat("Dot: {0}, Angle: {1} degrees", dot, angle);
if (angle > 45)
{
Debug.LogFormat("45보다 큽니다.");
}
else
{
Debug.LogFormat("45보다 작습니다.");
if (_isOn)
{
_hoverType = HoverType.OFF;
}
else
{
_hoverType = HoverType.ON;
_affordance.returnToInitialPosition = false;
}
}
//EditorApplication.isPaused = true;
// 다시 이전 위치를 현재 위치로 업데이트
previousPosition = transform.position;
}
}
});
_interactable.hoverExited.AddListener((arg0) =>
{
if (_isOn == false)
{
_affordance.returnToInitialPosition = true;
_boxCollider.center = initBoxColliderCenter;
}
_hoverType = HoverType.NONE;
isHoverEntered = false;
});
}
private void Update()
{
// 매 프레임마다 현재 위치를 업데이트
previousPosition = transform.position;
if (_interactable.isHovered && _isOn == false)
{
//-0.045+0.0398
_boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0);
}
if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero)
{
if (_hoverType == HoverType.ON)
{
if (_isOn == false)
{
_isOn = true;
Debug.Log("On");
}
}
else if(_hoverType == HoverType.OFF)
{
if (_isOn == true)
{
_isOn = false;
Debug.Log("Off");
_hoverType = HoverType.NONE;
}
}
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
public class PushButtonController : MonoBehaviour
{
public enum HoverType
{
NONE, ON, OFF
}
private XRSimpleInteractable _interactable;
private XRPokeFollowAffordance _affordance;
private BoxCollider _boxCollider;
private Vector3 initBoxColliderCenter;
private HoverType _hoverType;
private bool _isOn;
private Vector3 previousPosition;
[SerializeField] private Transform attachTransform;
private bool isHoverEntered = false;
private bool isValidated = false;
void Start()
{
_interactable = GetComponent<XRSimpleInteractable>();
_affordance = GetComponent<XRPokeFollowAffordance>();
_boxCollider = GetComponent<BoxCollider>();
initBoxColliderCenter = _boxCollider.center;
// 초기 위치 설정
previousPosition = attachTransform.position;
_interactable.hoverEntered.AddListener(arg0 =>
{
var poke = arg0.interactorObject as XRPokeInteractor;
if (isHoverEntered == false)
{
isHoverEntered = true;
if (_hoverType == HoverType.NONE)
{
// 콜리전 발생 시 이전 위치와 현재 위치를 비교하여 이동 방향 계산
Debug.LogFormat("previousPosition: {0}", previousPosition);
Debug.LogFormat("poke.attachTransform.position: {0}", poke.attachTransform.position);
Vector3 direction = (previousPosition - poke.attachTransform.position).normalized;// - previousPosition;
var ray = new Ray(_boxCollider.transform.position, direction);
//Debug.DrawRay(ray.origin, ray.direction, Color.red, 5f);
DrawArrow.ForDebug(ray.origin, ray.direction, 5f, Color.red, ArrowType.Solid);
var dot = Vector3.Dot(direction, -_boxCollider.transform.up);
//Debug.LogFormat("{0}", dot);
var ray2 = new Ray(_boxCollider.transform.position, -_boxCollider.transform.up);
Debug.DrawRay(ray2.origin, ray2.direction, Color.green, 5f);
// 코사인 값으로부터 각도(라디안)를 계산한 후, 라디안을 도로 변환
var angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
Debug.LogFormat("Dot: {0}, Angle: {1} degrees", dot, angle);
if (angle > 45)
{
Debug.LogFormat("45보다 큽니다.");
_affordance.enabled = false;
isValidated = false;
}
else
{
Debug.LogFormat("45보다 작습니다.");
_affordance.enabled = true;
if (_isOn)
{
_hoverType = HoverType.OFF;
}
else
{
_hoverType = HoverType.ON;
_affordance.returnToInitialPosition = false;
}
isValidated = true;
}
//EditorApplication.isPaused = true;
// 다시 이전 위치를 현재 위치로 업데이트
previousPosition = transform.position;
}
}
});
_interactable.hoverExited.AddListener((arg0) =>
{
if (isHoverEntered)
{
Debug.LogFormat("{0}, {1}", isValidated, _isOn);
if (isValidated)
{
if (_isOn == false)
{
_affordance.returnToInitialPosition = true;
_boxCollider.center = initBoxColliderCenter;
}
}
_hoverType = HoverType.NONE;
isHoverEntered = false;
isValidated = false;
}
});
}
private void Update()
{
// 매 프레임마다 현재 위치를 업데이트
previousPosition = transform.position;
if (isValidated == false) return;
if (_interactable.isHovered && _isOn == false)
{
//-0.045+0.0398
_boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0);
}
if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero)
{
if (_hoverType == HoverType.ON)
{
if (_isOn == false)
{
_isOn = true;
Debug.Log("On");
}
}
else if(_hoverType == HoverType.OFF)
{
if (_isOn == true)
{
_isOn = false;
Debug.Log("Off");
_hoverType = HoverType.NONE;
}
}
}
}
}
using System; using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; using UnityEngine.XR.Interaction.Toolkit.Filtering; using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets; public class PushButtonController : MonoBehaviour { public enum HoverType { NONE, ON, OFF } private XRSimpleInteractable _interactable; private XRPokeFollowAffordance _affordance; private BoxCollider _boxCollider; private Vector3 initBoxColliderCenter; private HoverType _hoverType; private bool _isOn; private Vector3 previousPosition; [SerializeField] private Transform attachTransform; private bool isHoverEntered = false; private bool isValidated = false; void Start() { _interactable = GetComponent<XRSimpleInteractable>(); _affordance = GetComponent<XRPokeFollowAffordance>(); _boxCollider = GetComponent<BoxCollider>(); initBoxColliderCenter = _boxCollider.center; // 초기 위치 설정 previousPosition = attachTransform.position; _interactable.hoverEntered.AddListener(arg0 => { var poke = arg0.interactorObject as XRPokeInteractor; if (isHoverEntered == false) { isHoverEntered = true; if (_hoverType == HoverType.NONE) { // 콜리전 발생 시 이전 위치와 현재 위치를 비교하여 이동 방향 계산 Debug.LogFormat("previousPosition: {0}", previousPosition); Debug.LogFormat("poke.attachTransform.position: {0}", poke.attachTransform.position); Vector3 direction = (previousPosition - poke.attachTransform.position).normalized;// - previousPosition; var ray = new Ray(_boxCollider.transform.position, direction); Debug.DrawRay(ray.origin, ray.direction, Color.red, 0.1f); //DrawArrow.ForDebug(ray.origin, ray.direction, 5f, Color.red, ArrowType.Solid); var dot = Vector3.Dot(direction, -_boxCollider.transform.up); //Debug.LogFormat("{0}", dot); var ray2 = new Ray(_boxCollider.transform.position, -_boxCollider.transform.up); Debug.DrawRay(ray2.origin, ray2.direction, Color.green, 5f); // 코사인 값으로부터 각도(라디안)를 계산한 후, 라디안을 도로 변환 var angle = Mathf.Acos(dot) * Mathf.Rad2Deg; Debug.LogFormat("Dot: {0}, Angle: {1} degrees", dot, angle); if (angle > 45) { Debug.LogFormat("45보다 큽니다."); _affordance.enabled = false; isValidated = false; } else { Debug.LogFormat("45보다 작습니다."); _affordance.enabled = true; if (_isOn) { _hoverType = HoverType.OFF; } else { _hoverType = HoverType.ON; _affordance.returnToInitialPosition = false; } isValidated = true; } //EditorApplication.isPaused = true; // 다시 이전 위치를 현재 위치로 업데이트 previousPosition = transform.position; } } }); _interactable.hoverExited.AddListener((arg0) => { if (isHoverEntered) { Debug.LogFormat("{0}, {1}", isValidated, _isOn); if (isValidated) { if (_isOn == false) { _affordance.returnToInitialPosition = true; _boxCollider.center = initBoxColliderCenter; } } _hoverType = HoverType.NONE; isHoverEntered = false; isValidated = false; } }); } private void Update() { // 매 프레임마다 현재 위치를 업데이트 previousPosition = transform.position; if (isValidated == false) return; if (_interactable.isHovered && _isOn == false) { //-0.045+0.0398 _boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0); } if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero) { if (_hoverType == HoverType.ON) { if (_isOn == false) { _isOn = true; Debug.Log("On"); } } else if(_hoverType == HoverType.OFF) { if (_isOn == true) { _isOn = false; Debug.Log("Off"); _hoverType = HoverType.NONE; } } } } }
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
public class PushButtonController : MonoBehaviour
{
public enum HoverType
{
NONE, ON, OFF
}
private XRSimpleInteractable _interactable;
private XRPokeFollowAffordance _affordance;
private BoxCollider _boxCollider;
private Vector3 initBoxColliderCenter;
private HoverType _hoverType;
private bool _isOn;
private Vector3 previousPosition;
[SerializeField] private Transform attachTransform;
private bool isHoverEntered = false;
private bool isValidated = false;
void Start()
{
_interactable = GetComponent<XRSimpleInteractable>();
_affordance = GetComponent<XRPokeFollowAffordance>();
_boxCollider = GetComponent<BoxCollider>();
initBoxColliderCenter = _boxCollider.center;
// 초기 위치 설정
previousPosition = attachTransform.position;
_interactable.hoverEntered.AddListener(arg0 =>
{
var poke = arg0.interactorObject as XRPokeInteractor;
if (isHoverEntered == false)
{
isHoverEntered = true;
if (_hoverType == HoverType.NONE)
{
// 콜리전 발생 시 이전 위치와 현재 위치를 비교하여 이동 방향 계산
Debug.LogFormat("previousPosition: {0}", previousPosition);
Debug.LogFormat("poke.attachTransform.position: {0}", poke.attachTransform.position);
Vector3 direction = (previousPosition - poke.attachTransform.position).normalized;// - previousPosition;
var ray = new Ray(_boxCollider.transform.position, direction);
Debug.DrawRay(ray.origin, ray.direction, Color.red, 0.1f);
//DrawArrow.ForDebug(ray.origin, ray.direction, 5f, Color.red, ArrowType.Solid);
var dot = Vector3.Dot(direction, -_boxCollider.transform.up);
//Debug.LogFormat("{0}", dot);
var ray2 = new Ray(_boxCollider.transform.position, -_boxCollider.transform.up);
Debug.DrawRay(ray2.origin, ray2.direction, Color.green, 5f);
// 코사인 값으로부터 각도(라디안)를 계산한 후, 라디안을 도로 변환
var angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
Debug.LogFormat("Dot: {0}, Angle: {1} degrees", dot, angle);
if (angle > 45)
{
Debug.LogFormat("45보다 큽니다.");
_affordance.enabled = false;
isValidated = false;
}
else
{
Debug.LogFormat("45보다 작습니다.");
_affordance.enabled = true;
if (_isOn)
{
_hoverType = HoverType.OFF;
}
else
{
_hoverType = HoverType.ON;
_affordance.returnToInitialPosition = false;
}
isValidated = true;
}
//EditorApplication.isPaused = true;
// 다시 이전 위치를 현재 위치로 업데이트
previousPosition = transform.position;
}
}
});
_interactable.hoverExited.AddListener((arg0) =>
{
if (isHoverEntered)
{
Debug.LogFormat("{0}, {1}", isValidated, _isOn);
if (isValidated)
{
if (_isOn == false)
{
_affordance.returnToInitialPosition = true;
_boxCollider.center = initBoxColliderCenter;
}
}
_hoverType = HoverType.NONE;
isHoverEntered = false;
isValidated = false;
}
});
}
private void Update()
{
// 매 프레임마다 현재 위치를 업데이트
previousPosition = transform.position;
if (isValidated == false) return;
if (_interactable.isHovered && _isOn == false)
{
//-0.045+0.0398
_boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0);
}
if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero)
{
if (_hoverType == HoverType.ON)
{
if (_isOn == false)
{
_isOn = true;
Debug.Log("On");
}
}
else if(_hoverType == HoverType.OFF)
{
if (_isOn == true)
{
_isOn = false;
Debug.Log("Off");
_hoverType = HoverType.NONE;
}
}
}
}
}
using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif
// adapted from http://wiki.unity3d.com/index.php/DrawArrow
public enum ArrowType
{
Default,
Thin,
Double,
Triple,
Solid,
Fat,
ThreeD,
}
public static class DrawArrow
{
public static void ForGizmo(Vector3 pos, Vector3 direction, Color? color = null, bool doubled = false, float arrowHeadLength = 0.2f, float arrowHeadAngle = 20.0f)
{
Gizmos.color = color ?? Color.white;
//arrow shaft
Gizmos.DrawRay(pos, direction);
if (direction != Vector3.zero)
{
//arrow head
Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180+arrowHeadAngle,0) * new Vector3(0,0,1);
Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180-arrowHeadAngle,0) * new Vector3(0,0,1);
Gizmos.DrawRay(pos + direction, right * arrowHeadLength);
Gizmos.DrawRay(pos + direction, left * arrowHeadLength);
}
}
public static void ForDebug(Vector3 pos, Vector3 direction, float duration = 0.5f, Color? color = null, ArrowType type = ArrowType.Default, float arrowHeadLength = 0.2f, float arrowHeadAngle = 30.0f, bool sceneCamFollows = false)
{
Color actualColor = color ?? Color.white;
duration = duration/Time.timeScale;
float width = 0.01f;
Vector3 directlyRight = Vector3.zero;
Vector3 directlyLeft = Vector3.zero;
Vector3 directlyBack = Vector3.zero;
Vector3 headRight = Vector3.zero;
Vector3 headLeft = Vector3.zero;
if (direction != Vector3.zero)
{
directlyRight = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180+90,0) * new Vector3(0,0,1);
directlyLeft = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180-90,0) * new Vector3(0,0,1);
directlyBack = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180,0) * new Vector3(0,0,1);
headRight = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180+arrowHeadAngle,0) * new Vector3(0,0,1);
headLeft = Quaternion.LookRotation(direction) * Quaternion.Euler(0,180-arrowHeadAngle,0) * new Vector3(0,0,1);
}
//draw arrow head
Debug.DrawRay(pos + direction, headRight * arrowHeadLength, actualColor, duration);
Debug.DrawRay(pos + direction, headLeft * arrowHeadLength, actualColor, duration);
switch (type) {
case ArrowType.Default:
Debug.DrawRay(pos, direction, actualColor, duration); //draw center line
break;
case ArrowType.Double:
Debug.DrawRay(pos + directlyRight * width, direction * (1-width), actualColor, duration); //draw line slightly to right
Debug.DrawRay(pos + directlyLeft * width, direction * (1-width), actualColor, duration); //draw line slightly to left
//draw second arrow head
Debug.DrawRay(pos + directlyBack * width + direction, headRight * arrowHeadLength, actualColor, duration);
Debug.DrawRay(pos + directlyBack * width + direction, headLeft * arrowHeadLength, actualColor, duration);
break;
case ArrowType.Triple:
Debug.DrawRay(pos, direction, actualColor, duration); //draw center line
Debug.DrawRay(pos + directlyRight * width, direction * (1-width), actualColor, duration); //draw line slightly to right
Debug.DrawRay(pos + directlyLeft * width, direction * (1-width), actualColor, duration); //draw line slightly to left
break;
case ArrowType.Fat:
break;
case ArrowType.Solid:
int increments = 20;
for (int i=0;i<increments;i++)
{
float displacement = Mathf.Lerp(-width, +width, i/(float)increments);
//draw arrow body
Debug.DrawRay(pos + directlyRight * displacement, direction, actualColor, duration); //draw line slightly to right
Debug.DrawRay(pos + directlyLeft * displacement, direction, actualColor, duration); //draw line slightly to left
//draw arrow head
Debug.DrawRay((pos + direction) + directlyRight * displacement, headRight * arrowHeadLength, actualColor, duration);
Debug.DrawRay((pos + direction) + directlyRight * displacement, headLeft * arrowHeadLength, actualColor, duration);
}
break;
case ArrowType.Thin:
Debug.DrawRay(pos, direction, actualColor, duration); //draw center line
break;
case ArrowType.ThreeD:
break;
}
/*#if UNITY_EDITOR
//snap the Scene view camera to a spot where it is looking directly at this arrow.
if (sceneCamFollows)
SceneViewCameraFollower.activateAt(pos + direction, duration, "_arrow");
#endif*/
}
public static void randomStar(Vector3 center, Color color)
{
//special: refuse to draw at 0,0.
if (center == Vector3.zero) return;
for(int i=0;i<2;i++)
DrawArrow.ForGizmo(center, UnityEngine.Random.onUnitSphere * 1, color, false, 0.1f, 30.0f);
}
public static void comparePositions(Transform t1, Transform t2)
{
//direct from one to the other:
ForDebug(t1.position, t2.position - t1.position);
//direction
//Vector3 moveDirection = (t2.position-t1.position).normalized;
}
}
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
[ExecuteAlways]
public class DrawOnBoxCollider : MonoBehaviour
{
public enum DrawStates
{
DrawCube,
DrawWireCube
}
public Transform boxColliderToDrawOn;
[Range(0f, 1f)]
public float colorTransparency;
public bool draw = false;
public DrawStates drawStates;
private BoxCollider boxCollider = null;
// Start is called before the first frame update
void Start()
{
if (boxColliderToDrawOn != null)
{
boxCollider = boxColliderToDrawOn.GetComponent<BoxCollider>();
}
drawStates = DrawStates.DrawCube;
}
// Update is called once per frame
void Update()
{
}
private void DrawGizmosOnRunTime(Color color)
{
if (boxCollider != null && draw)
{
Gizmos.color = color;
Matrix4x4 rotationMatrix = Matrix4x4.TRS(boxCollider.transform.position, boxCollider.transform.rotation, boxCollider.transform.lossyScale);
Gizmos.matrix = rotationMatrix;
switch (drawStates)
{
case DrawStates.DrawCube:
Gizmos.DrawCube(boxCollider.center, boxCollider.size);
break;
case DrawStates.DrawWireCube:
Gizmos.DrawWireCube(boxCollider.center, boxCollider.size);
break;
}
}
}
private void OnDrawGizmos()
{
Color c = Color.red;
c.a = colorTransparency;
DrawGizmosOnRunTime(c);
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
public class PushButtonController : MonoBehaviour
{
public enum HoverType
{
NONE, ON, OFF
}
private XRSimpleInteractable _interactable;
private XRPokeFollowAffordance _affordance;
private BoxCollider _boxCollider;
private Vector3 initBoxColliderCenter;
private HoverType _hoverType;
private bool _isOn;
private Vector3 previousPosition;
[SerializeField] private Transform attachTransform;
private bool isHoverEntered = false;
private bool isValidated = false;
void Start()
{
_interactable = GetComponent<XRSimpleInteractable>();
_affordance = GetComponent<XRPokeFollowAffordance>();
_boxCollider = GetComponent<BoxCollider>();
initBoxColliderCenter = _boxCollider.center;
// 초기 위치 설정
previousPosition = attachTransform.position;
_interactable.hoverEntered.AddListener(arg0 =>
{
var poke = arg0.interactorObject as XRPokeInteractor;
if (isHoverEntered == false)
{
isHoverEntered = true;
if (_hoverType == HoverType.NONE)
{
// 콜리전 발생 시 이전 위치와 현재 위치를 비교하여 이동 방향 계산
Debug.LogFormat("previousPosition: {0}", previousPosition);
Debug.LogFormat("poke.attachTransform.position: {0}", poke.attachTransform.position);
Vector3 direction = (previousPosition - poke.attachTransform.position).normalized;// - previousPosition;
var ray = new Ray(_boxCollider.transform.position, direction);
Debug.DrawRay(ray.origin, ray.direction, Color.red, 0.1f);
//DrawArrow.ForDebug(ray.origin, ray.direction, 5f, Color.red, ArrowType.Solid);
var dot = Vector3.Dot(direction, -_boxCollider.transform.up);
//Debug.LogFormat("{0}", dot);
var ray2 = new Ray(_boxCollider.transform.position, -_boxCollider.transform.up);
Debug.DrawRay(ray2.origin, ray2.direction, Color.green, 5f);
// 코사인 값으로부터 각도(라디안)를 계산한 후, 라디안을 도로 변환
var angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
Debug.LogFormat("Dot: {0}, Angle: {1} degrees", dot, angle);
if (angle > 45)
{
Debug.LogFormat("45보다 큽니다.");
_affordance.enabled = false;
isValidated = false;
}
else
{
Debug.LogFormat("45보다 작습니다.");
_affordance.enabled = true;
if (_isOn)
{
_hoverType = HoverType.OFF;
}
else
{
_hoverType = HoverType.ON;
_affordance.returnToInitialPosition = false;
}
isValidated = true;
}
//EditorApplication.isPaused = true;
// 다시 이전 위치를 현재 위치로 업데이트
previousPosition = transform.position;
}
}
});
_interactable.hoverExited.AddListener((arg0) =>
{
if (isHoverEntered)
{
Debug.LogFormat("{0}, {1}", isValidated, _isOn);
if (isValidated)
{
if (_isOn == false)
{
_affordance.returnToInitialPosition = true;
_boxCollider.center = initBoxColliderCenter;
}
}
_hoverType = HoverType.NONE;
isHoverEntered = false;
isValidated = false;
}
});
}
private void Update()
{
// 매 프레임마다 현재 위치를 업데이트
previousPosition = transform.position;
if (isValidated == false) return;
if (_interactable.isHovered && _isOn == false)
{
//-0.045+0.0398
_boxCollider.center = new Vector3(0, -0.045f + _affordance.pokeFollowTransform.localPosition.y, 0);
}
if (_interactable.isHovered && _affordance.pokeFollowTransform.localPosition == Vector3.zero)
{
if (_hoverType == HoverType.ON)
{
if (_isOn == false)
{
_isOn = true;
Debug.Log("On");
}
}
else if(_hoverType == HoverType.OFF)
{
if (_isOn == true)
{
_isOn = false;
Debug.Log("Off");
_hoverType = HoverType.NONE;
}
}
}
}
}
Left / Right Controller를 선택후 XR Controller를 제외한 나머지 컴포넌트들을 지웁니다.
Left Controller를 선택후 빈오브젝트 (Visual)를 생성하고 컨트롤러 모델을 부착 합니다.
XR Controller의 Model에 컨트롤러 모델을 넣어 줍니다.
오른손도 동일하게 작업 합니다.
실행후 결과를 확인 합니다.
2단계
프로젝트 창에서 Ray Interactor를 검색해 프리팹을 Left / Right Controller자식으로 넣어줍니다.
마키 만들기
다음과 같은 구조로 만들어 줍니다.
위치를 적당히 잡아 줍니다.
Maki를 선택하고 Rigidbody를 부착후 use Gravity를 비활성화 합니다.
이어서 XR Grab Interactable컴포넌트를 부착 합니다.
Throw On Detach는 체크 해제 합니다.
마키 메쉬를 선택후 메쉬 콜라이더를 부착 하고 convex를 체크 합니다.
실행후 결과를 확인 합니다.
3단계
다음과 같은 구조로 손전등을 만들어 줍니다.
위치와 크기를 조절 합니다.
Flashlight를 선택하고 빈오브젝트 (Collider) 를 생성 하고 BoxCollider를 부착 합니다.
Flashlight를 선택하고 Rigidbody를 부착 하고 use gravity는 체크 해제 합니다.
이어서 XR Grab Interactable컴포넌트를 부착 합니다. Throw on Detach는 체크 해제 합니다.
실행후 결과를 확인 합니다.
Left Controller의 Ray Interactor를 선택 하고
Ray Interactor컴포넌트의 Force Grab을 체크 하고 Anchor Control은 체크 해제 합니다.
Force Grab
Force grab moves the object to your hand rather than interacting with it at a distance.
Anchor Control Allows the user to move the attach anchor point using the joystick.
실행하고 결과를 확인 합니다.
Hide Controller on Select 를 체크 하면 그립 했을때 컨트롤러를 숨깁니다.
손전등의 메쉬를 선택해 X 축으로 90도 회전 합니다.
콜라이더도 위와 같은 방법으로 회전 시킵니다.
실행후 결과를 확인 합니다.
Flashlight 스크립트를 생성하고 다음과 같이 작성후 Flashlight게임 오브젝트에 부착 합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class Flashlight : MonoBehaviour
{
public void Activated(ActivateEventArgs args)
{
Debug.LogFormat("Activated: {0}", args.interactorObject);
}
public void Deactivated(DeactivateEventArgs args)
{
Debug.LogFormat("Deactivated: {0}", args.interactorObject);
}
}
Flashlight를 선택하고 이벤트가 발생하면 호출되는 메서드를 연결해줍니다. Activated 이벤트는 그립하고 트리거를 입력 하면 호출 됩니다. (Deactivated는 반대)
실행후 손전등을 그립 하고 인덱스 트리거를 땡겨 봅니다.
이제 Flashlight를 다음과 같이 수정 합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class Flashlight : MonoBehaviour
{
public Material material; // 렌더러의 메터리얼
public GameObject light;
private bool isOn = false;
public void Activated(ActivateEventArgs args)
{
this.isOn = !isOn;
if (isOn)
{
this.ActivateEmission();
this.light.SetActive(true);
}
else
{
DeactivateEmission();
this.light.SetActive(false);
}
}
public void ActivateEmission()
{
if (material != null)
{
material.EnableKeyword("_EMISSION");
material.SetColor("_EmissionColor", Color.yellow); // 원하는 색상으로 설정 가능
}
}
public void DeactivateEmission()
{
if (material != null)
{
material.DisableKeyword("_EMISSION");
}
}
}
기본으로 Left / Right Controller에 Ray Interactor가 붙어 있음
프로젝트 창에도 프리팹으로 지원
샘플을 보면 여러 인터렉터들을 사용할수 있기 때문에 Left Controller자식으로 붙어 있는 모습
Left Controller 에는 다음과 같은 컴포넌트들이 붙어 있음
우리는 XR Ray Interactor와 XR Interactio Group만 일단 알아볼거기 때문에
Left / Right Controller를 제거후 XR Controller컴포넌트이외 컴포넌트들은 제거 한다
프로젝트 창에서 Ray Interactor를 검색해 Left, Right Controller에 넣어 준다
실행후 결과를 확인 한다
지난 시간에 사용했던 스시로 테스트 해본다
Force Grab
강제 잡기는 멀리서 개체와 상호 작용하는 대신 개체를 손으로 이동시킵니다.
Anchor Control
사용자가 조이스틱을 사용하여 부착 앵커 포인트를 이동할 수 있습니다.
Translate Speed
앵커가 변환되는 속도입니다. 앵커 제어가 활성화된 경우에만 사용 및 표시됩니다.
Rotate Reference Frame
부착 앵커 포인트를 회전할 때 위쪽 축을 정의하는 선택적 참조 프레임입니다. 설정되지 않은 경우 연결 변환의 로컬 위쪽 축을 기준으로 회전합니다. 앵커 제어가 활성화된 경우에만 사용 및 표시됩니다.
Rotation Mode
앵커 회전을 제어하는 방법을 지정합니다. 앵커 제어가 활성화된 경우에만 사용 및 표시됩니다.
Rotate Over Time
회전 입력이 활성화된 동안 시간에 따른 앵커 회전을 제어하려면 회전 모드를 시간에 따른 회전으로 설정하세요.
Match Direction
회전 모드를 방향 일치로 설정하여 앵커 회전을 2차원 회전 입력 방향과 일치시킵니다.
Rotate Speed
앵커가 회전하는 속도입니다. 앵커 제어가 활성화되고 회전 모드가 시간에 따른 회전으로 설정된 경우에만 사용 및 표시됩니다.
Attach Transform
Interactable의 연결 지점으로 사용되는 변환입니다. 자동으로 인스턴스화되고 Awake if None으로 설정됩니다. 이를 설정하면 이전 개체가 자동으로 삭제되지 않습니다.
설정해본다
일단 레이가 나오는 시작지점부터 달라짐
Ray Origin Transform
광선 투사의 시작 위치와 방향입니다. 자동으로 인스턴스화되고 Awake if None으로 설정되며 XRBaseInteractor.attachTransform의 포즈로 초기화됩니다. 이를 설정하면 이전 개체가 자동으로 삭제되지 않습니다.
생성해서 넣어보자
Disable Visuals When Blocked In Group
이 인터랙터가 상호 작용 그룹의 일부이고 그룹 내 다른 인터랙터의 활성 상호 작용으로 인해 상호 작용할 수 없는 경우 시각적 개체를 비활성화할지 여부입니다.
Line Type
The type of ray cast.
Straight Line
설정된 광선 길이로 장면에 단일 광선 투사를 수행하려면 선 유형을 직선으로 설정합니다.
Projectile Curve
발사체 곡선을 생성하기 위해 발사체의 궤적을 샘플링하려면 선 유형을 발사체 곡선으로 설정합니다.
Bezier Curve
제어점과 끝점을 사용하여 2차 베지어 곡선을 생성하려면 선 유형을 베지어 곡선으로 설정하세요.
Max Raycast Distance
선 유형이 직선인 경우에만 사용 및 표시됩니다. 이 값을 늘리면 선이 더 멀리 도달하게 됩니다.
Reference Frame
선 유형이 투영 곡선 또는 베지어 곡선인 경우에만 사용 및 표시됩니다. 지면과 위쪽을 정의하는 곡선의 참조 프레임입니다. 시작 시 설정되지 않은 경우 XROrigin.Origin GameObject를 찾으려고 시도하고, 존재하지 않는 경우 기본적으로 글로벌 up 및 원점을 사용합니다.
Velocity
선 유형이 발사체 곡선인 경우에만 사용 및 표시됩니다. 발사체의 초기 속도. 이 값을 늘리면 곡선이 더 멀리 도달하게 됩니다.
Acceleration
선 유형이 발사체 곡선인 경우에만 사용 및 표시됩니다. 참조 프레임에서 발사체의 중력입니다.
Additional Flight Time
선 유형이 발사체 곡선인 경우에만 사용 및 표시됩니다. 발사체가 조정된 지면에 착륙한 후 추가 비행 시간입니다. 이 값을 늘리면 끝점의 높이가 낮아집니다.
Sample Frequency
선 유형이 투영 곡선 또는 베지어 곡선인 경우에만 사용 및 표시됩니다. Unity가 곡선 경로를 근사화하는 데 사용하는 샘플 포인트 수입니다. 값이 클수록 광선 투사 수로 인해 성능이 저하되는 대신 더 나은 품질을 얻을 수 있습니다. n 값은 레이 캐스팅을 위해 n - 1개의 선 세그먼트를 생성합니다. 유효한 값은 항상 2이므로 직선 유형을 사용할 때 이 속성은 사용되지 않습니다.
유효한 적중이나 선택이 발생하지 않을 때 시간이 지남에 따라 선 길이가 줄어들지 여부를 결정합니다.
체크 해제 하면 라인이 안줄어 듬
Raycast Trigger Interaction
레이캐스트를 통한 트리거 충돌체와의 상호 작용 유형입니다.
3가지 옵션이 있음
이건 머선 옵션인지 잘 몰겠네...
Is Trigger를 체크 하고
Raycast Trigger Interaction을 Collide로 변경하면
감지가 되야 하는거 아닌가?
레이어도 문제 없고
/// <summary>
/// Whether ray cast should include or ignore hits on trigger colliders that are snap volume colliders,
/// even if the ray cast is set to ignore triggers.
/// If you are not using gaze assistance or XR Interactable Snap Volume components, you should set this property
/// to <see cref="QuerySnapVolumeInteraction.Ignore"/> to avoid the performance cost.
/// </summary>
/// <remarks>
/// When set to <see cref="QuerySnapVolumeInteraction.Collide"/> when <see cref="raycastTriggerInteraction"/> is set to ignore trigger colliders
/// (when set to <see cref="QueryTriggerInteraction.Ignore"/> or when set to <see cref="QueryTriggerInteraction.UseGlobal"/>
/// while <see cref="Physics.queriesHitTriggers"/> is <see langword="false"/>),
/// the ray cast query will be modified to include trigger colliders, but then this behavior will ignore any trigger collider
/// hits that are not snap volumes.
/// <br />
/// When set to <see cref="QuerySnapVolumeInteraction.Ignore"/> when <see cref="raycastTriggerInteraction"/> is set to hit trigger colliders
/// (when set to <see cref="QueryTriggerInteraction.Collide"/> or when set to <see cref="QueryTriggerInteraction.UseGlobal"/>
/// while <see cref="Physics.queriesHitTriggers"/> is <see langword="true"/>),
/// this behavior will ignore any trigger collider hits that are snap volumes.
/// </remarks>
/// <seealso cref="raycastTriggerInteraction"/>
/// <seealso cref="XRInteractableSnapVolume.snapCollider"/>
/// <요약> /// 레이 캐스트가 스냅 볼륨 충돌체인 트리거 충돌체에 대한 적중을 포함할지 아니면 무시할지 여부, /// 레이캐스트가 트리거를 무시하도록 설정된 경우에도 마찬가지입니다. /// 응시 지원 또는 XR Interactable Snap Volume 구성 요소를 사용하지 않는 경우 이 속성을 설정해야 합니다. /// 성능 비용을 방지하려면 <see cref="QuerySnapVolumeInteraction.Ignore"/>로 설정하세요. /// </summary> /// <비고> /// <see cref="raycastTriggerInteraction"/>이 트리거 충돌자를 무시하도록 설정된 경우 <see cref="QuerySnapVolumeInteraction.Collide"/>로 설정된 경우 /// (<see cref="QueryTriggerInteraction.Ignore"/>로 설정된 경우 또는 <see cref="QueryTriggerInteraction.UseGlobal"/>로 설정된 경우) /// <see cref="Physics.queriesHitTriggers"/>가 <see langword="false"/>인 동안), /// 레이캐스트 쿼리는 트리거 충돌체를 포함하도록 수정되지만 이 동작은 모든 트리거 충돌체를 무시합니다. /// 스냅 볼륨이 아닌 적중입니다. /// <br /> /// <see cref="raycastTriggerInteraction"/>이 트리거 충돌을 적중하도록 설정된 경우 <see cref="QuerySnapVolumeInteraction.Ignore"/>로 설정된 경우 /// ( <see cref="QueryTriggerInteraction.Collide"/>로 설정된 경우 또는 <see cref="QueryTriggerInteraction.UseGlobal"/>로 설정된 경우 /// <see cref="Physics.queriesHitTriggers"/>가 <see langword="true"/>인 동안), /// 이 동작은 스냅 볼륨인 트리거 충돌체 적중을 무시합니다. /// </비고> /// <see also cref="raycastTriggerInteraction"/> /// <see also cref="XRInteractableSnapVolume.snapCollider"/>
When set to QuerySnapVolumeInteraction.Collide when raycastTriggerInteraction is set to ignore trigger colliders (when set to QueryTriggerInteraction.Ignore or when set to QueryTriggerInteraction.UseGlobal while Physics.queriesHitTriggers is false. the ray cast query will be modified to include trigger colliders, but then this behavior will ignore any trigger collider hits that are not snap volumes.
raycastTriggerInteraction이 트리거 충돌체를 무시하도록 설정된 경우 QuerySnapVolumeInteraction.Collide로 설정된 경우 (QueryTriggerInteraction.Ignore로 설정된 경우 또는 QueryTriggerInteraction.UseGlobal로 설정된 경우 Physics.queriesHitTriggers는 false입니다. 레이 캐스트 쿼리는 트리거 충돌체를 포함하도록 수정되지만 이 동작은 트리거 충돌체를 무시합니다. 스냅 볼륨이 아닌 적중.
Whether ray cast should include or ignore hits on trigger colliders that are snap volume colliders, even if the ray cast is set to ignore triggers. If you are not using gaze assistance or XR Interactable Snap Volume components, you should set this property QuerySnapVolumeInteraction.Ignore to avoid the performance cost.
레이 캐스트가 트리거를 무시하도록 설정된 경우에도 레이 캐스트가 스냅 볼륨 충돌자인 트리거 충돌기의 적중을 포함해야 하는지 아니면 무시해야 하는지 여부입니다. 시선 지원 또는 XR Interactable Snap Volume 구성 요소를 사용하지 않는 경우 성능 비용을 방지하려면 이 속성 QuerySnapVolumeInteraction.Ignore를 설정해야 합니다.
When set to QuerySnapVolumeInteraction.Ignore when raycastTriggerInteraction is set to hit trigger colliders (when set to QueryTriggerInteraction.Collide or when set to QueryTriggerInteraction.UseGlobal while Physics.queriesHitTriggers is true this behavior will ignore any trigger collider hits that are snap volumes.
QuerySnapVolumeInteraction.Ignore로 설정된 경우 raycastTriggerInteraction이 트리거 충돌체를 적중하도록 설정된 경우 (QueryTriggerInteraction.Collide로 설정되거나 Physics.queriesHitTriggers가 true인 동안 QueryTriggerInteraction.UseGlobal로 설정된 경우 이 동작은 스냅 볼륨인 모든 트리거 충돌체 적중을 무시합니다.
아무래도 볼륨이랑 연관이 있는듯 하다 ...
Raycast Snap Volume Interaction
레이 캐스트가 트리거를 무시하도록 설정된 경우에도 레이 캐스트가 스냅 볼륨 충돌자인 트리거 충돌기의 적중을 포함해야 하는지 아니면 무시해야 하는지 여부입니다. 응시 지원 또는 XR Interactable Snap Volume 구성 요소를 사용하지 않는 경우 성능 비용을 방지하려면 이 속성을 무시로 설정해야 합니다.
XR Ray Interactor가 연관된 상호작용 가능 항목에 시각적으로 스냅될 수 있도록 하는 구성 요소입니다. 이 구성 요소에는 트리거인 충돌체가 필요합니다.이 동작은 자동으로 XR Interaction Manager에 등록되어 Snap Collider와 Interactable Object 간의 연결을 생성합니다. 이 구성 요소는 일반적으로 상호 작용 가능 항목의 하위 GameObject에 추가되어야 합니다.
Snap Collider 충돌/충돌 시 상호작용 가능 항목과 연결할 트리거 충돌체입니다. 광선은 여기에서 Snap To Collider로 스냅됩니다.
Disable Snap Collider When Selected 상호 작용 가능 항목을 선택하거나 선택 취소할 때 Snap Collider를 자동으로 비활성화하거나 활성화합니다.
트리거모드에서도 된다 이거고
Hit Detection Type
레이 캐스트에 사용할 적중 감지 유형입니다.
Raycast
적중 감지 유형을 Raycast로 설정하여 Physics Raycast를 사용하여 충돌을 감지합니다.
Sphere Cast
Physics Sphere Cast를 사용하여 충돌을 감지하려면 적중 감지 유형을 Sphere Cast로 설정하세요.
자세히 보면 스피어가 보임
Hit Closest Only
Unity가 가장 가까운 Interactable만 상호 작용의 유효한 대상으로 간주하는지 여부입니다. 가장 가까운 Interactable만 호버 이벤트를 수신하도록 하려면 이 옵션을 활성화합니다. 그렇지 않으면 모든 적중 Interactable이 유효한 것으로 간주되고 이 Interactor는 다중 호버링됩니다.
Blend Visual Line Points
Unity가 광선 캐스팅에 사용하는 라인 샘플 포인트를 컨트롤러의 현재 포즈와 혼합합니다. 이를 사용하면 라인이 뒤처지지 않고 컨트롤러와 시각적으로 연결된 상태를 유지할 수 있습니다. 컨트롤러가 입력 대기 시간을 줄이기 위해 렌더링하기 전에 직접 추적 입력을 샘플링하도록 구성되면 컨트롤러는 레이 캐스팅에 사용되는 샘플 곡선의 시작점을 기준으로 새로운 위치 또는 회전에 있을 수 있습니다. 값이 false이면 선이 광선 투사 선의 끝을 향해 구부러지거나 휘어지는 대신 고정 참조 프레임에 시각적으로 유지됩니다.
Unity가 광선 캐스팅에 사용하는 라인 샘플 포인트를 컨트롤러의 현재 포즈와 혼합합니다.
이를 사용하면 라인이 뒤처지지 않고 컨트롤러와 시각적으로 연결된 상태를 유지할 수 있습니다.
컨트롤러가 입력 대기 시간을 줄이기 위해 렌더링하기 전에 직접 추적 입력을 샘플링하도록 구성되면 컨트롤러는 레이 캐스팅에 사용되는 샘플 곡선의 시작점을 기준으로 새로운 위치 또는 회전에 있을 수 있습니다.
값이 false이면 선이 광선 투사 선의 끝을 향해 구부러지거나 휘어지는 대신 고정 참조 프레임에 시각적으로 유지됩니다.
Select Action Trigger
Unity가 컨트롤러의 입력 동작 선택을 해석하는 방법을 선택합니다. 버튼이 현재 눌려져 있는지 또는 활성 상태를 토글하는지 여부와 같이 이 인터랙터가 선택할 수 있는지 여부를 결정하기 위해 다양한 입력 스타일을 제어합니다. 이것이 상태로 설정되고 여러 상호작용자가 InteractableSelectMode.Single로 설정된 상호작용 가능 항목을 선택하면 상호작용 가능 항목 선택이 매 프레임마다 상호작용자 간에 앞뒤로 전달되는 바람직하지 않은 동작이 발생할 수 있습니다. 이로 인해 선택 상호 작용 이벤트가 각 프레임을 실행하게 될 수도 있습니다. 이는 기본 권장 옵션인 State Change로 설정하여 해결할 수 있습니다.
State
Unity는 버튼을 누르고 있는 동안 입력이 활성화된 것으로 간주합니다. 사용자는 상호 작용이 가능해지기 전에 버튼을 누르고 있을 수 있으며 가능할 때 상호 작용을 계속 트리거할 수 있습니다.
State Change
Unity는 버튼을 누른 프레임에서만 입력이 활성화된 것으로 간주하고 성공하면 입력이 해제될 때까지 계속 연결된 상태를 유지합니다. 사용자는 상호작용이 가능한 동안 버튼을 눌러야 상호작용이 실행됩니다. 상호작용이 가능해지기 전에 버튼을 누르기 시작하면 상호작용이 실행되지 않습니다.
Toggle
상호 작용은 입력을 누른 프레임에서 시작되고 입력을 두 번째로 누를 때까지 계속 사용됩니다.
Sticky
상호 작용은 입력이 눌려진 프레임에서 시작되고 두 번째 입력이 해제될 때까지 계속 사용됩니다.
Unity가 컨트롤러의 입력 동작 선택을 해석하는 방법을 선택합니다.
버튼이 현재 눌려져 있는지 또는 활성 상태를 토글하는지 여부와 같이 이 인터랙터가 선택할 수 있는지 여부를 결정하기 위해 다양한 입력 스타일을 제어합니다.
이것이 State로 설정되고 여러 Interactor가 InteractableSelectMode.Single로 설정된 상호작용 가능 항목을 선택하면 상호작용 가능 항목 선택이 매 프레임마다 상호작용자 간에 앞뒤로 전달되는 바람직하지 않은 동작이 발생할 수 있습니다.
이로 인해 선택 상호 작용 이벤트가 각 프레임을 실행하게 될 수도 있습니다.
이는 기본 권장 옵션인 State Change로 설정하여 해결할 수 있습니다.
이걸 테스트 해보기는 쉽지 않겠는걸...
이건 이전시간에 테스트 해봤음 텔레포트 할때 잡고 있을거냐 놓을거냐 이거임
Keep Selected Target Valid
더 이상 유효한 대상이 아닌 경우에도 상호작용 가능 항목을 처음 선택한 후 계속 선택할지 여부입니다. Interactable이 유효한 대상 목록에 포함되지 않은 경우에도 XRInteractionManager가 선택을 유지하도록 하려면 활성화합니다. 유효한 대상 목록에 없는 경우 상호 작용 관리자가 선택 항목을 지우도록 하려면 비활성화합니다. 이를 비활성화하는 일반적인 용도는 순간 이동에 사용되는 Ray Interactor가 현재 가리키고 있지 않을 때 순간 이동 Interactable을 더 이상 선택하지 않도록 만드는 것입니다.
Hide Controller On Select
이 인터랙터가 선택 시 컨트롤러 모델을 숨겨야 하는지 여부를 제어합니다.
뭐지요?
안숨겨 지는디요?
이것 때문인가요?
네 맞습니다요
Allow Hovered Activate
현재 선택 항목이 없을 때 이 인터랙터를 마우스로 가리키지만 선택되지 않은 상호 작용 가능 항목에 활성화 및 비활성화 이벤트를 보낼지 여부를 제어합니다.
기본적으로 인터랙터는 선택한 인터랙티브 개체에만 활성화 및 비활성화 이벤트를 보냅니다.
인터랙터는 선택한 인터랙티브 개체에만 활성화 및 비활성화 이벤트를 보냅니다.
By default, the interactor will only send activate and deactivate events to interactables that it's selected.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class Maki : XRGrabInteractable
{
protected override void OnActivated(ActivateEventArgs args)
{
Debug.LogFormat("OnActivated: {0}", args.interactorObject);
base.OnActivated(args);
}
protected override void OnDeactivated(DeactivateEventArgs args)
{
Debug.LogFormat("OnDeactivated: {0}", args.interactorObject);
base.OnDeactivated(args);
}
}
XRBaseControllerInteractor는 Interactor가 이 Interactable에서 활성화 이벤트를 시작할 때 이 메서드를 호출합니다.
활성화/비활성화 이벤트?
/// <summary>
/// <see cref="XRBaseControllerInteractor"/> calls this method when the
/// Interactor begins an activation event on this Interactable.
/// </summary>
/// <param name="args">Event data containing the Interactor that is sending the activate event.</param>
/// <remarks>
/// <paramref name="args"/> is only valid during this method call, do not hold a reference to it.
/// </remarks>
/// <seealso cref="OnDeactivated"/>
protected virtual void OnActivated(ActivateEventArgs args)
{
m_Activated?.Invoke(args);
#pragma warning disable 618 // Calling deprecated method to help with backwards compatibility with existing user code.
OnActivate(args.interactor);
#pragma warning restore 618
}
/// <summary>
/// <see cref="XRBaseControllerInteractor"/> calls this method when the
/// Interactor ends an activation event on this Interactable.
/// </summary>
/// <param name="args">Event data containing the Interactor that is sending the deactivate event.</param>
/// <remarks>
/// <paramref name="args"/> is only valid during this method call, do not hold a reference to it.
/// </remarks>
/// <seealso cref="OnActivated"/>
protected virtual void OnDeactivated(DeactivateEventArgs args)
{
m_Deactivated?.Invoke(args);
#pragma warning disable 618 // Calling deprecated method to help with backwards compatibility with existing user code.
OnDeactivate(args.interactor);
#pragma warning restore 618
}
XRBaseControllerInteractor calls this method when the Interactor begins an activation event on this Interactable.
The event that is called when the selecting Interactor activates this Interactable. Not to be confused with activating or deactivating aGameObjectwithGameObject.SetActive. This is a generic event when an Interactor wants to activate an Interactable, such as from a trigger pull on a controller. TheActivateEventArgspassed to each listener is only valid while the event is invoked, do not hold a reference to it.
Deactivated
The event that is called when an Interactor deactivates this Interactable. Not to be confused with activating or deactivating a GameObject with GameObject.SetActive. This is a generic event when an Interactor wants to deactivate an Interactable, such as from a trigger release on a controller. The DeactivateEventArgs passed to each listener is only valid while the event is invoked, do not hold a reference to it.
Activated
The event that is called when the selecting Interactor activates this Interactable. Not to be confused with activating or deactivating a GameObject with GameObject.SetActive. This is a generic event when an Interactor wants to activate an Interactable, such as from a trigger pull on a controller. The ActivateEventArgs passed to each listener is only valid while the event is invoked, do not hold a reference to it.
Interactable을 활성화할 때?
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class Maki : XRGrabInteractable
{
protected override void OnSelectEntering(SelectEnterEventArgs args)
{
Debug.LogFormat("<color=yellow>OnSelectEntering</color>");
base.OnSelectEntering(args);
}
protected override void OnSelectEntered(SelectEnterEventArgs args)
{
Debug.LogFormat("<color=yellow>OnSelectEntered</color>");
base.OnSelectEntered(args);
}
protected override void OnActivated(ActivateEventArgs args)
{
Debug.LogFormat("<color=lime>OnActivated</color>");
base.OnActivated(args);
}
protected override void OnHoverEntering(HoverEnterEventArgs args)
{
Debug.LogFormat("<color=yellow>OnHoverEntering</color>");
base.OnHoverEntering(args);
}
protected override void OnHoverEntered(HoverEnterEventArgs args)
{
Debug.LogFormat("<color=yellow>OnHoverEntered</color>");
base.OnHoverEntered(args);
}
}
왜 OnActivated만 호출이 안되는거지?
Send activate and deactivate events to interactables that this interactor is hovered over 이 인터랙터 위에 마우스를 올려놓은 상호작용 가능 항목에 활성화 및 비활성화 이벤트를 보냅니다.
but not selected when there is no current selection 하지만 현재 선택 항목이 없으면 선택되지 않습니다.
아..
핸드 트리거로 잡고 인덱스 트리거 땡기면 호출 되는구만..
For the Activate events on the Interactable (Activated/Deactivated), the object must be selected first. So if you pick up an object by selecting it with the grip, and then pull the trigger to activate, it will fire the Activated event. This is for something like a flashlight that you can pick up and then use the trigger to toggle the light.
grip : 핸드 트리거 trigger : 인덱스 트리거
용어 헷갈리네 Oculus SDK 하다 와서 ㅋㅋ
일단 속성은 여기 까지 파고 실습 예제 만들기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class Flashlight : MonoBehaviour
{
public Material material; // 렌더러의 메터리얼
public GameObject light;
private bool isOn = false;
public void Activated(ActivateEventArgs args)
{
Debug.LogFormat("Activated: {0}", args.interactorObject);
this.isOn = !isOn;
if (isOn)
{
this.ActivateEmission();
this.light.SetActive(true);
}
else
{
DeactivateEmission();
this.light.SetActive(false);
}
}
public void Deactivated(DeactivateEventArgs args)
{
// Debug.LogFormat("Deactivated: {0}", args.interactorObject);
// DeactivateEmission();
// this.light.SetActive(false);
}
// Emission을 활성화하는 함수
public void ActivateEmission()
{
if (material != null)
{
material.EnableKeyword("_EMISSION");
material.SetColor("_EmissionColor", Color.yellow); // 원하는 색상으로 설정 가능
}
}
// Emission을 비활성화하는 함수
public void DeactivateEmission()
{
if (material != null)
{
material.DisableKeyword("_EMISSION");
}
}
}