Пример #1
0
        /// <summary>
        /// create pool (array) of fragment game objects with all necessary components
        /// </summary>
        /// <param name="poolSize">number of fragments</param>
        public void Allocate(int poolSize)
        {
            Exploder2DUtils.Assert(poolSize > 0, "");

            if (pool == null || pool.Length < poolSize)
            {
                DestroyFragments();

                pool = new Fragment2D[poolSize];

                for (int i = 0; i < poolSize; i++)
                {
                    var fragment = new GameObject("fragment_" + i);
                    fragment.AddComponent <SpriteRenderer>();
                    fragment.AddComponent <PolygonCollider2D>();
                    fragment.AddComponent <Rigidbody2D>();
                    fragment.AddComponent <Exploder2DOption>();

                    var fragmentComponent = fragment.AddComponent <Fragment2D>();

                    fragment.transform.parent = gameObject.transform;

                    pool[i] = fragmentComponent;

                    Exploder2DUtils.SetActiveRecursively(fragment.gameObject, false);

                    fragmentComponent.RefreshComponentsCache();

                    fragmentComponent.Sleep();
                }
            }
        }
Пример #2
0
        public void OnExplosionFinished(int id)
        {
            var explosion = queue.Dequeue();

            Exploder2DUtils.Assert(explosion.id == id, "Explosion id mismatch!");
            ProcessQueue();
        }
Пример #3
0
        private int GetLevel(float distance, float radius)
        {
            Exploder2DUtils.Assert(distance >= 0.0f, "");
            Exploder2DUtils.Assert(radius >= 0.0f, "");

            var normDistance = distance / radius * 6;
            var level        = (int)normDistance / 2 + 1;

            return(Mathf.Clamp(level, 0, 10));
        }
Пример #4
0
        /// <summary>
        /// explode cracked objects
        /// Use this method in combination with Crack()
        /// Purpose of this method is to get higher performance of explosion, Crack() will
        /// calculate the explosion and prepare all fragments. Calling ExplodeCracked() will
        /// then start the explosion (flying fragments...) immediately
        /// </summary>
        public void ExplodeCracked(OnExplosion callback)
        {
            Exploder2DUtils.Assert(crack, "You must call Crack() first!");

            if (cracked)
            {
                PostCrackExplode(callback);
                crack = false;
            }
        }
Пример #5
0
        /// <summary>
        /// crack will calculate fragments and prepare object for explosion
        /// Use this method in combination with ExplodeCracked()
        /// Purpose of this method is to get higher performance of explosion, Crack() will
        /// calculate the explosion and prepare all fragments. Calling ExplodeCracked() will
        /// then start the explosion (flying fragments...) immediately
        /// </summary>
        public void Crack(OnCracked callback)
        {
            Exploder2DUtils.Assert(!crack, "Another crack in progress!");

            if (!crack)
            {
                CrackedCallback = callback;
                crack           = true;
                cracked         = false;
                Explode(null);
            }
        }
Пример #6
0
        /// <summary>
        /// callback from queue, do not call this unles you know what you are doing!
        /// </summary>
        public void StartExplosionFromQueue(Vector2 pos, int id, OnExplosion callback)
        {
            Exploder2DUtils.Assert(state == State.None, "Wrong state: " + state);

            mainCentroid      = pos;
            explosionID       = id;
            state             = State.Preprocess;
            ExplosionCallback = callback;

#if DBG
            processingTime        = 0.0f;
            preProcessingTime     = 0.0f;
            postProcessingTime    = 0.0f;
            postProcessingTimeEnd = 0.0f;
            isolatingIslandsTime  = 0.0f;
            cuts = 0;
#endif
        }
Пример #7
0
        /// <summary>
        /// returns list of fragments with requested size
        /// this method pick fragments hidden from camera or sleeping rather then visible
        /// </summary>
        /// <param name="size">number of requested fragments</param>
        /// <returns>list of fragments</returns>
        public List <Fragment2D> GetAvailableFragments(int size)
        {
            if (size > pool.Length)
            {
                Debug.LogError("Requesting pool size higher than allocated! Please call Allocate first! " + size);
                return(null);
            }

            if (size == pool.Length)
            {
                return(new List <Fragment2D>(pool));
            }

            var fragments = new List <Fragment2D>();

            int counter = 0;

            // get deactivated fragments first
            foreach (var fragment in pool)
            {
                // get invisible fragments
                if (!fragment.activeObj)
                {
                    fragments.Add(fragment);
                    counter++;
                }

                if (counter == size)
                {
                    return(fragments);
                }
            }

            foreach (var fragment in pool)
            {
                // get invisible fragments
                if (!fragment.visible)
                {
                    fragments.Add(fragment);
                    counter++;
                }

                if (counter == size)
                {
                    return(fragments);
                }
            }

            // there are still live fragments ... get sleeping ones
            if (counter < size)
            {
                foreach (var fragment in pool)
                {
                    if (fragment.IsSleeping() && fragment.visible)
                    {
                        Exploder2DUtils.Assert(!fragments.Contains(fragment), "!!!");
                        fragments.Add(fragment);
                        counter++;
                    }

                    if (counter == size)
                    {
                        return(fragments);
                    }
                }
            }

            // there are still live fragments...
            if (counter < size)
            {
                foreach (var fragment in pool)
                {
                    if (!fragment.IsSleeping() && fragment.visible)
                    {
                        Exploder2DUtils.Assert(!fragments.Contains(fragment), "!!!");
                        fragments.Add(fragment);
                        counter++;
                    }

                    if (counter == size)
                    {
                        return(fragments);
                    }
                }
            }

            Exploder2DUtils.Assert(false, "ERROR!!!");
            return(null);
        }
Пример #8
0
        void Update()
        {
            long cuttingTime = 0;

            switch (state)
            {
            case State.None:
                break;

            case State.Preprocess:
            {
                timer.Reset();
                timer.Start();
#if DBG
                var watch = new Stopwatch();
                watch.Start();
#endif

                // get mesh data from game object in radius
                var readyToCut = Preprocess();

#if DBG
                watch.Stop();
                preProcessingTime = watch.ElapsedMilliseconds;
                processingFrames  = 0;
#endif

                // nothing to explode
                if (!readyToCut)
                {
#if DBG
                    Exploder2DUtils.Log("Nothing to explode " + mainCentroid);
#endif
                    OnExplosionFinished(false);
                }
                else
                {
                    // continue to cutter, don't wait to another frame
                    state = State.ProcessCutter;
                    goto case State.ProcessCutter;
                }
            }
            break;

            case State.ProcessCutter:
            {
#if DBG
                processingFrames++;
                var watch = new Stopwatch();
                watch.Start();
#endif

                // process main cutter
                var cuttingFinished = false;

                switch (CuttingStrategy)
                {
                case CutStrategy.Randomized:
                    cuttingFinished = ProcessCutterRandomized(out cuttingTime);
                    break;

//                            case CutStrategy.Grid:
//                                cuttingFinished = ProcessCutterGrid(out cuttingTime);
//                                break;

                default: Exploder2DUtils.Assert(false, "Invalid cutting strategy");
                    break;
                }

#if DBG
                watch.Stop();
                processingTime += watch.ElapsedMilliseconds;
#endif

                if (cuttingFinished)
                {
                    poolIdx  = 0;
                    postList = new List <CutMesh>(meshSet);

                    // continue to next state if the cutter is finished
                    if (splitMeshIslands)
                    {
#if DBG
                        isolatingIslandsFrames = 0;
                        isolatingIslandsTime   = 0.0f;
#endif
                        islands = new List <CutMesh>(meshSet.Count);

                        state = State.IsolateMeshIslands;
                        goto case State.IsolateMeshIslands;
                    }

                    state = State.PostprocessInit;
                    goto case State.PostprocessInit;
                }
            }
            break;

            case State.IsolateMeshIslands:
            {
#if DBG
                var watchPost = new Stopwatch();
                watchPost.Start();
#endif

                var isolatingFinished = IsolateMeshIslands(ref cuttingTime);

#if DBG
                watchPost.Stop();
                isolatingIslandsFrames++;
                isolatingIslandsTime += watchPost.ElapsedMilliseconds;
#endif

                if (isolatingFinished)
                {
                    // goto next state
                    state = State.PostprocessInit;
                    goto case State.PostprocessInit;
                }
            }
            break;

            case State.PostprocessInit:
            {
                InitPostprocess();

                state = State.Postprocess;
                goto case State.Postprocess;
            }

            case State.Postprocess:
            {
#if DBG
                var watchPost = new Stopwatch();
                watchPost.Start();
#endif
                if (Postprocess(cuttingTime))
                {
                    timer.Stop();
                }
#if DBG
                watchPost.Stop();
                postProcessingTime += watchPost.ElapsedMilliseconds;
#endif
            }
            break;
            }
        }
Пример #9
0
        private bool ProcessCutterRandomized(out long cuttingTime)
        {
            Exploder2DUtils.Assert(state == State.ProcessCutter || state == State.DryRun, "Wrong state!");

            var stopWatch = new Stopwatch();

            stopWatch.Start();

            bool cutting        = true;
            bool timeBudgetStop = false;
            var  cycleCounter   = 0;

            cuttingTime = 0;

            while (cutting)
            {
                cycleCounter++;

                if (cycleCounter > TargetFragments)
                {
                    Exploder2DUtils.Log("Explode Infinite loop!");
                    break;
                }

                newFragments.Clear();
                meshToRemove.Clear();

                cutting = false;
                var fragmentsCount = meshSet.Count;

                foreach (var mesh in meshSet)
                {
                    if (levelCount[mesh.level] > 0)
                    {
                        var randomLineNormalized = Random.insideUnitCircle;

                        if (!mesh.transform)
                        {
                            continue;
                        }

                        var plane = Core.Math.Line2D.CreateNormalPoint(randomLineNormalized, mesh.centroidLocal);

#if DBG
                        cuts++;
#endif

                        if (mesh.option)
                        {
                            splitMeshIslands |= mesh.option.SplitMeshIslands;
                        }

                        List <CutterMesh> meshes = null;
                        cutter.Cut(mesh.spriteMesh, mesh.transform, plane, ref meshes);

                        cutting = true;

                        if (meshes != null)
                        {
                            foreach (var cutterMesh in meshes)
                            {
                                newFragments.Add(new CutMesh
                                {
                                    spriteMesh    = cutterMesh.mesh,
                                    centroidLocal = cutterMesh.centroidLocal,

                                    sprite    = mesh.sprite,
                                    vertices  = mesh.vertices,
                                    transform = mesh.transform,
                                    distance  = mesh.distance,
                                    level     = mesh.level,
                                    fragments = mesh.fragments,
                                    original  = mesh.original,

                                    parent     = mesh.transform.parent,
                                    position   = mesh.transform.position,
                                    rotation   = mesh.transform.rotation,
                                    localScale = mesh.transform.localScale,

                                    sortingLayer = mesh.sortingLayer,
                                    orderInLayer = mesh.orderInLayer,
                                    color        = mesh.color,

                                    option = mesh.option,
                                });
                            }

                            meshToRemove.Add(mesh);

                            levelCount[mesh.level] -= 1;

                            // stop this madness!
                            if (fragmentsCount + newFragments.Count - meshToRemove.Count >= TargetFragments)
                            {
                                cuttingTime = stopWatch.ElapsedMilliseconds;
                                meshSet.ExceptWith(meshToRemove);
                                meshSet.UnionWith(newFragments);
                                return(true);
                            }

                            // computation took more than FrameBudget ...
                            if (stopWatch.ElapsedMilliseconds > FrameBudget)
                            {
                                timeBudgetStop = true;
                                break;
                            }
                        }
                    }
                }

                meshSet.ExceptWith(meshToRemove);
                meshSet.UnionWith(newFragments);

                if (timeBudgetStop)
                {
                    break;
                }
            }

            cuttingTime = stopWatch.ElapsedMilliseconds;

            // explosion is finished
            if (!timeBudgetStop)
            {
                return(true);
            }

            return(false);
        }