Unity 套圈捕捉 UI 實現分享:橢圓環 Shader + 動態進度

Unity 套圈捕捉 UI 實現分享

期望表現效果

《拼貼冒險傳 / PatchQuest》 捕捉進度 動態UI

實現效果

實現效果

  • 目標:角色 A 套圈怪物 B,進度環顯示圍繞角度。
  • 技術點:Shader 繪制橢圓環,支持描邊、順/逆時針,需要對兩個切口也進行描邊。

技術需求 & 準備

  • Unity
  • RawImage + 自定義 Shader
  • Canvas 設置為 World Space,UI 跟隨敵人
  • C# 腳本控制進度和方向

UI預制體的層級結構
Ui預制體結構


捕捉邏輯

  1. 玩家位置與敵人位置計算方向向量。
  2. 計算 DeltaAngle,累積角度。
  3. 正負值表示順/逆時針。
  4. LassoUI GameObject 始終對齊敵人位置。

`

PlayerController.cs捕捉邏輯實現

核心變量定義


// 角度計算相關變量
float totalAngle;           // 累計角度
Vector2 lastDir;           // 上一幀的玩家->獵物方向
Vector2 startDir;          // 初始方向 玩家->獵物方向
Role prey;                 // 獵物對象

進入捕捉狀態初始化

private void Catching_Enter()
{// UI跟隨獵物位置lassoUI.transform.position = prey.transform.position;lassoUI.SetRequiredAngle(prey.NeedAngle);// 初始化方向向量startDir = (transform.position - prey.transform.position).normalized;lassoUI.InitStartDir(startDir); lastDir = startDir;totalAngle = 0f;// 綁定滿圈事件lassoUI.OnFullRotation += HandleLassoFullRotation;lassoUI.Show();
}

核心角度計算邏輯

private void Catching_Update()
{// 讓LassoUI跟隨獵物位置if (lassoUI != null && prey != null){lassoUI.transform.position = prey.transform.position;}// 計算當前方向向量Vector2 currentDir = (transform.position - prey.transform.position).normalized;// 計算角度變化(相對上一次)float delta = Mathf.DeltaAngle(Mathf.Atan2(lastDir.y, lastDir.x) * Mathf.Rad2Deg,Mathf.Atan2(currentDir.y, currentDir.x) * Mathf.Rad2Deg);totalAngle += delta; // 累計總角度(正負都可以)lastDir = currentDir;lassoUI.UpdateProgress(totalAngle);// 檢查是否滿圈if (Mathf.Abs(totalAngle) >= prey.NeedAngle){HandleLassoFullRotation();lassoUI.ResetProgress();}
}

抓捕成功處理

void HandleLassoFullRotation()
{// 滿圈了,執行抓捕成功邏輯Debug.Log("執行抓捕成功");// 調用UI彈出動畫UIManager.instance.GetPanel<BattleMainPanel>().ShowImagePopUp();// 銷毀獵物if (prey != null){prey.Dead();}// 退出抓捕狀態,回到射擊模式fsm.ChangeState(PlayerControllerStates.Shooting);
}

關鍵技術點說明

1. 角度計算原理

  • 使用 Mathf.Atan2() 將方向向量轉換為角度
  • 使用 Mathf.DeltaAngle() 計算相對角度變化,自動處理角度跨越問題
  • 支持順時針和逆時針旋轉,正負值自動處理

2. UI跟隨機制

  • 每幀更新 lassoUI.transform.position = prey.transform.position
  • 確保UI始終跟隨獵物位置

3. 狀態管理

  • 使用狀態機管理不同游戲狀態(射擊、狩獵、捕捉)
  • 進入捕捉狀態時初始化角度計算
  • 退出時清理事件綁定

4. 事件驅動

  • 通過 OnFullRotation 事件觸發抓捕成功邏輯
  • 實現UI和游戲邏輯的解耦

UI 進度計算

LassoUI.cs

ringMaterial 為shader材質的引用

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;namespace Gameplay.Battle
{public class LassoUI : MonoBehaviour{// [SerializeField] private Image progressCircle; // 圓環Imageprivate CanvasGroup canvasGroup; // 控制顯示隱藏的透明度private float accumulatedAngle = 0f; // 累計角度private float requiredAngle = 360f;  // 默認1圈public Material ringMaterial;public Vector2  startDir = Vector2.up; // 初始方向 玩家->獵物方向public event Action OnFullRotation; // 觸發滿圈事件// Start is called before the first frame updatevoid Start(){canvasGroup = GetComponent<CanvasGroup>();Hide();}public void Show(){canvasGroup.alpha = 1;canvasGroup.blocksRaycasts = true;canvasGroup.interactable = true;}public void Hide(){canvasGroup.alpha = 0;canvasGroup.blocksRaycasts = false;canvasGroup.interactable = false;}public void InitStartDir(Vector2 dir){startDir = dir;float startAngle = Mathf.Atan2(startDir.y, startDir.x) * Mathf.Rad2Deg;// 只設置起始角度,不設置進度ringMaterial.SetFloat("_StartAngle", startAngle);ringMaterial.SetFloat("_Progress", 0f); // 進度從0開始Debug.Log($"LassoUI: 初始化角度 = {startAngle}°");}public void SetRequiredAngle(float angle){requiredAngle = angle;Debug.Log($"LassoUI: 設置所需角度 = {requiredAngle}°");}public void ResetProgress(){accumulatedAngle = 0f;}public void UpdateProgress(float angle){var Progress = Mathf.Clamp(angle / requiredAngle,-1f,1f);ringMaterial.SetFloat("_Progress", Progress);}}
}

Shader 實現

參數調整
在這里插入圖片描述

Shader "Unlit/EllipseRingProgress"
{Properties{_MainColor ("Fill Color", Color) = (1,0.5,0,1)           // 內圈填充顏色_EdgeColor ("Edge Color", Color) = (0,0,0,1)             // 描邊顏色_Progress ("Progress", Range(-1,1)) = 0                  // 進度,負數順時針,正數逆時針_Thickness ("Ring Thickness", Range(0.01,2)) = 1        // 環寬_EdgeWidth ("Edge Width", Range(0.001,0.1)) = 0.02      // 內外描邊寬度_CapEdgeAngle ("Cap Edge Width (Degrees)", Range(0,5)) = 1.0 // 封口兩端描邊角度_EllipseA ("Ellipse Semi-major Axis", Float) = 1        // 橢圓長軸_EllipseB ("Ellipse Semi-minor Axis", Float) = 1        // 橢圓短軸_StartAngle ("Start Angle Offset (Degrees)", Range(-180,180)) = 0 // 起始角度}SubShader{Tags { "Queue"="Transparent" "RenderType"="Transparent" }LOD 100Pass{Blend SrcAlpha OneMinusSrcAlphaCull OffZWrite OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"fixed4 _MainColor;fixed4 _EdgeColor;float _Progress;float _Thickness;float _EdgeWidth;float _CapEdgeAngle;float _EllipseA;float _EllipseB;float _StartAngle;struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};// 頂點程序v2f vert(appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);// 將 UV 從 [0,1] 映射到 [-1,1],中心在 (0,0)o.uv = v.uv * 2 - 1;return o;}fixed4 frag(v2f i) : SV_Target{float2 pos = i.uv;// 1?? 計算橢圓歸一化距離float ellipseDist = (pos.x * pos.x) / (_EllipseA * _EllipseA) +(pos.y * pos.y) / (_EllipseB * _EllipseB);float halfThickness = _Thickness * 0.5;float innerBoundary = 1.0 - halfThickness;float outerBoundary = 1.0 + halfThickness;// 不在環內的點直接丟棄if (ellipseDist < innerBoundary || ellipseDist > outerBoundary)discard;// 2?? 計算極角 (0~360)float angleRad = atan2(pos.y / _EllipseB, pos.x / _EllipseA);float angleDeg = degrees(angleRad);if (angleDeg < 0) angleDeg += 360;float relativeAngle = fmod(angleDeg - _StartAngle + 360, 360);// 3?? 處理順/逆時針顯示float absProgress = abs(_Progress); // 進度長度bool clockwise = (_Progress < 0);   // 順時針方向float progressAngle = absProgress * 360;if (clockwise){// 順時針:從起點往回走if (relativeAngle < (360 - progressAngle) && relativeAngle > 0)discard;}else{// 逆時針:原邏輯if (relativeAngle > progressAngle)discard;}// 4?? 內外描邊bool radialEdge = abs(ellipseDist - (1.0 - halfThickness)) < _EdgeWidth ||abs(ellipseDist - (1.0 + halfThickness)) < _EdgeWidth;// 5?? 封口描邊計算float startCap = 0;float endCap = progressAngle;if (clockwise){startCap = 360 - progressAngle;endCap = 360;}bool capEdge = (relativeAngle < _CapEdgeAngle) ||(abs(relativeAngle - startCap) < _CapEdgeAngle) ||(abs(relativeAngle - endCap) < _CapEdgeAngle);// 6?? 返回顏色if (radialEdge || capEdge)return _EdgeColor; // 描邊return _MainColor;    // 填充}ENDCG}}
}

說明

  • _Progress

    • 負值 → 順時針
    • 正值 → 逆時針
  • _StartAngle

    • 控制環起點位置
  • _EdgeWidth

    • 調整環內外描邊粗細
  • _CapEdgeAngle

    • 調整封口角度寬度
  • _EllipseA/B

    • 控制橢圓比例,可實現圓形或拉長效果
  • _Thickness

    • 環寬

📌 總結

  1. 通過 Shader 對橢圓環的歸一化計算,實現動態進度顯示。
  2. 支持順/逆時針顯示。
  3. 封口描邊、內外描邊,增強視覺效果。
  4. C# 控制 _Progress_StartAngle,UI 可隨角色位置和方向實時更新。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920082.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920082.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920082.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MyBatis-Plus代碼生成器

MyBatis-Plus 代碼生成器是一款高效、靈活的自動化工具,旨在簡化 Java 后端開發中的持久層代碼編寫。通過配置數據庫連接和模板參數,它可以一鍵生成實體類、Mapper 接口、XML 文件、Service 層及 Controller 層代碼,大幅提升開發效率,減少重復勞動。 核心優勢: 快速生成:…

06-導入Maven項目模塊

文章目錄1、文章介紹2、模塊復制3、導入pom文件4、效果圖1、文章介紹 視頻定位 2、模塊復制 復制資料“02.maven項目”中的兩個項目模塊到剛剛新建的項目文件路徑中 導入后的效果圖 3、導入pom文件 4、效果圖

Jenkins+docker 微服務實現自動化部署安裝和部署過程

Jenkins 是一款流行的開源自動化服務器&#xff0c;廣泛用于持續集成&#xff08;CI&#xff09;和持續交付&#xff08;CD&#xff09;流程的自動化。通過 Docker 部署 Jenkins 可以簡化安裝和配置過程&#xff0c;同時保證在不同環境下的一致性。本篇文章將介紹如何使用 Dock…

【芯片后端設計的靈魂:Placement的作用與重要性】

在芯片設計的浩瀚宇宙中&#xff0c;后端物理設計扮演著決定成敗的關鍵角色。其中&#xff0c;?Placement&#xff08;布局&#xff09;?? 作為整個流程的核心環節&#xff0c;被譽為芯片性能、功耗和面積的“奠基者”。今天&#xff0c;我們就來深入探討Placement的作用、重…

將FGUI的Shader全部預熱后,WebGL平臺沒有加載成功

1&#xff09;將FGUI的Shader全部預熱后&#xff0c;WebGL平臺沒有加載成功 2&#xff09;iOS如何確認內存擴展使用生效 3&#xff09;SpriteAtlasManager.atlasRequested延后一幀回調 4&#xff09;Unity如何使用Java 17打包 這是第442篇UWA技術知識分享的推送&#xff0c;精選…

Python二進制、八進制與十六進制高級操作指南:從底層處理到工程實踐

引言&#xff1a;為何需要掌握進制操作&#xff1f;在現代計算領域&#xff0c;直接操作不同進制的數值是一項核心技術能力。根據2024年Stack Overflow開發者調查報告&#xff1a;73%的低級系統開發涉及位級操作65%的網絡協議要求理解十六進制數據80%的硬件接口配置使用二進制控…

離線可用的網絡急救方案

在使用電腦的過程中&#xff0c;經常會遇到斷網的狀況&#xff0c;這種情況讓人十分頭疼&#xff0c;很多時候我們都不知道去哪里找相關的教程來解決這樣的問題。它能一鍵操作解決電腦的網絡故障問題&#xff0c;最關鍵的是它是完全免費的。它只需解壓就可以直接雙擊使用。把工…

華為云Stack環境中計算資源,存儲資源,網絡資源發放前的準備工作(中篇)

實驗流程說明再上期文章鏈接如下&#xff1a; 華為云Stack環境中計算資源&#xff0c;存儲資源&#xff0c;網絡資源發放前的準備工作&#xff08;上篇&#xff09; 華為云Stack環境中計算資源&#xff0c;存儲資源&#xff0c;網絡資源發放前的準備工作&#xff08;中篇篇&am…

設置密鑰連接服務器

要將本地電腦的 SSH 公鑰添加到服務器登錄&#xff0c;可按以下步驟操作&#xff0c;確保服務器僅允許密鑰認證&#xff1a; 一、將本地公鑰添加到服務器 &#xff08;前提&#xff1a;你已通過密碼或現有方式能登錄服務器&#xff0c;且本地已生成 SSH 密鑰對&#xff09; 1. …

k8s筆記04-常用部署命令

Kubernetes&#xff08;K8s&#xff09;部署與版本管理命令筆記 一、部署核心命令分類與應用場景 K8s中用于應用部署、版本控制與實例擴縮容的核心命令主要包括三類&#xff0c;分別對應“版本回滾”“手動擴縮容”“自動擴縮容”場景&#xff0c;是CKA考試中部署類題目的核心考…

[系統架構設計師]知識產權(二十)

[系統架構設計師]知識產權&#xff08;二十&#xff09; 一.知識產權的特性 1.特性 無體性&#xff1a;抽象財富 專有性&#xff1a;權利人同意或法律規定外&#xff0c;權利人以外的任何人不得享有或使用該項權力 地域性&#xff1a;只能在該國范圍內手法律保護 時間性&#x…

rk3566編譯squashfs報錯解決

項目場景&#xff1a; 提示&#xff1a;這里簡述項目相關背景&#xff1a; 編譯開源的rk3566代碼squashfs報錯&#xff0c;tspi_linux_sdk_repo_20240131.tar.gz 下之前先讀我 1.tspi_linux_sdk_20230916.tar.gz這個是之前老的沒有git和repo的版本&#xff0c;后面會刪除掉大家…

HTTP 協議與TCP 的其他機制

TCP 的其他機制TCP頭部的標志位SYN&#xff1a;請求建立連接標志位ACK&#xff1a;響應報文標志位PSH&#xff1a;攜帶數據標志位&#xff0c;通知接收方該從緩沖區讀數據FIN&#xff1a;請求斷開連接標志位RST&#xff1a;復位標志位URG&#xff1a;緊急數據標志位安全可靠機制…

點評《JMeter核心技術、性能測試與性能分析》一書

《JMeter核心技術、性能測試與性能分析》深度評價?該書作為清華大學出版社2025年推出的性能測試領域新作&#xff0c;展現了鮮明的技術深度與實踐導向性&#xff0c;具體評價如下&#xff1a;?1. 內容體系&#xff1a;系統性與前沿性兼備??知識架構完整?&#xff1a;覆蓋J…

深入解析:為什么應該避免使用 atoi、atol 和 atof 函數

問題本質深度分析 簡化源碼展示&#xff1a;看清本質 atoi 的典型實現&#xff1a; // atoi 的簡化實現 - 看清問題所在 int atoi(const char *str) {int sign 1;int result 0;// 跳過空白字符while (isspace(*str)) {str;}// 處理符號if (*str -) {sign -1;str;} else if …

計算機網絡:HTTP、抓包、TCP和UDP報文及重要概念

一、http超文本傳輸協議&#xff08;應用層&#xff09;&#xff08;一&#xff09;萬維網1.工作過程&#xff08;二&#xff09;統一資源定位符&#xff08;URL&#xff09;http的默認端口號是80&#xff08;三&#xff09;HTTP報文結構請求報文&#xff1a;客戶端-->服務器…

three.js+WebGL踩坑經驗合集(8.3):合理設置camera.near和camera.far緩解實際場景中的z-fighting疊面問題

本篇延續上篇內容&#xff1a; three.jsWebGL踩坑經驗合集(8.2):z-fighting疊面問題和camera.near的坑爹關系-CSDN博客 筆者也是狠佩服自己&#xff1a;一個還沒劃上句號的文章都能拖了半年才繼續寫。這次也是運氣好&#xff0c;工作上再次遇到疊面問題&#xff0c;可以借這機…

記一次生產環境Hbase填坑之路、Hbase客戶端登陸、kerberos認證、端口列表、Pod上手撕代碼【Hbase最佳實踐】

背景 1、軟件系統&#xff08;轉儲系統&#xff09;需要向生產環境遷移&#xff1a;遷到國產操作系統、國產資源池&#xff08;Hbase存儲不變&#xff09; 2、老環境上的轉儲系統本身存在寫入hbase的性能問題、及部分省份寫入hbase失敗的問題&#xff08;20%失敗&#xff09;…

C++知識雜項搜集

C使用如下庫優化事件的注冊和發布&#xff0c;ZeroMQzmqpp 通信機制&#xff0c;請求-應帶方式&#xff0c;push-pull方式&#xff0c;publisher-subcriber發布-訂閱模式eventpp 事件注冊和回調sockpp tcp/udp封裝threadpool 線程池Jinja 一個 python 的模板實現配置是實現…

連鎖零售排班難?自動排班系統來解決

零售、連鎖企業門店多、員工雜、班次密&#xff0c;排班時總有繞不開的問題&#xff1a;跨門店調人成本怎么算&#xff1f;節假日高峰期人手怎么補&#xff1f;全職兼職混合排班怎么平衡&#xff1f;其實&#xff0c;這些場景化難題&#xff0c;蓋雅自動排班系統早就有了針對性…