Unity案例10-水晶矿石碰撞碎裂案例

思来想去,由于我还是喜欢一些案例的,干脆这个系列就叫做Unity游戏案例合集,算不算一个完整的教程,文章基本上不会有很多的人会真的看到或者真的对你没有多大的帮助,但是还是要写一下,这不是一个教程,只是我分享的一个案例

首先打开Blender,或者其他的建模软件,我首推blender,因为它实在轻便,而且还开源免费,适合新手建模和独立游戏开发者,好上手,功能多

按照你自己的想法建模,然后五个小物件不要合并在一起,如果手滑合并了可以在编辑模式下按L键后右键拆分开来

接着我们进入Unity,导入矿石,给预制件添加一个Rigidbody,一个Box Collider碰撞,按照图示摆放一下

想要出发碰撞就需要有碰撞箱参与,同时,在实战操作中,不是所有的对象都要碰撞,因此,在地表那些你想要做碰撞的物体上面,添加一个Mesh Collider,例如:

然后就是关键的一步,无论你是要做玩家的碰撞或者物体的碰撞,都要有明确的标签或者图层,我们可以自定义标签:

然后我们新建一个脚本OreShatter.cs(现在让AI帮我们干一些简单零碎的活还是比较轻松的),把脚本拖给预制件,设置一下碎裂声音和VFX特效(可以设置一个列表随机播放,此处为Demo不给详细的代码)

cs 复制代码
using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 矿石碎裂控制器
/// 功能:矿石(含多个子物件)从空中掉落,第一次碰到地面后
///       解绑所有子物件,让它们各自受物理影响散落一地
/// 
/// 使用方式:
///   1. 把此脚本挂在一个父物体(如 "Ore")上
///   2. 父物体加 Rigidbody(Use Gravity ✔)和 MeshCollider(Convex ✔)
///   3. 所有子物件(碎片)也挂 MeshCollider(Convex ✔)
///   4. 地面标签设为 "Ground"
/// </summary>
public class OreShatter : MonoBehaviour
{
    [Header("基本设置")]
    [Tooltip("应用材质")]
    public Material oreMaterial;

    [Header("碎裂设置")]
    [Tooltip("碎裂后子物件受到的随机爆炸力大小")]
    public float explosionForce = 8f;

    [Tooltip("爆炸力作用半径")]
    public float explosionRadius = 2f;

    [Tooltip("随机向上冲量(让碎片弹起来)")]
    public float upwardBias = 3f;

    [Tooltip("每个碎片受到的随机扭矩大小")]
    public float randomTorque = 15f;

    [Tooltip("碎裂后子物件的重力缩放(>1 更重,<1 更飘)")]
    public float childGravityScale = 1f;

    [Tooltip("碎裂后子物件的阻力")]
    public float childDrag = 0.05f;

    [Tooltip("碎裂后子物件的角阻力")]
    public float childAngularDrag = 0.05f;

    [Header("效果")]
    [Tooltip("碎裂时是否播放粒子效果")]
    public ParticleSystem shatterVFX;

    [Tooltip("碎裂时是否播放音效")]
    public AudioClip shatterSFX;

    [Header("调试")]
    public bool showDebugLog = true;

    // ─── 内部状态 ───
    private Rigidbody parentRigidbody;
    private bool hasShattered = false;
    private List<Transform> childFragments = new List<Transform>();

    void Start()
    {
        ChangeAllChildrenMaterial();

        parentRigidbody = GetComponent<Rigidbody>();

        if (parentRigidbody == null)
        {
            Debug.LogError("[OreShatter] 父物体上没有 Rigidbody!请添加一个 Rigidbody 组件。");
            enabled = false;
            return;
        }

        // 收集所有一级子物件
        foreach (Transform child in transform)
        {
            childFragments.Add(child);
            
        }

        if (showDebugLog)
            Debug.Log($"[OreShatter] 初始化完成,共 {childFragments.Count} 个子碎片待命。");

        
    }

    public void ChangeAllChildrenMaterial()
    {
        // 第二个参数 true:连未激活的子物体也一起改
        Renderer[] renderers = GetComponentsInChildren<Renderer>(true);

        foreach (Renderer rend in renderers)
        {
            // 关键:多材质槽的物体,要整体新建数组再赋值
            Material[] mats = new Material[rend.materials.Length];
            for (int i = 0; i < mats.Length; i++)
                mats[i] = oreMaterial;

            rend.materials = mats;
        }
    }

    /// <summary>
    /// 碰撞检测 ------ 第一次碰到地面就碎裂
    /// </summary>
    void OnCollisionEnter(Collision collision)
    {
        if (hasShattered) return;

        // 只响应地面碰撞(按标签判断,避免碰到墙壁就碎)
        if (collision.gameObject.CompareTag("Ground"))
        {
            Shatter();
            Debug.Log($"[OreShatter] 碰撞到地面,触发碎裂。");
        }
    }

    /// <summary>
    /// 核心:碎裂逻辑
    /// </summary>
    public void Shatter()
    {
        if (hasShattered) return;
        hasShattered = true;

        if (showDebugLog)
            Debug.Log($"[OreShatter] 💥 矿石撞击地面,开始碎裂!共 {childFragments.Count} 个碎片。");

        // 记录父物体当前速度,用于给碎片继承
        Vector3 parentVelocity = parentRigidbody != null ? parentRigidbody.velocity : Vector3.zero;

        // 1. 解除父物体 Rigidbody 的动力学控制(不再整体运动)
        parentRigidbody.isKinematic = true;
        parentRigidbody.detectCollisions = false;

        // 2. 隐藏父物体的 MeshCollider(避免整体碰撞体还挡着)
        MeshCollider parentCollider = GetComponent<MeshCollider>();
        if (parentCollider != null) parentCollider.enabled = false;

        // 3. 遍历所有子碎片,解绑 + 加物理
        foreach (Transform fragment in childFragments)
        {
            if (fragment == null) continue;

            // ── 解绑:把子物件从父物体下移出,放到场景根层级
            //    这样它们就不再是父物体的子物件,各自独立受物理控制
            fragment.SetParent(null);

            // ── 添加 Rigidbody
            Rigidbody rb = fragment.gameObject.GetComponent<Rigidbody>();
            if (rb == null)
                rb = fragment.gameObject.AddComponent<Rigidbody>();

            rb.useGravity = true;
            rb.mass = Random.Range(0.5f, 2f);          // 随机质量,散落更自然
            rb.drag = childDrag;
            rb.angularDrag = childAngularDrag;

            // ── 确保有 Collider(没有就加 MeshCollider)
            Collider col = fragment.gameObject.GetComponent<Collider>();
            if (col == null)
            {
                MeshCollider mc = fragment.gameObject.AddComponent<MeshCollider>();
                mc.convex = true; // 必须 convex 才能和 Rigidbody 一起用
            }
            else
            {
                // 已有 collider,确保 convex
                MeshCollider mc = col as MeshCollider;
                if (mc != null) mc.convex = true;
            }

            // ── 施加爆炸力(从矿石中心向外推)
            Vector3 explosionDir = (fragment.position - transform.position).normalized;
            if (explosionDir == Vector3.zero) explosionDir = Random.insideUnitSphere.normalized;

            // 混合:继承父物体部分速度 + 爆炸冲量 + 向上偏移
            Vector3 finalVelocity = parentVelocity * 0.3f
                                  + explosionDir * explosionForce
                                  + Vector3.up * upwardBias;

            rb.velocity = finalVelocity;

            // ── 随机扭矩(让碎片旋转翻滚)
            rb.AddTorque(
                Random.Range(-randomTorque, randomTorque),
                Random.Range(-randomTorque, randomTorque),
                Random.Range(-randomTorque, randomTorque),
                ForceMode.Impulse
            );
        }

        // 4. 播放特效
        if (shatterVFX != null)
        {
            ParticleSystem vfx = Instantiate(shatterVFX, transform.position, Quaternion.identity);
            vfx.Play();
            Destroy(vfx.gameObject, vfx.main.duration + 1f);
        }

        // 5. 播放音效
        if (shatterSFX != null)
        {
            AudioSource.PlayClipAtPoint(shatterSFX, transform.position);
        }

        // 6. 延迟销毁原来的空壳父物体
        Destroy(gameObject, 0.5f);
    }

    /// <summary>
    /// 手动触发碎裂(可在动画事件或其他逻辑中调用)
    /// </summary>
    public void TriggerShatter()
    {
        Shatter();
    }

    /// <summary>
    /// 可视化爆炸半径(编辑器中可见)
    /// </summary>
    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, explosionRadius);
    }
}

测试一下:

矿石碎裂Demo

相关推荐
xcLeigh2 小时前
Unity基础:Input输入系统入门——键盘、鼠标与触摸检测
unity·计算机外设·游戏引擎
zhchyun200821 小时前
【Unity UI 进阶】仿 Element UI 打造企业级 Unity UI 组件库(05)
ui·unity·游戏引擎
_ZHOURUI_H_1 天前
不做完整 ECS,只优化数据布局:Unity EasyECS 到底是什么
unity·性能优化·游戏引擎
郝学胜-神的一滴2 天前
Horse3D 游戏引擎研发笔记(七):Clydesdale——从流式日志到多输出订阅
c++·qt·unity·游戏引擎·图形渲染·unreal engine·opengl
江上闲人.3 天前
Wardogs战犬\战狗Beta测试启动报错UE崩溃卡顿?解决方法来了
游戏·游戏引擎·虚幻·战犬·战狗·wardogs
小贺儿开发3 天前
Unity 程序员编曲花絮:汪峰《河流》细节修复
科技·学习·unity·程序员·音乐·demo·写代码
598866753@qq.com3 天前
Unity UniText笔记
unity·unitext
杨丰玮4183 天前
从零手写Java飞机躲障碍游戏|Swing绘图、鼠标跟随、计时器碰撞检测实战(五)
java·python·游戏·游戏引擎·图形渲染·动画·贴图