Пример #1
0
        /// <summary> Traces path using some kind of A* algorithm </summary>
        /// <param name="start"> Start index 2d </param>
        /// <param name="destination"> Destination index 2d </param>
        /// <param name="moveCost"> Move cost data 2d array </param>
        /// <param name="moveCost_width"> 2d array's width </param>
        /// <param name="heuristic_cost"> Cost heuristic multiplier. Figure out yourself what value works best for your specific moveCost data </param>
        /// <param name="heuristic_search"> Search heuristic multiplier. Figure out yourself what value works best for your specific moveCost data </param>
        /// <param name="output_path"> Resulting path goes here </param>
        public unsafe AStarJob
        (
            INT2 start,
            INT2 destination,
            NativeArray <float> moveCost,
            int moveCost_width,
            float heuristic_cost,
            float heuristic_search,
            NativeList <int2> output_path
        )
        {
            this.start            = start;
            this.destination      = destination;
            this.moveCost         = moveCost;
            this.moveCost_width   = moveCost_width;
            this.Results          = output_path;
            this.heuristic_cost   = heuristic_cost;
            this.heuristic_search = heuristic_search;

            int length  = moveCost.Length;
            int start1d = Index2dTo1d(start, moveCost_width);

            _F_      = new NativeArray <float>(length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
            solution = new NativeArray <int2>(length, Allocator.TempJob);
            frontier = new NativeMinHeap <int2, AStarJobComparer>(
                new AStarJobComparer(_F_, moveCost_width, destination, heuristic_search),
                Allocator.TempJob, length
                );
            visited    = new NativeHashMap <int2, byte>(length, Allocator.TempJob);          //TODO: use actual hashSet once available
            neighbours = new NativeList <int2>(8, Allocator.TempJob);
        }
Пример #2
0
        public string ToFixedSizeString()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(ID.ToString().PadLeft(10, '0'));
            sb.Append('^');
            sb.Append(INT1.ToString().PadLeft(10, '0'));
            sb.Append('^');
            sb.Append(INT2.ToString().PadLeft(10, '0'));
            sb.Append('^');
            sb.Append(INT3.ToString().PadLeft(10, '0'));
            sb.Append('^');
            sb.Append(DT1.PadLeft(25, '#'));
            sb.Append('^');
            sb.Append(DT2.PadLeft(25, '#'));
            sb.Append('^');
            sb.Append(DT3.PadLeft(25, '#'));
            sb.Append('^');
            sb.Append(VAR1.PadLeft(100, '#'));
            sb.Append('^');
            sb.Append(VAR2.PadLeft(100, '#'));
            sb.Append('^');
            sb.Append(VAR3.PadLeft(100, '#'));

            return(sb.ToString());
        }
Пример #3
0
 public AStarJobComparer(NativeArray <float> _G_, int weightsWidth, INT2 destination, float heuristic_search)
 {
     this._F_Ptr           = NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(_G_);
     this._width           = weightsWidth;
     this._dest            = destination;
     this.heuristic_search = heuristic_search;
 }
Пример #4
0
    /// <summary> Gets the surrounding field values </summary>
    /// <returns>
    /// 8-bit clockwise-enumerated bit values
    ///		7 0 1		[x-1,y+1]   [x,y+1]   [x+1,y+1]
    ///		6 ^ 2	==	[x-1,y]      [x,y]    [x+1,y]
    ///		5 4 3		[x-1,y-1]   [x,y-1]   [x+1,y-1]
    /// for example: 1<<0 is top, 1<<1 is top-right, 1<<2 is right, 1<<6|1<<4|1<<2 is both left,down and right
    /// </returns>
    public byte GetMarchingSquares(INT2 index2d, System.Predicate <STRUCT> predicate)
    {
        int x = index2d.x;
        int y = index2d.y;

        const byte zero   = 0b_0000_0000;
        byte       result = zero;

        //out of bounds test:
        bool xPlus  = x + 1 < Width;
        bool yPlus  = y + 1 < Height;
        bool xMinus = x - 1 >= 0;
        bool yMinus = y - 1 >= 0;

        //top, down:
        result |= yPlus && predicate(this[x, y + 1]) ? (byte)0b_0000_0001 : zero;
        result |= yMinus && predicate(this[x, y - 1]) ? (byte)0b_0001_0000 : zero;

        //right side:
        result |= xPlus && yPlus && predicate(this[x + 1, y + 1]) ? (byte)0b_0000_0010 : zero;
        result |= xPlus && predicate(this[x + 1, y]) ? (byte)0b_0000_0100 : zero;
        result |= xPlus && yMinus && predicate(this[x + 1, y - 1]) ? (byte)0b_0000_1000 : zero;

        //left side:
        result |= xMinus && yPlus && predicate(this[x - 1, y + 1]) ? (byte)0b_0010_0000 : zero;
        result |= xMinus && predicate(this[x - 1, y]) ? (byte)0b_0000_0100 : zero;
        result |= xMinus && yMinus && predicate(this[x - 1, y - 1]) ? (byte)0b_1000_0000 : zero;

        return(result);
    }
    /// <summary> Translate regional coordinate to outer array index 1d </summary>
    /// <param name="R">Outer RectInt</param>
    /// <param name="r">Inner, smaller RectInt</param>
    /// <param name="rx">Inner x coordinate</param>
    /// <param name="ry">Inner y coordinate</param>
    /// <param name="R_width">Outer RectInt.width</param>
    public static int2 IndexTranslate(RectInt r, INT2 rxy)
    {
        Assert_IndexTranslate(r, rxy.x, rxy.y);

        return(new int2 {
            x = r.x, y = r.y
        } +(int2)rxy);
    }
Пример #6
0
        public void TrackVisibleChunks(GameContext gameContext)
        {
            INT3 aPos = playerInfo.positionI.AlignedDown(16);

            for (int x = -128; x < 144; x += 16)
            {
                for (int z = -128; z < 144; z += 16)
                {
                    INT2 testPoint = new INT2(x + aPos.x, z + aPos.z);

                    if (gameContext.regionInfo.TryGetValue(testPoint, out var regionInfo))
                    {
                        for (int y = regionInfo.m_minHeight; y < regionInfo.m_maxHeight; y += 16)
                        {
                            var chunkPosition = new INT3(aPos.x + x, y, aPos.z + z);
                            //var chunk1 = gameCore.GetExistChunk(chunkPosition.x, chunkPosition.y, chunkPosition.z);
                            //if (playerInfo.visibleChunks.TryGetValue(chunkPosition, out var timeStamp1))
                            //{
                            //    if (chunk1 != null && chunk1.stamp > timeStamp1)
                            //    {
                            //        playerInfo.visibleChunks[chunkPosition] = chunk1.stamp;
                            //        NetBlock netBlock = netContext.GetNetBlock(1802856547/*'kuhc'*/, Chunk.c_minimumSize);
                            //        GUtility.ToNetBlock(chunk1, netBlock);
                            //        wrapLayout.SendNetBlock(netBlock);
                            //    }
                            //}
                            //else if (chunk1 != null)
                            //{
                            //    playerInfo.visibleChunks[chunkPosition] = chunk1.stamp;

                            //    NetBlock netBlock = netContext.GetNetBlock(1802856547/*'kuhc'*/, Chunk.c_minimumSize);
                            //    GUtility.ToNetBlock(chunk1, netBlock);
                            //    wrapLayout.SendNetBlock(netBlock);
                            //}
                        }
                        playerInfo.visibleRegions[testPoint] = regionInfo.m_recentUpdateTimeStamp;
                    }
                }
            }

            List <INT3> chunkCullResult = new List <INT3>();

            foreach (var pair in playerInfo.visibleChunks)
            {
                INT3 a = pair.Key - playerInfo.positionI + new INT3(8, 8, 8);
                if (Math.Abs(a.x) + Math.Abs(a.z) > 512)
                {
                    chunkCullResult.Add(pair.Key);
                }
            }
            for (int j = 0; j < chunkCullResult.Count; j++)
            {
                playerInfo.visibleChunks.Remove(chunkCullResult[j]);
            }
        }
Пример #7
0
            public static void PointToIndex2d_Test_1x1(float2 a, float2 b)
            {
                float2 worldSize = new float2 {
                    x = 2f, y = 1f
                }; const int width = 1, height = 1;
                INT2         A = NativeGrid.PointToIndex2d(a, worldSize, width, height);
                INT2         B = NativeGrid.PointToIndex2d(b, worldSize, width, height);

                // Debug.Log( $"a:{GetPositionInsideCell_GetDebugString(a,width,height,worldSize)}\nb:{GetPositionInsideCell_GetDebugString(b,width,height,worldSize)}" );
                Assert.AreEqual(A, B);
            }
Пример #8
0
            static void PointToIndex2d_Test(float2 a, float2 b)
            {
                float2    worldSize = new float2(3000f, 3000f);
                float2    gridOrigin = new float2(-1500f, -1500f);
                const int width = 1500, height = 1500;
                INT2      A = NativeGrid.PointToIndex2d(a - gridOrigin, worldSize, width, height);
                INT2      B = NativeGrid.PointToIndex2d(b - gridOrigin, worldSize, width, height);

                // Debug.Log( $"a:{GetPositionInsideCell_GetDebugString(a,width,height,worldSize)}\nb:{GetPositionInsideCell_GetDebugString(b,width,height,worldSize)}" );
                Assert.AreEqual(A, B);
            }
Пример #9
0
        public void FillRegion(int x, int z, GameContext gameContext)
        {
            INT2 position = new INT2(x, z);

            for (int i = -2; i < 4; i++)
            {
                GetChunk(x, i * 16, z, gameContext);
            }
            var regionInfo = GetRegionInfo(x, z, gameContext);

            regionInfo.m_generatorFilled     = true;
            gameContext.regionInfo[position] = regionInfo;
        }
Пример #10
0
 void ExpandMap()
 {
     foreach (var playerInfo in gameContext.playerInfos.Values)
     {
         INT3 p  = playerInfo.positionI;
         INT2 p1 = new INT2(p.x, p.z).AlignedDown(64);
         for (int x = -192; x < 256; x += 64)
         {
             for (int y = -192; y < 256; y += 64)
             {
                 cover64.Add(new INT2(p1.x + x, p1.y + y));
             }
         }
     }
     foreach (var p64 in cover64)
     {
         int filledCount = 0;
         if (gameContext.regionInfo64.TryGetValue(p64, out var regionInfo64))
         {
             if (regionInfo64.m_generatorFilled)
             {
                 continue;
             }
         }
         else
         {
             regionInfo64 = new RegionInfo();
             gameContext.regionInfo64[p64] = regionInfo64;
         }
         for (int x = 0; x < 64; x += 16)
         {
             for (int y = 0; y < 64; y += 16)
             {
                 INT2 position = new INT2(p64.x + x, p64.y + y);
                 if (!gameContext.regionInfo.TryGetValue(position, out var regionInfo) || !regionInfo.m_generatorFilled)
                 {
                     chunkGenerator.FillRegion(position.x, position.y, gameContext);
                 }
                 else
                 {
                     filledCount++;
                 }
             }
         }
         if (filledCount == 16)
         {
             regionInfo64.m_generatorFilled = true;
         }
     }
     cover64.Clear();
 }
Пример #11
0
            public static void PointToIndex2d_Test_2x2(float2 a, float2 b, bool equalityTest = true)
            {
                float2 worldSize = new float2(2f, 2f); const int width = 2, height = 2;
                INT2   A = NativeGrid.PointToIndex2d(a, worldSize, width, height);
                INT2   B = NativeGrid.PointToIndex2d(b, worldSize, width, height);

                // Debug.Log( $"a:{GetPositionInsideCell_GetDebugString(a,width,height,worldSize)}\nb:{GetPositionInsideCell_GetDebugString(b,width,height,worldSize)}" );
                if (equalityTest)
                {
                    Assert.AreEqual(A, B);
                }
                else
                {
                    Assert.AreNotEqual(A, B);
                }
            }
 public static void GetPositionInsideCell
 (
     FLOAT2 point, INT2 numCells, FLOAT2 worldSize,
     out int2 lowerCell, out int2 upperCell, out float2 normalizedPositionBetweenThoseTwoPoints
 )
 {
     GetPositionInsideCell(point.x, numCells.x, worldSize.x, out int xlo, out int xhi, out float xf);
     GetPositionInsideCell(point.y, numCells.y, worldSize.y, out int ylo, out int yhi, out float yf);
     lowerCell = new int2 {
         x = xlo, y = ylo
     };
     upperCell = new int2 {
         x = xhi, y = yhi
     };
     normalizedPositionBetweenThoseTwoPoints = new float2 {
         x = xf, y = yf
     };
 }
Пример #13
0
    /// <summary> Finds sequence of indices (a path) for given AStar solution </summary>
    /// <returns> Was destination reached </returns>
    public static bool BacktrackToPath
    (
        NativeArray <int2> solution,
        INT solutionWidth,
        INT2 destination,
        NativeList <int2> path
    )
    {
        path.Clear();
        if (path.Capacity < solutionWidth * 2)
        {
            path.Capacity = solutionWidth * 2;                                      //preallocate for reasonably common/bad scenario
        }
        int solutionLength = solution.Length;

        int2 pos   = destination;
        int  pos1d = Index2dTo1d(pos, solutionWidth);
        int  step  = 0;

        while (
            math.any(pos != solution[pos1d]) &&
            step < solutionLength
            )
        {
            path.Add(pos);
            pos   = solution[pos1d];
            pos1d = Index2dTo1d(pos, solutionWidth);
            step++;
        }
        bool wasDestinationReached = math.all(pos == solution[pos1d]);

        // TODO: can this step be avoided?
        ReverseArray <int2>(path);

        return(wasDestinationReached);
    }
 public static int2 ClampIndex2d(INT2 i2, INT width, INT height) => NativeGrid.ClampIndex2d(i2, width, height);
    /// <summary> Bresenham's line drawing algorithm (https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm). </summary>
    public static void TraceLine(NativeList <int2> results, INT2 A, INT2 B)
    {
        results.Clear();
        {
            int2 dir      = math.abs(A - B);
            int  capacity = math.max(dir.x, dir.y);
            if (results.Capacity < capacity)
            {
                results.Capacity = capacity;
            }
        }

        int2 pos = A;
        int  d, dx, dy, ai, bi, xi, yi;

        if (A.x < B.x)
        {
            xi = 1;
            dx = B.x - A.x;
        }
        else
        {
            xi = -1;
            dx = A.x - B.x;
        }

        if (A.y < B.y)
        {
            yi = 1;
            dy = B.y - A.y;
        }
        else
        {
            yi = -1;
            dy = A.y - B.y;
        }

        results.Add(pos);

        if (dx > dy)
        {
            ai = (dy - dx) * 2;
            bi = dy * 2;
            d  = bi - dx;

            while (pos.x != B.x)
            {
                if (d >= 0)
                {
                    pos.x += xi;
                    pos.y += yi;
                    d     += ai;
                }
                else
                {
                    d     += bi;
                    pos.x += xi;
                }

                results.Add(pos);
            }
        }
        else
        {
            ai = (dx - dy) * 2;
            bi = dx * 2;
            d  = bi - dy;

            while (pos.y != B.y)
            {
                if (d >= 0)
                {
                    pos.x += xi;
                    pos.y += yi;
                    d     += ai;
                }
                else
                {
                    d     += bi;
                    pos.y += yi;
                }

                results.Add(pos);
            }
        }
    }
    public static void EnumerateNeighbours(NativeArray <int2> results, int width, int height, INT2 origin2)
    {
                #if DEBUG
        ASSERT_TRUE(results.Length == 8, "Array length must be 8");
                #endif

        int2 origin2d = origin2;
        int2 o2       = int2.zero;
        int  i        = 0;

        //move 1 step up:
        o2 += new int2 {
            y = 1
        };
        results[i++] = ClampIndex2d(origin2d + o2, width, height);

        //move 1 step right:
        o2.x++;
        results[i++] = ClampIndex2d(origin2d + o2, width, height);

        //move 2 steps down:
        o2.y--;
        results[i++] = ClampIndex2d(origin2d + o2, width, height);
        o2.y--;
        results[i++] = ClampIndex2d(origin2d + o2, width, height);

        //move 2 steps left:
        o2.x--;
        results[i++] = ClampIndex2d(origin2d + o2, width, height);
        o2.x--;
        results[i++] = ClampIndex2d(origin2d + o2, width, height);

        //move 2 steps up:
        o2.y++;
        results[i++] = ClampIndex2d(origin2d + o2, width, height);
        o2.y++;
        results[i++] = ClampIndex2d(origin2d + o2, width, height);
    }
    /// <summary> Enumerates neighbours in growing spiral pattern (clockwise). </summary>
    /// <note> Offsets will repeat for border cells due to clamping (make sure this breaks nothing). </note>
    /// <note> Range > 1 will produce spiral pattern </note>
    /// <returns> Enumeration of index 2d coordinates to form this spiral. </returns>
    public static void EnumerateNeighbours(NativeList <int2> results, int width, int height, INT2 origin2, int range = 1)
    {
        // ..  24  25  26  27  28  29
        // 46  23   8   9  10  11  30
        // 45  22   7   0   1  12  31
        // 44  21   6   ^   2  13  32
        // 43  20   5   4   3  14  33
        // 42  19  18  17  16  15  34
        // 41  40  39  38  37  36  35

        results.Clear();
        {
            int capacity = (range * 2 + 1) * (range * 2 + 1) - 1;
            if (results.Capacity < capacity)
            {
                results.Capacity = capacity;
            }
        }

        int2 origin2d = origin2;
        int2 o2       = int2.zero;

        for (int ir = 1; ir <= range; ir++)
        {
            //move 1 step up:
            o2 += new int2 {
                y = 1
            };
            results.Add(ClampIndex2d(origin2d + o2, width, height));

            //move right:
            for (int imr = 0; imr < (ir * 2) - 1; imr++)
            {
                o2.x++;
                results.Add(ClampIndex2d(origin2d + o2, width, height));
            }

            //move down:
            for (int imd = 0; imd < ir * 2; imd++)
            {
                o2.y--;
                results.Add(ClampIndex2d(origin2d + o2, width, height));
            }

            //move left:
            for (int iml = 0; iml < ir * 2; iml++)
            {
                o2.x--;
                results.Add(ClampIndex2d(origin2d + o2, width, height));
            }

            //move up:
            for (int imu = 0; imu < ir * 2; imu++)
            {
                o2.y++;
                results.Add(ClampIndex2d(origin2d + o2, width, height));
            }
        }
    }
 public static bool IsIndex2dValid(INT2 i2, INT w, INT h) => NativeGrid.IsIndex2dValid(i2, w, h);
 public static int2 IndexTranslate(RectInt r, INT2 rxy) => NativeGrid.IndexTranslate(r, rxy);
Пример #20
0
    /// <summary> Finds sequence of indices (a path) for given AStar solution </summary>
    /// <remarks> Uses segmented array for output </remarks>
    /// <returns> Was destination reached </returns>
    public static bool BacktrackToPath
    (
        NativeArray <int2> solvedGrid,
        INT solvedGridWidth,
        INT2 destination,
        NativeArray <int2> segmentedIndices, // array segmented to store multiple paths
        INT segmentStart,                    // position for first path index2d
        INT segmentEnd,                      // position for last path index2d
        out int pathLength
    )
    {
                #if UNITY_ASSERTIONS
        ASSERT_TRUE(destination.x >= 0 && destination.y >= 0, $"destination: {destination} >= 0");
        ASSERT_TRUE(destination.x < solvedGridWidth && destination.y < solvedGridWidth, $"destination: {destination} < {solvedGridWidth} solutionWidth");
                #endif

        int2 pos            = destination;
        int  pos1d          = Index2dTo1d(pos, solvedGridWidth);
        int  availableSpace = segmentEnd - segmentStart;
        int  step           = 0;

                #if UNITY_ASSERTIONS
        localAssertions();
                #endif

        while (
            math.any(pos != solvedGrid[pos1d]) &&
            step < availableSpace
            )
        {
            int segmentedArrayIndex = segmentStart + step;

                        #if UNITY_ASSERTIONS
            if (segmentedArrayIndex < segmentStart || segmentedArrayIndex > segmentEnd)
            {
                // throw new System.Exception
                Debug.LogError($"segmentedArrayIndex {segmentedArrayIndex} is outside it's range of {{{segmentStart}...{segmentEnd}}}");
            }
                        #endif

            segmentedIndices[segmentedArrayIndex] = pos;
            pos   = solvedGrid[pos1d];
            pos1d = Index2dTo1d(pos, solvedGridWidth);

                        #if UNITY_ASSERTIONS
            localAssertions();
                        #endif

            step++;
        }
        pathLength = step;
        bool wasDestinationReached = math.all(pos == solvedGrid[pos1d]);

                #if UNITY_ASSERTIONS
        for (int n = 0; n < pathLength; n++)
        {
            int index = segmentStart + n;
            if (math.all(segmentedIndices[index] == int2.zero))
            {
                // throw new System.Exception
                Debug.LogError($"segmentedIndices[{index}] is {segmentedIndices[index]}, segmentStart: {segmentStart}, segmentEnd: {segmentEnd}, pathLength: {pathLength}, step: {step}");
            }
        }
                #endif

        // TODO: can this step be avoided?
        // reverse path order:
        {
            int first = segmentStart;
            int last  = segmentStart + math.max(pathLength - 1, 0);

                        #if UNITY_ASSERTIONS
            if (last > segmentEnd)
            {
                // throw new System.Exception
                Debug.LogError($"last {last} > {segmentEnd} segmentEnd");
            }
                        #endif

            ReverseArraySegment(segmentedIndices, first, last);
        }

                #if UNITY_ASSERTIONS
        void localAssertions()
        {
            FixedString128 debugInfo = $"pos: {pos}, pos1d:{pos1d}, solution.Length:{solvedGrid.Length}, solutionWidth:{solvedGridWidth} squared: {solvedGridWidth}";

            ASSERT_TRUE(pos1d >= 0, debugInfo);
            ASSERT_TRUE(pos1d < solvedGrid.Length, debugInfo);
        }
                #endif

        return(wasDestinationReached);
    }
Пример #21
0
 public static float EuclideanHeuristicNormalized(INT2 a, INT2 b, float maxLength) => math.length(a - b) / maxLength;
Пример #22
0
 public static float EuclideanHeuristic(INT2 a, INT2 b) => math.length(a - b);
 public static int2 ClampIndex2d(INT2 i2, INT width, INT height) => ClampIndex2d(i2.x, i2.y, width, height);
 public static float2 Index2dToPoint(INT2 i2, FLOAT stepX, FLOAT stepY) => Index2dToPoint(i2.x, i2.y, stepX, stepY);
 public static int Index2dTo1d(INT2 i2, INT width) => Index2dTo1d(i2.x, i2.y, width);
 public static float2 Index2dToPoint(INT2 i2, FLOAT2 step) => NativeGrid.Index2dToPoint(i2, step);
 public static bool IsIndex2dValid(INT2 i2, INT w, INT h) => IsIndex2dValid(i2.x, i2.y, w, h);
 public static void GetPositionInsideCell(FLOAT2 point, INT2 numCells, FLOAT2 worldSize, out int2 lowerCell, out int2 upperCell, out float2 normalizedPositionBetweenThoseTwoPoints) => NativeGrid.GetPositionInsideCell(point, numCells, worldSize, out lowerCell, out upperCell, out normalizedPositionBetweenThoseTwoPoints);
 public static float2 Index2dToPoint(INT2 i2, FLOAT2 step) => Index2dToPoint(i2.x, i2.y, step.x, step.y);
 public static float2 Index2dToPoint(INT2 i2, FLOAT stepX, FLOAT stepY) => NativeGrid.Index2dToPoint(i2, stepX, stepY);