大家好,欢迎大家多多交流,转载请注明出处,谢谢。
我来博客园的第一篇博客,想写的东西很多,基于目前工作手里的内容,先上一篇算法的。
上周做了个3D自由相机功能,需要对地形和墙壁进行自动缩进动画,效果经过多款游戏对比,决定编写《万王之王》的相机效果。
我发现很多同学,基础知识都不错,但是自己写的话,却不知道怎么去实现具体算法和方案,其实这种情况不用着急,养成好的需求分析习惯会帮助你提前解决很多难题。但是需求分析再好,对于算法来说,基础知识的掌握更加重要。
所以下面几个步骤是解决问题的流程。
第一步:首先就是自身基础了,关于3d向量的掌握,必须达到一定层次,否则很难处理好这种复杂环境下的算法策略,如果算法不好的,接着看,或许能让你有所提高。
第二步:其次就是对于相机功能的需求分析,需求分析是通过游戏还有和游戏策划沟通的结果,需求分析的准确与否决定了我们最终的结果,否则需求分析的错误或者不明确会带来代码策略的错误,影响工期。
第三步:很多同学到这里可能以为要开始动手写代码了,不着急,这里建议大家先整理游戏中相关流程,确定自己方案可行性,然后编写伪代码或者方案流程。伪代码能提前实验逻辑的可行性和纰漏,总览全局,避免实际编写代码过程中的盲目或者片面引起的时间成本。
第四步:接下来可以开始编写你的代码了。
相机涉及数学知识:
涉及的数学知识不多,作为游戏开发人员,这些知识点事必备技能,如果有不熟悉或者不知道的,一定要及时补充知识。
1:主要是对3d空间的理解,空间点,空间向量,空间角度的概念。
2:熟悉3D向量相关计算,余弦公式,角度计算,三角函数,向量投影计算和物理意义
3:熟悉笛卡尔坐标系,熟悉点乘积和叉乘积计算和物理意思,知道右手定则,平面法线等知识。
相机需求:
1:3D自由相机通用功能,滑动屏幕可以随意调整方向和缩进显示距离。
2:在1基础上对相机进行遮挡碰撞处理,相机和角色中间有阻挡时相机自动越过障碍物(渐进运动方式),并且保持相机视域内不在物体内部(穿墙情况)。
3:相机离开遮挡的时候回复原来自由相机设置距离,并且动态回复到合适位置。
4:对任意形状的阻挡(墙面,地面,任意角度斜坡)进行处理后不穿强。
5:发生视觉遮挡时,缩进的速度与滑动屏幕的速度成正比(这样体验会好,慢慢转动相机,遇到视觉阻挡时,正常缩进动画,当快速滑动屏幕时,就需要快速缩进相机,以防止视线穿墙)
方案:
1:射线检测,因为需要考虑各种地形的角度,所以射线检测方案修改成,视椎体射线检测方案,请看函数NearClipPlanePoints。(相机视口的4个点分别向目标点打射线进行检测,碰撞点取距离角色最近的)
2:相机提前预检测,提前计算下一帧相机碰撞,再相机被遮挡实际发生前,进行预测。
3:相机终点调整,使用最优碰撞点进行调整,调整算法考虑到相机视口宽度,使相视口机完全避免边界与碰撞点穿插。
4:相机终点确认后,对相机进行平滑过渡动画的方式往目标点移动。
5:平滑过渡速率和触摸拖动角度变化速率成正比,防止滑动太大穿墙,也增加体验。
实现参考图:
如下图,椎体为四条碰撞检测射线:
穿墙后,相机进行调整以后的截图:
实现效果:参考 万王之王,一样的效果。
代码实现:代码中关键步骤已经有注释,核心函数是CameraMovement。
1 using UnityEngine; 2 using System.Collections; 3 using System.Collections.Generic; 4 using Invector; 5 using System; 6 public class vThirdPersonCamera : MonoBehaviour 7 { 8 public enum CameraState 9 { 10 Drag, 11 Auto, 12 AutoFixY, 13 } 14 public static vThirdPersonCamera Create() 15 { 16 var go = new GameObject("vThirdPersonCamera"); 17 vThirdPersonCamera comp = go.AddComponent<vThirdPersonCamera>(); 18 GameObject.DontDestroyOnLoad(go); 19 return comp; 20 } 21 #region inspector properties 22 [Tooltip("Lerp speed between Camera States")] 23 public float smoothCameraRotation = 12f; 24 public float smoothCameraRotationAuto = 1; 25 private float m_currentCameraRotation; 26 [Tooltip("What layer will be culled")] 27 public LayerMask cullingLayer = 1 << 0;//碰撞检测用的layer,请根据自己游戏进行设置。 28 public float rightOffset = 0f; 29 public float defaultDistance = 12.5f; 30 public float height = 2f; 31 private float defaultHeight = 2f; 32 public float smoothFollow = 5f; 33 public float cameraAutoRotationSensitivity = 3f; 34 public float xMouseSensitivity = 0.2f;//3f; 35 public float yMouseSensitivity = 0.2f;//3f; 36 public float yMinLimit = -70; 37 public float yMaxLimit = 80f; 38 public const float MinDistance = 0f; 39 public const float MaxDistance = 35; 40 public float currentDistance = 0; 41 public float zoomSensitivity = 0.05f; 42 public Transform cameraTransform; 43 //public Transform otherTargetTransform;//相机动画锁定目标Trans 44 #endregion 45 #region hide properties 46 [HideInInspector] 47 public float offSetPlayerPivot; 48 //[HideInInspector] 49 [HideInInspector] 50 public Vector2 movementSpeed; 51 private Vector3 currentPlayerPos; 52 private Vector3 currentPlayerDirection; 53 private Vector3 currentPlayerDirectionRight; 54 private Transform targetLookAt; //目标观察点 55 private Transform nextTargetLookAt;//目标观察点 56 private Vector3 currentTargetPos; 57 private Vector3 lookPoint; 58 private Vector3 nextLookPoint; 59 private Vector3 current_cPos; 60 private Vector3 desired_cPos; 61 private Vector3 nextTargetCameraPos = Vector3.zero; 62 private Vector3 nextCameraPos = Vector3.zero; 63 private Camera _camera; 64 private float distance = 0f; 65 private float mouseY = 0f; 66 private float mouseX = 0f; 67 private float currentHeight; 68 private float cullingDistance; 69 //private float checkHeightRadius = 0.4f; 70 private float clipPlaneMargin = 0f; 71 private float forward = -1f; 72 private float xMinLimit = -360f; 73 private float xMaxLimit = 360f; 74 private RaycastHit hitInfo; 75 private float distanceTime = 0f; 76 private float curSmoothFollow = 5f; 77 private CameraState mState = CameraState.Drag; 78 private float clipOffset = 1f;//相机视口区域射线检测区扩大 79 public CameraState State 80 { 81 get 82 { 83 return mState; 84 } 85 set 86 { 87 if (value == mState) 88 return; 89 mState = value; 90 UpdateState(); 91 } 92 } 93 #endregion 94 #region 相机基础功能函数区-------------------------------------------------------------------------------- 95 private void UpdateState() 96 { 97 switch (mState) 98 { 99 case CameraState.Auto: 100 m_currentCameraRotation = smoothCameraRotationAuto; 101 break; 102 case CameraState.AutoFixY: 103 break; 104 default: 105 m_currentCameraRotation = smoothCameraRotation; 106 break; 107 } 108 } 109 public float GetMouseX() 110 { 111 return mouseX; 112 } 113 public float GetMouseY() 114 { 115 return mouseY; 116 } 117 public void SetRotateY(float targetX) 118 { 119 mouseX = targetX; 120 } 121 public void Release() 122 { 123 if (targetLookAt != null) 124 { 125 GameObject.Destroy(targetLookAt); 126 targetLookAt = null; 127 } 128 if (nextTargetLookAt != null) 129 { 130 GameObject.Destroy(nextTargetLookAt); 131 nextTargetLookAt = null; 132 } 133 } 134 public void Init() 135 { 136 cullingLayer = 1 << (int)PublicConst.E_Layer.Layer_Road; 137 currentTargetPos = new Vector3(currentPlayerPos.x, currentPlayerPos.y + offSetPlayerPivot, currentPlayerPos.z); 138 if (targetLookAt == null) 139 { 140 targetLookAt = new GameObject("targetLookAt").transform; 141 GameObject.DontDestroyOnLoad(targetLookAt); 142 } 143 if (nextTargetLookAt == null) 144 { 145 nextTargetLookAt = new GameObject("nextTargetLookAt").transform; 146 GameObject.DontDestroyOnLoad(nextTargetLookAt); 147 } 148 targetLookAt.position = currentTargetPos; 149 targetLookAt.rotation = Quaternion.Euler(mouseY, mouseX, 0); 150 nextTargetLookAt.position = currentTargetPos; 151 nextTargetLookAt.rotation = Quaternion.Euler(mouseY, mouseX, 0); 152 defaultDistance = Mathf.Clamp(defaultDistance, MinDistance, MaxDistance); 153 if (State == CameraState.AutoFixY) 154 { 155 currentDistance = distance = camera2DDistance; 156 } 157 else 158 { 159 currentDistance = distance = defaultDistance; 160 } 161 currentHeight = height; 162 UpdateState(); 163 } 164 public void SetCamera(Camera cam, Transform cameraRootTrans, int xRot, int yRot) 165 { 166 mouseY = xRot; 167 mouseX = yRot; 168 _camera = cam; 169 cameraTransform = cameraRootTrans; 170 Init(); 171 } 172 /// <summary> 173 /// 相机看向角色的高度值,目的是看向人脸 174 /// 1:不同高度的角色,需要配置这个值 175 /// 2:在坐骑上 高度 = 坐骑坐垫高度 + 角色高度*0.5 176 /// </summary> 177 /// <param name="_height">高度 (单位 米)</param> 178 public void SetHeight(float pHeight) 179 { 180 if(pHeight <= 0) 181 { 182 LiteCommon.BLog.E("相机中角色高度设置错误! 值<=0!"); 183 height = defaultHeight; 184 return; 185 } 186 height = pHeight; 187 } 188 /// <summary> 189 /// 相机在lateUpdate中的持续更新 190 /// </summary> 191 public void UpdatePosition() 192 { 193 CameraMovement(); 194 } 195 /// <summary> 196 /// 持续缩进相机距离(触控滑动交互) 197 /// </summary> 198 /// <param name="delta"></param> 199 public void UpdateZoomFactor(float delta) 200 { 201 currentDistance -= delta * zoomSensitivity; 202 currentDistance = Mathf.Clamp(currentDistance, MinDistance, MaxDistance); 203 } 204 /// <summary> 205 /// 持续旋转相机角度(触控滑动交互) 206 /// </summary> 207 /// <param name="delta"></param> 208 public void UpdateRotate(Vector2 delta) 209 { 210 RotateCamera(delta.x, delta.y); 211 } 212 /// <summary> 213 /// 设置相机到固定位置 214 /// </summary> 215 /// <param name="_fixedAngleX">水平角度</param> 216 /// <param name="_fixedAngleY">垂直角度</param> 217 /// <param name="distance">相机对目标位置距离</param> 218 public void SetCameraPos(float _fixedAngleX, float _fixedAngleY, float distance) 219 { 220 mouseX = _fixedAngleX; 221 mouseY = _fixedAngleY; 222 currentDistance = distance; 223 } 224 /// <summary> 225 /// 更新角色位置 226 /// </summary> 227 /// <param name="pos"></param> 228 public void UpdateRolePosition(Vector3 pos) 229 { 230 currentPlayerPos = pos; 231 if (isLookingToOtherTarget) 232 { 233 if (!isAutoLookingTargetSmooth && otherlookTarget != null) 234 { 235 //Debug.Log("---->UpdateRolePosition otherlookTarget!"); 236 currentTargetPos = otherlookTarget.position; 237 } 238 } 239 else 240 { 241 currentTargetPos = currentPlayerPos; 242 } 243 } 244 /// <summary> 245 /// 跟新相机目标对象的方向 246 /// </summary> 247 /// <param name="directionForward"></param> 248 public void UpdateRoleDirection(Vector3 directionForward) 249 { 250 currentPlayerDirection = directionForward; 251 currentPlayerDirectionRight = Vector3.Cross(directionForward, Vector3.up); 252 } 253 /// <summary> 254 /// Camera Update 实时刷新相机方位,并且避免碰撞 255 /// </summary> 256 void CameraMovement() 257 { 258 if (_camera == null) 259 return; 260 //next position and direction 261 //distance = Mathf.Lerp(distance, currentDistance, smoothFollow * Time.deltaTime); 262 //cullingDistance = Mathf.Lerp(cullingDistance, distance, Time.deltaTime); 263 var camDir = (forward * targetLookAt.forward); 264 camDir.Normalize(); 265 var targetPos = new Vector3(currentTargetPos.x, currentTargetPos.y + offSetPlayerPivot, currentTargetPos.z); 266 currentTargetPos = targetPos; 267 //look direction 268 lookPoint = desired_cPos + targetLookAt.forward * 2f; 269 lookPoint += (targetLookAt.right * Vector3.Dot(camDir * (distance), targetLookAt.right)); 270 //set current real looking height p;osition 271 currentHeight = height; 272 desired_cPos = targetPos + new Vector3(0, height, 0); 273 current_cPos = currentTargetPos + new Vector3(0, currentHeight, 0); 274 //next look Target 275 nextTargetLookAt.position = desired_cPos; 276 Quaternion newNextRot = Quaternion.Euler(mouseY, mouseX, 0); 277 nextTargetLookAt.rotation = newNextRot; 278 //next camera direction 279 var nextCamDir = (forward * nextTargetLookAt.forward); 280 nextCamDir.Normalize(); 281 //next look direction 282 nextLookPoint = desired_cPos + nextTargetLookAt.forward * 2f; 283 nextLookPoint += (nextTargetLookAt.right * Vector3.Dot(nextCamDir * (currentDistance), nextTargetLookAt.right)); 284 nextCameraPos = desired_cPos + (nextCamDir * currentDistance); 285 //calculate next ClipPlanePoints 286 ClipPlanePoints nextPlanePoints = _camera.NearClipPlanePoints(nextCameraPos, clipPlaneMargin, clipOffset); 287 //if hit not success, then next target camera pos --> current camera pos 288 //else calculate next target camera pos 289 if (CullingRayCast(desired_cPos, nextPlanePoints, out hitInfo, currentDistance, cullingLayer, Color.cyan)) 290 { 291 ///处理相机缩进和穿墙 292 Vector3 nextPlayerToHitVec = desired_cPos - hitInfo.point; 293 //下一幀和当前帧的弧度,弧度大小可以用来衡量变化快慢,这个快慢将会用来调节缩进速率 294 float angleRad = Mathf.PI / 2 - Mathf.Acos(Vector3.Dot((-nextPlayerToHitVec).normalized, hitInfo.normal)); 295 //换算相机侧翼宽度为相机到目标位置方向上的距离,用来避免相机侧翼太近导致的穿墙。 296 //clipOffset * 0.9f 参数 0.9 使 297 float length = Mathf.Abs((nextPlanePoints.width- clipOffset * 0.9f) / Mathf.Tan(angleRad)); 298 float hitLenth = nextPlayerToHitVec.magnitude; 299 if (length > hitLenth) 300 { 301 length = hitLenth; 302 } 303 ClipPlanePoints planePoints = _camera.NearClipPlanePoints(cameraTransform.position, clipPlaneMargin, 1f); 304 //根据下一个相机位置的弧度,决定相机缩进动画的速率 305 if (CullingViewRayCast(desired_cPos, planePoints, out hitInfo, distance + 0.2f, cullingLayer, Color.cyan)) 306 { 307 curSmoothFollow = smoothFollow * Mathf.Abs(angleRad) * 40f / Mathf.PI; 308 } 309 else 310 { 311 curSmoothFollow = smoothFollow; 312 } 313 //调整相机位置,避免相机边缘发生穿墙 314 Vector3 targetCameraVector = nextPlayerToHitVec - nextPlayerToHitVec.normalized * length; 315 cullingDistance = targetCameraVector.magnitude; 316 cullingDistance = Mathf.Clamp(cullingDistance, 0f, currentDistance); 317 distance = Mathf.Lerp(distance, cullingDistance, curSmoothFollow * Time.deltaTime); 318 distance = Mathf.Clamp(distance, MinDistance, MaxDistance); 319 distanceTime += curSmoothFollow * Time.deltaTime; 320 Vector3 camDirComp = Vector3.Lerp(camDir, nextCamDir, distanceTime); 321 cameraTransform.position = desired_cPos + (camDirComp * distance); 322 cameraTransform.rotation = Quaternion.LookRotation((desired_cPos) - cameraTransform.position); 323 } 324 else 325 { 326 distanceTime = 0; 327 distance = Mathf.Lerp(distance, currentDistance, smoothFollow * Time.deltaTime); 328 targetLookAt.position = desired_cPos; 329 targetLookAt.rotation = Quaternion.Euler(mouseY, mouseX, 0); 330 Vector3 desiredCameraPos = desired_cPos + (forward * targetLookAt.forward * distance); 331 ClipPlanePoints planePoints = _camera.NearClipPlanePoints(desiredCameraPos, clipPlaneMargin, 1); 332 if (!CullingViewRayCast(desired_cPos, planePoints, out hitInfo, distance + 0.2f, cullingLayer, Color.cyan)) 333 { 334 cameraTransform.position = desiredCameraPos; 335 cameraTransform.rotation = Quaternion.LookRotation((desired_cPos) - cameraTransform.position); 336 } 337 } 338 Debug.DrawLine(desired_cPos, nextPlanePoints.LowerLeft, Color.red); 339 Debug.DrawLine(desired_cPos, nextPlanePoints.LowerRight, Color.red); 340 Debug.DrawLine(desired_cPos, nextPlanePoints.UpperRight, Color.red); 341 Debug.DrawLine(desired_cPos, nextPlanePoints.UpperLeft, Color.red); 342 Debug.DrawLine(nextTargetLookAt.position, nextTargetLookAt.position + nextTargetLookAt.forward, Color.blue); 343 Debug.DrawLine(targetLookAt.position, targetLookAt.position + targetLookAt.forward, Color.green); 344 movementSpeed = Vector2.zero; 345 } 346 /// <summary> 347 /// 视口投射射线检测 348 /// </summary> 349 /// <param name="from">起点</param> 350 /// <param name="_to">目标点</param> 351 /// <param name="hitInfo">RaycastHit</param> 352 /// <param name="distance">距离</param> 353 /// <param name="cullingLayer">裁剪层</param> 354 /// <param name="color">调试颜色</param> 355 /// <returns></returns> 356 bool CullingViewRayCast(Vector3 from, ClipPlanePoints _to, out RaycastHit hitInfo, float distance, LayerMask cullingLayer, Color color) 357 { 358 bool value = false; 359 if (Physics.Raycast(from, _to.LowerRight - from, out hitInfo, distance, cullingLayer)) 360 { 361 value = true; 362 } 363 else if (Physics.Raycast(from, _to.LowerLeft - from, out hitInfo, distance, cullingLayer)) 364 { 365 value = true; 366 } 367 else if (Physics.Raycast(from, _to.UpperRight - from, out hitInfo, distance, cullingLayer)) 368 { 369 value = true; 370 } 371 else if (Physics.Raycast(from, _to.UpperLeft - from, out hitInfo, distance, cullingLayer)) 372 { 373 value = true; 374 } 375 return value; 376 } 377 /// <summary> 378 ///获取最远点检测点离想几点最远位置进行检测 379 ///判断相机在视口边缘坐标的最远点 380 /// </summary> 381 /// <param name="_to"></param> 382 /// <param name="from"></param> 383 /// <param name="hitInfo"></param> 384 /// <param name="distance"></param> 385 /// <param name="cullingLayer"></param> 386 /// <returns></returns> 387 bool CullingRayCast(Vector3 from, ClipPlanePoints _to, out RaycastHit hitInfo, float distance, LayerMask cullingLayer, Color color) 388 { 389 bool value = false; 390 RaycastHit curHitInfo; 391 if (Physics.Raycast(from, _to.LowerRight - from, out hitInfo, distance, cullingLayer)) 392 { 393 value = true; 394 } 395 if (Physics.Raycast(from, _to.LowerLeft - from, out curHitInfo, distance, cullingLayer)) 396 { 397 if (value) 398 { 399 if (hitInfo.distance > curHitInfo.distance) 400 hitInfo = curHitInfo; 401 } 402 else 403 { 404 hitInfo = curHitInfo; 405 } 406 value = true; 407 } 408 if (Physics.Raycast(from, _to.UpperRight - from, out curHitInfo, distance, cullingLayer)) 409 { 410 if (value) 411 { 412 if (hitInfo.distance > curHitInfo.distance) 413 hitInfo = curHitInfo; 414 } 415 else 416 { 417 hitInfo = curHitInfo; 418 } 419 value = true; 420 } 421 if (Physics.Raycast(from, _to.UpperLeft - from, out curHitInfo, distance, cullingLayer)) 422 { 423 if (value) 424 { 425 if (hitInfo.distance > curHitInfo.distance) 426 hitInfo = curHitInfo; 427 } 428 else 429 { 430 hitInfo = curHitInfo; 431 } 432 value = true; 433 } 434 return value; 435 } 436 #endregion 相机基础功能区 End-------------------------------------------------------------------------------- 437 } 438 439 //视口信息数据类 440 public static class vExtensions 441 { 442 public static T[] Append<T>(this T[] arrayInitial, T[] arrayToAppend) 443 { 444 if (arrayToAppend == null) 445 { 446 throw new ArgumentNullException("The appended object cannot be null"); 447 } 448 if ((arrayInitial is string) || (arrayToAppend is string)) 449 { 450 throw new ArgumentException("The argument must be an enumerable"); 451 } 452 T[] ret = new T[arrayInitial.Length + arrayToAppend.Length]; 453 arrayInitial.CopyTo(ret, 0); 454 arrayToAppend.CopyTo(ret, arrayInitial.Length); 455 return ret; 456 } 457 /// <summary> 458 /// Normalized the angle. between -180 and 180 degrees 459 /// </summary> 460 /// <param Name="eulerAngle">Euler angle.</param> 461 public static Vector3 NormalizeAngle(this Vector3 eulerAngle) 462 { 463 var delta = eulerAngle; 464 if (delta.x > 180) delta.x -= 360; 465 else if (delta.x < -180) delta.x += 360; 466 if (delta.y > 180) delta.y -= 360; 467 else if (delta.y < -180) delta.y += 360; 468 if (delta.z > 180) delta.z -= 360; 469 else if (delta.z < -180) delta.z += 360; 470 return new Vector3(delta.x, delta.y, delta.z);//round values to angle; 471 } 472 public static Vector3 Difference(this Vector3 vector, Vector3 otherVector) 473 { 474 return otherVector - vector; 475 } 476 public static void SetActiveChildren(this GameObject gameObjet, bool value) 477 { 478 foreach (Transform child in gameObjet.transform) 479 child.gameObject.SetActive(value); 480 } 481 public static void SetLayerRecursively(this GameObject obj, int layer) 482 { 483 obj.layer = layer; 484 foreach (Transform child in obj.transform) 485 { 486 child.gameObject.SetLayerRecursively(layer); 487 } 488 } 489 public static float ClampAngle(float angle, float min, float max) 490 { 491 do 492 { 493 if (angle < -360) 494 angle += 360; 495 if (angle > 360) 496 angle -= 360; 497 } while (angle < -360 || angle > 360); 498 return Mathf.Clamp(angle, min, max); 499 } 500 public static ClipPlanePoints NearClipPlanePoints(this Camera camera, Vector3 pos, float clipPlaneMargin, float clipOffset = 0f) 501 { 502 var clipPlanePoints = new ClipPlanePoints(); 503 clipPlanePoints.center = pos; 504 var transform = camera.transform; 505 var halfFOV = (camera.fieldOfView / 2) * Mathf.Deg2Rad; 506 var aspect = camera.aspect; 507 var distance = camera.nearClipPlane; 508 var height = distance * Mathf.Tan(halfFOV); 509 var width = height * aspect; 510 height *= 1 + clipPlaneMargin; 511 width *= 1 + clipPlaneMargin; 512 clipPlanePoints.width = width * 2; 513 clipPlanePoints.heigth = height * 2; 514 height += clipOffset; 515 width += clipOffset; 516 clipPlanePoints.Right = pos + transform.right * width; 517 clipPlanePoints.Left = pos - transform.right * width; 518 clipPlanePoints.Top = pos + transform.up * height; 519 clipPlanePoints.Lower = pos - transform.up * height; 520 //todo:will delete 521 clipPlanePoints.LowerRight = pos + transform.right * width; 522 clipPlanePoints.LowerRight -= transform.up * height; 523 clipPlanePoints.LowerRight += transform.forward * distance; 524 clipPlanePoints.LowerLeft = pos - transform.right * width; 525 clipPlanePoints.LowerLeft -= transform.up * height; 526 clipPlanePoints.LowerLeft += transform.forward * distance; 527 clipPlanePoints.UpperRight = pos + transform.right * width; 528 clipPlanePoints.UpperRight += transform.up * height; 529 clipPlanePoints.UpperRight += transform.forward * distance; 530 clipPlanePoints.UpperLeft = pos - transform.right * width; 531 clipPlanePoints.UpperLeft += transform.up * height; 532 clipPlanePoints.UpperLeft += transform.forward * distance; 533 return clipPlanePoints; 534 } 535 536 public static BoxPoint GetBoxPoint(this BoxCollider boxCollider) 537 { 538 BoxPoint bp = new BoxPoint(); 539 bp.center = boxCollider.transform.TransformPoint(boxCollider.center) ; 540 var height = boxCollider.transform.lossyScale.y * boxCollider.size.y; 541 var ray = new Ray(bp.center, boxCollider.transform.up); 542 543 bp.top = ray.GetPoint((height * 0.5f)); 544 bp.bottom = ray.GetPoint(-(height * 0.5f)); 545 546 return bp; 547 } 548 public static Vector3 BoxSize(this BoxCollider boxCollider) 549 { 550 var length = boxCollider.transform.lossyScale.x * boxCollider.size.x; 551 var width = boxCollider.transform.lossyScale.z * boxCollider.size.z; 552 var height = boxCollider.transform.lossyScale.y * boxCollider.size.y; 553 return new Vector3(length, height, width); 554 } 555 public static bool Contains(this Enum keys, Enum flag) 556 { 557 if (keys.GetType() != flag.GetType()) 558 throw new ArgumentException("Type Mismatch"); 559 return (Convert.ToUInt64(keys) & Convert.ToUInt64(flag)) != 0; 560 } 561 562 } 563 public struct BoxPoint 564 { 565 public Vector3 top; 566 public Vector3 center; 567 public Vector3 bottom; 568 569 } 570 public struct ClipPlanePoints 571 { 572 public Vector3 UpperLeft; 573 public Vector3 UpperRight; 574 public Vector3 LowerLeft; 575 public Vector3 LowerRight; 576 public Vector3 center; 577 public Vector3 Right; 578 public Vector3 Left; 579 public Vector3 Top; 580 public Vector3 Lower; 581 public float width; 582 public float heigth; 583 } 584 }