1、unity 之UGUI 屏蔽鼠标(射线)穿透
方法一
/// <summary>
/// 检测是否点击在UI上
/// </summary>
/// <returns></returns>
private bool IsClickUI()
{
if (EventSystem.current != null)
{
PointerEventData eventData = new PointerEventData(EventSystem.current);
eventData.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventData, results);
return results.Count > 0;
}
return false;
}
方法二
private void OnMouseDown()
{
// 如果点击的是 UI 元素,不执行后续逻辑
if (IsPointerUI())
{
// 点击的是 UI 元素,不做处理
return;
}
//下面执行对应点击逻辑
}
private bool IsPointerUI()
{
// 创建 pointerEventData
PointerEventData pointerEventData = new PointerEventData(EventSystem.current)
{
position = Input.mousePosition // 获取鼠标位置
};
// 存储所有 UI 元素的射线结果
var results = new List<RaycastResult>();
// 检查与 UI 的交互
EventSystem.current.RaycastAll(pointerEventData, results);
// 如果有结果证明鼠标在 UI 上
return results.Count > 0;
}
2、射线交互多个物体 Physics.RaycastNonAlloc
private RaycastHit[] Raycasts = new RaycastHit[10];
private void RayObject()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
for (int i = 0; i < Raycasts.Length; i++)
{
Raycasts[i].distance = 999;
};
int count = Physics.RaycastNonAlloc(ray, Raycasts);
if (count > 0)
{
//for (int i = 0; i < count; i++)
//{
// var hit = Raycasts[i];
// OnRayClick(hit.transform);
//}
var hit = Raycasts[0];
OnRayClick(hit.transform);
}
}
3、射线点击的最近物体
比较射线击中物体的距离并返回最近的物体,可以在使用 Physics.RaycastNonAlloc
方法后,遍历返回的 RaycastHit
数组,获取最小距离的物体。
using UnityEngine;
public class RaycastNearestObject : MonoBehaviour
{
private RaycastHit[] Raycasts = new RaycastHit[10]; // 可以调整数组大小以支持更多物体
void Update()
{
if (Input.GetMouseButtonDown(0)) // 监听鼠标左键点击
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// 重置所有射线击中的距离到一个非常大的值
for (int i = 0; i < Raycasts.Length; i++)
{
Raycasts[i].distance = float.MaxValue; // 或用999
}
// 使用射线投射获取击中的物体
int count = Physics.RaycastNonAlloc(ray, Raycasts);
// 找到最近的物体
RaycastHit nearestHit = new RaycastHit();
float closestDistance = float.MaxValue;
for (int i = 0; i < count; i++)
{
if (Raycasts[i].distance < closestDistance)
{
closestDistance = Raycasts[i].distance;
nearestHit = Raycasts[i]; // 更新最近的击中信息
}
}
if (closestDistance < float.MaxValue) // 如果找到有效的击中
{
Debug.Log("最近的物体: " + nearestHit.collider.gameObject.name);
// 你可以在这里对最近的物体执行其它逻辑
}
else
{
Debug.Log("没有击中任何物体");
}
}
}
}
方法二
private static int CompareDistance(RaycastHit r1, RaycastHit r2)
{
// 获取 r1 和 r2 的 Collider 组件
var r1Collider = r1.transform != null ? r1.transform.GetComponent<Collider>() : null;
var r2Collider = r2.transform != null ? r2.transform.GetComponent<Collider>() : null;
// 检查 r1 是否有 Collider,而 r2 没有
if (r1Collider != null && r2Collider == null)
{
return -1; // r1 优先
}
// 检查 r2 是否有 Collider,而 r1 没有
else if (r1Collider == null && r2Collider != null)
{
return 1; // r2 优先
}
// 如果两个都有 Collider,或者都没有,比较距离
return r1.distance.CompareTo(r2.distance);
}
4、跳转场景
private bool isLoading = false;
IEnumerator GotoScene(string sceneName)
{
if (isLoading) yield break;
isLoading = true;
yield return null;
var asyncOp = SceneManager.LoadSceneAsync(sceneName);
asyncOp.allowSceneActivation = false;
while (!asyncOp.isDone)
{
if (!asyncOp.allowSceneActivation && asyncOp.progress >= 0.9f)
{
asyncOp.allowSceneActivation = true;
break;
}
yield return null;
}
isLoading = false;
}
5、RectTransformUtility.RectangleContainsScreenPoint的应用
RectTransform.RectangleContainsScreenPoint 方法顾名思义,判断矩阵范围是否包含屏幕点,参数:位置矩阵、屏幕点、使用的Camera 三个。
if(RectTransformUtility.RectangleContainsScreenPoint(transform.GetComponent<RectTransform>(),Input.mousePosition, Cameras.UI))
{
Debug.Log("在遮挡范围内");
}
两个参数:
RectTransformUtility.RectangleContainsScreenPoint(rect,position,camera)
判断第二个参数代表的位置是否在第一个参数rect的范围内,
注意:两个参数的只能在Canvas为Screen Space -Overlay时的情况用,三个参数的重载是正确用法。(Canvas为Screen Space -Camera)
IEnumerator Check()
{
yield return null;
//判断第二个参数代表的位置是否在第一个参数rect的范围内,
var inRect = RectTransformUtility.RectangleContainsScreenPoint(DetailRect, Input.mousePosition);
if (!inRect)
CloseDetail();
}
6、模拟打气球效果
使用SkinnedMeshRenderer.SetBlendShapeWeight设置此渲染器的 BlendShape 的权重。
int[] val = new int[] { 0, 50, 70, 80, 90, 95, 100 };
int currentNum = 0;
int Progress = val[currentNum];
int EndValue = val[currentNum + 1];
if (DOTween.IsTweening(Progress))
{
DOTween.Kill(Progress);
}
Tweener tweener = DOTween.To(() => Progress, x => Progress = x, EndValue, 0.5f).OnUpdate(() =>
{
balloonCQ.GetComponent<SkinnedMeshRenderer>().SetBlendShapeWeight(0, Progress);
});
currentNum++;
注意:需要动画设计师制作带有blendShapes控制的气球模型
7、代码控制相机打开后处理
if(Camera.main != null)
{
var cameraData = Camera.main.GetUniversalAdditionalCameraData();
if (cameraData != null)
cameraData.renderPostProcessing = true; // 启用后处理
}
8、Scroll View 自动滚动效果
public void OpenEndPanel(string content, AudioClip clip = null, Action callback = null)
{
EndPanel.GetChild(0).DOKill();
EndPanel.gameObject.SetActive(true);
AudioSource source = EndPanel.GetComponent<AudioSource>();
//先重置 ScrollRect 的位置
EndPanelScroll.verticalNormalizedPosition = 1f; // 默认为顶部
// 初始位置
EndPanelContent.DOAnchorPos3DY(0, 0).SetDelay(0.1f);// 延迟动画
// 设置内容文本
EndPanelTxt.text = "\u3000\u3000" + content + "。";
// 确保在位置重置后再创建动画
EndPanel.GetChild(0).DOScale(Vector3.one, 0.5f).OnComplete(() =>
{
if (clip)
{
source.Stop();
source.clip = clip;
source.Play();
}
callback?.Invoke();
//确保 Scroll 视图在动画调用前完成处理
AutoScrollView(EndPanelScroll, clip);
});
Invoke("CalEndPanel", 0.1f);
}
/// <summary>
/// 根据音频长度自动滚动视图
/// </summary>
/// <param name="clip"></param>
private void AutoScrollView(ScrollRect scrollRect, AudioClip clip)
{
var duration = clip != null ? clip.length : 3f;
CommonFunc.Delay(0.1f, delegate
{
scrollRect.DOKill();
scrollRect.verticalNormalizedPosition = 1f;
scrollRect.DONormalizedPos(Vector2.zero, duration).SetEase(Ease.Linear);
}, this);
}
private void CalEndPanel()
{
EndPanelContent.sizeDelta = new Vector2(550, EndPanelTxt.GetComponent<RectTransform>().sizeDelta.y);
EndPanelScroll.vertical = EndPanelContent.sizeDelta.y > 230;
}
public void CloseEndPanel()
{
EndPanel.GetChild(0).DOKill();
EndPanel.GetChild(0).DOScale(Vector3.zero, 0.5f).OnComplete(() =>
{
EndPanel.gameObject.SetActive(false);
EndPanelTxt.text = "";
});
// 重置 ScrollRect 的位置到顶部
EndPanelScroll.DOKill();
EndPanelScroll.verticalNormalizedPosition = 1f; // 滚动条置顶
EndPanelContent.anchoredPosition = new Vector2(0, 0); // 也可以直接设置锚点位置
AudioSource source = EndPanel.GetComponent<AudioSource>();
source.Stop();
source.clip = null;
}
9、unity中设置鼠标状态
/// <summary>
/// 设置鼠标状态
/// </summary>
/// <param name="_cursorState">状态:true显示,false隐藏</param>
public void SetCursor(bool _cursorState)
{
//隐藏鼠标
Cursor.visible = _cursorState;
Cursor.lockState = _cursorState ? CursorLockMode.Confined : CursorLockMode.Locked;
}
10、unity编辑器模式下运行(按下Play键)后的启动指定场景, 启动场景需要添加到BuildSetting中
/// <summary>
/// c
/// </summary>
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize()
{
if (SceneManager.GetActiveScene().path.StartsWith("Assets/Scenes/"))
{
SceneManager.LoadScene("Main");
}
}