Skip to main content
2 of 7
add optimization
47 kk
  • 21
  • 4

Well, I have found a solution used backtrack. It recorded the snake route every frame and the bodies trace the route back to find their property position.

using Saltyfish.Util;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Saltyfish.Logic
{
    public class SnakeMovement : MonoBehaviour
    {
        [SerializeField]

        private List<SnakePoint> m_SnakePath = new List<SnakePoint>();

        [SerializeField]
        private List<Unit> m_Bodies = new List<Unit>();

        public List<Unit> Bodies => m_Bodies;


        [SerializeField]
        [Range(0.1f, 5)]
        private float m_BodySpace;

        public Unit Head => m_Bodies.SafeGet(0);

        private bool m_Moved;


        // Update is called once per frame
        void Update()
        {
            OnUpdate();
        }

        protected virtual void OnUpdate()
        {
            UpdateBodies();
        }

        protected void UpdateMovement(Vector2 direction)
        {
            if (Head == null)
                return;
            Vector2 curPos = transform.position;
            var nextPos = curPos + direction * Head.GetSpeed() * Time.deltaTime;
            transform.position = new Vector3(nextPos.x, nextPos.y, transform.position.z);
            // Optimization 1 : Only update path when moved
            if(direction != Vector2.zero)
            {
                UpdatePath();
            }
            // Optimization 1
        }


        private void UpdatePath()
        {
            if (Head == null)
                return;
            var curPoint = new SnakePoint(Head.transform.position);
            if (m_SnakePath.Count > 0)
            {
                var lastPoint = m_SnakePath[m_SnakePath.Count - 1];
                curPoint.deltaDistance = Vector3.Distance(curPoint.Position, lastPoint.Position);
            }
            m_SnakePath.Add(curPoint);
        }
        private void UpdateBodies()
        {
            if (m_Bodies.Count <= 1)
                return;
            for (int i = 1; i < m_Bodies.Count; ++i)
            {
                if (m_Bodies[i].IsDead)
                    continue;
                float remainDistance = Mathf.Clamp(m_BodySpace, 0.1f, 5) * i;
                for (int j = m_SnakePath.Count - 1; j > 0; j--)
                {
                    if (remainDistance <= m_SnakePath[j].deltaDistance)
                    {
                        float LerpProgress = 0;
                        if (m_SnakePath[i].deltaDistance > 0)
                        {
                            LerpProgress = remainDistance / m_SnakePath[j].deltaDistance;
                        }
                        m_Bodies[i].transform.position = Vector3.Lerp(
                            m_SnakePath[j].Position,
                            m_SnakePath[j - 1].Position,
                            LerpProgress);
                        // Optimization 2 : remove the points before the waypoint that last body has reached
                        if (i == m_Bodies.Count - 1)
                        {
                            m_SnakePath.RemoveRange(0, j);
                        }
                        // Optimization 2
                        break;
                    }
                    remainDistance -= m_SnakePath[j].deltaDistance;
                }
            }
        }

        public void InitBodies(IList<Unit> units)
        {
            m_Bodies.Clear();
            for (int i = 0; i < units.Count; i++)
            {
                m_Bodies.Add(units[i]);
            }
        }


        public void PushBody(Unit unit)
        {
            if (m_Bodies.Contains(unit))
            {
                return;
            }
            m_Bodies.Add(unit);
        }

        public void InsertBody(Unit unit, int pos = 0)
        {
            if (pos < 0 || pos >= m_Bodies.Count)
                return;
            m_Bodies.Insert(pos, unit);
        }


        public Unit GetBody(int pos)
        {
            var unit = m_Bodies.SafeGet(pos);
            return unit;
        }

        public bool RemoveBody(Unit unit)
        {
            return m_Bodies.Remove(unit);
        }

        public void RemoveAt(int pos)
        {
            m_Bodies.RemoveAt(pos);
        }

        private void OnGUI()
        {
            GUIStyle textStyle = new GUIStyle();
            textStyle.fontSize = 35;
            GUI.Label(new Rect(25, 25, 200, 100),"BodySpace", textStyle);
            m_BodySpace = GUI.HorizontalSlider(new Rect(25, 100, 200, 80), m_BodySpace, 0.1F, 5);
        }

    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace Saltyfish.Logic
{
    [Serializable]
    public class SnakePoint
    {
        public Vector3 Position;

        public float deltaDistance;

        public SnakePoint(Vector3 pos)
        {
            Position = pos;
        }
    }
}

sc2

Though the route point list grows endless, the memory cost is acceptable because a single game only last for 5 minutes. And I can clear the list before the next game begins.

47 kk
  • 21
  • 4