http://www.linkedin.com/pulse/using-raycasts-dynamically-generated-geometry-create-line-thomas

José Augusto Thomas

Unity Engineer at Imgnation Studios

On this article, I'd like to propose an implementation of a Line of Sight, those that you'd see on old Commandos games (see picture above).

This approach will use raycasts to provide scenario detection and dynamically generated geometry to create a visual representation of it.

This kind of system is useful to, of course, provide the accurate view of a character, given an aperture angle, a sight distance and a number of iterations (i.e. rays to be launched).

Our initial system will be targeting functionality over performance, but on this same article I'll write about some optimization tips.

Raycasting

Orienting our rays correctly and working with the aperture angle

The aperture angle means the left-most ray's direction will be -apertureAngle/2 degrees from the character's transform.forward vector and the right-most, +apertureAngle/2 degrees. Also, every ray we cast will depart from the character's position. Note that we can have control over the magnitude of the resulting vector, therefore creating a sightDistance(how far can the character see) effect. The picture below tries to illustrate this idea more clearly.

This kind of behaviour can be achieved with the Quaternion.AngleAxis (float degrees, Vector3 axis) function, which rotates degrees around axis. You then multiply the resulting Quaternion by the vector you want to rotate, in this case transform.forward. The result is the transform.forward vector rotated degrees around axis. A working code (given that angleAperture is a defined variable) would be:

Vector3 rotatedVector = Quaternion.AngleAxis (-apertureAngle/2, Vector3.up) * transform.forward:

The axis you pass in as a parameter (Vector3.up) most likely will be orthogonal to the one you want to rotate (transform.forward).

Our raycasting algorithm will work like so:

  • Cast a ray towards the current desired direction with a maximum distance of sightDistance. If we catch something in between, the final vector will be the direction * distance to the hit point. If not, the final vector is the direction * sightDistance.
  • For each new final point (character's position + final vector) we have in the system, we create a new triangle to generate geometry, though we'll explore this later.

Click above to see the raycasting animation.

The number of lines in the image above is basically the number of iterations we set and setting this value correctly will play a huge role when generating geometry.

Geometry

Using the rays to generate a dynamic mesh

Generating dynamic geometry is extremely powerful though kind of tricky to get right and master. There are a few specific points we have to pay attention to:

  • Vertices are an array of Vector3 (positions);
  • Triangles are an array of int. The integers represent the indices of the array of vertices, so the mesh knows which vertices compose which triangle;
  • The 3 vertices of each triangle need to be passed in counter-clock wise order, so the normal of the face gets calculated correctly, pointing out of the geometry;
  • If you set up vertices and triangles and no geometry is displayed, it might be the order you passed the vertex indices, making the normals to be pointing inwards;
  • The structure that holds geometry data is Mesh.

Something that makes live easier in this case is that the position of the character is always a vertex of a triangle, so each triangle is composed by the index 0, the current index and the one that came before.

Below you can see an example of how to generate mesh in Unity. The vertices are represented just by their index, starting at 0.

Click above to see the mesh generation workflow animation.

Here's the code that returns the Mesh structure with data correspondent to the generated geometry.

Click above to see the function workflow.

Note how iterations directly control the "resolution" of the geometry.

The function is pretty simple. First, we start by allocation memory space for the Lists that will keep the vertex and triangle data. We also define an angleStep, that is the amount of degrees that must be added each time in order to reach apertureAngle after iterations.

At each iteration we define the currentAngle (note how it starts at                           -apertureAngle * 0.5). We then cast a ray towards the desired direction. If we hit something, the hit position (i.e. startingPoint + sightVector * hit.distance) is the new vertex position, if not the vertex is placed at startingPoint + sightVector * maxSightDistance.

We start to define triangles after the second iteration (i >= 1) to make sure we have at least 3 vertices (the character's position and the first two). Also, note how vertex of index 0 (the character position) is always present in each triangle.

Optimization tip: instead of defining Lists every single time, we can allocate arrays with fixed size, because it is already known. We will always have iterations + 1 vertices (the extra one is for the character's position), so that's the vertex array size. Also, we'll have iterationstriangles. Since each triangle is composed of 3 vertices, the triangles array size is iterations * 3.

Conclusion

Putting it all together

To get this to work, we just need to dynamically create a GameObject and attach the correct components to it, those that allow it to display geometry (MeshFilter and MeshRenderer). It's also important to give the MeshRenderer a Material just so we won't see that purple colour which bothers me, particularly speaking.

The position of the new GameObject doesn't have to be updated every frame because the mesh already takes into consideration the position of the character. Also, this script considers that it is attached to the Line of Sight's owner.

Note how GenerateSightMesh is not declared here for simplicity purposes. Also, click on the image above to see the full plain-text code.

Optimization tip: instead of calling GenerateSightMesh every frame, we can call it in a coroutine and give it an update frequency, so every function called is delayed by 1/frequency.

You can attach a MeshCollider to this same GameObject and trigger events based on that, so you know exactly when something has entered the Line of Sight of your character. Another, more mathematical way of doing this would be to Dot product the transform.forward vector with the direction of your character pointing to the target (normalized). If that Dot product is less than the cosine of the apertureAngle and there are no obstacles between them and the distance is less than sightDistance, then the target is also in the character's view angle.

Well, I guess this is it for this article. I hope it was somehow useful to you! :)

5

【转】Using Raycasts and Dynamically Generated Geometry to Create a Line of Sight on Unity3D的更多相关文章

  1. Click event doesn't work on dynamically generated elements

    I couldn't get live or delegate to work on a div in a lightbox (tinybox). I used setTimeout successf ...

  2. Optimized Pagination using MySQL---reference

    Dealing with large data sets makes it necessary to pick out only the newest or the hottest elements ...

  3. Enhancing the Application: Advanced JDBC Features(转)

    Enhancing the Application: Advanced JDBC Features This chapter describes additional functionality th ...

  4. ArcMap所有Command GUID

    The information in this topic is useful if you're trying to programmatically find a built-in command ...

  5. Geometry关系高级操作

    一些高级的操作 几何形状Geometry缓冲(buffer) 线段的融合(linemerge)是将Geometry A中相互连接的线段进行连接 多边形化操作(polygonize)对Geometry ...

  6. MySQL 5.7新特性之Generated Column(函数索引)

    MySQL 5.7引入了Generated Column,这篇文章简单地介绍了Generated Column的使用方法和注意事项,为读者了解MySQL 5.7提供一个快速的.完整的教程.这篇文章围绕 ...

  7. JTS Geometry关系判断和分析

    关系判断 Geometry之间的关系有如下几种: 相等(Equals): 几何形状拓扑上相等. 脱节(Disjoint): 几何形状没有共有的点. 相交(Intersects): 几何形状至少有一个共 ...

  8. JTS(Geometry)(转)

    原文链接:http://blog.csdn.net/cdl2008sky/article/details/7268577 空间数据模型(1).JTS Geometry model (2).ISO Ge ...

  9. JTS Geometry

    JTS Geometry关系判断和分析 JTS Geometry关系判断和分析 1.关系判断 1.1实例 2.关系分析 2.1实例 JTS(Geometry) JTS Geometry关系判断和分析 ...

随机推荐

  1. PPII打不开 更改I.bat

    http://jingyan.baidu.com/article/3a2f7c2e7d277126afd6118d.html

  2. JAVA设计模式初探之桥接模式

    生活中的一个例子:    拿汽车在路上行驶的来说.既有小汽车又有公共汽车,它们都不但能在市区中的公路上行驶,也能在高速公路上行驶.这你会发现,对于交通工具(汽车)有不同的类型,它们所行驶的环境(路)也 ...

  3. oracle安装报错[INS-30131]执行安装程序验证所需的初始设置失败(无法访问临时位置)解决方法!

    最近在电脑上安装oracle12c,安装时,在执行检查环境步骤时候报错: [INS-30131]执行安装程序验证所需的初始设置失败(无法访问临时位置) 最后在网上搜索解决方法,特记录下,以防以后再用到 ...

  4. JS let和const关键字

    ES2015 引入了两个重要的 JavaScript 新关键词:let 和 const. Let关键字 1.用于作用域:块作用域,循环作用域,函数作用域,全局作用域, 在 ES2015 之前,Java ...

  5. Activiti学习记录(一)

    1.工作流的概念 工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档.信息或任务的过程自动进行,从而实现 ...

  6. fstatfs/statfs详解

    [fstatfs/statfs系统调用]       功能描述:   查询文件系统相关的信息.     用法:   #include <sys/vfs.h>    /* 或者 <sy ...

  7. 32-2题:LeetCode102. Binary Tree Level Order Traversal二叉树层次遍历/分行从上到下打印二叉树

    题目 给定一个二叉树,返回其按层次遍历的节点值. (即逐层地,从左到右访问所有节点). 例如: 给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 ...

  8. 基础篇(2):c++顺序结构程序设计

    一个程序最基本的结构莫过于3种:顺序,选择,循环.这篇讲讲顺序结构. c++语言的运算符与表达式数量之多,在高级语言中是少见的,也使得它的语言功能十分完善. c++的运算符有单目与双目之分(作用于一个 ...

  9. 回数是指从左向右读和从右向左读都是一样的数,例如 12321 , 909 。请利用 filter() 滤掉非回数

    不管在什么地方,什么时候,学习是快速提升自己的能力的一种体现!!!!!!!!!!! 最近一段时间学习了廖雪峰老师学的Python学习资料,给自己的帮助很大,同时也学到的了很多,今天做了一道练习题,对于 ...

  10. k8s资源配置清单的书写格式(yaml文件)

    yaml文件书写格式:5大类:apiVersion: 选择kubectl api-versions里面存在的版本kind: 选择kubectl api-resources结果中的对象资源metadat ...