·略带吐槽的序言

Unity是一款功能强大且运用广泛的引擎,但它也存在着一些颇受诟病的缺点。

对于想要快速做出可玩作品的开发者而言,Unity整个引擎的功能体系较为“白板”:它看上去很像是复杂化的代码编辑器,一切内容等待你的书写,而你很难认为,它针对某一类游戏的典型需求进行过优化。或者说,Unity不易直接实现任何一种令人惊喜或熟悉的游戏功能。(相比之下,虚幻引擎有着广受喜爱的蓝图机制,可以为开发者实现常见游戏功能提供很大便利)

简单解释一下前面是什么意思。

作为游戏开发者,每当构思并试图创作自己的游戏时,我们并不会首先从int整数、class...这样的程序基本概念来考虑如何开发它。从玩家的角度,我们非常容易去设想,自己的游戏应该具有哪些基本功能单元,这些功能单元在Unity中能否实现,是否容易实现。以下列举了一些功能单元的例子。

·创建有数个装备格子的背包,可以装入、取出、丢弃或使用武器、护甲、药水等物品;(背包系统)

·展示一个炫酷的技能栏,玩家可以通过点击各个按钮,使用英雄角色的各种法术;(技能系统)

·使用WASD键或屏幕轮盘来操控人物的前后左右移动;(FPS游戏的操控系统)

·使用鼠标或触控来改变观察方向,开火时向屏幕中心(准星)指向的位置发射子弹;(FPS游戏的射击系统)

·使用左键点选或框选士兵,右键移动它们到指定位置或攻击敌人;(RTS游戏的操控系统)

......

以上这些,是在各种类型的游戏中极为常见且通用的【基本功能单元】;但可惜的是,Unity基本没有提供过针对此类功能单元的标准解决方案。而当我们试图自己去实现这些功能模块时,时常会遇到一些出乎意料的难点,踩上很多暗藏的大坑。因此,我们要想熟练而稳定地实现某些较为"定型"的游戏功能,就需要有一些【套路级】的方法积累。

在这个系列,Victor想要分享的就是若干【成套】的解决方案——它们可以在你试着开发自己的游戏时,成为至关重要的功能起步模块。

让我们从一个大家都很熟悉的例子——矩形框选开始吧。

1.矩形框

1.1 游戏中的框选操作

画矩形框,或者说【框选】,是一个随处可见的PC游戏操作逻辑:玩家用鼠标拖移(Drag)的方式,在屏幕上画出一个透明或半透明方框,选中方框内所有可以交互的物体,忽略方框外的物体。框选最常见的用法是在RTS(即时战略)类游戏中,玩家通过画框的方式来选中若干个属于己方的作战单位,然后对它们下达集体指令。当然,这种画方框的操作也可以用在其它类型游戏中,例如在塔防游戏中选中一群防御塔、或者在建造类游戏中铺设地板和墙纸。

图1:《帝国时代2》中的框选效果

图2:《魔兽争霸3》中的框选效果

图3:《星际争霸2》中的框选效果

此外还有一个奇特的事实:在Unity本身的Scene场景视图内,也可以通过框选来批量选中物体,可见Unity内其实是有这个功能的代码的。(然而这个框选功能竟然没有被做成一个用户可调用的API,实在是令人费解......)

图4:Unity场景视图中的框选效果

1.2 描述功能逻辑

我们首先用通俗的自然语言,把框选的详细逻辑陈述一遍。这不是一件难事,大家也可以自行陈述,然后与Victor的陈述比对,看意思是否相同。

Step 1: 玩家按下鼠标左键时,以此时鼠标指针所在的点为【画框起点】;

Step 2: 在玩家未放开左键,并不断移动鼠标指针的过程中,屏幕上会动态绘制以【画框起点】和【当前鼠标指针所在点】的连线为对角线的矩形框;

Step 3: 当玩家放开鼠标左键时,以此时鼠标指针所在的点为【画框终点】;此时就正式画出了一个以【画框起点】和【画框终点】的连线为对角线的【框】,游戏会执行一次框选逻辑,例如选定所有位于框内的士兵。

这个流程和你想的内容一样吗?是不是很简单?

现在,我们可以将这个功能单元划分成三个部分,然后编写代码来实现它。

第一部分:在玩家进行按下左键、拖移鼠标和放开左键操作时,确定玩家的鼠标指针指在哪里,并根据按下和放开的时机,确定画框的起点和终点;

第二部分:在玩家按下左键之后的拖移过程中,动态绘制玩家正在试图画出的矩形框;

第三部分:在玩家放开鼠标左键后,执行游戏的画框逻辑。(开放性内容,因为不同游戏画框后发生什么是不确定的)

现在,我们在Unity中建立新场景,准备编写代码来实现框选功能。

这是Victor写作本文时使用的测试场景:(风景应该还算不错)

使用这个场景当然是为了美观;你在根据本文自行测试时,只要场景中有一个主摄像机(Main Camera)即可,其余没有要求。

1.3 实现第一部分:确定框的起止点

第一部分非常简单,它是一项Unity的基础知识。

在Unity中,玩家鼠标指针在屏幕上的位置是这样获取的:

Input.mousePosition

这是一个Vector3(三维坐标)变量。以屏幕的左下角为原点,该坐标的x值和y值,分别表示鼠标指针位于屏幕上的横向第几个像素纵向第几个像素;(和初中学过的平面直角坐标系没有区别)该坐标的z值始终为0,没有实际意义。

在项目内新建一个空的C# Script,命名为RectRender.cs,将其挂载到主摄像机(Main Camera)上。我们编写代码来进行一个最简单的测试,观察Input.mousePosition的含义,代码如下。

using UnityEngine;public class RectRender : MonoBehaviour
{void Update(){if (Input.GetKeyDown(KeyCode.Mouse0))//单击鼠标左键时{Vector3 mousePoint = Input.mousePosition;//获取鼠标指针在屏幕上的坐标Debug.LogFormat("当前鼠标指针位置:{0}", mousePoint);//输出上述坐标}}
}

保存,运行游戏。将鼠标指针移动到屏幕的不同位置并单击左键,观察控制台的输出。

可以看到,在单击左键时,我们的代码正确识别了鼠标指针在屏幕上的位置,并将其打印了出来。

在这个测试完成后,框选功能的第一部分已经触手可及:我们只要检测玩家按下/放开鼠标左键的操作,然后在对应的时机调取Input.mousePosition,即可轻松获取玩家“画框”的的起止点。

我们来修改并扩充RectRender.cs代码文件。只需补充一点逻辑,即可让它监控玩家画框的动作,并打印“框”的起止点。

using UnityEngine;public class RectRender : MonoBehaviour
{private bool onDrawingRect;//是否正在画框(即鼠标左键处于按住的状态)private Vector3 startPoint;//框的起始点,即按下鼠标左键时指针的位置private Vector3 currentPoint;//在拖移过程中,玩家鼠标指针所在的实时位置private Vector3 endPoint;//框的终止点,即放开鼠标左键时指针的位置void Update(){//玩家按下鼠标左键,此时进入画框状态,并确定框的起始点if (Input.GetKeyDown(KeyCode.Mouse0)){onDrawingRect = true;startPoint = Input.mousePosition;Debug.LogFormat("开始画框,起点:{0}", startPoint);}//在鼠标左键未放开时,实时记录鼠标指针的位置if (onDrawingRect){currentPoint = Input.mousePosition;}//玩家放开鼠标左键,说明框画完,确定框的终止点,退出画框状态if (Input.GetKeyUp(KeyCode.Mouse0)){endPoint = Input.mousePosition;onDrawingRect = false;Debug.LogFormat("画框结束,终点:{0}", endPoint);}}
}

再次运行游戏,然后你可以像在真正的游戏中一样,执行一次画框动作——在屏幕上的一个点按下鼠标左键,拖移到另一个点,然后放开。如下图所示。

观察控制台的输出,结果大致会是这样:

注意,我们尚未编写任何【绘制】方框的代码,因此执行这个动作的全过程不会看到任何反应。但通过控制台的输出,我们可以确信——现在我们已经能够识别到玩家的画框操作,并正确获取框的起始点和终止点。

1.4 实现第二部分:绘制方框

俗话说得好,”光说不练假把式”既然捕捉到了玩家所画的框,如果不能把它漂漂亮亮地显示到屏幕上,那显然也没有什么本领可言。如何根据玩家的鼠标动作,实现前面列举过的各款游戏的效果——在屏幕上绘出矩形选定框呢?

Q:对于这个我们想要画出的方框,我们能够知道它的哪些信息?

A:当玩家通过鼠标指针拖移进行画框时,我们可以知道两个屏幕坐标,即框的起始点当前鼠标指针所在点

(注意:框需要在玩家拖移过程中的每一帧都画出,并跟着玩家的指针不断变形,而不是仅在鼠标左键放开时绘制一次;因此当前鼠标指针所在点,也就是上面那段代码中的currentPoint,在画框时应被认为是框的终止点。这里的终止点指的是图形上的终止点,而不是框选动作生效时,框在逻辑上的终止点。)

所以——我们画框的目标就是下图的样子。

对吗?

Victor上高中的时候,英语考试有一道题目“短文改错”,要求我们在短文中找到用错的单词,并用斜杠" \ "将其划掉。我们的英语老师,Miss Ma,经常提醒我们:【划斜杠的时候一定要“从左上到右下”】,即划成" \ "形,而不要划另一种方向" / ",否则在阅卷时可能不被识别。

哈哈哈~就是这个问题!

玩家在拖移鼠标指针来画框时,画框的方向有四种可能:从左上到右下,从右下到左上,从右上到左下、从左下到右上。鉴于玩家才是大爷,我们当然要让这四种不同的画框方向都能够正确地画出框来,而不是只识别“从左上到右下”这一种。

“画框有四种方向”是一个极易被忽略的“坑”,但在发现这个“坑”之后,处理起来的难度只不过是Hello World级别而已。通过初一的数学知识,就可以轻松想到确定这个框的四角坐标的方法:对于玩家给出的起始点和终止点,比较两个点x值的大小,记为Xmin和Xmax;再比较两个点y值的大小,记为Ymin和Ymax,如下图。由图可见,无论起止点具体属于哪种情况,我们都可以简单明了地获知矩形方框四个角的坐标。

到这里,我们已经能够计算所需矩形框四个角的坐标,可以着手绘制了;对于初步熟悉Unity功能的同学来说,可能最先想到的,就是使用Unity自带的UI系统,即OnGUI方法。

1.4.1 通过OnGUI绘制矩形框

OnGUI方法其实藏着一个大坑(这与Unity UI系统的历史沿革有关)——在GUI相关方法内,对屏幕坐标系的规定与Unity其它地方的规定不一样。GUI指令中的屏幕坐标系是以屏幕左上角为原点,x轴正方向指向右方,y轴正方向指向下方的。

Unity在一般情况下的屏幕坐标系规定

Unity在UGUI系统中的屏幕坐标系规定

因此,在后面将要展示的OnGUI()相关代码内,某些坐标值已经经过了必要的换算。如果你一时觉得有疑问,可以自行探究验证。

通过GUI.Box方法我们可以在画面上呈现一个矩形的"Box",这或许可以作为我们想要的“框”。

现在我们继续扩充RectRender.cs代码,在Update方法之后添加OnGUI方法,来实现对框的绘制。

    public GUIStyle rectStyle;void OnGUI(){if (onDrawingRect){//获取确定矩形框各角坐标所需的各个数值float Xmin = Mathf.Min(startPoint.x, currentPoint.x);float Xmax = Mathf.Max(startPoint.x, currentPoint.x);float Ymin = Mathf.Min(startPoint.y, currentPoint.y);float Ymax = Mathf.Max(startPoint.y, currentPoint.y);//确定方框的定位点(左上角点)的横坐标、纵坐标,以及方框的横向宽度和纵向高度Rect rect = new Rect(Xmin, Screen.height - Ymax, Xmax - Xmin, Ymax - Ymin);//画框GUI.Box(rect, "", rectStyle);}}

这段代码中的GUI.Box用到了三个参数。

第一个参数rect是对屏幕上矩形的定义(由左上角点的横坐标、左上角点的纵坐标、矩形横向宽度、矩形纵向高度四个值来定义,其中左上角点的纵坐标已经进行了UGUI坐标系下的换算);

第二个参数是一个字符串,表示在Box方框内显示什么文本,这里我们不显示任何文本;

第三个参数是GUIStyle变量,表示该方框使用什么样的UI风格设定。GUIStyle有许多设定项,不过后面我们只需要设置一个选项——方框的背景图片,就可以啦。

在Inspector中编译修改后的RectRender组件,为rectStyle-Normal-BackGround设定项赋予一张图片,作为我们将要画出的框的背景样式。这里Victor采用了一张半透明的绿色纯色图片,i如下图所示;当然你也可以采用自己喜欢的图片。

再次运行游戏!拿起鼠标,在屏幕上拖移画框,这次可以看到预期的效果啦。

1.4.2 通过Unity的GL库绘制矩形框(推荐)

通过GUI方法,我们对于画框的需求得到了初步的解决;但需要承认的是,GUI.Box本身并不是为了满足“画框”这样的游戏功能性需求而设计的。它的原本目的,只是用来在屏幕上显示弹窗、提示文字等UI内容,因此上面的办法显得有些“投机取巧”。说白了,它是一种“障眼法”,只是把一张半透明的图片按照算出的位置贴到了屏幕上。

Q:为什么说GUI.Box是投机取巧的方法?

A:因为这个方法没有“一般性”。如果我们要绘制复杂一点的几何图样(而不是纯色矩形)——例如为矩形加上描边,或者将矩形的四个点两两相连,再或者只显示矩形的半边三角形那么使用上述方法很快就会露馅。你只能试图贴上去一张包含预期图样的图片,而该图片会在变形时出现缩放和拉伸问题,从而损失正常的比例和清晰度;以矩形描边为例,当你改变矩形框的大小和长宽比时,边框线条的粗细也会不停地改变。

从“授人以渔”的角度来说,如果我们要让这个功能变得更加“一般化”,即在屏幕上画出指定的几何图形,那么最好使用一套更加“专业对口”的工具。

Unity为我们提供了一套官方GL库,我们可以用它来绘制图形;使用Unity GL库的指令与OpenGL的代码风格非常类似(不过你在使用它的时候并不需要了解OpenGL),我们只要在OnPostRender()函数中写入GL指令,然后保证代码组件挂在主摄像机上即可。

关于GL库的详情,可以参阅官方文档,或看这篇文章:https://blog.csdn.net/Htlas/article/details/79748512

现在我们尝试新方案。注释掉原RectRender.cs代码中的OnGUI()部分,写入新的内容。修改后的RectRender.cs全文如下:

using UnityEngine;public class RectRender : MonoBehaviour
{private bool onDrawingRect;//是否正在画框(即鼠标左键处于按住的状态)private Vector3 startPoint;//框的起始点,即按下鼠标左键时指针的位置private Vector3 currentPoint;//在拖移过程中,玩家鼠标指针所在的实时位置private Vector3 endPoint;//框的终止点,即放开鼠标左键时指针的位置void Update(){//玩家按下鼠标左键,此时进入画框状态,并确定框的起始点if (Input.GetKeyDown(KeyCode.Mouse0)){onDrawingRect = true;startPoint = Input.mousePosition;Debug.LogFormat("开始画框,起点:{0}", startPoint);}//在鼠标左键未放开时,实时记录鼠标指针的位置if (onDrawingRect){currentPoint = Input.mousePosition;}//玩家放开鼠标左键,说明框画完,确定框的终止点,退出画框状态if (Input.GetKeyUp(KeyCode.Mouse0)){endPoint = Input.mousePosition;onDrawingRect = false;Debug.LogFormat("画框结束,终点:{0}", endPoint);}}public Material GLRectMat;//绘图的材质,在Inspector中设置public Color GLRectColor;//矩形的内部颜色,在Inspector中设置public Color GLRectEdgeColor;//矩形的边框颜色,在Inspector中设置void OnPostRender(){if (onDrawingRect){//准备工作:获取确定矩形框各角坐标所需的各个数值float Xmin = Mathf.Min(startPoint.x, currentPoint.x);float Xmax = Mathf.Max(startPoint.x, currentPoint.x);float Ymin = Mathf.Min(startPoint.y, currentPoint.y);float Ymax = Mathf.Max(startPoint.y, currentPoint.y);GL.PushMatrix();//GL入栈//在这里,只需要知道GL.PushMatrix()和GL.PopMatrix()分别是画图的开始和结束信号,画图指令写在它们中间if (!GLRectMat)return;GLRectMat.SetPass(0);//启用线框材质rectMatGL.LoadPixelMatrix();//设置用屏幕坐标绘图/*------第一步,绘制矩形------*/GL.Begin(GL.QUADS);//开始绘制矩形,这一段画的是框中间的半透明部分,不包括边界线GL.Color(GLRectColor);//设置矩形的颜色,注意GLRectColor务必设置为半透明//陈述矩形的四个顶点GL.Vertex3(Xmin, Ymin, 0);//陈述第一个点,即框的左下角点,记为点1GL.Vertex3(Xmin, Ymax, 0);//陈述第二个点,即框的左上角点,记为点2GL.Vertex3(Xmax, Ymax, 0);//陈述第三个点,即框的右上角点,记为点3GL.Vertex3(Xmax, Ymin, 0);//陈述第四个点,即框的右下角点,记为点4GL.End();//告一段落,此时画好了一个无边框的矩形/*------第二步,绘制矩形的边框------*/GL.Begin(GL.LINES);//开始绘制线,用来描出矩形的边框GL.Color(GLRectEdgeColor);//设置方框的边框颜色,建议设置为不透明的//描第一条边GL.Vertex3(Xmin, Ymin, 0);//起始于点1GL.Vertex3(Xmin, Ymax, 0);//终止于点2//描第二条边GL.Vertex3(Xmin, Ymax, 0);//起始于点2GL.Vertex3(Xmax, Ymax, 0);//终止于点3//描第三条边GL.Vertex3(Xmax, Ymax, 0);//起始于点3GL.Vertex3(Xmax, Ymin, 0);//终止于点4//描第四条边GL.Vertex3(Xmax, Ymin, 0);//起始于点4GL.Vertex3(Xmin, Ymin, 0);//返回到点1GL.End();//画好啦!GL.PopMatrix();//GL出栈}}
}

新加入的OnPostRender()方法内,我们使用GL库提供的GL.QUADS方法绘制了所需的矩形,然后用GL.LINES方法依次描出了矩形的四条边框线。我们只需在Inspector内设定绘图材质(使用Unity附送的Sprites-Default材质十分合适)、矩形内部颜色矩形边框颜色,即可完整地实现矩形框的绘制。

保存编译,在Inspector内设置如下:

(喜欢什么颜色就设置什么颜色,只要内部颜色是半透明,边框颜色不透明即可)

运行游戏,这次可以画出十分标准的矩形框了,完美地复刻了《星际争霸2》级别的效果;

1.5 实现第三部分:依据选定框,捕获游戏物体

到这里,我们实现了矩形框的绘制,但这个画框的动作在游戏中还没有实际的功能。回到我们一开始举的游戏效果示例:

在图中我们可以看到矩形选定框的功能。通过矩形选定框,游戏应能识别出哪些可交互物体在画面上位于框内,并将它们拾取出来;同时排除不在框内的物体。

如何将框中的物体拾取出来?要实现这一点,我们需要在框选事件发生时,将可能被选中的物体的坐标转换为屏幕坐标,然后判断物体的屏幕坐标是否位于框内

在Unity中,对于一个游戏物体GameObject A,要将一个游戏内的普通3D坐标,转换为主摄像机视图的屏幕坐标,方法如下:

Vector3 ScreenPos = Camera.main.WorldToScreenPoint(A.transform.position)

是不是很简单?

有了这一方法后,我们只需来讨论一下,一个屏幕坐标在什么情况下“位于框内”。

回到先前用来讲解”框“的那张图:

通过中学的线性规划知识,我们不难知道,一个点(x0,y0)位于由图中四个蓝点围成的框内部的条件是

    x0 > Xmin;

    x0 < Xmax;

    y0 > Ymin;

    y0 < Ymax.

归纳前面的分析可知,要用矩形选定框捕获游戏物体,流程应该是以下这样:

第1步.确定框选的范围

当玩家放开鼠标左键来结束框选时,创建一个选定框对象,并依次算出该选定框的左边界Xmin、右边界Ymin、下边界Ymin、上边界Ymax;

第2步.执行框选事件

对于所有可能被选中的物体(通常是具备某一特定Tag或者Layer的物体),依次判定它们的屏幕坐标是否位于框内;

第3步.执行游戏的选定功能

如果位于框内,则执行相应的游戏功能事件(例如选中该物体,将其高亮显示,等等)。

为了实现并演示这一部分,在场景中加入三个人物模型,分别名为ChanYunaRin。将它们的Tag设置为“Unit”

下面,按照刚刚总结过的三步流程,继续扩充RectRender.cs,形成完整的功能演示代码。扩充后的代码包含CheckSelection方法,该方法会在每次画框完成后,找出所有具备Unit标签且位于框内的物体,并在控制台打印出所有这些物体的名字。

这个版本的代码可以作为完整的框选功能模块了。你可以拷贝它,继续扩充以加入你自己想要的个性化功能。

using UnityEngine;public class RectRender : MonoBehaviour
{/// <summary>/// 这是一个自定义类,用来表示一个生效的选定框;创建它时会自动算出包含四角坐标的四项数据/// </summary>public class Selector{public float Xmin;public float Xmax;public float Ymin;public float Ymax;//构造函数,在创建选定框时自动计算Xmin/Xmax/Ymin/Ymaxpublic Selector(Vector3 start, Vector3 end){Xmin = Mathf.Min(start.x, end.x);Xmax = Mathf.Max(start.x, end.x);Ymin = Mathf.Min(start.y, end.y);Ymax = Mathf.Max(start.y, end.y);}}private bool onDrawingRect;//是否正在画框(即鼠标左键处于按住的状态)private Vector3 startPoint;//框的起始点,即按下鼠标左键时指针的位置private Vector3 currentPoint;//在拖移过程中,玩家鼠标指针所在的实时位置private Vector3 endPoint;//框的终止点,即放开鼠标左键时指针的位置private Selector selector;void Update(){//玩家按下鼠标左键,此时进入画框状态,并确定框的起始点if (Input.GetKeyDown(KeyCode.Mouse0)){onDrawingRect = true;startPoint = Input.mousePosition;Debug.LogFormat("开始画框,起点:{0}", startPoint);}//在鼠标左键未放开时,实时记录鼠标指针的位置if (onDrawingRect){currentPoint = Input.mousePosition;}//玩家放开鼠标左键,说明框画完,确定框的终止点,退出画框状态if (Input.GetKeyUp(KeyCode.Mouse0)){endPoint = Input.mousePosition;onDrawingRect = false;Debug.LogFormat("画框结束,终点:{0}", endPoint);//当框画完时,创建一个生效的选定框selectorselector = new Selector(startPoint, endPoint);//执行框选事件CheckSelection(selector, "Unit");}}//框选事件//按照选定框的范围,捕获标签为tag的所有物体,并打印这些物体的名字void CheckSelection(Selector selector, string tag){GameObject[] Units = GameObject.FindGameObjectsWithTag(tag);foreach(GameObject Unit in Units){Vector3 screenPos = Camera.main.WorldToScreenPoint(Unit.transform.position);if (screenPos.x > selector.Xmin && screenPos.x < selector.Xmax && screenPos.y > selector.Ymin && screenPos.y < selector.Ymax){Debug.LogFormat("已选中目标:{0}", Unit.name);}}}public Material GLRectMat;//绘图的材质,在Inspector中设置public Color GLRectColor;//矩形的内部颜色,在Inspector中设置public Color GLRectEdgeColor;//矩形的边框颜色,在Inspector中设置void OnPostRender(){if (onDrawingRect){//准备工作:获取确定矩形框各角坐标所需的各个数值float Xmin = Mathf.Min(startPoint.x, currentPoint.x);float Xmax = Mathf.Max(startPoint.x, currentPoint.x);float Ymin = Mathf.Min(startPoint.y, currentPoint.y);float Ymax = Mathf.Max(startPoint.y, currentPoint.y);GL.PushMatrix();//GL入栈//在这里,只需要知道GL.PushMatrix()和GL.PopMatrix()分别是画图的开始和结束信号,画图指令写在它们中间if (!GLRectMat)return;GLRectMat.SetPass(0);//启用线框材质rectMatGL.LoadPixelMatrix();//设置用屏幕坐标绘图/*------第一步,绘制矩形------*/GL.Begin(GL.QUADS);//开始绘制矩形,这一段画的是框中间的半透明部分,不包括边界线GL.Color(GLRectColor);//设置矩形的颜色,注意GLRectColor务必设置为半透明//陈述矩形的四个顶点GL.Vertex3(Xmin, Ymin, 0);//陈述第一个点,即框的左下角点,记为点1GL.Vertex3(Xmin, Ymax, 0);//陈述第二个点,即框的左上角点,记为点2GL.Vertex3(Xmax, Ymax, 0);//陈述第三个点,即框的右上角点,记为点3GL.Vertex3(Xmax, Ymin, 0);//陈述第四个点,即框的右下角点,记为点4GL.End();//告一段落,此时画好了一个无边框的矩形/*------第二步,绘制矩形的边框------*/GL.Begin(GL.LINES);//开始绘制线,用来描出矩形的边框GL.Color(GLRectEdgeColor);//设置方框的边框颜色,建议设置为不透明的//描第一条边GL.Vertex3(Xmin, Ymin, 0);//起始于点1GL.Vertex3(Xmin, Ymax, 0);//终止于点2//描第二条边GL.Vertex3(Xmin, Ymax, 0);//起始于点2GL.Vertex3(Xmax, Ymax, 0);//终止于点3//描第三条边GL.Vertex3(Xmax, Ymax, 0);//起始于点3GL.Vertex3(Xmax, Ymin, 0);//终止于点4//描第四条边GL.Vertex3(Xmax, Ymin, 0);//起始于点4GL.Vertex3(Xmin, Ymin, 0);//返回到点1GL.End();//画好啦!GL.PopMatrix();//GL出栈}}
}

现在进行效果测试。运行游戏,在屏幕上画框来圈选Chan和Rin两个人物,排除Yuna。

操作完成后,控制台将会打印出两个被圈定的角色名字。

到这里,整个功能的实现终于大功告成!我们现在可以在屏幕上画出矩形选定框,并正确地拾取出位于框内的游戏物体啦。

1.6 What's more......

依照你所开发的游戏类型的不同,你可能还需要以自己的方式进一步扩充代码。后续,你需要对被你选中的物体采取各种操作,来实现你想要的游戏功能。以下是一些Victor认为常见且简易可行的后续操作,你可以用来参考:

·将所有被选中的物体放入一个列表(List),表明当前选中了这些物体,然后以适当方式对它们进行群体控制;

·为被选中的所有物体开启“被选中效果”,例如闪烁、高亮描边、在脚下显示光环、在头上显示生命值条,等等;

示例效果:

·选中物体后弹出一个UI框以询问玩家要对选中的目标做什么,例如【升级】【出售】;

·当选中了一个或多个角色时,播放相应的角色应答台词;

......

有关Unity中矩形框选的实现方式,讲到这里就算全部完成啦!不知你是否已经阅读了讲解并拷贝了代码,取得了自己的收获呢?

后面,Victor还会为大家讲述更多的Unity套路与技巧。欲知后文如何,请看下回分解!Bye~

【Vic的小课堂】Unity实现游戏功能(1)—矩形框选相关推荐

  1. 【Vic的小课堂】Unity游戏功能(2)—第一人称镜头

    ·序言 "你最早喜欢上的游戏类型是什么?" 如果询问每一位游戏爱好者和游戏开发者,那么FPS(First-Person Shooting,第一人称射击)类型游戏必定是一个高频次出现 ...

  2. python print 换行_Python小课堂第21课:规整一下我们的输出之打印格式化与字符串...

    整齐的输出,不仅美观,还能方便我们更容易的定位问题的重点.所以我们有必要将我们的输出内容美化一下! 请点击右上角"关注"按钮关注我们,跟着木辛老师深入浅出的掌握输出格式化的方法吧! ...

  3. 饱和气压与温度的关系_凯米斯小课堂 | 溶解氧与水产养殖的关系

    点击上方蓝字关注我们 随着物联网技术的高速发展,传感器和软件通过移动平台或者电脑平台对农业生产进行控制,使传统农业更具智慧.这其中也包括我们的水产养殖业.众所周知,养鱼先养水,水是鱼类赖以生存的环境, ...

  4. 有条件截取字符串_Python小课堂之木辛老师特别讲解:再次深入浅出字符串的格式化...

    我们已经学习了字符串和数字基础的处理方法和逻辑,大家有没有觉得使用起来很方便,编程的过程中也是很给力的呀!其实Python还有更多字符串处理的方法,大家今天就一起来体验一下吧 小朋友们可以先复习一下前 ...

  5. word邮件合并一页8个_办公软件小课堂||word邮件合并

    你是否还在为考证烦恼?是否不知从何学起?学习部每周的办公软件小课堂!这里总有你想要的! 很多人留言给小编说不太理解邮件合并今天给大家简单的推送一下 1.邮件合并的含义 2.邮件合并的文档类型 那这些文 ...

  6. app h5 上传按钮多选_稿定小课堂之教你如何制作H5

    卡卡小课堂 ---第五堂课--- 主要是卡卡在社群中分享的一些小技巧课程总结,在小课堂里可以学习到一些很简单但是确很实用的技巧,获得不错的效果. 如果想要分享更多的技巧方法,可以私戳卡卡的窗哟~ 本次 ...

  7. java string 返回匹配正则的字符串的起始位置_Python小课堂正则表达式

    Python3 正则表达式 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式. ...

  8. 云信小课堂丨简单四步,快速搭建协同办公系统!

    Vol. 8 企业作为社会分工参与的主体,在自身发展以及内部协作中,势必会涉及到各类自有技术和商业信息的互动与交流,而这类信息由于有着高度的保密性和隐私性,所以如微信.QQ 等通用的即时通讯工具很难满 ...

  9. 云信小课堂|聊天室内容审核很头疼?3步解决!

    简介 随着 5G . AI 等颠覆性的技术创新,各类产品的互动性和沉浸感都在快速提升.强互动作为产品快速发展的生命力,也带来了一定程度的安全和监管风险,对通信.安全等技术和服务提出了更高要求. 以娱乐 ...

  10. 云信小课堂 | 聊天室内容审核很头疼? 3 步解决!

    Vol. 7 随着 5G . AI 等颠覆性的技术创新,各类产品的互动性和沉浸感都在快速提升.强互动作为产品快速发展的生命力,也带来了一定程度的安全和监管风险,对通信.安全等技术和服务提出了更高要求. ...

最新文章

  1. Great Power, Great Responsibility: The 2018 Big Data AI Landscape
  2. MySQL中实现分组排序
  3. 如何确定固定资产入账价值
  4. Android PC投屏简单尝试- 自定义协议章(Socket+Bitmap)
  5. 洛谷 2820 局域网
  6. 多值参数-元组和字典的拆包
  7. Linux闲时自动抢占GPU脚本
  8. 关于WM_NCHITTEST消息
  9. 甲骨文重磅发布:客户现可将自治数据库部署在自己的数据中心
  10. _IO, _IOR, _IOW, _IOWR 宏的用法与解析
  11. 基于java的enigma的加密程序
  12. 为什么不要用System.out.println()
  13. MHDD修复UNC和TIMEOUT
  14. 下载谷歌浏览器旧版本的方法
  15. 组件化、模块化、Composing Builds
  16. 构造方法--带参构造方法
  17. C语言中的指针应用,函数指针,指针函数,结构体中定义函数指针。
  18. getElementByClassName
  19. 隆云通露点温度传感器
  20. springboot基础学习笔记

热门文章

  1. [Algorithm]Bubble_冒泡算法代码实现
  2. CSRNet: Dilated Convolutional Neural Networks for Understanding the Highly Congested Scen 论文阅读
  3. Android 应用商店评分+APP分享
  4. docker限制容器下载速度
  5. 通过top查看程序cpu使用率为什么会超过100%
  6. Dual-edge triggered flip_flop(Dualedge)
  7. unity 球体表面平均分割点
  8. Oracle之FORALL与BULK COLLECT简介(转载)
  9. excel电子表格发展历史
  10. 敏捷开发系列学习总结(18)——Scrum Master的情景领导力模型