Beispiel #1
0
        public virtual void DoUpdate(LFloat deltaTime, EntityAttri ownerAttri)
        {
            if (timer > counter * config.triggerInterval)
            {
                counter++;
                //trigger buff
                for (int i = 0; i < config.count; i++)
                {
                    var at   = config.Attris[i];
                    var type = (EEntityAttriType)System.Math.Abs(at.attriType);
                    switch (type)
                    {
                    case EEntityAttriType.Hp: {
                        ownerAttri.Hp += at.val + LMath.FloorToInt(at.percent * triggerAttri.Hp);
                        break;
                    }

                    case EEntityAttriType.MaxHp: {
                        ownerAttri.MaxHp += at.val + LMath.FloorToInt(at.percent * triggerAttri.Hp);
                        break;
                    }
                    }
                }
            }
        }
Beispiel #2
0
        public bool ContainsCircle(LVector2 circleCenter, LFloat radius)
        {
            var center = _bounds.center;
            var dx     = LMath.Abs(circleCenter.x - center.x);
            var dy     = LMath.Abs(circleCenter.y - center.y);

            if (dx > (_bounds.width / 2 + radius))
            {
                return(false);
            }

            if (dy > (_bounds.height / 2 + radius))
            {
                return(false);
            }

            if (dx <= (_bounds.width / 2))
            {
                return(true);
            }

            if (dy <= (_bounds.height / 2))
            {
                return(true);
            }

            var dsx        = (dx - _bounds.width / 2);
            var dsy        = (dy - _bounds.height / 2);
            var cornerDist = dsx * dsx + dsy * dsy;

            return(cornerDist <= (radius * radius));
        }
Beispiel #3
0
        public static ColliderPrefab CreateColliderPrefab(ColliderData data)
        {
            CBaseShape collider = null;

            if (LMath.Abs(data.deg - 45) < 1)
            {
            }

            //warning data.deg is unity deg
            //changed unity deg to ccw deg
            var collisionDeg = -data.deg + 90;

            if (data.radius > 0)
            {
                //circle
                collider = new CCircle(data.radius);
            }
            else
            {
                //obb
                collider = new COBB(data.size, collisionDeg);
            }

            collider.high = data.high;
            var colFab = new ColliderPrefab();

            colFab.parts.Add(new ColliderPart()
            {
                transform = new CTransform2D(data.pos, data.y, data.deg),
                collider  = collider
            });
            return(colFab);
        }
        public static LVector3 Transform(LVector3 point, LVector3 forward, LVector3 trans, LVector3 scale)
        {
            LVector3 up   = LVector3.up;
            LVector3 vInt = Cross(LVector3.up, forward);

            return(LMath.Transform(ref point, ref vInt, ref up, ref forward, ref trans, ref scale));
        }
Beispiel #5
0
    public static LFloat Angle(LQuaternion a, LQuaternion b)
    {
        LQuaternion q = b * LQuaternion.Inverse(a);

        q.Normalize();
        return(LMath.Acos(q.w));
    }
        public void DoAfterInit()
        {
            for (int i = 0; i < gridInfo.tileMaps.Length; i++)
            {
                var tilemap = gridInfo.tileMaps[i];
                if (tilemap.isTagMap)
                {
                    continue;
                }
                var tilemapMin  = tilemap.min;
                var tilemapSize = tilemap.size;
                mapDataMin.x = LMath.Min(mapDataMin.x, tilemapMin.x);
                mapDataMin.y = LMath.Min(mapDataMin.y, tilemapMin.y);
                mapDataMax.x = LMath.Max(mapDataMax.y, tilemapMin.x + tilemapSize.x);
                mapDataMax.y = LMath.Max(mapDataMax.y, tilemapMin.y + tilemapSize.y);
            }

            mapDataSize = (mapDataMax - mapDataMin) + LVector2Int.one;
            mapDataIds  = new ushort[mapDataSize.x, mapDataSize.y];
            for (int x = 0; x < mapDataSize.x; x++)
            {
                for (int y = 0; y < mapDataSize.y; y++)
                {
                    var pos = new Vector2Int(mapDataMin.x + x, mapDataMin.y + y);
                    mapDataIds[x, y] = RawPos2TileId(pos, true);
                }
            }
        }
Beispiel #7
0
        public LFloat GetMaxMoveDist(EDir dir, LVector2 fHeadPos, LVector2 fTargetHeadPos, LFloat borderSize)
        {
            var iTargetHeadPos =
                new LVector2Int(LMath.FloorToInt(fTargetHeadPos.x), LMath.FloorToInt(fTargetHeadPos.y));
            var hasCollider = HasColliderWithBorder(dir, fTargetHeadPos, borderSize);
            var maxMoveDist = LFloat.MaxValue;

            if (hasCollider)
            {
                switch (dir)
                {
                case EDir.Up:
                    maxMoveDist = iTargetHeadPos.y - fHeadPos.y;
                    break;

                case EDir.Right:
                    maxMoveDist = iTargetHeadPos.x - fHeadPos.x;
                    break;

                case EDir.Down:
                    maxMoveDist = fHeadPos.y - iTargetHeadPos.y - 1;
                    break;

                case EDir.Left:
                    maxMoveDist = fHeadPos.x - iTargetHeadPos.x - 1;
                    break;
                }
            }

            return(maxMoveDist);
        }
Beispiel #8
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="o">射线起点</param>
        /// <param name="d">射线终点</param>
        /// <param name="min">矩形的左下角坐标</param>
        /// <param name="max">矩形的右上角坐标</param>
        /// <param name="tmin">返回距离</param>
        /// <returns></returns>
        public static bool TestRayAABB(LVector2 o, LVector2 d, LVector2 min, LVector2 max, out LFloat tmin)
        {
            tmin = LFloat.zero;
            LFloat tmax = LFloat.FLT_MAX;

            for (int i = 0; i < 2; i++)
            {
                if (LMath.Abs(d[i]) < LFloat.EPSILON)
                {
                    if (o[i] < min[i] || o[i] > max[i])
                    {
                        return(false);
                    }
                }
                else
                {
                    LFloat ood = LFloat.one / d[i];
                    LFloat t1  = (min[i] - o[i]) * ood;
                    LFloat t2  = (max[i] - o[i]) * ood;
                    if (t1 > t2)
                    {
                        var temp = t1;
                        t1 = t2;
                        t2 = temp;
                    }
                    tmin = LMath.Max(tmin, t1);
                    tmax = LMath.Min(tmax, t2);
                    if (tmin > tmax)
                    {
                        return(false);
                    }
                }
            }
            return(true);
        }
Beispiel #9
0
        /// <summary>
        /// 球形插值(无限制)
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public static LQuaternion SlerpUnclamped(LQuaternion q1, LQuaternion q2, LFloat t)
        {
            LFloat dot = Dot(q1, q2);

            LQuaternion tmpQuat = new LQuaternion();

            if (dot < 0)
            {
                dot = -dot;
                tmpQuat.Set(-q2.x, -q2.y, -q2.z, -q2.w);
            }
            else
            {
                tmpQuat = q2;
            }


            if (dot < 1)
            {
                LFloat angle = LMath.Acos(dot);
                LFloat sinadiv, sinat, sinaomt;
                sinadiv = 1 / LMath.Sin(angle);
                sinat   = LMath.Sin(angle * t);
                sinaomt = LMath.Sin(angle * (1 - t));
                tmpQuat.Set((q1.x * sinaomt + tmpQuat.x * sinat) * sinadiv,
                            (q1.y * sinaomt + tmpQuat.y * sinat) * sinadiv,
                            (q1.z * sinaomt + tmpQuat.z * sinat) * sinadiv,
                            (q1.w * sinaomt + tmpQuat.w * sinat) * sinadiv);
                return(tmpQuat);
            }
            else
            {
                return(Lerp(q1, tmpQuat, t));
            }
        }
        private void UpdatePingVal(float deltaTime)
        {
            _pingTimer += deltaTime;
            if (_pingTimer > 0.5f)
            {
                _pingTimer = 0;
                DelayVal   = (int)(_delays.Sum() / LMath.Max(_delays.Count, 1));
                _delays.Clear();
                PingVal = (int)(_pings.Sum() / LMath.Max(_pings.Count, 1));
                _pings.Clear();

                if (_minPing < _historyMinPing && _simulatorService._gameStartTimestampMs != -1)
                {
                    _historyMinPing = _minPing;
#if UNITY_EDITOR
                    Debug.LogWarning(
                        $"Recalc _gameStartTimestampMs {_simulatorService._gameStartTimestampMs} _guessServerStartTimestamp:{_guessServerStartTimestamp}");
#endif
                    _simulatorService._gameStartTimestampMs = LMath.Min(_guessServerStartTimestamp,
                                                                        _simulatorService._gameStartTimestampMs);
                }

                _minPing = Int64.MaxValue;
                _maxPing = Int64.MinValue;
            }
        }
Beispiel #11
0
        //http://geomalgorithms.com/

//https://stackoverflow.com/questions/1073336/circle-line-segment-collision-detection-algorithm
        public static bool TestRayCircle(LVector2 cPos, LFloat cR, LVector2 rB, LVector2 rDir, ref LFloat t)
        {
            var d            = rDir;
            var f            = rB - cPos;
            var a            = LVector2.Dot(d, d);
            var b            = 2 * LVector2.Dot(f, d);
            var c            = LVector2.Dot(f, f) - cR * cR;
            var discriminant = b * b - 4 * a * c;

            if (discriminant < 0)
            {
                // no intersection
                return(false);
            }
            else
            {
                discriminant = LMath.Sqrt(discriminant);
                var t1 = (-b - discriminant) / (2 * a);
                var t2 = (-b + discriminant) / (2 * a);
                if (t1 >= 0)
                {
                    t = t1;
                    return(true);
                }

                if (t2 >= 0)
                {
                    t = t2;
                    return(true);
                }

                return(false);
            }
        }
Beispiel #12
0
        public static bool TestAABBAABB(LVector2 posA, LFloat rA, LVector2 sizeA, LVector2 posB, LFloat rB,
                                        LVector2 sizeB)
        {
            var diff      = posA - posB;
            var allRadius = rA + rB;

//circle 判定
            if (diff.sqrMagnitude > allRadius * allRadius)
            {
                return(false);
            }

            var absX = LMath.Abs(diff.x);
            var absY = LMath.Abs(diff.y);

//AABB and AABB
            var allSize = sizeA + sizeB;

            if (absX > allSize.x)
            {
                return(false);
            }
            if (absY > allSize.y)
            {
                return(false);
            }
            return(true);
        }
Beispiel #13
0
        public void Execute()
        {
            foreach (var entity in _moveRequest.GetEntities())
            {
                var deltaTime = _gameStateService.DeltaTime;
                var mover     = entity.move;
                var dir       = entity.dir.value;
                var pos       = entity.pos.value;
                var moveSpd   = mover.moveSpd;

                //can move 判定
                var dirVec       = DirUtil.GetDirLVec(dir);
                var moveDist     = (moveSpd * deltaTime);
                var fTargetHead  = pos + (TankUtil.TANK_HALF_LEN + moveDist) * dirVec;
                var fPreviewHead = pos + (TankUtil.TANK_HALF_LEN + TankUtil.FORWARD_HEAD_DIST) * dirVec;

                LFloat maxMoveDist = moveSpd * deltaTime;
                var    headPos     = pos + (TankUtil.TANK_HALF_LEN) * dirVec;
                var    dist        = _gameCollisionService.GetMaxMoveDist(dir, headPos, fTargetHead);
                var    dist2       = _gameCollisionService.GetMaxMoveDist(dir, headPos, fPreviewHead);
                maxMoveDist = LMath.Max(LFloat.zero, LMath.Min(maxMoveDist, dist, dist2));

                var diffPos = maxMoveDist * dirVec;
                pos = pos + diffPos;
                entity.pos.value = pos;
            }
        }
        public static void CheckBulletWithMap(LVector2Int iPos, GameEntity entity, IGameAudioService audioService, IMap2DService map2DService)
        {
            var unit   = entity.unit;
            var bullet = entity.bullet;
            var id     = map2DService.Pos2TileId(iPos, false);

            if (id != 0 && unit.health > 0)
            {
                //collide bullet with world
                if (id == TilemapUtil.TileID_Brick)
                {
                    if (unit.camp == ECampType.Player)
                    {
                        audioService.PlayClipHitBrick();
                    }

                    map2DService.ReplaceTile(iPos, id, 0);
                    unit.health--;
                }
                else if (id == TilemapUtil.TileID_Iron)
                {
                    if (!bullet.canDestoryIron)
                    {
                        if (unit.camp == ECampType.Player)
                        {
                            audioService.PlayClipHitIron();
                        }

                        unit.health = 0;
                    }
                    else
                    {
                        if (unit.camp == ECampType.Player)
                        {
                            audioService.PlayClipDestroyIron();
                        }

                        unit.health = LMath.Max(unit.health - 2, 0);
                        map2DService.ReplaceTile(iPos, id, 0);
                    }
                }
                else if (id == TilemapUtil.TileID_Grass)
                {
                    if (bullet.canDestoryGrass)
                    {
                        if (unit.camp == ECampType.Player)
                        {
                            audioService.PlayClipDestroyGrass();
                        }

                        unit.health -= 0;
                        map2DService.ReplaceTile(iPos, id, 0);
                    }
                }
                else if (id == TilemapUtil.TileID_Wall)
                {
                    unit.health = 0;
                }
            }
        }
 public void DoAfterInit()
 {
     for (int i = 0; i < this.gridInfo.tileMaps.Length; i++)
     {
         TileInfos tileInfos = this.gridInfo.tileMaps[i];
         bool      isTagMap  = tileInfos.isTagMap;
         if (!isTagMap)
         {
             LVector2Int min  = tileInfos.min;
             LVector2Int size = tileInfos.size;
             this.mapDataMin.x = LMath.Min(this.mapDataMin.x, min.x);
             this.mapDataMin.y = LMath.Min(this.mapDataMin.y, min.y);
             this.mapDataMax.x = LMath.Max(this.mapDataMax.y, min.x + size.x);
             this.mapDataMax.y = LMath.Max(this.mapDataMax.y, min.y + size.y);
         }
     }
     this.mapDataSize = this.mapDataMax - this.mapDataMin + LVector2Int.one;
     this.mapDataIds  = new ushort[this.mapDataSize.x, this.mapDataSize.y];
     for (int j = 0; j < this.mapDataSize.x; j++)
     {
         for (int k = 0; k < this.mapDataSize.y; k++)
         {
             LVector2Int pos = new LVector2Int(mapDataMin.x + j, mapDataMin.y + k);
             this.mapDataIds[j, k] = this.RawPos2TileId(pos, false);
         }
     }
 }
Beispiel #16
0
        public static bool TestAABBOBB(LVector2 posA, LFloat rA, LVector2 sizeA, LVector2 posB, LFloat rB, LVector2 sizeB,
                                       LVector2 upB)
        {
            var diff      = posA - posB;
            var allRadius = rA + rB;

            //circle 判定
            if (diff.sqrMagnitude > allRadius * allRadius)
            {
                return(false);
            }

            var absUPX = LMath.Abs(upB.x); //abs(up dot aabb.right)
            var absUPY = LMath.Abs(upB.y); //abs(right dot aabb.right)

            {
                //轴 投影 AABBx
                var distX = absUPX * sizeB.y + absUPY * sizeB.x;
                if (LMath.Abs(diff.x) > distX + sizeA.x)
                {
                    return(false);
                }

                //轴 投影 AABBy
                //absUPX is abs(right dot aabb.up)
                //absUPY is abs(up dot aabb.up)
                var distY = absUPY * sizeB.y + absUPX * sizeB.x;
                if (LMath.Abs(diff.y) > distY + sizeA.y)
                {
                    return(false);
                }
            }

            {
                var right     = new LVector2(upB.y, -upB.x);
                var diffPObbX = LVector2.Dot(diff, right);
                var diffPObbY = LVector2.Dot(diff, upB);

                //absUPX is abs(aabb.up dot right )
                //absUPY is abs(aabb.right dot right)
                //轴 投影 OBBx
                var distX = absUPX * sizeA.y + absUPY * sizeA.x;
                if (LMath.Abs(diffPObbX) > distX + sizeB.x)
                {
                    return(false);
                }

                //absUPX is abs(aabb.right dot up )
                //absUPY is abs(aabb.up dot up)
                //轴 投影 OBBy
                var distY = absUPY * sizeA.y + absUPX * sizeA.x;
                if (LMath.Abs(diffPObbY) > distY + sizeB.y)
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #17
0
 public OBB(LVector2 pos, LVector2 size, LVector2 up)
 {
     this.pos  = pos;
     this.size = size;
     radius    = size.magnitude;
     this.up   = up;
     this.deg  = LMath.Atan2(-up.x, up.y);
 }
Beispiel #18
0
        public LFloat height; // 高

        /**
         * 数据检测,客户端的顶点坐标和三角形数据有可能是重复的ç∂
         * TODO 小三角形合并成大三角形或多边形;判断顶点是否在寻路层中,寻路层中的顶点不能作为路径点;两点所连线段是否穿过阻挡区,不穿过,直接获取坐标点
         */
        public void check(int scale)
        {
            amendmentSameVector(pathTriangles, pathVertices);
            scaleVector(pathVertices, scale);

            this.width  = LMath.Abs(this.getEndX() - this.getStartX());
            this.height = LMath.Abs(this.getEndZ() - this.getStartZ());
        }
 public static int3 floor(LVector3 vec)
 {
     return(new int3(
                LMath.FloorToInt(vec.x),
                LMath.FloorToInt(vec.y),
                LMath.FloorToInt(vec.z)
                ));
 }
Beispiel #20
0
        public void DoUpdate(LFloat deltaTime)
        {
            if (!isEnable)
            {
                return;
            }
            if (!TestOnFloor(transform.Pos3))
            {
                isSleep = false;
            }

            lastPos = transform.Pos3;
            lastDeg = transform.deg;
            if (!isSleep)
            {
                if (!isOnFloor)
                {
                    Speed.y -= G * deltaTime;
                    Speed.y  = LMath.Max(MinYSpd, Speed.y);
                }

                var pos = transform.Pos3;
                pos += Speed * deltaTime;
                LFloat y = pos.y;
                //Test floor
                isOnFloor = TestOnFloor(transform.Pos3, ref y);
                if (isOnFloor && Speed.y <= 0)
                {
                    Speed.y = LFloat.zero;
                }

                if (Speed.y <= 0)
                {
                    pos.y = y;
                }

                //Test walls
                if (TestOnWall(ref pos))
                {
                    Speed.x = LFloat.zero;
                    Speed.z = LFloat.zero;
                }

                if (isOnFloor)
                {
                    var speedVal = Speed.magnitude - FloorFriction * deltaTime;
                    speedVal = LMath.Max(speedVal, LFloat.zero);
                    Speed    = Speed.normalized * speedVal;
                    if (speedVal < MinSleepSpeed)
                    {
                        isSleep = true;
                    }
                }

                transform.Pos3 = pos;
            }
        }
Beispiel #21
0
 public static LVector3 ToEulerAngles(LQuaternion rotation)
 {
     rotation.Normalize();
     return(new LVector3(
                LMath.Atan2(2 * (rotation.w * rotation.z + rotation.x * rotation.y), 1 - 2 * (rotation.z * rotation.z + rotation.x * rotation.x)),
                LMath.Asin(2 * (rotation.w * rotation.x - rotation.y * rotation.z)),
                LMath.Atan2(2 * (rotation.w * rotation.y + rotation.z * rotation.x), 1 - 2 * (rotation.x * rotation.x + rotation.y * rotation.y))
                ));
 }
Beispiel #22
0
    public static LQuaternion AngleAxis(LFloat angle, LVector3 axis)
    {
        LFloat radian2 = angle * 0.5d * LMath.DegToRad;
        LFloat sina    = LMath.Sin(radian2);
        LFloat cosa    = LMath.Cos(radian2);

        axis = axis.normalized;
        return(new LQuaternion(sina * axis.x, sina * axis.y, sina * axis.z, cosa));
    }
Beispiel #23
0
        public void SetDeg(LFloat rdeg)
        {
            deg = rdeg;
            var rad = LMath.Deg2Rad * deg;
            var c   = LMath.Cos(rad);
            var s   = LMath.Sin(rad);

            up = new LVector2(-s, c);
        }
Beispiel #24
0
        public static int Sqrt(int a)
        {
            if (a <= 0)
            {
                return(0);
            }

            return((int)LMath.Sqrt32((uint)a));
        }
        public static LVector3 normalizesafe(LVector3 x)
        {
            LFloat len = LMath.Dot(x, x);

            if (len > LFloat.EPSILON)
            {
                return(x * (LFloat.one / LMath.Sqrt(len)));
            }
            return(LVector3.zero);
        }
Beispiel #26
0
    public static LVector3 Slerp(LVector3 from, LVector3 to, LFloat t)
    {
        t = LMath.Clamp(t, 0, 1);
        LFloat diff   = Angle(from, to) * LMath.DegToRad;
        LFloat sind   = LMath.Sin(diff);
        LFloat sintd  = LMath.Sin(t * diff);
        LFloat sin1td = LMath.Sin((1 - t) * diff);

        return((sin1td / sind) * from + (sintd / sind) * to);
    }
Beispiel #27
0
    internal void ApplyForceEffect(LFloat deltaTime)
    {
        bool sliding  = mLinearVelocity.sqrMagnitude > 0;
        bool rotating = mAngularVelocity > 0;

        // linear velocity
        LVector2 va = mUserForce * mMassInverse;

        if (!mIsFixed)
        {
            va += LPhysicsStatic2D.LGravity * mGravityScale;
        }

        if (sliding && mLinearDrag > 0)
        {
            LFloat da   = mLinearDrag * mMassInverse;
            LFloat minr = (mLinearVelocity / deltaTime).sqrMagnitude;
            if (da * da >= minr)
            {
                va = LVector2.zero;
                mLinearVelocity = LVector2.zero;
            }
            else
            {
                va -= da * mLinearVelocity.normalized;
            }
        }
        if (va != LVector2.zero)
        {
            mLinearVelocity += va * deltaTime;
        }

        // angular velocity
        LFloat ra = mUserTorque * mInertiaInverse;

        if (rotating && mAngularDrag > 0)
        {
            LFloat da   = mAngularDrag * mInertiaInverse;
            LFloat minr = mAngularVelocity / deltaTime;
            if (da >= minr)
            {
                ra = 0;
                mAngularVelocity = 0;
            }
            else
            {
                ra -= da * LMath.Sign(ra);
            }
        }
        if (ra != 0)
        {
            mAngularVelocity += ra * deltaTime;
        }
    }
        public LFloat Estimate(Triangle node, Triangle endNode)
        {
            LFloat dst2;
            LFloat minDst2 = LFloat.MaxValue;

            A_AB = (node.a).Add(node.b) * LFloat.half;
            A_AB = (node.b).Add(node.c) * LFloat.half;
            A_AB = (node.c).Add(node.a) * LFloat.half;

            B_AB = (endNode.a).Add(endNode.b) * LFloat.half;
            B_BC = (endNode.b).Add(endNode.c) * LFloat.half;
            B_CA = (endNode.c).Add(endNode.a) * LFloat.half;

            if ((dst2 = A_AB.dst2(B_AB)) < minDst2)
            {
                minDst2 = dst2;
            }
            if ((dst2 = A_AB.dst2(B_BC)) < minDst2)
            {
                minDst2 = dst2;
            }
            if ((dst2 = A_AB.dst2(B_CA)) < minDst2)
            {
                minDst2 = dst2;
            }

            if ((dst2 = A_BC.dst2(B_AB)) < minDst2)
            {
                minDst2 = dst2;
            }
            if ((dst2 = A_BC.dst2(B_BC)) < minDst2)
            {
                minDst2 = dst2;
            }
            if ((dst2 = A_BC.dst2(B_CA)) < minDst2)
            {
                minDst2 = dst2;
            }

            if ((dst2 = A_CA.dst2(B_AB)) < minDst2)
            {
                minDst2 = dst2;
            }
            if ((dst2 = A_CA.dst2(B_BC)) < minDst2)
            {
                minDst2 = dst2;
            }
            if ((dst2 = A_CA.dst2(B_CA)) < minDst2)
            {
                minDst2 = dst2;
            }

            return((LFloat)LMath.Sqrt(minDst2));
        }
Beispiel #29
0
    public static LFloat RoundIfNear(LFloat val, LFloat roundDist)
    {
        var roundVal = LMath.Round(val);
        var diff     = LMath.Abs(val - roundVal);

        if (diff < roundDist)
        {
            return(roundVal);
        }

        return(val);
    }
Beispiel #30
0
 void DrawCurve()
 {
     for (int i = 1; i <= _segmentNum; i++)
     {
         float   t         = i / ( float )_segmentNum;
         int     nodeIndex = 0;
         Vector3 pixel     = LMath.BezierCurve(controlPoints[nodeIndex].position,
                                               controlPoints[nodeIndex + 1].position, controlPoints[nodeIndex + 2].position, t);
         lineRenderer.positionCount = i;
         lineRenderer.SetPosition(i - 1, pixel);
     }
 }