You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity(C#)无网格模式下蛇身跟随蛇头实现方法咨询

无网格Unity贪吃蛇:蛇身无重叠跟随实现方案

我在Unity中重制无网格版贪吃蛇(Snake)游戏以学习引擎,目前卡在实现蛇身无重叠跟随蛇头的功能上。现有教程均基于网格实现,找不到适配无网格场景的方案,以下是我改编后的代码:

using System.Collections;
using System.Collections.Generic;
using System.Net.WebSockets;
using UnityEngine;

public class Snake : MonoBehaviour
{
    public Rigidbody2D myRigidbody;
    public float SVelocity;

    private List<Transform> _segments;

    public Transform segmentPreFab;
    public Vector3 SnakePosition;
    public Vector3 SnakeVelocity;
    public Vector3 SnakeHead;
    
    void Start()
    {
  
        _segments = new List<Transform>();
        _segments.Add(this.transform);
        myRigidbody.velocity = Vector2.down * SVelocity;
     
    }

    // Update is called once per frame
    void Update()
    {
        if (myRigidbody.velocity == Vector2.up * SVelocity)
        {
            if (Input.GetKeyDown(KeyCode.D))

                myRigidbody.velocity = Vector2.right * SVelocity;
                SnakePosition = myRigidbody.position;

            if (Input.GetKeyDown(KeyCode.A))

                myRigidbody.velocity = Vector2.left * SVelocity;
                SnakePosition = myRigidbody.position;
        }

        if (myRigidbody.velocity == Vector2.down * SVelocity)
        {
            if (Input.GetKeyDown(KeyCode.D))

                myRigidbody.velocity = Vector2.right * SVelocity;
                SnakePosition = myRigidbody.position;

            if (Input.GetKeyDown(KeyCode.A))

                myRigidbody.velocity = Vector2.left * SVelocity;
                SnakePosition = myRigidbody.position;
        }

        if (myRigidbody.velocity == Vector2.right * SVelocity)
        {
            if (Input.GetKeyDown(KeyCode.W))

                myRigidbody.velocity = Vector2.up * SVelocity;
                SnakePosition = myRigidbody.position;

            if (Input.GetKeyDown(KeyCode.S))

                myRigidbody.velocity = Vector2.down * SVelocity;
                SnakePosition = myRigidbody.position;
        }

        if (myRigidbody.velocity == Vector2.left * SVelocity)
        {
            if (Input.GetKeyDown(KeyCode.W))

                myRigidbody.velocity = Vector2.up * SVelocity;
                SnakePosition = myRigidbody.position;


            if (Input.GetKeyDown(KeyCode.S))

                myRigidbody.velocity = Vector2.down * SVelocity;
                SnakePosition = myRigidbody.position;
        }
    }

    private void FixedUpdate()
    {
       
        for (int i = _segments.Count - 1; i > 0; i--)
        {

            _segments[i].position = _segments[i - 1].position;         

        }
        SnakeVelocity = myRigidbody.velocity;
    }
    private void Grow()
    {
        Transform segment = Instantiate(this.segmentPreFab);

        segment.position = _segments[_segments.Count - 1].position;

        _segments.Add(segment);
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.tag == "Food")
        {
            Grow();
        }
    }
    
}

问题分析

现有代码的核心问题在于FixedUpdate中直接将蛇身段的位置设置为前一段的位置——这种方式在网格场景中可行(移动是步进式的),但在无网格自由移动场景下,蛇头持续移动,蛇身段会瞬间瞬移到前一段位置,导致重叠,且无法保持段与段之间的固定距离。同时原输入逻辑缺少大括号,导致部分代码会无条件执行,存在逻辑错误。

解决方案

要实现无网格下的蛇身无重叠跟随,需要让每个蛇身段平滑跟随前一段的移动轨迹,并保持固定间距。以下是修改后的完整代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Snake : MonoBehaviour
{
    public Rigidbody2D myRigidbody;
    public float SVelocity;
    public Transform segmentPrefab;
    // 蛇身段之间的间距,建议设置为蛇身预制体的宽度/高度
    public float segmentSpacing = 0.5f;

    private List<Transform> _segments;
    // 记录每个段的目标位置,用于轨迹跟随
    private List<Vector3> _targetPositions;
    
    void Start()
    {
        _segments = new List<Transform>();
        _targetPositions = new List<Vector3>();
        
        _segments.Add(transform);
        _targetPositions.Add(transform.position);
        
        myRigidbody.velocity = Vector2.down * SVelocity;
    }

    void Update()
    {
        Vector2 currentVelocity = myRigidbody.velocity;
        
        if (currentVelocity == Vector2.up * SVelocity)
        {
            if (Input.GetKeyDown(KeyCode.D))
            {
                myRigidbody.velocity = Vector2.right * SVelocity;
            }
            if (Input.GetKeyDown(KeyCode.A))
            {
                myRigidbody.velocity = Vector2.left * SVelocity;
            }
        }
        else if (currentVelocity == Vector2.down * SVelocity)
        {
            if (Input.GetKeyDown(KeyCode.D))
            {
                myRigidbody.velocity = Vector2.right * SVelocity;
            }
            if (Input.GetKeyDown(KeyCode.A))
            {
                myRigidbody.velocity = Vector2.left * SVelocity;
            }
        }
        else if (currentVelocity == Vector2.right * SVelocity)
        {
            if (Input.GetKeyDown(KeyCode.W))
            {
                myRigidbody.velocity = Vector2.up * SVelocity;
            }
            if (Input.GetKeyDown(KeyCode.S))
            {
                myRigidbody.velocity = Vector2.down * SVelocity;
            }
        }
        else if (currentVelocity == Vector2.left * SVelocity)
        {
            if (Input.GetKeyDown(KeyCode.W))
            {
                myRigidbody.velocity = Vector2.up * SVelocity;
            }
            if (Input.GetKeyDown(KeyCode.S))
            {
                myRigidbody.velocity = Vector2.down * SVelocity;
            }
        }
    }

    private void FixedUpdate()
    {
        // 更新蛇头的目标位置
        _targetPositions[0] = transform.position;

        // 从后往前更新每个蛇身段的位置
        for (int i = 1; i < _segments.Count; i++)
        {
            Transform currentSegment = _segments[i];
            Vector3 prevTarget = _targetPositions[i - 1];
            
            // 计算当前段到前一段目标位置的方向和距离
            Vector3 direction = prevTarget - currentSegment.position;
            float distance = direction.magnitude;

            if (distance > segmentSpacing)
            {
                // 让当前段以蛇头速度向目标位置移动
                currentSegment.position += direction.normalized * SVelocity * Time.fixedDeltaTime;
                _targetPositions[i] = prevTarget;
            }
            else
            {
                // 距离不足时,保持段间间距,避免重叠
                currentSegment.position = prevTarget - direction.normalized * segmentSpacing;
            }
        }
    }

    private void Grow()
    {
        Transform segment = Instantiate(segmentPrefab);
        // 新段初始位置设置为最后一段的后方,避免生成时重叠
        Vector3 lastSegmentPos = _segments[_segments.Count - 1].position;
        Vector3 direction = lastSegmentPos - _segments[_segments.Count - 2].position;
        segment.position = lastSegmentPos - direction.normalized * segmentSpacing;
        
        _segments.Add(segment);
        _targetPositions.Add(segment.position);
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Food"))
        {
            Grow();
            Destroy(other.gameObject);
        }
    }
}

关键修改点

  • 修复输入逻辑:为if语句添加大括号,保证方向切换逻辑的正确性,避免无关代码无条件执行。
  • 新增段间距控制:通过segmentSpacing变量精准控制蛇身段之间的距离,从根源避免重叠。
  • 轨迹跟踪机制:用_targetPositions列表记录每个段的目标位置,让蛇身段跟随前一段的移动轨迹,而非直接瞬移。
  • 平滑跟随逻辑:在FixedUpdate中计算段与前一段的距离,当距离超过设定值时,让段以蛇头速度向目标位置移动,保证跟随过程平滑自然。
  • 生长逻辑优化:新增段时将其放置在最后一段的后方,避免刚生成就与现有段重叠。

内容的提问来源于stack exchange,提问作者João Pestana Teixeira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 02:55:59