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

C#实现的Unity集群系统编辑器正常 Android AR打包后粒子随机飞散

Unity Flocking集群系统Android AR构建运行异常求助

问题描述

基于两份C#脚本实现flocking集群效果:通过自定义逻辑控制集群对象周期性切换追踪目标、停止追踪目标状态,最终呈现类似呼吸的集群内外收放动态效果。

  • 运行表现差异:
    • Unity编辑器内运行:效果完全符合预期
    • 打包为Android平台AR应用后:粒子在前5-10秒可正常遵循脚本逻辑运动,之后会完全脱离控制随机飞散
  • 已排除诱因:有推测认为异常由AR Session Origin组件工作异常导致,但测试验证无论是否配置Image Target,异常都会稳定触发
  • 目前构建目标平台为Android,寻求有同类问题排查经验的开发者提供排查方向参考
  • 效果参考视频:

相关脚本代码

FlockManager.cs

using System.Collections.Generic;
using UnityEngine;

public class FlockManager : MonoBehaviour 
{
    // 集群粒子对象列表
    public List<GameObject> particles;
    public GameObject particlePrefab;
    public Vector3 range = new Vector3(2,2,2);
    public Material[] mats;

    public bool seekGoal = true;
    public bool obedient = true;
    public bool willful = false;

    [Range(0, 200)]
    public int neighbourDistance =50;

    [Range(0,2)]
    public float maxForce = 1.0f;

    [Range(0,5)]
    public float maxvelocity = 2.0f;
    
    // 集群粒子数量动态变化数据集
    public int[] dataSet = new int[] {128, 135, 151, 168, 187, 206, 229, 253, 279, 307, 338, 371, 407, 446, 487, 533};
    
    int dataIndex = 0;

    void Start()
    {
        generateParticles(128);
        willful = false;
        // 每5秒切换一次追踪/自由状态
        InvokeRepeating("toggleSeek", 0f, 5f);
        // 每10秒调整一次集群粒子数量
        InvokeRepeating("setParticleNumbers", 0f, 10f);
    }

    void setParticleNumbers()
    {
        if(dataIndex == dataSet.Length)
        {
            dataIndex = 0;
        }
        generateParticles(dataSet[dataIndex]);
        dataIndex = dataIndex + 1;
    }

    
    void toggleSeek()
    {
        seekGoal = !seekGoal;
        obedient = !obedient;
        willful = !willful;
    }

    void generateParticles(int numberOfParticles)
    {
        var difference = numberOfParticles - particles.Count;
        if(difference >= 0)
        {
            for(int i = 0; i < difference; i++)
            {
                addParticle(); 
            }
        }
        else
        {
            for(int i = 0; i < difference * -1; i++)
            {
                // 销毁场景中多余粒子
                Destroy(particles[particles.Count - 1]); 
                // 从列表中移除对应粒子引用
                particles.RemoveAt(particles.Count - 1);
            }
        }
    }

    void addParticle()
    {
        Vector3 particlePos = new Vector3(Random.Range(-range.x, range.x), Random.Range(-range.y, range.y), Random.Range(-range.z, range.z));
        var GO = Instantiate(particlePrefab, this.transform.position + particlePos, Quaternion.identity) as GameObject;
        GO.GetComponent<FlockParticleBehaviour>().manager = this.gameObject;
        particles.Add(GO);
        GO.GetComponent<Renderer>().material = mats[Random.Range(0,mats.Length)];
    }
}

FlockParticleBehaviour.cs

using System.Collections.Generic;
using UnityEngine;

public class FlockParticleBehaviour : MonoBehaviour
{
    public GameObject manager;
    public Vector3 location = Vector3.zero;
    public Vector3 velocity;
    Vector3 goalPos = Vector3.zero;
    Vector3 currentForce;

    void Start()
    {
        velocity = new Vector3(Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f));
        location = new Vector3(this.gameObject.transform.position.x, this.gameObject.transform.position.y, this.gameObject.transform.position.z);
    }

    // 指向目标位置的引导向量
    Vector3 seek(Vector3 target)
    {
        return(target - location);
    }

    void applyForce(Vector3 f)
    {
        Vector3 force = new Vector3(f.x, f.y, f.z);
        if(force.magnitude > manager.GetComponent<FlockManager>().maxForce)
        {
            force = force.normalized;
            force *= manager.GetComponent<FlockManager>().maxForce;
        }
        this.GetComponent<Rigidbody>().AddForce(force);

        if(this.GetComponent<Rigidbody>().velocity.magnitude > manager.GetComponent<FlockManager>().maxvelocity)
        {
            this.GetComponent<Rigidbody>().velocity = this.GetComponent<Rigidbody>().velocity.normalized;
            this.GetComponent<Rigidbody>().velocity *= manager.GetComponent<FlockManager>().maxvelocity;
        }

        Debug.DrawRay(this.transform.position, force, Color.white);
    }

    // 对齐规则:匹配邻域粒子平均速度
    Vector3 align()
    {
        float neighbourdist = manager.GetComponent<FlockManager>().neighbourDistance;
        Vector3 sum = Vector3.zero;
        int count = 0;
        foreach (GameObject other in manager.GetComponent<FlockManager>().particles)
        {
            if(other == this.gameObject) continue;

            float d = Vector3.Distance(location, other.GetComponent<FlockParticleBehaviour>().location);

            if (d < neighbourdist) {
                sum += other.GetComponent<FlockParticleBehaviour>().velocity;
                count++;
            }
        }
        if (count >0)
        {
            sum /= count;
            Vector3 steer = sum - velocity;
            return steer;
        }

        return Vector3.zero;
    }

    // 聚合规则:向邻域粒子平均位置移动
    Vector3 cohesion()
    {
        float neighbourdist = manager.GetComponent<FlockManager>().neighbourDistance;
        Vector3 sum = Vector3.zero;
        int count = 0;
        foreach (GameObject other in manager.GetComponent<FlockManager>().particles)
        {
            if(other == this.gameObject) continue;

            float d = Vector3.Distance(location, other.GetComponent<FlockParticleBehaviour>().location);
            if(d < neighbourdist)
            {
                sum += other.GetComponent<FlockParticleBehaviour>().location;
                count++;
            }
        }

        if (count > 0)
        {
            sum /= count;
            return seek(sum);
        }

        return Vector3.zero;
    }


    void flock()
    {
        location = this.transform.position;
        velocity = this.GetComponent<Rigidbody>().velocity;

        if(manager.GetComponent<FlockManager>().obedient && Random.Range(0,50) <=1)
        {
            Vector3 ali = align();
            Vector3 coh = cohesion();
            Vector3 gl;

            if(manager.GetComponent<FlockManager>().seekGoal)
            {
                gl = seek(goalPos);
                currentForce = gl + ali +coh;
            }
            else
            currentForce = ali + coh;

            currentForce = currentForce.normalized;
        }

        if(manager.GetComponent<FlockManager>().willful && Random.Range(0,50)<=1)
        {
            if(Random.Range(0,50)<1) // 随机改变运动方向
            currentForce = new Vector3(Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f),Random.Range(0.01f, 0.1f));
        }

        applyForce(currentForce);

    }

    void Update()
    {
        flock();
        goalPos = manager.transform.position;
    }
}

内容的提问来源于stack exchange,提问作者Delayne Rutter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 20:39:21