三分钟制作反弹球 打砖块反弹效果
弹珠碰撞效果
弹珠碰撞效果指的是弹珠碰撞到碰撞体,随机方向反弹,类似于打砖块中的小球反弹效果
场景准备
首先,物体发生碰撞的条件是,两个物体都需要添加 碰撞器,且其中一方带有刚体组件(最好是移动的一方)
这里使用 SpriteShape创建了一个地形,给地形创建了一个边缘碰撞器。
小球挂载了刚体和碰撞器,小球作为移动的一方,重力锁定了为 0 ,且冻结 Z 轴
实现分析
一、首先我们要理清楚的是小球的反弹的规则
如上图所示,小球由 A 点向 B 点发射,假设不受到重力的影响,小球自然而然的会向 C 点发射,而中间的那一条虚线就是法线,由于法线完全平均的将入射角和反射角切开,当我们知道 法线 时,就自然可以求出发射角的方向。
二、在 unity 的学习中 碰撞器的学习中,我们学过一系列的关于碰撞器的相关方法或者属性,如下:
void OnCollisionEnter2D(Collision2D collision) { } //碰撞触发接触时会自动执行这个函数(函数名,函数参数的类型不可以改动)
collision2D.collider //碰撞到的对象碰撞器的信息
collision2D.gameObject //碰撞对象的依附对象(GameObject)
collision2D.transform //碰撞对象的依附对象的位置信息
collision2D.contactCount //触碰点数相关
ContactPoint[] pos = collision2D.contacts //接触点 具体的坐标
pos[0].normal; //获取接触点的法线方向
void OnCollisionStay2D(Collision2D collision) { } //碰撞结束分离时会自动执行这个函数(函数名,函数参数的类型不可以改动)
void OnCollisionExit2D(Collision2D collision) { } //两个物体相互接触摩擦时 会不停调用该函数
void OnTriggerEnter2D(Collider2D other) { } //触发开始的函数 当第一次接触时 会自动调用
void OnTriggerExit2D(Collider2D other) { } //触发结束的函数 当接触的状态结束时 会调用一次
void OnTriggerStay2D(Collider2D other) { } //持续触发的函数 当两个对象持续触发(结合)时 会一直调用
实现反弹的效果主要要使用的方法有这三行代码,需要做补充说明的是第三行代码
ContactPoint[] pos = collision2D.contacts //接触点 具体的坐标
pos[0].normal; //获取接触点的法线方向
Vector3 reflect = Vector3.Reflect(moveDirection, contactNormal); //获取反射方向
Vector3.Reflect(incoming, normal)
方法用于计算一个向量(通常代表光线、物体的移动方向等)在另一个向量(代表平面或表面的法线)上的反射方向。这个方法非常有用,在模拟物理碰撞、光线追踪、反射效果等方面都扮演着重要角色。
参数解释:
-
incoming
:入射向量,即原始方向或即将接触表面的向量。在这个的例子中,moveDirection
代表物体的移动方向。
-
normal
:法线向量,表示平面或表面的法线方向。contactNormal
代表碰撞点的法线方向。
返回值:
- 返回一个
Vector3
,表示入射向量在给定法线向量上的反射方向。
按照以上的信息,就可以看是制作了。
碰撞检测制作方法
给移动的一方写一个移动用的脚本 :CirclePlayer
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 类描述:<br/>
/// 这是一个用来 的类
/// </summary>
/// <remarks>
/// 修改时间:无
/// <br/>
/// 修改内容:无
/// </remarks>
public class CirclePlayer : MonoBehaviour
{
/// <summary>
/// 移动方向
/// </summary>
private Vector3 moveDirection;
[Header("移动速度")]
public float moveSpeed;
void Start()
{
moveDirection = Vector3.right; //给小球一个初始的方向
}
void Update()
{
transform.position += moveDirection.normalized * Time.deltaTime * moveSpeed;
}
private void OnCollisionEnter2D(Collision2D collision)
{
//获取接触点信息
ContactPoint2D contactPoint = collision.contacts[0];
//获取接触点的法线方向
Vector3 contactNormal = contactPoint.normal;
//获取反射方向
Vector3 reflect = Vector3.Reflect(moveDirection, contactNormal);
moveDirection = reflect;
}
}
在上边的代码中,我么使用了 OnCollisionEnter2D 进行制作,如果没有什么意外的话,现在已经可以正常的运行了。
但是实现到此,我们还是发现了一个 BUG ,那就是当我们不断地加速去提升小球球的速度时,会发现,当速度提升到了一定的高度时,小球就飞走了。尽管后来将检测方式改为了连续检测,依旧是不可以将小球禁锢在咱么设置好的笼子里。
此时我们就可以引出第二种检测方式了“射线碰撞检测”
射线碰撞检测
我们可以在小球的移动方向上声明一条 长度为 1f 左右的射线,不断的检测射线是否与墙进行了碰撞,倘若检测到了,就修改小球的移动轨迹为反射角度。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 类描述:<br/>
/// 这是一个用来 的类
/// </summary>
/// <remarks>
/// 修改时间:无
/// <br/>
/// 修改内容:无
/// </remarks>
public class CirclePlayer : MonoBehaviour
{
/// <summary>
/// 移动方向
/// </summary>
private Vector3 moveDirection;
[Header("移动速度")]
public float moveSpeed;
void Start()
{
moveDirection = Vector3.right;
}
void Update()
{
transform.position += moveDirection.normalized * Time.deltaTime * moveSpeed;
MyOnCollision();
}
//private void OnCollisionEnter2D(Collision2D collision)
//{
// //获取接触点信息
// ContactPoint2D contactPoint = collision.contacts[0];
// //获取接触点的法线方向
// Vector3 contactNormal = contactPoint.normal;
// //获取反射方向
// Vector3 reflect = Vector3.Reflect(moveDirection, contactNormal);
// moveDirection = reflect;
//}
private void MyOnCollision()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, moveDirection, 0.8f, 1 << LayerMask.NameToLayer("obstacle"));
if (hit)
{
//获取接触点的法线信息
Vector3 contactNormal = hit.normal;
//获取反射方向
Vector3 reflect = Vector3.Reflect(moveDirection, contactNormal);
//重新赋值给移动方向
moveDirection = reflect;
}
}
}
注释掉原来的碰撞检测方法,手动写一个碰撞检测,再次运行,可以发现。
原来当小球的运动速度达到 20 左右小球就会飞出去的情况已经得到了改善,虽然依旧不能在极快的速度禁锢住小球,但也完全可以正常的使用了。