Unity 實現(xiàn)框選游戲戰(zhàn)斗單位的思路詳解
?? Preface
本文簡單介紹如何實現(xiàn)即時戰(zhàn)略游戲中框選戰(zhàn)斗單位的功能,如圖所示:

?? 實現(xiàn)思路:
本文將該功能的實現(xiàn)拆分為以下部分:
- 在屏幕坐標系中繪制框選范圍;
- 根據(jù)框選范圍定位其在世界坐標系中對應(yīng)的區(qū)域;
- 在該區(qū)域內(nèi)進行物理檢測。
? 如何在屏幕坐標系內(nèi)繪制框選框
使用Line Renderer光線渲染器組件來進行范圍繪制,當鼠標按下時,可以獲得框選范圍的起始點,鼠標持續(xù)按下時,鼠標位置則是框選范圍的結(jié)束點,根據(jù)這兩個點的坐標可以求得另外兩個頂點的坐標,如圖所示:

首先設(shè)置Line Renderer光線渲染器的屬性:

Enable:默認設(shè)為false,當鼠標按下時將其設(shè)為true;Loop:設(shè)為true,為了讓第三個頂點與起始點相連形成閉環(huán);Size:設(shè)為4,框選范圍有4個頂點;Width:設(shè)為0.001即可,線框不需要很粗,可適當調(diào)整;
代碼部分:
using UnityEngine;
using SK.Framework;
using System.Collections.Generic;
public class Example : MonoBehaviour
{
//光線渲染器組件
private LineRenderer lineRenderer;
//屏幕坐標系起始點
private Vector3 screenStartPoint;
//屏幕坐標系結(jié)束點
private Vector3 screenEndPoint;
private void Start()
{
//獲取光線渲染器組件
lineRenderer = GetComponent<LineRenderer>();
}
private void Update()
{
//鼠標按下
if (Input.GetMouseButtonDown(0))
{
//激活光線渲染器
lineRenderer.enabled = true;
//屏幕坐標系起始點
screenStartPoint = Input.mousePosition;
screenStartPoint.z = 1;
}
//鼠標持續(xù)按下
if (Input.GetMouseButton(0))
{
//屏幕坐標系結(jié)束點
screenEndPoint = Input.mousePosition;
screenEndPoint.z = 1;
//求得框選框的另外兩個頂點的位置
Vector3 point1 = new Vector3(screenEndPoint.x, screenStartPoint.y, 1);
Vector3 point2 = new Vector3(screenStartPoint.x, screenEndPoint.y, 1);
//接下來使用光線渲染器畫出框選范圍
lineRenderer.SetPosition(0, Camera.main.ScreenToWorldPoint(screenStartPoint));
lineRenderer.SetPosition(1, Camera.main.ScreenToWorldPoint(point1));
lineRenderer.SetPosition(2, Camera.main.ScreenToWorldPoint(screenEndPoint));
lineRenderer.SetPosition(3, Camera.main.ScreenToWorldPoint(point2));
}
//鼠標抬起
if (Input.GetMouseButtonUp(0))
{
//取消光線渲染器
lineRenderer.enabled = false;
}
}
}如圖所示,已經(jīng)實現(xiàn)框選范圍的繪制:

?? 根據(jù)框選范圍定位其在世界坐標系中對應(yīng)的區(qū)域
該部分的實現(xiàn)主要依靠物理射線檢測,在鼠標位置發(fā)出射線,檢測與地面的碰撞點,首先為Plane地面設(shè)置Layer層級:

在鼠標按下時根據(jù)射線檢測信息確定世界坐標系中的起始點:
//鼠標按下
if (Input.GetMouseButtonDown(0))
{
//激活光線渲染器
lineRenderer.enabled = true;
//屏幕坐標系起始點
screenStartPoint = Input.mousePosition;
screenStartPoint.z = 1;
//射線檢測
if (Physics.Raycast(mainCamera.ScreenPointToRay(Input.mousePosition), out hit, 1 << LayerMask.NameToLayer("Ground")))
{
//世界坐標系起始點
worldStartPoint = hit.point;
}
}在鼠標抬起時根據(jù)射線檢測信息確定世界坐標系中的結(jié)束點:
//鼠標抬起
if (Input.GetMouseButtonUp(0))
{
//取消光線渲染器
lineRenderer.enabled = false;
//射線檢測
if (Physics.Raycast(mainCamera.ScreenPointToRay(Input.mousePosition), out hit, 1 << LayerMask.NameToLayer("Ground")))
{
//世界坐標系結(jié)束點
worldEndPoint = hit.point;
}
}?? 在該區(qū)域內(nèi)進行物理檢測
該部分用的的核心API:

可以理解為創(chuàng)建一個碰撞盒來檢測該范圍內(nèi)的碰撞體,首先計算出該API需要傳入的參數(shù):
center:該盒子的中心點;halfExtents:該盒子長寬高的一半。
//盒子中心點 Vector3 center = new Vector3((worldEndPoint.x + worldStartPoint.x) * .5f, 1f, (worldEndPoint.z + worldStartPoint.z) * .5f); //盒子長寬高的一半 Vector3 halfExtents = new Vector3(Mathf.Abs(worldEndPoint.x - worldStartPoint.x) * .5f, 1f, Mathf.Abs(worldEndPoint.z - worldStartPoint.z) * .5f);
有了這兩個參數(shù),調(diào)用該API可以獲得該區(qū)域內(nèi)的所有碰撞體,遍歷判斷碰撞體身上如果包含指定的組件,則將其選中,這里使用Outline高亮組件來表示:
//盒子中心點
Vector3 center = new Vector3((worldEndPoint.x + worldStartPoint.x) * .5f, 1f, (worldEndPoint.z + worldStartPoint.z) * .5f);
//盒子長寬高的一半
Vector3 halfExtents = new Vector3(Mathf.Abs(worldEndPoint.x - worldStartPoint.x) * .5f, 1f, Mathf.Abs(worldEndPoint.z - worldStartPoint.z) * .5f);
//檢測到盒子內(nèi)的碰撞器
Collider[] colliders = Physics.OverlapBox(center, halfExtents);
for (int i = 0; i < colliders.Length; i++)
{
var collider = colliders[i];
var outline = collider.GetComponent<Outline>();
if (outline != null)
{
outline.enabled = true;
}
}如圖所示,我們已經(jīng)實現(xiàn)了基本的框選功能:

在框選時,還需要清除上一次框選的內(nèi)容,因此我們使用一個List列表來記錄當前框選的戰(zhàn)斗單位,框選前遍歷該列表來清除框選記錄,完整代碼如下:
public class Example : MonoBehaviour
{
//光線渲染器組件
private LineRenderer lineRenderer;
//屏幕坐標系起始點
private Vector3 screenStartPoint;
//屏幕坐標系結(jié)束點
private Vector3 screenEndPoint;
//主相機
private Camera mainCamera;
//碰撞信息
private RaycastHit hit;
//世界坐標系起始點
private Vector3 worldStartPoint;
//世界坐標系結(jié)束點
private Vector3 worldEndPoint;
//框選記錄列表
private List<Outline> list = new List<Outline>();
private void Start()
{
//獲取光線渲染器組件
lineRenderer = GetComponent<LineRenderer>();
//獲取主相機
mainCamera = Camera.main != null ? Camera.main : FindObjectOfType<Camera>();
}
private void Update()
{
//鼠標按下
if (Input.GetMouseButtonDown(0))
{
//激活光線渲染器
lineRenderer.enabled = true;
//屏幕坐標系起始點
screenStartPoint = Input.mousePosition;
screenStartPoint.z = 1;
//射線檢測
if (Physics.Raycast(mainCamera.ScreenPointToRay(Input.mousePosition), out hit, 1 << LayerMask.NameToLayer("Ground")))
{
//世界坐標系起始點
worldStartPoint = hit.point;
}
}
//鼠標持續(xù)按下
if (Input.GetMouseButton(0))
{
//屏幕坐標系結(jié)束點
screenEndPoint = Input.mousePosition;
screenEndPoint.z = 1;
//求得框選框的另外兩個頂點的位置
Vector3 point1 = new Vector3(screenEndPoint.x, screenStartPoint.y, 1);
Vector3 point2 = new Vector3(screenStartPoint.x, screenEndPoint.y, 1);
//接下來使用光線渲染器畫出框選范圍
lineRenderer.SetPosition(0, Camera.main.ScreenToWorldPoint(screenStartPoint));
lineRenderer.SetPosition(1, Camera.main.ScreenToWorldPoint(point1));
lineRenderer.SetPosition(2, Camera.main.ScreenToWorldPoint(screenEndPoint));
lineRenderer.SetPosition(3, Camera.main.ScreenToWorldPoint(point2));
}
//鼠標抬起
if (Input.GetMouseButtonUp(0))
{
//取消光線渲染器
lineRenderer.enabled = false;
//首先清除上一次的框選記錄
for (int i = 0; i < list.Count; i++)
{
list[i].enabled = false;
}
list.Clear();
//射線檢測
if (Physics.Raycast(mainCamera.ScreenPointToRay(Input.mousePosition), out hit, 1 << LayerMask.NameToLayer("Ground")))
{
//世界坐標系結(jié)束點
worldEndPoint = hit.point;
//盒子中心點
Vector3 center = new Vector3((worldEndPoint.x + worldStartPoint.x) * .5f, 1f, (worldEndPoint.z + worldStartPoint.z) * .5f);
//盒子長寬高的一半
Vector3 halfExtents = new Vector3(Mathf.Abs(worldEndPoint.x - worldStartPoint.x) * .5f, 1f, Mathf.Abs(worldEndPoint.z - worldStartPoint.z) * .5f);
//檢測到盒子內(nèi)的碰撞器
Collider[] colliders = Physics.OverlapBox(center, halfExtents);
for (int i = 0; i < colliders.Length; i++)
{
var collider = colliders[i];
var outline = collider.GetComponent<Outline>();
if (outline != null)
{
list.Add(outline);
outline.enabled = true;
}
}
}
}
}
}到此這篇關(guān)于Unity 如何實現(xiàn)框選游戲戰(zhàn)斗單位的文章就介紹到這了,更多相關(guān)Unity框選游戲戰(zhàn)斗單位內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
WPF PasswordBox進行數(shù)據(jù)綁定方法
有的時候會遇見PasswordBox的Password屬性不是依賴屬性,因此無法進行數(shù)據(jù)綁定。本文介紹如何通過添加附加屬性解決該問題,有此問題的同學(xué)可以參考下本文2021-06-06
C#中FileSystemWatcher類實現(xiàn)監(jiān)控文件夾
在C#中,如果你想要監(jiān)控一個文件夾內(nèi)文件的變動情況,比如文件的創(chuàng)建、刪除、修改等,你可以使用FileSystemWatcher類,下面就來介紹一下FileSystemWatcher監(jiān)控的使用,感興趣的可以了解一下2024-03-03

