【原】3D游戏中的算法--MMO游戏全视⾓3D相机,全地形中视觉障
碍物⾃动缩进调整
⼤家好,欢迎⼤家多多交流,转载请注明出处,谢谢。
我来博客园的第⼀篇博客,想写的东西很多,基于⽬前⼯作⼿⾥的内容,先上⼀篇算法的。
上周做了个3D⾃由相机功能,需要对地形和墙壁进⾏⾃动缩进动画,效果经过多款游戏对⽐,决定编写《万王之王》的相机效果。
我发现很多同学,基础知识都不错,但是⾃⼰写的话,却不知道怎么去实现具体算法和⽅案,其实这种情况不⽤着急,养成好的需求分析习惯会帮助你提前解决很多难题。但是需求分析再好,对于算法来说,基础知识的掌握更加重要。
所以下⾯⼏个步骤是解决问题的流程。
第⼀步:⾸先就是⾃⾝基础了,关于3d向量的掌握,必须达到⼀定层次,否则很难处理好这种复杂环境下的算法策略,如果算法不好的,接着看,或许能让你有所提⾼。
第⼆步:其次就是对于相机功能的需求分析,需求分析是通过游戏还有和游戏策划沟通的结果,需求分析的准确与否决定了我们最终的结果,否则需求分析的错误或者不明确会带来代码策略的错误,影响⼯期。
第三步:很多同学到这⾥可能以为要开始动⼿写代码了,不着急,这⾥建议⼤家先整理游戏中相关流程,确定⾃⼰⽅案可⾏性,然后编写伪代码或者⽅案流程。伪代码能提前实验逻辑的可⾏性和纰漏,总览全局,避免实际编写代码过程中的盲⽬或者⽚⾯引起的时间成本。
第四步:接下来可以开始编写你的代码了。
相机涉及数学知识:
涉及的数学知识不多,作为游戏开发⼈员,这些知识点事必备技能,如果有不熟悉或者不知道的,⼀定要及时补充知识。
1:主要是对3d空间的理解,空间点,空间向量,空间⾓度的概念。
2:熟悉3D向量相关计算,余弦公式,⾓度计算,三⾓函数,向量投影计算和物理意义
3:熟悉笛卡尔坐标系,熟悉点乘积和叉乘积计算和物理意思,知道右⼿定则,平⾯法线等知识。
相机需求:
1:3D⾃由相机通⽤功能,滑动屏幕可以随意调整⽅向和缩进显⽰距离。
2:在1基础上对相机进⾏遮挡碰撞处理,相机和⾓⾊中间有阻挡时相机⾃动越过障碍物(渐进运动⽅式),并且保持相机视域内不在物体内部(穿墙情况)。
3:相机离开遮挡的时候回复原来⾃由相机设置距离,并且动态回复到合适位置。
4:对任意形状的阻挡(墙⾯,地⾯,任意⾓度斜坡)进⾏处理后不穿强。
5:发⽣视觉遮挡时,缩进的速度与滑动屏幕的速度成正⽐(这样体验会好,慢慢转动相机,遇到视觉阻挡时,正常缩进动画,当快速滑动屏幕时,就需要快速缩进相机,以防⽌视线穿墙)
⽅案:
1:射线检测,因为需要考虑各种地形的⾓度,所以射线检测⽅案修改成,视椎体射线检测⽅案,请看函数NearClipPlanePoints。(相机视⼝的4个点分别向⽬标点打射线进⾏检测,碰撞点取距离⾓⾊最近的)
2:相机提前预检测,提前计算下⼀帧相机碰撞,再相机被遮挡实际发⽣前,进⾏预测。
3:相机终点调整,使⽤最优碰撞点进⾏调整,调整算法考虑到相机视⼝宽度,使相视⼝机完全避免边界与碰撞点穿插。
4:相机终点确认后,对相机进⾏平滑过渡动画的⽅式往⽬标点移动。
英语六级答案5:平滑过渡速率和触摸拖动⾓度变化速率成正⽐,防⽌滑动太⼤穿墙,也增加体验。
实现参考图:
如下图,椎体为四条碰撞检测射线:
穿墙后,相机进⾏调整以后的截图:
实现效果:参考万王之王,⼀样的效果。
代码实现:代码中关键步骤已经有注释,核⼼函数是CameraMovement。 1using UnityEngine;
2using System.Collections;
3using System.Collections.Generic;
4using Invector;
5using System;
6public class vThirdPersonCamera : MonoBehaviour
7 {
cdc是什么缩写
8public enum CameraState
9 {
10 Drag,
11 Auto,
12 AutoFixY,
13 }
14public static vThirdPersonCamera Create()
15 {
16var go = new GameObject("vThirdPersonCamera");
17 vThirdPersonCamera comp = go.AddComponent<vThirdPersonCamera>();
18 GameObject.DontDestroyOnLoad(go);
19return comp;
21#region inspector properties
22 [Tooltip("Lerp speed between Camera States")]
23public float smoothCameraRotation = 12f;
24public float smoothCameraRotationAuto = 1;
25private float m_currentCameraRotation;
26 [Tooltip("What layer will be culled")]
27public LayerMask cullingLayer = 1 << 0;//碰撞检测⽤的layer,请根据⾃⼰游戏进⾏设置。 28public float rightOfft = 0f;
29public float defaultDistance = 12.5f;
30public float height = 2f;
31private float defaultHeight = 2f;
32public float smoothFollow = 5f;
33public float cameraAutoRotationSensitivity = 3f;
34public float xMouSensitivity = 0.2f;//3f;
35public float yMouSensitivity = 0.2f;//3f;
36public float yMinLimit = -70;
37public float yMaxLimit = 80f;
38public const float MinDistance = 0f;
39public const float MaxDistance = 35;
40public float currentDistance = 0;
41public float zoomSensitivity = 0.05f;
42public Transform cameraTransform;
43//public Transform otherTargetTransform;//相机动画锁定⽬标Trans
44#endregion
45#region hide properties
46 [HideInInspector]
47public float offSetPlayerPivot;
48//[HideInInspector]
49 [HideInInspector]
50public Vector2 movementSpeed;
51private Vector3 currentPlayerPos;
52private Vector3 currentPlayerDirection;
53private Vector3 currentPlayerDirectionRight;
54private Transform targetLookAt; //⽬标观察点
55private Transform nextTargetLookAt;//⽬标观察点
56private Vector3 currentTargetPos;
57private Vector3 lookPoint;
58private Vector3 nextLookPoint;
59private Vector3 current_cPos;
60private Vector3 desired_cPos;
61private Vector3 nextTargetCameraPos = ;
62private Vector3 nextCameraPos = ;
63private Camera _camera;
64private float distance = 0f;
65private float mouY = 0f;
66private float mouX = 0f;
67private float currentHeight;
68private float cullingDistance;
69//private float checkHeightRadius = 0.4f;
70private float clipPlaneMargin = 0f;
71private float forward = -1f;
72private float xMinLimit = -360f;
73private float xMaxLimit = 360f;
74private RaycastHit hitInfo;
75private float distanceTime = 0f;
76private float curSmoothFollow = 5f;
77private CameraState mState = CameraState.Drag;
78private float clipOfft = 1f;//相机视⼝区域射线检测区扩⼤
79public CameraState State
80 {
81get
82 {
83return mState;
84 }
85t
86 {
87if (value == mState)
88return;
89 mState = value;
90 UpdateState();
91 }
92 }
93#endregion
94#region相机基础功能函数区--------------------------------------------------------------------------------
95private void UpdateState()
96 {
97switch (mState)
98 {
99ca CameraState.Auto:
100 m_currentCameraRotation = smoothCameraRotationAuto;
101break;
102ca CameraState.AutoFixY:
103break;
104default:
105 m_currentCameraRotation = smoothCameraRotation;
106break;
107 }
108 }
109public float GetMouX()
111return mouX;
112 }
113public float GetMouY()
郑中基与阿sa114 {
115return mouY;
116 }
117public void SetRotateY(float targetX)
118 {
119 mouX = targetX;
120 }
121public void Relea()
122 {
123if (targetLookAt != null)
124 {
125 GameObject.Destroy(targetLookAt);
126 targetLookAt = null;
127 }
128if (nextTargetLookAt != null)
129 {
130 GameObject.Destroy(nextTargetLookAt);
131 nextTargetLookAt = null;
谷歌是什么
132 }
133 }
134public void Init()
135 {
136 cullingLayer = 1 << (int)PublicConst.E_Layer.Layer_Road;
137 currentTargetPos = new Vector3(currentPlayerPos.x, currentPlayerPos.y + offSetPlayerPivot, currentPlayerPos.z); 138if (targetLookAt == null)
139 {
140 targetLookAt = new GameObject("targetLookAt").transform;
141 GameObject.DontDestroyOnLoad(targetLookAt);
142 }
143if (nextTargetLookAt == null)
144 {
145 nextTargetLookAt = new GameObject("nextTargetLookAt").transform;
146 GameObject.DontDestroyOnLoad(nextTargetLookAt);
147 }
148 targetLookAt.position = currentTargetPos;
149 ation = Quaternion.Euler(mouY, mouX, 0);
150 nextTargetLookAt.position = currentTargetPos;
151 ation = Quaternion.Euler(mouY, mouX, 0);
152 defaultDistance = Mathf.Clamp(defaultDistance, MinDistance, MaxDistance);
153if (State == CameraState.AutoFixY)
154 {
155 currentDistance = distance = camera2DDistance;
156 }
157el
158 {
159 currentDistance = distance = defaultDistance;
160 }
161 currentHeight = height;
162 UpdateState();
dove是什么意思163 }
164public void SetCamera(Camera cam, Transform cameraRootTrans, int xRot, int yRot)
165 {
166 mouY = xRot;
167 mouX = 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>
178public void SetHeight(float pHeight)
179 {
180if(pHeight <= 0)
181 {
182 LiteCommon.BLog.E("相机中⾓⾊⾼度设置错误!值<=0!");
183 height = defaultHeight;
184return;
185 }
186 height = pHeight;
187 }
188///<summary>
189///相机在lateUpdate中的持续更新
190///</summary>
191public void UpdatePosition()
192 {
193 CameraMovement();
194 }
195///<summary>
196///持续缩进相机距离(触控滑动交互)
197///</summary>
198///<param name="delta"></param>
199public void UpdateZoomFactor(float delta)
201 currentDistance -= delta * zoomSensitivity;
202 currentDistance = Mathf.Clamp(currentDistance, MinDistance, MaxDistance);
203 }
204///<summary>
205///持续旋转相机⾓度(触控滑动交互)
206///</summary>
207///<param name="delta"></param>
208public 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>
218public void SetCameraPos(float _fixedAngleX, float _fixedAngleY, float distance)
219 {
220 mouX = _fixedAngleX;
marshal
221 mouY = _fixedAngleY;
222 currentDistance = distance;
223 }
224///<summary>
225///更新⾓⾊位置
226///</summary>
227///<param name="pos"></param>
228public void UpdateRolePosition(Vector3 pos)
229 {
230 currentPlayerPos = pos;
231if (isLookingToOtherTarget)
232 {
frequency是什么意思>domino harvey233if (!isAutoLookingTargetSmooth && otherlookTarget != null)
234 {
235//Debug.Log("---->UpdateRolePosition otherlookTarget!");
236 currentTargetPos = otherlookTarget.position;
237 }
238 }
239el
240 {
241 currentTargetPos = currentPlayerPos;
242 }
243 }
244///<summary>
245///跟新相机⽬标对象的⽅向
246///</summary>
247///<param name="directionForward"></param>
248public void UpdateRoleDirection(Vector3 directionForward)
249 {
250 currentPlayerDirection = directionForward;
251 currentPlayerDirectionRight = Vector3.Cross(directionForward, Vector3.up);
252 }
253///<summary>
254/// Camera Update 实时刷新相机⽅位,并且避免碰撞
255///</summary>
256void CameraMovement()
257 {
258if (_camera == null)
259return;
260//next position and direction
is anyone up
261//distance = Mathf.Lerp(distance, currentDistance, smoothFollow * Time.deltaTime);
262//cullingDistance = Mathf.Lerp(cullingDistance, distance, Time.deltaTime);
263var camDir = (forward * targetLookAt.forward);
264 camDir.Normalize();
265var 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//t current real looking height p;osition
271 currentHeight = height;
referrals272 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(mouY, mouX, 0);
277 ation = newNextRot;
278//next camera direction
279var 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, clipOfft); 287//if hit not success, then next target camera pos --> current camera pos
288//el calculate next target camera pos
289if (CullingRayCast(desired_cPos, nextPlanePoints, out hitInfo, currentDistance, cullingLayer, an))