当摄像机处于跟随主角状态时,那么主角移动后很有可能摄像机被别的模型挡住。这样用户体验就非常不好,一般3D游戏在处理视角的时候有两种方法,第一种是让被挡住的模型变成透明,第二种是拉近摄像机。前者时候固定视角游戏使用,后者适合变化视角游戏使用。两个我们都学一学蛤蛤。
如下图所示,MOMO写了一个简单的例子,通过鼠标或触摸来牵引主角向不同的角度移动,旁边有一面墙当主角被这面墙挡住,此时摄像机将拉近。
首先,为了方面鼠标与移动平台上的触摸同时相应我写了一个通用的方法。
包括 开始触摸 触摸中 结束触摸 ,电脑手机都可以用这个方法相应到。
JFConst.cs
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世界中移动的坐标。
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 挂在射线机上
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是记录上次透明的对象,用于模型透明还原使用。
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是摄像机与角色的高度。在编辑器中手动调节即可。
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 () 中最后加入代码
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;
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全局变量即可。当摄像机不被遮挡的时候在恢复之前的距离。 如有问题请留言,我会即时解答。。 谢谢
//补充/////////
之前的代码还是有点问题,后来我又仔细的重构了一下摄像机的代码。下面的代码在我的项目中已经完美无暇的模拟摄像机拉近了。。 代码对外有两个公有类型, 一个是模型的 一个是模型位置 与头顶的偏移, 因为射线应该是重头顶向后发射的。
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;这里不是十分理解。。。