unity Snake Vs Block 经典的Snake游戏蛇身移动

PS 最近在做一个有关 Snake的项目 但是蛇身体移动在csdn也没有找到合适的方案

主要原因蛇头前进方向是匀速 但是需求是 蛇头左右移动不是匀速
产生问题
1 如果我使用和贪吃蛇经典做法 保留蛇头和蛇身位置 那就会蛇头和蛇身出现 断层(组要原因是左右不是运动)
2 如果我保留蛇所有身体和头的数据 计算每个蛇身对上一个方向 的位置 距离每次都是一个固定 (那么就会出现 左右快速移动蛇头的话 就会出现 前面身体在动 最尾巴都不动 很奇怪)
研究了1天 的方案 终于解决了 但是我感觉 性能计算有点大 希望有大佬能指正一下 或者给点修改意见 毕竟 学无止境

直接上代码

代码
class PathItem
{
    public Vector3 position;
    public float distance;
}
//蛇头代码
public class SnakeHeader :MonoBehaviour
{
 public float snakeSpeed;
    // 记录位置信息
    LinkedList<PathItem> previousPositions = new LinkedList<PathItem>();
    // 记录蛇身节点
    List<SnakeBody> m_SnakeBodys = new List<SnakeBody>();
    // 固定的距离;
    float CONST_LENGTH = 1.5f;
    
    private float m_PreDistance = 0.0f;
    private bool m_ConsumeState = false;
    private bool removeFirst = false;
    public GameObject SankeBodyGa;//蛇身预制体
    public Transform SankeHeadTran;//蛇头父物体
    public bool ConsumeState//设置 是否移动
    {
        set
        {
            if (value)
            {
                if (m_ConsumeState == false)
                {
                    m_PreDistance = CONST_LENGTH;
                }
            }
            m_ConsumeState = value;
        }
        get
        {
            return m_ConsumeState;
        }
    }
    private void Awake()
    {
        this.RecordPosition(transform.position, true);
    }

    private void Update()
    {
        if (!m_ConsumeState)
        {
            this.RecordPosition(transform.position);
        }
        else
        {
            /// 消耗状态下可以移动头部的特殊处理
            var preNodeInfo = this.previousPositions.First;
            var dis = Vector3.Distance(preNodeInfo.Value.position, transform.position);
            if (dis > 0.1f)
            {
                // 1. 必须增加头部节点的记录
                this.RecordPosition(transform.position);
                // 2. 维护身体移动相关变量
                //m_PreDistance += dis;
                if (m_PreDistance > CONST_LENGTH)
                {
                    m_PreDistance = CONST_LENGTH;
                }
            }
        }
        this.followHead(m_SnakeBodys, previousPositions, m_ConsumeState);
    }

    //排序蛇身
    void followHead(List<SnakeBody> tail, LinkedList<PathItem> path, bool isConsumeState)
    {
        // 包含父亲节点的路径的起点
        LinkedListNode<PathItem> startNode = path.First;
        // 父节点距离其路径片段的尾部的距离
        float distanceToEnd = startNode.Value.distance;
        removeFirst = false;
        int count = 0;
        foreach (SnakeBody body in tail)
        {

            bool isFirstBody = (count == 0);
            count++;
             计算 body 位置 
            if (startNode.Next == null)
            {
                // 已经是尾巴了
                break;
            }
            // 距离不足,无法设置位置
            var DISTANCE = CONST_LENGTH;
            if (isConsumeState && isFirstBody)
            {
                DISTANCE = m_PreDistance;
            }

            while (distanceToEnd < DISTANCE)
            {
                if (startNode.Next.Next == null)
                {
                    return;
                }
                startNode = startNode.Next;
                distanceToEnd = distanceToEnd + startNode.Value.distance;
            }

            if (distanceToEnd >= DISTANCE)
            {
                // body 应该在片段 startNode 开头的线段上
                var ds = distanceToEnd - DISTANCE;
                var rate = 1.0f - ds / startNode.Value.distance;
                var pos = Vector3.LerpUnclamped(startNode.Value.position, startNode.Next.Value.position, rate);
                body.transform.position = new Vector3(pos.x, pos.y - 0.055f, pos.z);
                distanceToEnd = distanceToEnd - DISTANCE;
            }
            if (isConsumeState && isFirstBody)
            {
                m_PreDistance -= snakeSpeed;

                if (m_PreDistance < 0.001f)
                {

                    m_PreDistance = CONST_LENGTH;
                    // 标记移除第一个;
                    removeFirst = true;

                }
            }
        }


        while (startNode.Next != path.Last)
        {
            path.RemoveLast();
            path.Last.Value.distance = 0;
        }

    }
   
    //计入蛇身体位置 
    LinkedListNode<PathItem> RecordPosition(Vector3 pos, bool first = false)
    {
        if (first)
        {
            var item = new PathItem();
            item.position = pos;
            item.distance = 0;
            return this.previousPositions.AddFirst(item);
        }
        else
        {
            var preItem = this.previousPositions.First.Value;
            var item = new PathItem();
            item.position = pos;
            item.distance = Vector3.Distance(preItem.position, item.position);
            return this.previousPositions.AddFirst(item);
        }
    }
    /// <summary>
    /// 删除蛇身
    /// </summary>
    public void DesSnakeBody()
    {
        if (m_SnakeBodys.Count > 0)
        {
            var item = m_SnakeBodys[0];
            m_SnakeBodys.RemoveAt(0);
            Destroy(item.gameObject);
        }

    }
    // 获取最后一个节点的位置,作为新的节点的初始位置
    Vector3 GetLastTailPosition()
    {
        if (m_SnakeBodys.Count == 0)
        {
            return this.transform.position;
        }
        else
        {
            return m_SnakeBodys[m_SnakeBodys.Count - 1].transform.position;
        }
    }
    // 添加一个蛇身
    public void AppendSnakeBody()
    {
        // 放在尾部节点位置放蛇的 body
        var lastPos = this.GetLastTailPosition();
        var gameObj = Instantiate(SankeBodyGa);
       
        gameObj.transform.parent = this.SankeHeadTran.transform;//lastPos
        gameObj.transform.position = new Vector3(lastPos.x, lastPos.y, lastPos.z);
        gameObj.transform.rotation = transform.rotation;
        var body = gameObj.GetComponent<SnakeBody>();
        this.m_SnakeBodys.Add(body);
    }
 }
public class SnakeBody : MonoBehaviour
{
   
   //这里没啥 主要是我蛇身的逻辑
   
 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_283886981

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值