【Unity造輪子】制作一個簡單的2d抓勾效果(類似蜘蛛俠的技能)

文章目錄

  • 前言
  • 開始
    • 1. 實現簡單的抓勾效果
    • 2. 高階鉤爪效果
  • 源碼
  • 參考
  • 完結

前言

歡迎閱讀本文,本文將向您介紹如何使用Unity游戲引擎來實現一個簡單而有趣的2D抓勾效果,類似于蜘蛛俠的獨特能力。抓勾效果是許多動作游戲和平臺游戲中的常見元素,給玩家帶來了無限的想象和挑戰。
請添加圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

不需要擔心,即使您是一位新手,也可以輕松跟隨本文學習。我們將從頭開始,從創建一個新的Unity項目開始,一直到最終的完成效果。

借助本文提供的步驟和技巧,您將能夠為您的游戲增添一個的特色。希望您能享受這個過程,并從中獲得靈感,探索更多關于游戲開發的樂趣。

照例,我們先來看看本文實現的最終效果,以決定你是否繼續往下看
在這里插入圖片描述
在這里插入圖片描述

源碼我放在文章末尾了

開始

1. 實現簡單的抓勾效果

新建一個2d項目,添加一個2對象作為我們的角色物體,并掛載rigidbody 2d、碰撞器、Distance Joint 2d、Line Renderer(記得配置好材質和線寬)
在這里插入圖片描述
書寫腳本代碼,代碼已經加了詳細的解釋了,這里就不得過多介紹了

using System.Collections;
using System.Collections.Generic;
using UnityEngine;// Grappler類,用于處理角色的抓取動作
public class Grappler : MonoBehaviour
{// 主攝像機public Camera mainCamera;// 線渲染器,用于渲染抓取線public LineRenderer _lineRenderer;// 距離關節,用于處理抓取物體的物理效果public DistanceJoint2D _distanceJoint;void Start(){// 初始化時,禁用距離關節_distanceJoint.enabled = false;}void Update(){// 檢測鼠標左鍵是否按下if (Input.GetKeyDown(KeyCode.Mouse0)){// 獲取鼠標在世界坐標中的位置Vector2 mousePos = (Vector2)mainCamera.ScreenToWorldPoint(Input.mousePosition);// 設置線渲染器的起始和結束位置_lineRenderer.SetPosition(0, mousePos);_lineRenderer.SetPosition(1, transform.position);// 設置距離關節的連接點_distanceJoint.connectedAnchor = mousePos;// 啟用距離關節和線渲染器_distanceJoint.enabled = true;_lineRenderer.enabled = true;}// 檢測鼠標左鍵是否松開else if (Input.GetKeyUp(KeyCode.Mouse0)){// 禁用距離關節和線渲染器_distanceJoint.enabled = false;_lineRenderer.enabled = false;}// 如果距離關節啟用if (_distanceJoint.enabled){// 更新線渲染器的結束位置_lineRenderer.SetPosition(1, transform.position);}}
}

掛載腳本和綁定對象
在這里插入圖片描述
簡單配置一下環境
在這里插入圖片描述
運行效果
在這里插入圖片描述

可以看到,如果連線時碰撞會出現問題,如果你先實現好的碰撞效果,可以勾選Distance Joint 2d的Enable Collision及開啟碰撞
在這里插入圖片描述
效果
在這里插入圖片描述

2. 高階鉤爪效果

在場景中創建GameObject如下(由父對象到子對象一一進行講解):
在這里插入圖片描述

Player:(示例中是一個 圓形的sprite)對其添加RigidBody2D、Circle Collider2D 、Spring Joint2D組件(跟前面一樣Spring Joint2D組件開啟Enable Collision碰撞)

Gunpivot:鉤鎖槍的錨點,其為空對象,位置設置在Player的中心即0.0位置(后續用于實現鉤鎖槍隨著鼠標旋轉的效果)

GrapplingGun:(示例中為一個長方形的sprite)鉤鎖槍,用于后期實現發射鉤鎖,僅添加Box Collider2D即可

FirePoint:鉤鎖的發射點,空對象,即鉤鎖發射的起始位置,設置在鉤鎖槍的邊緣即可

Rope:在空對象上添加LineRenderer并適當改變寬度和材質即可。

下有兩個腳本分別添加給GrapplingGun和Rope即可

腳本1:Perfecter_Grapple添加給GrapplingGun

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Perfecter_Grapple : MonoBehaviour
{[Header("腳本引用:")]public Grappling_Rope grappleRope;[Header("層設置:")][SerializeField] private bool grappleToAll = false;[SerializeField] private int grappableLayerNumber = 9;[Header("主攝像機:")]public Camera m_camera;[Header("變換引用:")]public Transform gunHolder;public Transform gunPivot;public Transform firePoint;[Header("物理引用:")]public SpringJoint2D m_springJoint2D;public Rigidbody2D m_rigidbody;[Header("旋轉:")][SerializeField] private bool rotateOverTime = true;[Range(0, 60)] [SerializeField] private float rotationSpeed = 4;[Header("距離:")][SerializeField] private bool hasMaxDistance = false;[SerializeField] private float maxDistnace = 20;private enum LaunchType //發射類型{Transform_Launch,Physics_Launch}[Header("發射:")][SerializeField] private bool launchToPoint = true;[SerializeField] private LaunchType launchType = LaunchType.Physics_Launch;[SerializeField] private float launchSpeed = 1;[Header("無發射點")][SerializeField] private bool autoConfigureDistance = false;[SerializeField] private float targetDistance = 3;[SerializeField] private float targetFrequncy = 1;[HideInInspector] public Vector2 grapplePoint;[HideInInspector] public Vector2 grappleDistanceVector;private void Start() //開始{grappleRope.enabled = false;m_springJoint2D.enabled = false;}private void Update() //更新函數,控制輸入{if (Input.GetKeyDown(KeyCode.Mouse0)) //通過Input的順序設定函數的執行順序,先進行鉤爪選取點的定位{SetGrapplePoint();          }else if (Input.GetKey(KeyCode.Mouse0)) //從上一步的定位中把grapplerope啟用{if (grappleRope.enabled){RotateGun(grapplePoint, false); //進行鉤鎖槍的旋轉}else{Vector2 mousePos = m_camera.ScreenToWorldPoint(Input.mousePosition);RotateGun(mousePos, true);}if (launchToPoint && grappleRope.isGrappling) //如果選擇點對點發射且正在鉤中目標{if (launchType == LaunchType.Transform_Launch) //如果發射類型是使用Transform類型發射{Vector2 firePointDistnace = firePoint.position - gunHolder.localPosition;Vector2 targetPos = grapplePoint - firePointDistnace;gunHolder.position = Vector2.Lerp(gunHolder.position, targetPos, Time.deltaTime * launchSpeed); //采用插值的形式,模擬繩索命中的物理效果}}}else if (Input.GetKeyUp(KeyCode.Mouse0)) //若抬起左鍵,則將一切啟用的相關布爾置否,恢復原狀{grappleRope.enabled = false;m_springJoint2D.enabled = false;m_rigidbody.gravityScale = 1;}else //時刻獲取鼠標的屏幕信息位置{Vector2 mousePos = m_camera.ScreenToWorldPoint(Input.mousePosition);RotateGun(mousePos, true);}}void RotateGun(Vector3 lookPoint, bool allowRotationOverTime) //實現繩索槍根據鼠標進行旋轉功能{Vector3 distanceVector = lookPoint - gunPivot.position; //定義三維距離向量=朝向點-槍錨點位置float angle = Mathf.Atan2(distanceVector.y, distanceVector.x) * Mathf.Rad2Deg; //定義一個角度,其值等于距離向量tan所對應的弧度值*弧度值轉化為角度值的常量if (rotateOverTime && allowRotationOverTime) //當采用根據時間延遲旋轉時,采用四元數的插值旋轉,在原本的旋轉角和獲得的繞軸的新角度中進行隨時間{gunPivot.rotation = Quaternion.Lerp(gunPivot.rotation, Quaternion.AngleAxis(angle, Vector3.forward), Time.deltaTime * rotationSpeed);}else{gunPivot.rotation = Quaternion.AngleAxis(angle, Vector3.forward); //不采用時間插值變化時時,直接讓強旋轉角角度等于計算出的角度繞軸的四元數即可}}void SetGrapplePoint() //設定鉤取點(主要是位置的計算和注意某些添加的限定條件){Vector2 distanceVector = m_camera.ScreenToWorldPoint(Input.mousePosition) - gunPivot.position; //設置一個二維向量distance用于記錄鼠標點擊的點和槍錨點之間的距離if (Physics2D.Raycast(firePoint.position, distanceVector.normalized)) //發射一條射線,起始點為開火點,方向為distance的方向向量{RaycastHit2D _hit = Physics2D.Raycast(firePoint.position, distanceVector.normalized); //保存剛才的射線為hitif (_hit.transform.gameObject.layer == grappableLayerNumber || grappleToAll) //選擇是否選中任意的可抓取圖層或是某一指定圖層{if (Vector2.Distance(_hit.point, firePoint.position) <= maxDistnace || !hasMaxDistance) //當命中點和開火電站之間的距離小于最大距離或者不限定最大距離時{grapplePoint = _hit.point; //將命中點位置賦予抓取點位置grappleDistanceVector = grapplePoint - (Vector2)gunPivot.position; //抓鉤的距離向量等于鉤鎖點減去鉤鎖槍的錨點位置grappleRope.enabled = true; //打開繩索變量}}}}public void Grapple() //鉤鎖執行(真正決定移動)                              {m_springJoint2D.autoConfigureDistance = false; //設定彈簧關節組建的自動計算距離屬性為假if (!launchToPoint && !autoConfigureDistance) //當對點發射和自動計算距離均為假時,將目標距離和目標頻率賦給彈簧組件的屬性{m_springJoint2D.distance = targetDistance;m_springJoint2D.frequency = targetFrequncy;}if (!launchToPoint) //如果僅為不對點發射{if (autoConfigureDistance) //若自動計算距離{m_springJoint2D.autoConfigureDistance = true;m_springJoint2D.frequency = 0; //彈簧組件頻率屬性為0,該值越大,彈簧越硬}m_springJoint2D.connectedAnchor = grapplePoint; //不自動計算距離且不對點發射時m_springJoint2D.enabled = true;}else //對點發射時,選擇發射類型,有物理類發射和Transform類發射{switch (launchType){case LaunchType.Physics_Launch:m_springJoint2D.connectedAnchor = grapplePoint; //當使用物理發射時,將鉤取點賦予彈簧的連接錨點Vector2 distanceVector = firePoint.position - gunHolder.position; //長度變量等于開火點距離減去持槍距離m_springJoint2D.distance = distanceVector.magnitude; //將長度變量賦給彈簧組建的距離屬性,保證鉤爪拉到盡頭時有一定的距離m_springJoint2D.frequency = launchSpeed; //彈簧頻率(強度)等于發射速度m_springJoint2D.enabled = true; //打開彈簧組件,進行拉伸break;case LaunchType.Transform_Launch:m_rigidbody.gravityScale = 0; //當使用Transform發射時,將物體的重力設置為0m_rigidbody.velocity = Vector2.zero; //啟動鉤爪時,將物體速度清零break;}}}private void OnDrawGizmosSelected() //始終在場景中繪制可視的Gizmo,On方法{if (firePoint != null && hasMaxDistance){Gizmos.color = Color.green;Gizmos.DrawWireSphere(firePoint.position, maxDistnace);}}}

腳本2:Grappling_Rope 添加給Rope即可

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Grappling_Rope : MonoBehaviour
{[Header("一般引用:")]public Perfecter_Grapple grapplingGun; //抓鉤槍public LineRenderer m_lineRenderer; //線渲染器[Header("一般設置:")][SerializeField] private int percision = 40; //精度[Range(0, 20)] [SerializeField] private float straightenLineSpeed = 5; //直線速度[Header("繩索動畫設置:")]public AnimationCurve ropeAnimationCurve; //繩索動畫曲線[Range(0.01f, 4)] [SerializeField] private float StartWaveSize = 2; //起始波動大小float waveSize = 0; //波動大小[Header("繩索進度:")]public AnimationCurve ropeProgressionCurve; //繩索進度曲線[SerializeField] [Range(1, 50)] private float ropeProgressionSpeed = 1; //繩索進度速度float moveTime = 0; //移動時間[HideInInspector] public bool isGrappling = true; //是否正在抓取bool strightLine = true; //是否為直線private void OnEnable() //啟用時執行{moveTime = 0;m_lineRenderer.positionCount = percision;waveSize = StartWaveSize;strightLine = false;LinePointsToFirePoint(); //線點對準發射點m_lineRenderer.enabled = true;}private void OnDisable() //禁用時執行{m_lineRenderer.enabled = false;isGrappling = false;}private void LinePointsToFirePoint() //線點對準發射點{for (int i = 0; i < percision; i++){m_lineRenderer.SetPosition(i, grapplingGun.firePoint.position); //繪制連接抓取點和抓鉤槍位置的繩子}}private void Update() //更新函數{moveTime += Time.deltaTime;DrawRope(); //繪制繩索}void DrawRope() //繪制繩索{if (!strightLine){if (m_lineRenderer.GetPosition(percision - 1).x == grapplingGun.grapplePoint.x){strightLine = true;}else{DrawRopeWaves(); //繪制繩索波動}}else{if (!isGrappling){grapplingGun.Grapple(); //抓取isGrappling = true;}if (waveSize > 0){waveSize -= Time.deltaTime * straightenLineSpeed;DrawRopeWaves(); //繪制繩索波動}else{waveSize = 0;if (m_lineRenderer.positionCount != 2) { m_lineRenderer.positionCount = 2; }DrawRopeNoWaves(); //繪制無波動的繩索}}}void DrawRopeWaves() //繪制繩索波動{for (int i = 0; i < percision; i++){float delta = (float)i / ((float)percision - 1f);Vector2 offset = Vector2.Perpendicular(grapplingGun.grappleDistanceVector).normalized * ropeAnimationCurve.Evaluate(delta) * waveSize; //計算偏移量Vector2 targetPosition = Vector2.Lerp(grapplingGun.firePoint.position, grapplingGun.grapplePoint, delta) + offset; //目標位置Vector2 currentPosition = Vector2.Lerp(grapplingGun.firePoint.position, targetPosition, ropeProgressionCurve.Evaluate(moveTime) * ropeProgressionSpeed); //當前位置m_lineRenderer.SetPosition(i, currentPosition); //設置線的位置}}void DrawRopeNoWaves() //繪制無波動的繩索{m_lineRenderer.SetPosition(0, grapplingGun.firePoint.position); //設置線的起始位置m_lineRenderer.SetPosition(1, grapplingGun.grapplePoint); //設置線的結束位置}
}

注意:腳本賦予對象后注意賦值,在Rope中的函數曲線繪制以及添加點時一定要注意不能逾越(0,0)以及(1,1)否則會出現鉤鎖無法發射的問題。

代碼賦值部分參數可以參照:
在這里插入圖片描述

在這里插入圖片描述

繩索發射曲線樣式參照:
在這里插入圖片描述
最終效果
在這里插入圖片描述

源碼

https://gitcode.net/unity1/unity2d-clawhook
在這里插入圖片描述

參考

【視頻】https://www.youtube.com/watch?v=dnNCVcVS6uw

完結

贈人玫瑰,手有余香!如果文章內容對你有所幫助,請不要吝嗇你的點贊評論和關注,以便我第一時間收到反饋,你的每一次支持都是我不斷創作的最大動力。當然如果你發現了文章中存在錯誤或者有更好的解決方法,也歡迎評論私信告訴我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奮斗的開發者,出于興趣愛好,于是最近才開始自習unity。如果你遇到任何問題,也歡迎你評論私信找我, 雖然有些問題我可能也不一定會,但是我會查閱各方資料,爭取給出最好的建議,希望可以幫助更多想學編程的人,共勉~
在這里插入圖片描述

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

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

相關文章

【AI繪畫】3分鐘學會ikun幻術圖

目錄 前言一、效果展示二、準備工作三、操作步驟3.1平臺創建實例3.2 啟動SD 四、安裝QR Code Monster 模型五、成圖 前言 大家熱愛的ikun幻術在今天的分享中將呈現。在本文中&#xff0c;我們將揭示一個備受歡迎的圖像幻術技術&#xff0c;讓您感受到令人驚嘆的視覺創造力。 …

springboot+vue游戲攻略推薦網站的設計與開發_s5832

熱門網游推薦網站是一個利用JAVA技術建設的網上管理系統&#xff0c;在熱門網游推薦管理中實現信息化。系統的設計就是為了迎合廣大用戶需求而創建的一個界面簡潔、有定向內容、業務邏輯簡單易操作的熱門網游推薦網站。本文以熱門網游推薦為例&#xff0c;提出了利用JAVA技術設…

Angular中的ActivatedRoute和Router

Angular中的ActivatedRoute和Router解釋 在Angular中&#xff0c;ActivatedRoute和Router是兩個核心的路由服務。他們都提供可以用來檢查和操作當前頁面路由信息的方法和屬性。 ActivatedRoute ActivatedRoute是一個保存關于當前路由狀態&#xff08;如路由參數、查詢參數以…

Linux下grep通配容易混淆的地方

先上一張圖: 我希望找到某個版本為8的一個libXXX.8XXX.so ,那么應該怎么寫呢? 先看這種寫法對不對: 是不是結果出乎你的意料之外? 那么我們來看一下規則: 這里的 "*" 表示匹配前一個字符的零個或多個 于是我們就不難理解了: lib*8*.so 表示 包…

醫療PACS源碼,支持三維多平面重建、三維容積重建、三維表面重建、三維虛擬內窺鏡

C/S架構的PACS系統源碼&#xff0c;PACS主要進行病人信息和影像的獲取、處理、存儲、調閱、檢索、管理&#xff0c;并通過網絡向全院提供病人檢查影像及診斷報告&#xff1b;各影像科室之間共享不同設備的病人檢查影像及診斷報告;在診斷工作站上&#xff0c;調閱HIS中病人的其它…

拒絕擺爛!C語言練習打卡第二天

&#x1f525;博客主頁&#xff1a;小王又困了 &#x1f4da;系列專欄&#xff1a;每日一練 &#x1f31f;人之為學&#xff0c;不日近則日退 ??感謝大家點贊&#x1f44d;收藏?評論?? 目錄 一、選擇題 &#x1f4dd;1.第一題 &#x1f4dd;2.第二題 &#x1f4dd;…

P1304 哥德巴赫猜想

題目描述 輸入一個偶數 N N N&#xff0c;驗證 4 ~ N 4\sim N 4~N 所有偶數是否符合哥德巴赫猜想&#xff1a;任一大于 2 2 2 的偶數都可寫成兩個質數之和。如果一個數不止一種分法&#xff0c;則輸出第一個加數相比其他分法最小的方案。例如 10 10 10&#xff0c; 10 …

Springboot寫單元測試

導入依賴 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintag…

django實現悲觀鎖樂觀鎖

前期準備 # 線上賣圖書-圖書表 圖書名字&#xff0c;圖書價格&#xff0c;庫存字段-訂單表&#xff1a; 訂單id&#xff0c;訂單名字# 表準備class Book(models.Model):name models.CharField(max_length32)price models.IntegerField() #count models.SmallIntegerField…

Python實時監控鍵盤的輸入并打印出來

要實現Python實時監控鍵盤的輸入并打印出來&#xff0c;可以使用pynput模塊。 首先&#xff0c;需要安裝pynput模塊&#xff1a; pip install pynput 然后&#xff0c;可以編寫以下代碼來實現實時監控鍵盤輸入并打印出來的功能&#xff1a; from pynput import keyboard# 定…

【Unity每日一記】方位辨別—向量的叉乘點乘結合

&#x1f468;?&#x1f4bb;個人主頁&#xff1a;元宇宙-秩沅 &#x1f468;?&#x1f4bb; hallo 歡迎 點贊&#x1f44d; 收藏? 留言&#x1f4dd; 加關注?! &#x1f468;?&#x1f4bb; 本文由 秩沅 原創 &#x1f468;?&#x1f4bb; 收錄于專欄&#xff1a;uni…

grafana 的 ws websocket 連接不上的解決方式

使用了多層的代理方式&#xff0c;一層沒有此問題 錯誤 WebSocket connection to ‘wss://ip地址/grafana01/api/live/ws’ failed: 日志報錯 msg“Request Completed” methodGET path/api/live/ws status403 解決方式 # allowed_origins is a comma-separated list of o…

Flutter:簡單搞一個內容高亮

內容高亮并不陌生&#xff0c;特別是在搜索內容頁面&#xff0c;可以說四處可見&#xff0c;就拿掘金這個應用而言&#xff0c;針對某一個關鍵字&#xff0c;我們搜索之后&#xff0c;與關鍵字相同的內容&#xff0c;則會高亮展示&#xff0c;如下圖所示&#xff1a; 如上的效果…

【Linux操作系統】舉例解釋Linux系統編程中文件io常用的函數

在Linux系統編程中&#xff0c;文件IO操作是非常常見和重要的操作之一。通過文件IO操作&#xff0c;我們可以打開、讀取、寫入和關閉文件&#xff0c;對文件進行定位、復制、刪除和重命名等操作。本篇博客將介紹一些常用的文件IO操作函數。 文章目錄 1. open()1.1 原型、參數及…

讀書筆記 |【項目思維與管理】? 順勢而動

讀書筆記 |【項目思維與管理】? 順勢而動 一、企業步入“終結者時代”二、過去成功的經驗也許是最可怕的三、做好非重復性的事四、適應客戶是出發點五、向知識型企業轉變六、速度是決勝條件 &#x1f496;The Begin&#x1f496;點點關注&#xff0c;收藏不迷路&#x1f496; …

docker私有倉庫harbor

一、安裝docker-compose yum install docker-compose -y 二、下載harbor安裝包 tar -xf harbor-online-installer-v2.1.0.tgz cp harbor.yml.tmpl harbor.yml 三、修改harbor配置 [rootharbor ~]# vim harbor.ymlhostname: "修改為本機ip" harboradminpassword:…

每天一道leetcode:1218. 最長定差子序列(動態規劃中等)

今日份題目&#xff1a; 給你一個整數數組 arr 和一個整數 difference&#xff0c;請你找出并返回 arr 中最長等差子序列的長度&#xff0c;該子序列中相鄰元素之間的差等于 difference 。 子序列 是指在不改變其余元素順序的情況下&#xff0c;通過刪除一些元素或不刪除任何…

iTOP-i.MX8M開發板添加USB網絡設備驅動

選中支持 USB 網絡設備驅動&#xff0c;如下圖所示&#xff1a; [*] Device Drivers→ *- Network device support → USB Network Adapters→ {*} Multi-purpose USB Networking Framework 將光標移動到 save 保存&#xff0c;如下圖所示&#xff1a; 保存到 arch/arm64/c…

樹形動態規劃——樹形dp

樹形動態規劃 樹形 d p dp dp算法是一種用于解決樹相關問題的動態規劃算法。它把樹的問題分解成了子問題&#xff0c;并通過子問題的求解來構建整個問題的解。 當我們面對一棵樹的問題時&#xff0c;我們可以使用樹形 d p dp dp來解決。這種算法的基本思想是通過定義一個用于…