You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Unity3D C#中实现支持Skinned Mesh Renderer的无碰撞器自定义射线检测

Custom Raycast for Skinned Mesh Renderer (No Colliders Needed)

Hey there! Let's tackle your problem step by step. First, let's address your questions about Unity's RaycastHit source code, then dive into building that custom raycast system for your FPS.

Can I Access Unity's RaycastHit Source Code?

Unfortunately, Unity's core physics engine (including the internal implementation of RaycastHit and built-in raycast logic) is closed-source. You won't be able to view the raw source code for these components. That said, Unity's official documentation thoroughly explains all properties and usage of RaycastHit—which is all you need to replicate its functionality for your shooting detection use case.

Building a Custom Raycast System for Skinned Mesh Renderer

The key here is to work directly with the animated vertex data of your Skinned Mesh Renderer, instead of relying on collision components. Here's a structured, performance-friendly approach:

1. Bake the Skinned Mesh in Real-Time

Skinned meshes update vertex positions dynamically as animations play. To get the current, animated state of the mesh, use SkinnedMeshRenderer.BakeMesh() to render the current pose into a temporary Mesh object. This gives you up-to-date vertices, triangles, and normals for raycasting.

// In your script, declare these variables
[SerializeField] private SkinnedMeshRenderer _playerSkinnedRenderer;
private Mesh _tempBakedMesh;

void Awake()
{
    _tempBakedMesh = new Mesh();
}

// Call this when you need to perform a raycast (e.g., on player shoot)
void PerformShootRaycast(Ray shootRay)
{
    // Bake the current animated mesh into the temp mesh
    _playerSkinnedRenderer.BakeMesh(_tempBakedMesh);
    
    // Now use _tempBakedMesh for ray-triangle intersection checks
}

2. Ray-Triangle Intersection Logic

The core of your custom raycast will be checking if your shoot ray intersects any triangle in the baked mesh. The Möller–Trumbore algorithm is the industry standard for fast, accurate ray-triangle intersection. Here's a simplified C# implementation tailored for Unity:

// Helper struct to replicate RaycastHit functionality (customize as needed)
public struct CustomHitInfo
{
    public Vector3 point;
    public float distance;
    public Vector3 normal;
    // Add extra properties like triangle index or bone ID if needed
}

// Check if a ray intersects a single triangle
private bool RayIntersectsTriangle(Ray ray, Vector3 v0, Vector3 v1, Vector3 v2, out CustomHitInfo hitInfo)
{
    hitInfo = new CustomHitInfo();
    
    Vector3 edge1 = v1 - v0;
    Vector3 edge2 = v2 - v0;
    Vector3 h = Vector3.Cross(ray.direction, edge2);
    float a = Vector3.Dot(edge1, h);
    
    // Ray is parallel to triangle
    if (a > -Mathf.Epsilon && a < Mathf.Epsilon)
        return false;
    
    float f = 1f / a;
    Vector3 s = ray.origin - v0;
    float u = f * Vector3.Dot(s, h);
    
    if (u < 0f || u > 1f)
        return false;
    
    Vector3 q = Vector3.Cross(s, edge1);
    float v = f * Vector3.Dot(ray.direction, q);
    
    if (v < 0f || u + v > 1f)
        return false;
    
    // Calculate intersection distance
    float t = f * Vector3.Dot(edge2, q);
    
    if (t > Mathf.Epsilon)
    {
        hitInfo.point = ray.origin + ray.direction * t;
        hitInfo.distance = t;
        hitInfo.normal = Vector3.Cross(edge1, edge2).normalized;
        return true;
    }
    
    return false;
}

3. Optimize the Detection

Traversing every triangle can be slow for high-poly models. Add these optimizations to keep performance snappy:

  • Bounds Check First: Before checking every triangle, test if the ray intersects the baked mesh's Bounds. If not, skip all triangle checks entirely:
    if (!_tempBakedMesh.bounds.IntersectRay(shootRay))
        return;
    
  • Only Run on Shooting: Don't perform this check every frame. Trigger it only when the player fires a shot—this drastically reduces calculation frequency.
  • Use a Simplified Mesh: If your player model is extremely high-poly, bake a low-poly skinned mesh specifically for raycasting (it'll be faster and still accurate enough for hit detection).

4. Assemble the Full Raycast Function

Put it all together to find the closest intersection point (just like Unity's built-in Physics.Raycast):

public bool CustomSkinnedMeshRaycast(Ray shootRay, out CustomHitInfo closestHit)
{
    closestHit = new CustomHitInfo();
    closestHit.distance = Mathf.Infinity;
    
    // Bake the current mesh pose
    _playerSkinnedRenderer.BakeMesh(_tempBakedMesh);
    
    // Early exit if ray doesn't hit the mesh bounds
    if (!_tempBakedMesh.bounds.IntersectRay(shootRay))
        return false;
    
    // Get mesh data
    int[] triangles = _tempBakedMesh.triangles;
    Vector3[] vertices = _tempBakedMesh.vertices;
    Transform meshTransform = _playerSkinnedRenderer.transform;
    
    bool hitFound = false;
    
    // Iterate through each triangle
    for (int i = 0; i < triangles.Length; i += 3)
    {
        // Convert local vertex positions to world space
        Vector3 v0 = meshTransform.TransformPoint(vertices[triangles[i]]);
        Vector3 v1 = meshTransform.TransformPoint(vertices[triangles[i+1]]);
        Vector3 v2 = meshTransform.TransformPoint(vertices[triangles[i+2]]);
        
        CustomHitInfo tempHit;
        if (RayIntersectsTriangle(shootRay, v0, v1, v2, out tempHit))
        {
            hitFound = true;
            // Keep track of the closest hit
            if (tempHit.distance < closestHit.distance)
            {
                closestHit = tempHit;
            }
        }
    }
    
    return hitFound;
}

Usage Example

Call this function when the player shoots:

void Update()
{
    if (Input.GetMouseButtonDown(0))
    {
        Ray shootRay = Camera.main.ScreenPointToRay(Input.mousePosition);
        CustomHitInfo hit;
        
        if (CustomSkinnedMeshRaycast(shootRay, out hit))
        {
            Debug.Log($"Hit player at {hit.point}, distance: {hit.distance}");
            // Handle damage, hit effects, etc.
        }
    }
}

Final Notes

This approach avoids the performance overhead of Mesh Collider (which recalculates collision data every frame for animated meshes) by only computing intersections when needed. Since you're working directly with the mesh's vertex data, it's fully accurate to the animated pose of your player model.

内容的提问来源于stack exchange,提问作者Je moder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:28:24