2D类Train Valley游戏铁路路径排序问题技术求助
Alright, let's tackle this path generation issue you're facing with your Train Valley-style grid-based railway game. The core problem here is that using a simple List<Vector3> to store waypoints doesn't account for the topological connection of your railway nodes—when you fill in a skipped cell later, the list's linear order breaks, and your train has no way to understand the actual track layout.
Here are two practical solutions to fix this, along with code adjustments tailored to your existing setup:
Instead of relying on a rigid ordered list, model your railway as a graph where each TrainCell knows its adjacent track nodes. This lets you dynamically generate paths based on actual connections, no matter the order you place tracks in.
Step 1: Update the TrainCell Class
Add adjacency tracking to your TrainCell to store connected nodes:
public class TrainCell : MonoBehaviour { // Keep your existing properties (CurrentChildIndex, used, etc.) public List<TrainCell> adjacentTrackCells = new List<TrainCell>(); }
Step 2: Build Adjacency Relationships When Placing Tracks
When a player places a track cell, automatically detect and link it to neighboring active tracks. Adjust the direction check based on your track types (straight, curved, etc.):
// Insert this in your OnCellClickEvent method, right after marking target.used = true if (target.used) { // Check all 4 cardinal directions (adjust for diagonal tracks if needed) TrainCell[] neighboringCells = GetAdjacentTrainCells(target); foreach (var neighbor in neighboringCells) { if (neighbor != null && neighbor.used && !target.adjacentTrackCells.Contains(neighbor)) { // Bidirectional link: add each cell to the other's adjacency list target.adjacentTrackCells.Add(neighbor); neighbor.adjacentTrackCells.Add(target); } } } // Helper method to get adjacent grid cells (implement based on your grid system) private TrainCell[] GetAdjacentTrainCells(TrainCell target) { // Example: replace with your grid's logic to fetch up/down/left/right cells return new TrainCell[] { GetCellAtPosition(target.transform.position + Vector3.up), GetCellAtPosition(target.transform.position + Vector3.down), GetCellAtPosition(target.transform.position + Vector3.left), GetCellAtPosition(target.transform.position + Vector3.right) }; }
Step 3: Generate Paths Dynamically with Graph Traversal
Use a BFS (Breadth-First Search) to find the correct path between stations whenever a train needs to travel. This ensures you always get the latest valid route, even after modifying tracks:
public List<Vector3> GenerateTrainPath(TrainCell startStation, TrainCell endStation) { Queue<TrainCell> searchQueue = new Queue<TrainCell>(); Dictionary<TrainCell, TrainCell> pathHistory = new Dictionary<TrainCell, TrainCell>(); searchQueue.Enqueue(startStation); pathHistory[startStation] = null; while (searchQueue.Count > 0) { TrainCell current = searchQueue.Dequeue(); if (current == endStation) break; // Found our destination foreach (var neighbor in current.adjacentTrackCells) { if (!pathHistory.ContainsKey(neighbor)) { pathHistory[neighbor] = current; searchQueue.Enqueue(neighbor); } } } // Reconstruct the path by backtracking from end to start List<Vector3> finalPath = new List<Vector3>(); TrainCell currentCell = endStation; while (currentCell != null) { // Add the cell's position first finalPath.Add(currentCell.transform.position); // Add any child waypoints (for curved tracks) foreach (Transform child in currentCell.CurrentChild.transform) { finalPath.Add(child.position); } currentCell = pathHistory[currentCell]; } finalPath.Reverse(); // Reverse to get start-to-end order return finalPath; }
If your game focuses mostly on linear tracks (like early Train Valley levels), a doubly linked list can maintain order even when filling gaps.
Step 1: Add Linked List Pointers to TrainCell
public class TrainCell : MonoBehaviour { // Existing properties... public TrainCell previousCell; public TrainCell nextCell; }
Step 2: Insert New Cells Into the Linked List
When placing a track in a gap, find the adjacent cells that should connect to it and update the pointers:
// In your track placement logic TrainCell leftNeighbor = GetCellAtPosition(target.transform.position + Vector3.left); TrainCell rightNeighbor = GetCellAtPosition(target.transform.position + Vector3.right); // Link to left neighbor if it exists and has no next cell if (leftNeighbor != null && leftNeighbor.used && leftNeighbor.nextCell == null) { leftNeighbor.nextCell = target; target.previousCell = leftNeighbor; } // Link to right neighbor if it exists and has no previous cell if (rightNeighbor != null && rightNeighbor.used && rightNeighbor.previousCell == null) { rightNeighbor.previousCell = target; target.nextCell = rightNeighbor; }
Step 3: Generate Path by Traversing the Linked List
public List<Vector3> GenerateLinearPath(TrainCell start) { List<Vector3> path = new List<Vector3>(); TrainCell current = start; while (current != null) { path.Add(current.transform.position); foreach (Transform child in current.CurrentChild.transform) { path.Add(child.position); } current = current.nextCell; } return path; }
- Remove all direct operations on the
WayPointslist—instead, generate paths dynamically using one of the methods above when a train needs to start its route. - When deleting tracks, make sure to clean up adjacency lists or linked list pointers (e.g., if you delete a cell, update its neighbors to remove references to it).
- For curved tracks, keep adding child waypoints to the final path as you already do—just attach them to the corresponding node's data instead of a global list.
内容的提问来源于stack exchange,提问作者Gheorghe C.

