要处理两个问题:
- 如何设置地面人不掉下去
方法一、 游戏物体加刚体,将游戏物体和地面加collider。如果是地形,可以使用 Terrain Collider;如果是简单的平面,可以添加 Box Collider 或者 Mesh Collider(如果平面是复杂的网格形状)。
方法二、在游戏物体上增加刚体,在刚体组件的 Constraints 选项中,可以限制物体在某些轴向上的运动。例如,如果不想让物体在 Y 轴(垂直方向)下落,可以勾选 “Freeze Position” 中的 Y 选项。这样,刚体在物理模拟过程中,其 Y 轴位置就不会因为重力而改变,从而不会掉下去。
缺点:当冻住y的时候, 遇到地上有坑的时候,也不会按地势走
方法三、使用navi ai,给人物挂上agent后,烘焙后任务就不会掉下去了 - 鼠标控制方向,可以参考
// 鼠标控制镜头方向
float rotLeftRight = Input.GetAxis("Mouse X") * mouseSensitivity;
float horizontalRotationTarget = horizontalRotation + rotLeftRight;
horizontalRotation = Mathf.SmoothDamp(horizontalRotation, horizontalRotationTarget, ref horizontalRotationVelocity, horizontalSmoothTime);
horizontalRotation = Mathf.Clamp(horizontalRotation, -leftRightRange, leftRightRange);
transform.localEulerAngles = new Vector3(transform.localEulerAngles.x, horizontalRotation, transform.localEulerAngles.z);
float verticalRotationTarget = verticalRotation - Input.GetAxis("Mouse Y") * mouseSensitivity;
verticalRotation = Mathf.SmoothDamp(verticalRotation, verticalRotationTarget, ref verticalRotationVelocity, smoothTime);
verticalRotation = Mathf.Clamp(verticalRotation, -upDownRange, upDownRange);
Camera.main.transform.localEulerAngles = new Vector3(verticalRotation, 0, 0);
- 如何漫游,代码实现如下
方向键控制方向,挂载到游戏物体player上
- Rigidbody 模式(任选)
public Rigidbody rd;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
float f = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
rd.AddForce(new Vector3(f,0,v)*1);
}
- transform模式(任选)
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
// 角色的移动速度
public float speed = 5.0f;
void Update()
{
// 获取水平(左右)和垂直(前后)输入
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
// 计算移动方向
Vector3 moveDirection = new Vector3(horizontalInput, 0, verticalInput);
// 应用移动
transform.position += moveDirection * speed * Time.deltaTime;
}
}
镜头跟随玩家,挂载到镜头上
public class FollowTarget : MonoBehaviour {
private Transform player;
private Vector3 offset;
private float smoothing = 3;
void Start (){
player = GameObject.FindGameObjectWithTag("Player”).transform;
offset = transform.position - player.position;
}
void LateUpdate (){
Vector3 targetPosition = player.position + player.TransformDirection( offset)
transform.position = Vector3.Lerp(transform.position, targetPosition,Time.deltaTime*smoothing);
transform.LookAt(player.position);
}
}
七八
- 为地面增加ground标签,collider,取消Is Kinematic,
- 角色挂上rigidbody组件
- 有一些属性可以影响其与碰撞体的相互作用。例如,Mass(质量)和Drag(空气阻力)
using UnityEngine;
public class PlayerMovementWithJumpAndMouse : MonoBehaviour
{
// 角色的移动速度
public float speed = 5.0f;
// 角色的跳跃力
public float jumpForce = 10.0f;
// 判断角色是否在地面上
private bool isGrounded = true;
// 地面的检测半径
private float groundCheckDistance = 0.4f;
// 地面检测层的层掩码
private LayerMask groundLayerMask;
// 用于存储鼠标的当前和上一个位置
private Vector3 mouseCurrentPosition;
private Vector3 mouseLastPosition;
// 新增的变量,用于存储调整后的移动速度和斜坡的法线
private float adjustedSpeed;
private Vector3 slopeNormal;
void Start()
{
// 设置地面检测层的层掩码
groundLayerMask = LayerMask.GetMask("Ground");
// 初始化鼠标位置
mouseLastPosition = Input.mousePosition;
}
void Update()
{
// 获取鼠标输入
mouseCurrentPosition = Input.mousePosition;
Vector3 mouseDelta = mouseCurrentPosition - mouseLastPosition;
// 将鼠标移动转换为旋转角度(这里假设每100像素移动对应1度旋转)
float rotationX = mouseDelta.y * -1 * Time.deltaTime * 100f; // 乘以-1是因为我们希望向上移动鼠标时角色向下看(Y轴反转)
float rotationY = mouseDelta.x * Time.deltaTime * 100f;
// 限制旋转角度,防止角色倒立或过度旋转
rotationX = Mathf.Clamp(rotationX, -30f, 30f); // 限制X轴旋转在-30到30度之间
// 计算旋转后的方向
Quaternion targetRotation = Quaternion.Euler(rotationX, transform.eulerAngles.y + rotationY, transform.eulerAngles.z);
// 平滑过渡到目标旋转方向(可选)
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f);
// 重置鼠标位置为当前位置,以便下一次计算移动量
mouseLastPosition = mouseCurrentPosition;
// 获取水平(左右)和垂直(前后)输入(这里仍然保留键盘输入,以便可以同时使用)
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
// 计算移动方向
Vector3 moveDirection = new Vector3(horizontalInput, 0, verticalInput);
// 检测斜坡并获取其法线
RaycastHit slopeHit;
if (Physics.Raycast(transform.position, transform.forward, out slopeHit, 100f, groundLayerMask))
{
// 获取斜坡的法线
slopeNormal = slopeHit.normal;
// 根据斜坡倾斜程度调整移动速度
float slopeAngle = Vector3.Angle(Vector3.up, slopeNormal);
AdjustMovementBySlope(slopeAngle);
// 应用移动
transform.position += moveDirection * adjustedSpeed * Time.deltaTime;
// 根据斜坡法线调整角色方向
AdjustCharacterDirection(slopeNormal);
}
else
{
// 如果没有检测到斜坡,则使用默认速度,并重置斜坡法线
adjustedSpeed = speed;
slopeNormal = Vector3.up; // 或者你可以设置为其他默认方向
}
// 检测跳跃输入
if (Input.GetButtonDown("Jump") && isGrounded)
{
// 给角色一个向上的力
Vector3 jump = new Vector3(0, jumpForce, 0);
rigidbody.velocity = Vector3.zero; // 重置垂直速度,防止跳跃时受其他力影响
rigidbody.AddForce(jump, ForceMode.Impulse);
isGrounded = false; // 标记角色已经起跳
}
}
// 用于检测角色是否在地面上
private void OnCollisionStay(Collision collision)
{
// 检查是否与地面层的对象接触
if ((groundLayerMask & (1 << collision.gameObject.layer)) != 0)
{
// 检查是否在地面检测半径内
if (Mathf.Abs(collision.contacts[0].point.y - transform.position.y) <= groundCheckDistance)
{
isGrounded = true; // 标记角色在地面上
}
}
}
// 如果没有地面碰撞,则重置isGrounded,防止角色在空中时误判为已落地
private void OnCollisionExit(Collision collision)
{
if ((groundLayerMask & (1 << collision.gameObject.layer)) != 0)
{
isGrounded = false;
}
}
// 检测斜坡的倾斜角度,并调整移动速度(此方法已被整合到Update中,但保留此方法签名以便理解)
/*private float DetectSlopeAngle()
{
// ...(原代码已整合到Update中)
}*/
// 根据斜坡倾斜程度调整移动速度或方向
private void AdjustMovementBySlope(float slopeAngle)
{
// 设置一个最大斜坡角度阈值,超过此角度则减少速度
float maxSlopeAngle = 45f;
// 根据斜坡角度调整速度
if (slopeAngle > maxSlopeAngle)
{
// 计算速度调整因子(0到1之间)
float speedReductionFactor = 1f - (slopeAngle - maxSlopeAngle) / (90f - maxSlopeAngle);
speedReductionFactor = Mathf.Clamp01(speedReductionFactor); // 确保因子在0到1之间
// 应用速度调整因子
adjustedSpeed = speed * speedReductionFactor;
}
else
{
// 没有超过最大斜坡角度,使用正常速度
adjustedSpeed = speed;
}
}
// 根据斜坡法线调整角色方向
private void AdjustCharacterDirection(Vector3 slopeNormal)
{
// 计算角色应该面向的方向,即斜坡法线的水平分量(忽略Y轴)
Vector3 targetDirection = new Vector3(slopeNormal.x, 0, slopeNormal.z).normalized;
// 如果角色当前面向的方向与目标方向差异较大,则平滑过渡到目标方向
// 这里我们使用Quaternion.Slerp来实现平滑过渡
Quaternion targetRotation = Quaternion.LookRotation(targetDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f);
// 注意:上面的平滑过渡可能会导致角色在陡峭的斜坡上时旋转过快。
// 为了解决这个问题,你可以根据斜坡角度来限制旋转速度,或者完全禁用平滑过渡,直接设置旋转。
// 例如:
// float maxRotationSpeed = 30f; // 最大旋转速度(度/秒)
// float rotationAngle = Vector3.Angle(transform.forward, targetDirection);
// float rotationSpeed = Mathf.Min(maxRotationSpeed, rotationAngle / Time.deltaTime);
// transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
}
参考
https://www.jianshu.com/p/0e8d4dd55c2c