目标
玩家控制的大立方体 碰到小立方体时变成一样的大小,与小立方体分离后变为原来大小
实现过程
首先创建一个Cube,改个名称,作为玩家控制的大立方体
然后创建几个小立方体,标签设置为 Cube,可以改个颜色,方便区分
然后给玩家挂上 下文的脚本,设置一下移动和旋转的速度
直接变小,发现Bug
挂在玩家身上的脚本代码是这样的:
// 这是段有Bug的代码
using UnityEngine;
public class PlayerController_00 : MonoBehaviour
{
private Rigidbody rb;
private MeshRenderer meshRenderer;
[Header("Move")]
private Vector3 moveDir; // 移动方向
private Quaternion quaternionDir; // 旋转朝向
public float moveSpeed; // 移动速度
public float rotateSpeed; // 旋转速度
[Header("Change Scale")]
private Vector3 originScale; // 初始比例
private void Awake()
{
rb = GetComponent<Rigidbody>(); // 获取刚体组件
meshRenderer = GetComponent<MeshRenderer>(); // 获取渲染器
originScale = transform.localScale; // 获取玩家的初始比例
}
private void FixedUpdate()
{
// 获取水平、前后方向的输入
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
// 获取移动方向向量
moveDir.Set(horizontal, 0, vertical);
// 归一化向量
moveDir.Normalize();
// 随移方向旋转朝向
quaternionDir = Quaternion.LookRotation(Vector3.RotateTowards(transform.forward, moveDir, rotateSpeed * Time.deltaTime, 0));
// 用刚体移动和旋转
rb.MovePosition(rb.position + moveDir * moveSpeed * Time.deltaTime);
rb.MoveRotation(quaternionDir);
}
private void OnCollisionEnter(Collision collision)
{
// 小立方体的标签为 Cube
if (collision.gameObject.CompareTag("Cube"))
{
// 将玩家的比例设为碰撞目标的比例
transform.localScale = collision.transform.localScale;
// 为了看的更清楚,改为红色
meshRenderer.material.color = Color.red;
}
}
private void OnCollisionExit(Collision collision)
{
if (collision.gameObject.CompareTag("Cube"))
{
// 将玩家比例设为初值
transform.localScale = originScale;
// 改为白色
meshRenderer.material.color = Color.white;
}
}
}
//这是段有Bug的代码
像这样如果碰撞瞬间直接变小,会立刻与小立方体分离,然后瞬间恢复原来大小,然后会继续碰撞,出现如下Bug
变小的瞬间,改变位置
想解决这个Bug,可以让玩家控制的方块在碰到小立方体的一瞬间直接贴住小立方体,那么就要得到变小瞬间的落点
直接计算落点比较麻烦
这里提供一种比较容易理解的方法:
Unity API 提供了Collider.ClosestPoint 方法,可以帮我们在碰撞时找到小立方体上距离玩家的最近点,然后可以根据碰撞最近点计算出 玩家下一刻的落点
为了方便理解,我将计算过程拆成两步,分别是平面上的位移、竖直方向的位移
平面上的位移
平面上碰撞的角度大致分为四种,这些情况全部可以用通过 .ClosestPoint() 获得碰撞最近点的位置来计算落点位置
竖直方向上的位移
竖直方向也就是Y轴,如果你创建的小立方体并不是很小,那么靠自由下落也可以,这一步可以忽略
以下为碰撞瞬间的代码片段:
//碰撞瞬间
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Cube"))
{
// 获取目标物体比例
float targetScale = collision.transform.localScale.x;
// 获取目标物体与玩家的最近点
Vector3 collisionPoint = collision.collider.ClosestPoint(rb.position);
// 最近点投影在平面上的点
Vector3 collisionPointOnGround = new Vector3(collisionPoint.x, 0, collisionPoint.z);
// 先将玩家的比例设为碰撞目标的比例
transform.localScale = collision.transform.localScale;
// 目标相对位置的向量(与小立方体在平面上的相对位置)
Vector3 targetPointOnGround = new Vector3(rb.position.x, 0, rb.position.z) - collisionPointOnGround;
Debug.Log(targetPointOnGround);
// Y轴偏移量,可以不加,让物体自由下落
float offsetY = -(originScale.x - targetScale) * 0.5f;
// 最后变换位置 = 平面上的最近点坐标 + 平面上的目标相对位置 - Y轴的偏移量
rb.position = collisionPointOnGround + targetPointOnGround * targetScale + new Vector3(0, offsetY, 0);
}
}
撞到后保持紧贴,离开时慢慢恢复大小
要实现这个效果,需要对玩家控制的方块做一些修改
首先创建一个空物体,重置其Transform,创建一个Cube作为其子物体
将本文最后的完整代码的脚本 挂在这个空物体上
然后设置一下Cube的位置
完整代码
using UnityEngine;
public class PlayerController_02 : MonoBehaviour
{
public GameObject cube;
private Rigidbody rb;
private SphereCollider sphereCollider;
private MeshRenderer meshRenderer;
[Header("Move")]
private Vector3 moveDir; // 移动方向
private Quaternion quaternionDir; // 旋转朝向
public float moveSpeed; // 移动速度
public float rotateSpeed; // 旋转速度
[Header("Change Scale")]
private Vector3 originScale; // 初始比例
private float targetScale; // 目标物体比例
private float baseDistance; // 碰撞方块离开后的变换距离
private float tempDistance; // 与碰撞目标的当前距离
private bool startChange; // 是否开始根据距离做比例调整
private void Awake()
{
rb = GetComponent<Rigidbody>(); // 获取刚体组件
sphereCollider = GetComponent<SphereCollider>(); // 获取触发器
meshRenderer = cube.GetComponent<MeshRenderer>(); // 获取渲染器
originScale = cube.transform.lossyScale; // 获取玩家的初始比例
baseDistance = sphereCollider.radius; // 设置变换距离
}
private void FixedUpdate()
{
// 获取水平、前后方向的输入
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
// 获取移动方向向量
moveDir.Set(horizontal, 0, vertical);
// 归一化向量
moveDir.Normalize();
// 随移方向旋转朝向
quaternionDir = Quaternion.LookRotation(Vector3.RotateTowards(transform.forward, moveDir, rotateSpeed * Time.deltaTime, 0));
// 用刚体移动和旋转
rb.MovePosition(rb.position + moveDir * moveSpeed * Time.deltaTime);
rb.MoveRotation(quaternionDir);
}
//碰撞瞬间
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Cube"))
{
// 获取目标物体比例
targetScale = collision.transform.localScale.x;
// 获取目标物体与玩家的最近点
Vector3 collisionPoint = collision.collider.ClosestPoint(rb.position);
// 最近点投影在平面上的点
Vector3 collisionPointOnGround = new Vector3(collisionPoint.x, 0, collisionPoint.z);
// 先将玩家的比例设为碰撞目标的比例
cube.transform.localScale = collision.transform.localScale;
// 目标相对位置的向量(与小立方体在平面上的相对位置)
Vector3 targetPointOnGround = new Vector3(rb.position.x, 0, rb.position.z) - collisionPointOnGround;
Debug.Log(targetPointOnGround);
// Y轴偏移量
float offsetY = -(originScale.x - targetScale) * 0.5f;
// 最后变换位置 = 平面上的最近点坐标 + 平面上的目标相对位置 - Y轴的偏移量
rb.position = collisionPointOnGround + targetPointOnGround * targetScale + new Vector3(0, offsetY, 0);
}
}
//在贴住的过程中
private void OnCollisionStay(Collision collision)
{
if (collision.gameObject.CompareTag("Cube"))
{
startChange = false;
// 和Cube相撞时,玩家速度设为0,避免被挤开
rb.velocity = Vector3.zero;
// 将玩家的比例设为碰撞目标的比例
cube.transform.localScale = collision.transform.localScale;
// 为了看的更清楚,改为红色
meshRenderer.material.color = Color.red;
}
}
//分离瞬间
private void OnCollisionExit(Collision collision)
{
if (collision.gameObject.CompareTag("Cube"))
{
// 和Cube分离时,可以开始变换比例
startChange = true;
}
}
private void OnTriggerStay(Collider other)
{
if (other.CompareTag("Cube") && startChange)
{
// 获取玩家和目标物体在平面上的位置
Vector2 playerPos = new Vector2(rb.position.x, rb.position.z);
Vector2 targetPos = new Vector2(other.transform.position.x, other.transform.position.z);
// 获取在平面上的距离,- targetScale * 1.5f是为预留一部分空间,避免距离过近时出现不停碰撞的Bug
tempDistance = Vector2.Distance(playerPos, targetPos) - targetScale * 1.5f;
// 计算需要变换比例的部分,限制在 0 到 1 - targetScale 之间
float tempScale = Mathf.Clamp(tempDistance / baseDistance * (1 - targetScale), 0, 1 - targetScale);
// 玩家当前的比例 = 初始比例 * 目标缩放值 + 根据距离计算的动态值
cube.transform.localScale = originScale * targetScale + originScale * tempScale;
// 改为黄色
meshRenderer.material.color = Color.yellow;
}
}
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Cube"))
{
startChange = false;
// 将玩家比例设为初值
cube.transform.localScale = originScale;
// 改为白色
meshRenderer.material.color = Color.white;
}
}
}
关于分离时慢慢恢复大小的讲解详见视频
最终效果
文章来源:https://www.toymoban.com/news/detail-501615.html
感谢观看!文章来源地址https://www.toymoban.com/news/detail-501615.html
到了这里,关于【Unity】碰撞后缩小比例并与碰撞目标紧贴(吸附效果)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!