当摄像机处于跟随主角状态时,那么主角移动后很有可能摄像机被别的模型挡住。这样用户体验就非常不好,一般3D游戏在处理视角的时候有两种方法,第一种是让被挡住的模型变成透明,第二种是拉近摄像机。前者时候固定视角游戏使用,后者适合变化视角游戏使用。两个我们都学一学蛤蛤。
如下图所示,MOMO写了一个简单的例子,通过鼠标或触摸来牵引主角向不同的角度移动,旁边有一面墙当主角被这面墙挡住,此时摄像机将拉近。
首先,为了方面鼠标与移动平台上的触摸同时相应我写了一个通用的方法。
包括 开始触摸 触摸中 结束触摸 ,电脑手机都可以用这个方法相应到。
JFConst.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
using UnityEngine; using System.Collections; public class JFConst { public static bool TouchBegin() { if(Input.GetMouseButtonDown(0)) { return true; } if(Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began) { return true; } return false; } public static bool TouchEnd() { if(Input.GetMouseButtonUp(0)) { return true; } if(Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended) { return true; } return false; } public static bool TouchIng() { if(Input.GetMouseButton(0)) { return true; }else if(Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved) { return true; } return false; } } |
以前写过一篇文章关于移动主角的,如果对移动主角还有不懂的请看这篇 Unity3D研究院之鼠标控制角色移动与奔跑示例(二十四) 。 这里我就不解释移动主角这部分的代码了。 下面是角色控制器ThirdPersonController.cs 绑定在主角身上。
下面代码中需要详细看的就是161行到174行 ,这部分就是根据触摸屏幕的2D坐标计算主角在3D世界中移动的坐标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 |
using UnityEngine; using System.Collections; /** * @Author : www.xuanyusong.com */ [RequireComponent(typeof(CharacterController))] public class ThirdPersonController : MonoBehaviour { public AnimationClip idleAnimation ; public AnimationClip walkAnimation ; public AnimationClip runAnimation ; public AnimationClip jumpPoseAnimation; public float walkMaxAnimationSpeed = 0.75f; public float trotMaxAnimationSpeed = 1.0f; public float runMaxAnimationSpeed = 1.0f; public float jumpAnimationSpeed = 1.15f; public float landAnimationSpeed = 1.0f; private Animation _animation; enum CharacterState { Idle = 0, Walking = 1, Trotting = 2, Running = 3, Jumping = 4, } private CharacterState _characterState; // The speed when walking public float walkSpeed = 2.0f; // after trotAfterSeconds of walking we trot with trotSpeed public float trotSpeed = 4.0f; // when pressing "Fire3" button (cmd) we start running public float runSpeed = 6.0f; public float inAirControlAcceleration = 3.0f; // How high do we jump when pressing jump and letting go immediately public float jumpHeight = 0.5f; // The gravity for the character public float gravity = 20.0f; // The gravity in controlled descent mode public float speedSmoothing = 10.0f; public float rotateSpeed = 500.0f; public float trotAfterSeconds = 3.0f; public bool canJump = true; private float jumpRepeatTime = 0.05f; private float jumpTimeout = 0.15f; private float groundedTimeout = 0.25f; // The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around. private float lockCameraTimer = 0.0f; // The current move direction in x-z private Vector3 moveDirection = Vector3.zero; // The current vertical speed private float verticalSpeed = 0.0f; // The current x-z move speed private float moveSpeed = 0.0f; // The last collision flags returned from controller.Move private CollisionFlags collisionFlags; // Are we jumping? (Initiated with jump button and not grounded yet) private bool jumping = false; private bool jumpingReachedApex = false; // Are we moving backwards (This locks the camera to not do a 180 degree spin) private bool movingBack = false; // Is the user pressing any keys? private bool isMoving = false; // When did the user start walking (Used for going into trot after a while) private float walkTimeStart = 0.0f; // Last time the jump button was clicked down private float lastJumpButtonTime = -10.0f; // Last time we performed a jump private float lastJumpTime = -1.0f; // the height we jumped from (Used to determine for how long to apply extra jump power after jumping.) private float lastJumpStartHeight = 0.0f; private Vector3 inAirVelocity = Vector3.zero; private float lastGroundedTime = 0.0f; private bool isControllable = true; void Awake () { moveDirection = transform.TransformDirection(Vector3.forward); _animation = GetComponent<Animation>(); if(!_animation) Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird."); /* public var idleAnimation : AnimationClip; public var walkAnimation : AnimationClip; public var runAnimation : AnimationClip; public var jumpPoseAnimation : AnimationClip; */ if(!idleAnimation) { _animation = null; Debug.Log("No idle animation found. Turning off animations."); } if(!walkAnimation) { _animation = null; Debug.Log("No walk animation found. Turning off animations."); } if(!runAnimation) { _animation = null; Debug.Log("No run animation found. Turning off animations."); } if(!jumpPoseAnimation && canJump) { _animation = null; Debug.Log("No jump animation found and the character has canJump enabled. Turning off animations."); } } void UpdateSmoothedMovementDirection () { Transform cameraTransform = Camera.main.transform; bool grounded = IsGrounded(); // Forward vector relative to the camera along the x-z plane Vector3 forward = cameraTransform.TransformDirection(Vector3.forward); forward.y = 0; forward = forward.normalized; // Right vector relative to the camera // Always orthogonal to the forward vector Vector3 right = new Vector3(forward.z, 0, -forward.x); float v = Input.GetAxisRaw("Vertical"); float h = Input.GetAxisRaw("Horizontal"); // Are we moving backwards or looking backwards if (v < -0.2f) movingBack = true; else movingBack = false; bool wasMoving = isMoving; isMoving = Mathf.Abs (h) > 0.1f || Mathf.Abs (v) > 0.1f; // Target direction relative to the camera Vector3 targetDirection = h * right + v * forward; if(JFConst.TouchIng()) { Vector3 vpos3 = Camera.main.WorldToScreenPoint(transform.position); Vector2 vpos2 = new Vector2 (vpos3.x,vpos3.y); Vector2 input = new Vector2 (Input.mousePosition.x,Input.mousePosition.y); if(Vector2.Distance(vpos2,input) > 10.0f) { Vector2 normalied = ((vpos2 - input)).normalized; targetDirection = new Vector3(normalied.x,0.0f,normalied.y) ; float y = Camera.main.transform.rotation.eulerAngles.y; targetDirection = Quaternion.Euler(0f,y - 180,0f) * targetDirection; } } // Grounded controls if (grounded) { // Lock camera for short period when transitioning moving & standing still lockCameraTimer += Time.deltaTime; if (isMoving != wasMoving) lockCameraTimer = 0.0f; // We store speed and direction seperately, // so that when the character stands still we still have a valid forward direction // moveDirection is always normalized, and we only update it if there is user input. if (targetDirection != Vector3.zero) { // If we are really slow, just snap to the target direction if (moveSpeed < walkSpeed * 0.9f && grounded) { moveDirection = targetDirection.normalized; } // Otherwise smoothly turn towards it else { moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000); moveDirection = moveDirection.normalized; } } // Smooth the speed based on the current target direction float curSmooth = speedSmoothing * Time.deltaTime; // Choose target speed //* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways float targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f); _characterState = CharacterState.Idle; // Pick speed modifier if (Input.GetKey (KeyCode.LeftShift) | Input.GetKey (KeyCode.RightShift)) { targetSpeed *= runSpeed; _characterState = CharacterState.Running; } else if (Time.time - trotAfterSeconds > walkTimeStart) { targetSpeed *= trotSpeed; _characterState = CharacterState.Trotting; } else { targetSpeed *= walkSpeed; _characterState = CharacterState.Walking; } moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth); // Reset walk time start when we slow down if (moveSpeed < walkSpeed * 0.3f) walkTimeStart = Time.time; } // In air controls else { // Lock camera while in air if (jumping) lockCameraTimer = 0.0f; if (isMoving) inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration; } } void ApplyJumping () { // Prevent jumping too fast after each other if (lastJumpTime + jumpRepeatTime > Time.time) return; if (IsGrounded()) { // Jump // - Only when pressing the button down // - With a timeout so you can press the button slightly before landing if (canJump && Time.time < lastJumpButtonTime + jumpTimeout) { verticalSpeed = CalculateJumpVerticalSpeed (jumpHeight); SendMessage("DidJump", SendMessageOptions.DontRequireReceiver); } } } void ApplyGravity () { if (isControllable) // don't move player at all if not controllable. { // Apply gravity bool jumpButton = Input.GetButton("Jump"); // When we reach the apex of the jump we send out a message if (jumping && !jumpingReachedApex && verticalSpeed <= 0.0f) { jumpingReachedApex = true; SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver); } if (IsGrounded ()) verticalSpeed = 0.0f; else verticalSpeed -= gravity * Time.deltaTime; } } float CalculateJumpVerticalSpeed (float targetJumpHeight) { // From the jump height and gravity we deduce the upwards speed // for the character to reach at the apex. return Mathf.Sqrt(2 * targetJumpHeight * gravity); } void DidJump () { jumping = true; jumpingReachedApex = false; lastJumpTime = Time.time; lastJumpStartHeight = transform.position.y; lastJumpButtonTime = -10; _characterState = CharacterState.Jumping; } void Update() { if (!isControllable) { // kill all inputs if not controllable. Input.ResetInputAxes(); } if (Input.GetButtonDown ("Jump")) { lastJumpButtonTime = Time.time; } UpdateSmoothedMovementDirection(); // Apply gravity // - extra power jump modifies gravity // - controlledDescent mode modifies gravity ApplyGravity (); // Apply jumping logic ApplyJumping (); // Calculate actual motion Vector3 movement = moveDirection * moveSpeed + new Vector3 (0, verticalSpeed, 0) + inAirVelocity; movement *= Time.deltaTime; // Move the controller CharacterController controller = GetComponent<CharacterController>(); collisionFlags = controller.Move(movement); // ANIMATION sector if(_animation) { if(_characterState == CharacterState.Jumping) { if(!jumpingReachedApex) { _animation[jumpPoseAnimation.name].speed = jumpAnimationSpeed; _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever; _animation.CrossFade(jumpPoseAnimation.name); } else { _animation[jumpPoseAnimation.name].speed = -landAnimationSpeed; _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever; _animation.CrossFade(jumpPoseAnimation.name); } } else { if(controller.velocity.sqrMagnitude < 0.1f) { _animation.CrossFade(idleAnimation.name); } else { if(_characterState == CharacterState.Running) { _animation[runAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, runMaxAnimationSpeed); _animation.CrossFade(runAnimation.name); } else if(_characterState == CharacterState.Trotting) { _animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, trotMaxAnimationSpeed); _animation.CrossFade(walkAnimation.name); } else if(_characterState == CharacterState.Walking) { _animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, walkMaxAnimationSpeed); _animation.CrossFade(walkAnimation.name); } } } } // ANIMATION sector // Set rotation to the move direction if (IsGrounded()) { transform.rotation = Quaternion.LookRotation(moveDirection); } else { Vector3 xzMove = movement; xzMove.y = 0; if (xzMove.sqrMagnitude > 0.001f) { transform.rotation = Quaternion.LookRotation(xzMove); } } // We are in jump mode but just became grounded if (IsGrounded()) { lastGroundedTime = Time.time; inAirVelocity = Vector3.zero; if (jumping) { jumping = false; SendMessage("DidLand", SendMessageOptions.DontRequireReceiver); } } } void OnControllerColliderHit (ControllerColliderHit hit ) { // Debug.DrawRay(hit.point, hit.normal); if (hit.moveDirection.y > 0.01f) return; } float GetSpeed () { return moveSpeed; } public bool IsJumping () { return jumping; } bool IsGrounded () { return (collisionFlags & CollisionFlags.CollidedBelow) != 0; } Vector3 GetDirection () { return moveDirection; } public bool IsMovingBackwards () { return movingBack; } public float GetLockCameraTimer () { return lockCameraTimer; } bool IsMoving () { return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5f; } bool HasJumpReachedApex () { return jumpingReachedApex; } bool IsGroundedWithTimeout () { return lastGroundedTime + groundedTimeout > Time.time; } void Reset () { gameObject.tag = "Player"; } } |
如此我们就可以通过鼠标与触摸牵引控制主角移动了,别急这紧紧是开始,下面是本章的要点。
这里我们分析一下摄像机到底什么时候该拉近,什么时候该不拉近。 我的作法是这样的,当角色移动的时候我会从主角身上向摄像机方向发射一条射线,如果射线碰撞到的第一个对象是“摄像机” 那么就标示主角和摄像机之间没有别的模型挡住,此时摄像机就不应该拉近。如果射线碰撞到的第一个对象不是摄像机,那么就标示主角和摄像机之间有别的模型所挡住,此时取得射线与别的模型碰撞的3D坐标接着将“摄像机”的坐标移动到这个坐标上即可。
如下图所示,(红色表示由主角发射到摄像机的射线)主角身后的射线没有被别的模型挡住,射线机不会拉近。
如下图所示,MOMO改变了一下主角的位置,此时主角身后的射向已经被墙挡住,这时把摄像机的坐标移动到被墙挡住点的坐标,这样摄像机就拉近了。
原理大概就是这样,接着我们学习一下这段代码该如何来写。
MyCamera.cs 挂在射线机上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
using UnityEngine; using System.Collections; public class MyCamera : MonoBehaviour { //摄像机朝向的目标模型 public Transform target; //摄像机与模型保持的距离 public float distance = 10.0f; //射线机与模型保持的高度 public float height = 5.0f; //高度阻尼 public float heightDamping = 2.0f; //旋转阻尼 public float rotationDamping = 3.0f; //主角对象 private GameObject controller; void Start() { //得到主角对象 controller = GameObject.FindGameObjectWithTag("Player"); } void Update() { } void LateUpdate () { // Early out if we don't have a target if (!target) return; //当鼠标或者手指在触摸中时 if(JFConst.TouchIng()) { bool follow = true; //计算相机与主角Y轴旋转角度的差。 float abs = Mathf.Abs(transform.rotation.eulerAngles.y - controller.transform.rotation.eulerAngles.y); //abs等于180的时候标示摄像机完全面对这主角, 》130 《 230 表示让面对的角度左右偏移50度 //这样做是不希望摄像机跟随主角,具体效果大家把代码下载下来看看,这样的摄像机效果很好。 if(abs > 130 && abs < 230) { follow = false; }else { follow = true; } float wantedRotationAngle = target.eulerAngles.y; float wantedHeight = target.position.y + height; float currentRotationAngle = transform.eulerAngles.y; float currentHeight = transform.position.y; //主角面朝射线机 和背对射线机 计算正确的位置 if(follow) { currentRotationAngle = Mathf.LerpAngle (currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime); currentHeight = Mathf.Lerp (currentHeight, wantedHeight, heightDamping * Time.deltaTime); Quaternion currentRotation = Quaternion.Euler (0, currentRotationAngle, 0); Vector3 positon = target.position; positon -= currentRotation * Vector3.forward * distance; positon = new Vector3(positon.x,currentHeight,positon.z); transform.position = Vector3.Lerp(transform.position,positon,Time.time); }else { Vector3 positon = target.position; Quaternion cr = Quaternion.Euler (0, currentRotationAngle, 0); positon += cr * Vector3.back * distance; positon = new Vector3(positon.x,target.position.y + height,positon.z); transform.position = Vector3.Lerp(transform.position,positon,Time.time); } } //这里是计算射线的方向,从主角发射方向是射线机方向 Vector3 aim = target.position; //得到方向 Vector3 ve = (target.position - transform.position).normalized; float an = transform.eulerAngles.y; aim -= an * ve ; //在场景视图中可以看到这条射线 Debug.DrawLine(target.position,aim,Color.red); //主角朝着这个方向发射射线 RaycastHit hit; if(Physics.Linecast(target.position,aim,out hit)) { string name = hit.collider.gameObject.tag; if(name != "MainCamera" && name !="terrain") { //当碰撞的不是摄像机也不是地形 那么直接移动摄像机的坐标 transform.position = hit.point; } } // 让射线机永远看着主角 transform.LookAt (target); } } |
牛头人被墙挡住了,摄像机直接拉近了,小牛头人是不是很帅气?哈哈哈!!
最后雨松MOMO把源码放出来,希望大家学习愉快,哇咔咔。。。
下载地址:http://vdisk.weibo.com/s/mAhiI
补充 :摄像机遮挡物体透明。
遮挡透明最简单的办法就是修改材质的透明度,前提需要把材质设置成支持透明通道的 shader Transparent 。 如下图所示,摄像机挡住的那面墙已经成透明状态了。
看看代码是如何写的,把上述代码MyCamera.cs 简单的改一下就可以。 主要是92行到101行。
代码核心就是通过射线得到碰撞模型的材质,然后修改材质的透明度。color.a就是透明度 1是完全不透明,0是完全透明。注意一定是支持透明通道的模型才可以。lastobj是记录上次透明的对象,用于模型透明还原使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
if(Physics.Linecast(target.position,aim,out hit)) { GameObject obj = hit.collider.gameObject; string name = obj.tag; if(name != "MainCamera" && name !="terrain") { Color color = obj.renderer.material.color; color.a = 0.5f; obj.renderer.material.SetColor("_Color", color); lastobj = obj; }else { if(lastobj != null) { Color color = lastobj.renderer.material.color; color.a = 1f; lastobj.renderer.material.SetColor("_Color", color); } } } |
最后,假设你的模型做的非常大,贴图材质也非常的多,你像透明其中一小部分那么上述方法就不合适了,除非把他们都拆出来。我觉得也可以自己写shader实现。我们的项目采用了摄像机拉近的方式。欢迎讨论!!!
回答楼下问题:上帝视角代码,看到楼下有朋友问我,那么我就贴出来,其实很简单。下面代码挂在摄像机上, target就是角色对象,distance 是摄像机与角色的距离 height是摄像机与角色的高度。在编辑器中手动调节即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
using UnityEngine; using System.Collections; public class ICamera : MonoBehaviour { public Transform target; // The distance in the x-z plane to the target public float distance = 10.0f; // the height we want the camera to be above the target public float height = 5.0f; void LateUpdate () { // Early out if we don't have a target if (!target) return; // Set the position of the camera on the x-z plane to: // distance meters behind the target transform.position = target.position; transform.position -= Vector3.forward * distance; transform.position = new Vector3(transform.position.x,transform.position.y + height,transform.position.z); // Always look at the target transform.LookAt (target); } } |
回答问题 :摄像机抖动如何修改
今天微薄上有个朋友问我这样的问题,我在这里解答一下。
来福12345:我又厚颜无耻的来了,您的《Unity3D的研究院之处理摄像机跟随避免相机穿墙拉近或透明的方法(四十四)》对于不规则的物体该怎么弄,我是直接在unity上弄了几个山脉,由于地面凹凸不平,沿着山走的时候发现摄像机会一直透视,而且抖动的很厉害,请问该怎么解决啊
1.从设计上避免射线遇到一些复杂的面,如下图所示,红颜色是主角身后的射线,当主角身后的射线碰撞到这样的面,如果此时移动速快快一些摄像机肯定会抖动。 这里最好直接删除Mesh Collider 换成BoxCollider 效率还可以得到提升。产经设计这块我觉得可以参考一下魔兽世界,我在项目中遇到这样问题的地方就是 比较小的门(棱角那种) 或者这样的树。
2.但是还有一些比较特殊的地方,如果你的项目必需使用MeshColider 的话 我建议你写触发脚本来修改摄像机,假设 当摄像机被挡住后,变成跟随视角,只到摄像机没被挡住的时候在回到原来视角。
3.我检查了一下上面的代码中我们在加一个判断,应该就可以解决大部分问题、在LateUpdate () 中最后加入代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
if(!closer) { transform.position = positon; } else { float dis = Vector3.Distance(transform.position,controller.transform.position); if(dis < 8) { distance = dis; }else { closer = false; } } |
bool closer 是表示是否拉近。 当摄像机被挡住的时候这个数值等于true;
1 2 3 4 5 6 7 8 9 10 11 12 |
if(Physics.Linecast(tirggerTaget,aim,out hit)) { string name = hit.collider.gameObject.tag; if(name != "MainCamera") { closer = true; transform.position = hit.point; distance = Vector3.Distance(transform.position,controller.transform.position) - 2f; } } |
当closer等于假的时候,此时直接修更新摄像机的位置,因为没被挡住。 当closer等于真的时候,摄像机已经被挡住了,我们不修改摄像机的位置,计算摄像机保证不被挡住时,摄像机此时和主角的距离是多少,然后修改distance全局变量即可。当摄像机不被遮挡的时候在恢复之前的距离。 如有问题请留言,我会即时解答。。 谢谢
//补充/////////
之前的代码还是有点问题,后来我又仔细的重构了一下摄像机的代码。下面的代码在我的项目中已经完美无暇的模拟摄像机拉近了。。 代码对外有两个公有类型, 一个是模型的 一个是模型位置 与头顶的偏移, 因为射线应该是重头顶向后发射的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
using UnityEngine; using System.Collections; using System.Threading; public class PersonCamera : MonoBehaviour { public const float DISTANCE_DEAFULT = 8.0f; private float distance = 0.0f; public Transform target; public float target_offsety = 1.8f; private Vector3 payerTaget; void Awake() { distance = DISTANCE_DEAFULT; payerTaget = new Vector3(target.position.x,target.position.y + target_offsety , target.position.z); Quaternion cr = Quaternion.Euler (30f, transform.eulerAngles.y, 0); Vector3 positon = payerTaget; positon += cr * Vector3.back * distance; transform.position = positon; transform.LookAt(payerTaget); } void LateUpdate() { payerTaget = new Vector3(target.position.x,target.position.y + target_offsety , target.position.z); Quaternion cr = Quaternion.Euler (30f, transform.eulerAngles.y, 0); Vector3 positon = payerTaget + (cr * Vector3.back * distance); RaycastHit []hits = Physics.RaycastAll(new Ray(payerTaget,(positon -payerTaget).normalized)); distance = DISTANCE_DEAFULT; if(hits.Length > 0) { RaycastHit stand = hits[0]; foreach(RaycastHit hit in hits) { if(hit.collider.gameObject.tag == "terrain") { if(hit.distance < stand.distance) { stand = hit; } } } Debug.Log(stand.point + " " +stand.collider.gameObject.tag); string tag = stand.collider.gameObject.tag; distance = Vector3.Distance(stand.point,payerTaget); if(distance > DISTANCE_DEAFULT) { distance = DISTANCE_DEAFULT; } } positon = payerTaget + (cr * Vector3.back * distance); transform.position = Vector3.Lerp(transform.position,positon,0.3f);; transform.LookAt(payerTaget); Debug.DrawRay(payerTaget,positon -payerTaget,Color.red); } } |
- 本文固定链接: https://www.xuanyusong.com/archives/1991
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
大神,透明那个过时了,能更新么
大神还在嘛,好像这个脚本还有点小问题,不知道怎么上传图片,之好符号代替了_____←墙2 当射线第一次碰到墙1的时候 |←墙1 可以拉近相机 |O←相机 | | ←射线 | | O角色_____←墙2 相机离开墙体的时候会穿过去, O |←墙1 碰到墙2,这时墙1又遮挡了 相 | 机 | | ←射线 | | O角色是不是因为默认射线的起点不对啊
哎,排版乱了
貌似没有考虑到角色离障碍物太近的情况
momo老师你好,请教一下,UpdateSmoothedMovementDirection函数是否只是算出移动的方向?我想知道屏幕的2D坐标在3D世界中的具体坐标,该怎么算呢?
再问一个问题,如果写游戏时对一个对象编写了2种第三人称的控制代码,一个是自动跟踪,另一个是鼠标自由控制(可以360°观察),我想在游戏中通过按钮切换不同的控制效果,是不是用组件的enable来控制就可以了??还有什么别的便捷高效的做法吗?!
雨松老师,我想请问下,最后你重构的代码中,哪个参数可以修改,摄像机跟随物体时,旋转的速度?我改了好多个,都没改出来!!
你好。。。我请问一下,有时候会出现这种问题,怎么解决,并且,你这个场景比较简单,假如东西多的话,这种问题会出现很多。
这不利于祖国的统一。。。。
请问您的MyCamera.cs里,target和controller是同一个人物吧?
还有射线机是个什么东西?是摄像机么?
换成unity自带的smoothfollow脚本没有画面一卡一卡和抖动(但是就是摄像机会跑到山体里)
你好,使用了你最后发布的代码,目标跟随一辆赛车,赛车前进的时候,发现画面有一顿、-顿的抖动现象,这是为什么?要调整那些参数?谢谢指点
这代码 其实就是提供一个思路。。。 卡顿的话是不是有地方在卡帧?
写得挺不错的。不过你上面的关于透明度的那段还是有点问题。这种方法只适合一个物体挡住的情况,多个物体挡住的情况只能一个物体变透明,建议用RaycastHit[] hit来判断。
MoMo老师,我想请教你一个问题,如果我想通过其他触控方式控制第三视角移动和旋转,我应该怎么写第三视角的移动h和旋转接口。
雨松老师,请教一下,像计算机单机<<游击队保罗>>(保罗队长)游戏中,若两人或多人分屏(split screen)模式下摄像机及人物移动到边界时如何控制?描述:当两人或多人向同一方向移动时摄像机跟随移动,当不同方向移动时,若人物移动到边界时人物将不能移动
Vector3 aim = target.transform.position;//得到方向(主角到相机):Vector3 ve = (target.transform.position – transform.position).normalized;float an = transform.eulerAngles.y;aim -= an * ve ; //在场景视图中可以看到这条射线Debug.DrawLine(target.transform.position,aim,Color.red); //主角朝着这个方向发射射线RaycastHit hit;if(Physics.Linecast(target.transform.position ,aim,out hit)){string name = hit.transform.gameObject.tag;if(name != “MainCamera” && name !=”terrain” && name!=”Plane” ){//当碰撞的不是摄像机也不是地形 那么直接移动摄像机的坐标transform.position = hit.point;} }这段代码我试了一下,有时能捕获碰撞,有时却不行。不行的时候大多是开始时角色移动到障碍后面,障碍挡住了相机的视线,但依然无法获得碰撞。能捕获碰撞的时候是角色不动,相机以角色为中心旋转的时候,当障碍挡住相机视线时
Vector3 aim = target.transform.position; //得到方向(主角到相机): Vector3 ve = (target.transform.position – transform.position).normalized; float an = transform.eulerAngles.y; aim -= an * ve ; //在场景视图中可以看到这条射线 Debug.DrawLine(target.transform.position,aim,Color.red); //主角朝着这个方向发射射线 RaycastHit hit; if(Physics.Linecast(target.transform.position ,aim,out hit)) { string name = hit.transform.gameObject.tag; if(name != “MainCamera” && name !=”terrain” && name!=”Plane” ) { //当碰撞的不是摄像机也不是地形 那么直接移动摄像机的坐标 transform.position = hit.point; } }这段代码我试了一下,有时能捕获碰撞,有时却不行。不行的时候大多是开始时角色移动到障碍后面,障碍挡住了相机的视线,但依然无法获得碰撞。能捕获碰撞的时候是角色不动,相机以角色为中心旋转的时候,当障碍挡住相机视线时
NullReferenceExceptionUnityEngine.Component.get_transform () (at C:/BuildAgent/work/cac08d8a5e25d4cb/Runtime/ExportGenerated/Editor/UnityEngineComponent.cs:21)ThirdPersonController.UpdateSmoothedMovementDirection () (at Assets/ThirdPersonController.cs:138)ThirdPersonController.Update () (at Assets/ThirdPersonController.cs:329)指向的代码为Transform cameraTransform = Camera.main.transform;
NullReferenceExceptionUnityEngine.Component.get_transform () (at C:/BuildAgent/work/cac08d8a5e25d4cb/Runtime/ExportGenerated/Editor/UnityEngineComponent.cs:21)ThirdPersonController.UpdateSmoothedMovementDirection () (at Assets/ThirdPersonController.cs:138)ThirdPersonController.Update () (at Assets/ThirdPersonController.cs:329)指向的代码为 Transform cameraTransform = Camera.main.transform;
求老师解析下:Vector3 ve = (target.position – transform.position ).normalized;float an = transform.eulerAngles.y;aim -= an * ve;这里不是十分理解。。。
求老师解析下:Vector3 ve = (target.position – transform.position ).normalized; float an = transform.eulerAngles.y; aim -= an * ve;这里不是十分理解。。。