unity3d结合轮廓显示,实现完整的框选目标(附Demo代码)
原地址:http://dong2008hong.blog.163.com/blog/static/469688272013111554511948/
在unity里实现,其实很简单,因为有两个前提:
1.画的方框始终是在屏幕空间进行的,而屏幕空间其实就是摄像机的视锥空间的投射了,不需要另外计算视锥。
2.unity摄像机内建的功能,可以方便的把屏幕坐标和世界坐标互换。
 
这样最简单的画框选物体就简化成了:
1.用GL在屏幕空间动态画框。
2.把备选对象的transform.position用camera.WorldToScreenPoiont变换成屏幕坐标。
3.判断这些position的点是否落在画的方框之内,如果是就把对象切换到画边框的层,呈现选中效果。
 
一.屏幕动态画框
这个过程相对简单 就是在鼠标按下的时候记下鼠标位置,然后在鼠标移动时在当前鼠标位置和按下的位置之间画一个方形就行了。
using UnityEngine;
using System.Collections;
 
public class DrawRectangle : MonoBehaviour {
    
public Color rectColor = Color.green;
private Vector3 start = Vector3.zero;//记下鼠标按下位置
    
private Material rectMat = null;//画线的材质 不设定系统会用当前材质画线 结果不可控
    
private bool drawRectangle = false;//是否开始画线标志
// Use this for initialization
    
void Start () {
        
rectMat =  new Material( "Shader \"Lines/Colored Blended\" {" +
            
"SubShader { Pass { " +
            
"    Blend SrcAlpha OneMinusSrcAlpha " +
            
"    ZWrite Off Cull Off Fog { Mode Off } " +
            
"    BindChannels {" +
            
"      Bind \"vertex\", vertex Bind \"color\", color }" +
            
"} } }" );//生成画线的材质
        
rectMat.hideFlags = HideFlags.HideAndDontSave;
        
rectMat.shader.hideFlags = HideFlags.HideAndDontSave;    
    
}
 
 
void Update () {
        
if(Input.GetMouseButtonDown(0)){
            
drawRectangle = true;//如果鼠标左键按下 设置开始画线标志
            
start = Input.mousePosition;//记录按下位置
        
}else if (Input.GetMouseButtonUp(0)){
            
drawRectangle = false;//如果鼠标左键放开 结束画线
        
} 
}
    
 
    
void OnPostRender() {//画线这种操作推荐在OnPostRender()里进行 而不是直接放在Update,所以需要标志来开启
        
if(drawRectangle){
            
Vector3 end = Input.mousePosition;//鼠标当前位置
            
GL.PushMatrix();//保存摄像机变换矩阵
       
if (! rectMat)
                
return;
          
rectMat.SetPass( 0 );
            
GL.LoadPixelMatrix();//设置用屏幕坐标绘图
            
GL.Begin(GL.QUADS);
            
GL.Color( new Color(rectColor.r,rectColor.g,rectColor.b,0.1f) );//设置颜色和透明度,方框内部透明
            
GL.Vertex3( start.x,start.y,0);
            
GL.Vertex3( end.x,start.y,0);
               
GL.Vertex3( end.x,end.y,0 );
            
GL.Vertex3( start.x,end.y,0 );
            
GL.End();
            
GL.Begin(GL.LINES);
            
GL.Color(rectColor);//设置方框的边框颜色 边框不透明
            
GL.Vertex3( start.x,start.y,0);
            
GL.Vertex3( end.x,start.y,0);
            
GL.Vertex3( end.x,start.y,0);
            
GL.Vertex3( end.x,end.y,0 );
            
GL.Vertex3( end.x,end.y,0 );
            
GL.Vertex3( start.x,end.y,0 );
            
GL.Vertex3( start.x,end.y,0 );
            
GL.Vertex3(start.x,start.y,0);
            
GL.End();
            
GL.PopMatrix();//恢复摄像机投影矩阵
        
}
    
}
}
效果如图:
注意GL绘图都是每帧进行的,所以不需要清除,直接不绘制方框就消失了。 
 
二.判断物体是否选中
有了方框,要判断物体是否在方框内,按照经典的数学算法可以根据直线方程和点的坐标计算判断点在线的左边 右边 还是线上
 
空间平面方程可表示为:
Ax+By+Cz=0
 
对于点(x1, y1, z1),有
若 Ax1+By1+Cz1 = 0,则点在平面上;
若 Ax1+By1+Cz1 < 0,则点在平面的一侧;
若 Ax1+By1+Cz1 > 0,则点在平面的另一侧;
 
但是在这里,因为都是水平、垂直的线,我们并不需要考虑画斜线、曲线框选物体........,所以只需要简单的比大小就行了,土了那么一点但其实效率更高,毕竟作游戏不是算法比赛,怎么简单高效怎么用。
如果:物体的屏幕position.x >方框左下角.x ;; 物体的屏幕position.y >方框左下角.y ;; 物体的屏幕position.x <方框右上角.x ;; 物体的屏幕position.y <方框右上角.y, 那么这个物体就是在框选范围里了。至于z的判定,直接选择摄像机的near和far距离就行了,小于near大于far都无视。因为屏幕空间是左下角为0,0,判断是否在方框内要注意这个前提。
有Unity内置的世界坐标->屏幕坐标转换,其实根本不需要什么复杂的裁剪算法,也不需要搞一大堆参考物体,往外发射一堆射线什么的,一次坐标转换加6个判断条件的一条if语句,就完成全部选择逻辑了,简单高效。
void checkSelection(Vector3 start,Vector3 end){
        
Vector3 p1 = Vector3.zero;
        
Vector3 p2 = Vector3.zero;
        
if(start.x>end.x){//这些判断是用来确保p1的xy坐标小于p2的xy坐标,因为画的框不见得就是左下到右上这个方向的
            
p1.x = end.x;
            
p2.x = start.x;
        
}
        
else{
            
p1.x = start.x;
            
p2.x = end.x;
        
}
        
if(start.y>end.y){
            
p1.y = end.y;
            
p2.y = start.y;
        
}
        
else{
            
p1.y = start.y;
            
p2.y = end.y;
        
}            
        
foreach(GameObject obj in characters){//把可选择的对象保存在characters数组里
            
Vector3 location = camera.WorldToScreenPoint(obj.transform.position);//把对象的position转换成屏幕坐标
            
if(location.x<p1.x || location.x>p2.x ||location.y<p1.y ||location.y>p2.y    
                
||location.z<camera.nearClipPlane || location.z > camera.farClipPlane)//z方向就用摄像机的设定值,看不见的也不需要选择了
            
{
disselecting(obj);//上面的条件是筛选 不在选择范围内的对象,然后进行取消选择操作,比如把物体放到default层,就不显示轮廓线了
            
}
            
else
            
{
selecting(obj);//否则就进行选中操作,比如把物体放到画轮廓线的层去
            
}
        
}
    
}
效果如图![]()
 
注意这种判定是以物体的position点为基准,如果物体中心不在几何中心可能会有奇怪的效果,但对作游戏来说模型中心在几何中心是基本要求,所以应该不是问题。当然也可以按照部分顶点相交来选中,但是那样程序就会复杂很多,要先解构物体的mesh顶点,然后把顶点变换到屏幕坐标,然后判定是否在方框范围内。实际上对大多数游戏来说划过中心点算作选中条件完全可以满足,足够有说服力了。
 
附上一个自己作的小Demo,综合了之前的显示边缘光、轮廓效果,按钮遮罩效果,加上框选物体的效果。 
鼠标左键按下画框 右键双击地面移动选中的群体 按住滚轮移动鼠标拖动地图,按住右键旋转鼠标转动视角,滚动滚轮缩放视角。
语音借用的是那啥3的人类语音,你懂的。
 
选择:![]()
 
双击右键移动目标,目标用一组粒子显示,到达目标或者目标改变时自动消失
 
可以同时移动几组,有点像1代的C;C......![]()
 
点按钮生成新的人物,选中按delete删除人物![]()
 
鼠标划过显示边缘光,单击目标或者画框选中目标显示轮廓线![]()
 
选中的目标会始终对着镜头,未选中的目标会随机行动![]()
 
 
Unity的便利性显现无疑,基本无需复杂的几何、线代知识,也没有繁琐的外围代码,用几句判断语句搞定框选。加上所见即所得的集成开发环境,写到这样程度的Demo实际大概也就花了几个小时的人时,真正框选部分不超过1小时,有点像游戏开发的卡拉OK,优势确实很明显。
unity3d结合轮廓显示,实现完整的框选目标(附Demo代码)的更多相关文章
- [转]结合轮廓显示,实现完整的框选目标(附Demo代码)
		
原地址:http://www.cnblogs.com/88999660/articles/2887078.html 几次看见有人问框选物体的做法,之前斑竹也介绍过,用画的框生成的视椎,用经典图形学的视 ...
 - python PIL图像处理-框选
		
框选图中位置 代码 from PIL import Image,ImageDraw,ImageFont,ImageFilter import random #--------------------- ...
 - Js控制显示、隐藏文本框中的密码
		
Js控制显示.隐藏文本框中的密码,也可称为是一款小型的JavaScript星号密码破解器,点击会显示出密码类型的文本框中的真实信息,再次点击则还原,程序 主要是获取HTML元素对象,然后强制更改元素属 ...
 - Ajax实现在textbox中输入内容,动态从数据库中模糊查询显示到下拉框中
		
功能:在textbox中输入内容,动态从数据库模糊查询显示到下拉框中,以供选择 1.建立一aspx页面,html代码 <HTML> <HEAD> <title>We ...
 - 基于定位下拉框或者需要点击link才显示的下拉框,二次定位与多次定位实现的实际效果区别
		
还是基于上次那个练习的后续出现的思考,http://www.cnblogs.com/8013-cmf/p/6555790.html 界面: 源码: 写法如下: 继续解释这两种的区别: 1.其实基于定 ...
 - Linux显示服务器完整的状态信息
		
Linux显示服务器完整的状态信息 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ apachectl [fullstatus] Usage: /usr/sbi ...
 - Cesium 中由 Logarithmic Depth Buffer 引起的模型显示不完整的问题
		
当 Cesium 单个模型过长时,会遇到某些视角模型显示不完整的问题,如下图所示: 经过在官方论坛上询问,该问题由 viewer.scene.logarithmicDepthBuffer 开启造成,关 ...
 - 百度“搜索设置”之基于定位下拉框或者需要点击link才显示的下拉框,二次定位与多次定位实现的实际效果区别
		
还是基于上次那个练习的后续出现的思考,http://www.cnblogs.com/8013-cmf/p/6555790.html 界面: 源码: 写法如下: 继续解释这两种的区别: 1.其实基于定 ...
 - 在word中粘贴的图片为什么显示不完整
		
一.背景 整理系统测试说明文档,截得图片粘贴到word中显示不完整. 二.错误问题 问题:在word中粘贴的图片为什么显示不完整,如图所示: 三.分析问题: 原因是原来设置的行间距是固定值,图片也作一 ...
 
随机推荐
- 一步一步学习C++
			
根据<C++ primer>第五版 总结学习心得. 在实践中,不必全面地使用C++语言的各种特性,而应根据工程的实际情况,适当取舍(譬如动态类型信息,虚拟继承.异常等特性的使用,很值得商榷 ...
 - laravel步骤  (我是新手)
			
1/需要一个wnmp之类的虚拟服务器 2/创建路由 php artisan make:route routes Route::group(['middleware' => ['web','a ...
 - 《linux下sudo服务的使用》RHEL6
			
/bin/ 下放的二进制文件命令都是普通用户可以使用的 Sbin 下放的二进制文件命令都是超级用户root可以使用的 普通用户也想使用Sbin下的文件可以通过sudo来实现: 默认普通用户是不可以 ...
 - 《自动共享LDAP用户并且访问其家目录》RHEL6
			
实验的目的: 实现ldap服务器上的ldap用户被客户端访问,自动挂载到客户端,并且可以访问ldap用户的家目录. 服务端: 1.只需要配置文件: Iptables –F 关闭selinu ...
 - linux 定时执行任务 crontab
			
欲编写定时任务访问网页和打开图片 原来写法为 #!/bin/bash #可以执行 echoecho "Hello" > dir/file.txt #不可以执行 xdg-ope ...
 - mount.nfs: access denied by server while mounting localhost:/home/xuwq/minilinux/system
			
在执行命令如下: mount -t nfs localhost:/home/xuwq/minilinux/system /mnt 出现的错误: mount.nfs: access denied by ...
 - Windows下关于Composer使用时出现的问题及解决办法
			
问题一: Fatal error: Call to undefined method Composer\Package\CompletePackage::getTrans portOptions() ...
 - python with语句上下文管理的两种实现方法
			
在编程中会经常碰到这种情况:有一个特殊的语句块,在执行这个语句块之前需要先执行一些准备动作:当语句块执行完成后,需要继续执行一些收尾动作.例如,文件读写后需要关闭,数据库读写完毕需要关闭连接,资源的加 ...
 - rtx信息泄漏利结合弱口令导致被批量社工思路
			
腾讯通RTX(Real Time eXchange)是腾讯公司推出的企业级实时通信平台. rtx server 存在暴露用户信息的漏洞,通过web访问 http://RtxServerIp:8012/ ...
 - linux 文件属性
			
关于属性的结构 在linux下文件和文件夹都被认为是文件, 所以以下的这个属性对文件和文件夹通用 获取属性的函数有stat/fstat/lstat/fstat struct stat{ mode_t ...