Ejemplo n.º 1
0
        public void DestroyIfRequired()
        {
            if (IsTrackGenerated)
            {
                foreach (GameObject trackPiece in GeneratedTrackPieces)
                {
                    NetworkServer.Destroy(trackPiece);
                }

                GeneratedTrackPieces.Clear();
                IsTrackGenerated  = false;
                CheckpointsInRace = null;
            }
        }
Ejemplo n.º 2
0
        IEnumerator GenerateTrack(int trackLength, IReadOnlyList <GameObject> availableTrackPiecePrefabs, IReadOnlyCollection <Player> playersToSpawn)
        {
            SentrySdk.AddBreadcrumb("Starting track generation.");
            GameObject origin            = new GameObject("Temporary Origin for Track Generator");
            GameObject currentTrackPiece = firstTrackPiecePrefab;
            int        numTracks         = 0;

            // Stores a validity map for the current track marked by numTrack index, where all of the possible track piece candidates are either valid or invalid.
            bool[,] validAvailableTracks = new bool[trackLength, availableTrackPiecePrefabs.Count];
            for (int numTracksIndex = 0; numTracksIndex < trackLength; numTracksIndex++)
            {
                for (int candidateTrackPiece = 0; candidateTrackPiece < availableTrackPiecePrefabs.Count; candidateTrackPiece++)
                {
                    validAvailableTracks[numTracksIndex, candidateTrackPiece] = true;
                }
            }

            while (numTracks < trackLength)
            {
                // Compile a list of valid track piece options.
                List <int> validTrackOptions = new List <int>();
                for (int candidateTrackPiece = 0; candidateTrackPiece < availableTrackPiecePrefabs.Count; candidateTrackPiece++)
                {
                    if (validAvailableTracks[numTracks, candidateTrackPiece] == true && IsSameTrackPieceStyle(availableTrackPiecePrefabs[candidateTrackPiece]))
                    {
                        validTrackOptions.Add(candidateTrackPiece);
                    }
                }

                // BACKTRACK
                // Check if there exists any valid track pieces to choose from. If not, delete the recently placed piece.
                if (validTrackOptions.Count == 0)
                {
                    // All track options for the current track piece are exhausted with no valid tracks.
                    // Must backtrack from the current track piece by destroying the current track piece.
                    NetworkServer.Destroy(currentTrackPiece);
                    Destroy(currentTrackPiece);
                    GeneratedTrackPieces.RemoveAt(GeneratedTrackPieces.Count - 1);
                    currentTrackPiece = GeneratedTrackPieces[GeneratedTrackPieces.Count - 1];
                    // Reset validAvailableTracks memory of this track's options for the future track pieces to use this space.
                    for (int candidateTrackPiece = 0; candidateTrackPiece < availableTrackPiecePrefabs.Count; candidateTrackPiece++)
                    {
                        validAvailableTracks[numTracks, candidateTrackPiece] = true;
                    }
                    numTracks--;
                    continue;
                }

                Transform  trackPieceLinkTransform;
                GameObject newTrackPiecePrefab;

                if (numTracks == 0)
                {
                    newTrackPiecePrefab     = firstTrackPiecePrefab;
                    trackPieceLinkTransform = origin.transform;
                }
                else if (numTracks == trackLength - 1)
                {
                    // We want to force the algorithm to place the Final Track Piece only, so mark every other track piece as invalid.
                    // This way, if the Final Track Piece cannot be placed, the algorithm will backtrack.
                    for (int candidateTrackPiece = 0; candidateTrackPiece < availableTrackPiecePrefabs.Count; candidateTrackPiece++)
                    {
                        validAvailableTracks[numTracks, candidateTrackPiece] = false;
                    }

                    // If the Final Track Piece doesn't have the same track piece style as the previously placed track,
                    // then it cannot be placed, and we must backtrack.
                    if (!IsSameTrackPieceStyle(finalTrackPiecePrefab))
                    {
                        // We marked every track as invalid above, so backtracking will occur on the next iteration.
                        continue;
                    }

                    newTrackPiecePrefab     = finalTrackPiecePrefab;
                    trackPieceLinkTransform = LoadTrackPieceLinkTransform(currentTrackPiece);
                }
                else
                {
                    int randomTrack = validTrackOptions[UnityEngine.Random.Range(0, validTrackOptions.Count)];
                    newTrackPiecePrefab = availableTrackPiecePrefabs[randomTrack];
                    validAvailableTracks[numTracks, randomTrack] = false;
                    trackPieceLinkTransform = LoadTrackPieceLinkTransform(currentTrackPiece);
                }

                if (trackPieceLinkTransform == null)
                {
                    throw new MissingComponentException("An error has occurred loading the track piece link during track generation for this track piece.");
                }

                GameObject newTrackPiece = Instantiate(newTrackPiecePrefab);
                newTrackPiece.name = $"Auto Generated Track Piece { numTracks + 1 } ({ newTrackPiecePrefab.name })";
                newTrackPiece.transform.position  = trackPieceLinkTransform.transform.position;
                newTrackPiece.transform.rotation *= trackPieceLinkTransform.rotation;

                // Spawn the players cars onto the starting piece of the track
                if (numTracks == 0)
                {
                    yield return(SpawnManager.Singleton.SpawnAllRaceCarsOnStartingGrid(newTrackPiece, playersToSpawn));
                }

                // Wait for next physics calculation so that Track Piece Collision Detector works properly.
                yield return(new WaitForSeconds(0.15f));

                if (newTrackPiece.GetComponent <TrackGeneratorCollisionDetector>().IsValidTrackPlacementUponConnection)
                {
                    NetworkServer.Spawn(newTrackPiece);
                    GeneratedTrackPieces.Add(newTrackPiece);
                    currentTrackPiece = newTrackPiece;
                    numTracks++;
                }
                else
                {
                    Destroy(newTrackPiece);
                }
            }

            // By default track piece props are kinematic so they don't fall over when track pieces collide.
            // Once done we should set kinematic to false so cars can collide into them.
            foreach (GameObject trackPiece in GeneratedTrackPieces)
            {
                TrackPieceManager trackPieceState = trackPiece.GetComponent <TrackPieceManager>();
                trackPieceState.MakeReadyForRace();
                trackPieceState.MakePropsNonKinematic();
            }

            if (finalTrackPiecePrefab.transform.Find(GameObjectIdentifiers.FinishLineCheckpoint) == null)
            {
                throw new MissingComponentException($"Final Track Piece Prefab must have a GameObject named { GameObjectIdentifiers.FinishLineCheckpoint } in order for the finish line to function.");
            }

            // Cleanup and finish track generation
            Destroy(origin);
            IsTrackGenerating = false;
            IsTrackGenerated  = true;
            CheckpointsInRace = GeneratedTrackPieces
                                .SelectMany(trackPiece => trackPiece.GetComponentsInChildren <TrackPieceCheckpointDetector>())
                                .Select(trackPieceCheckpointDetector => trackPieceCheckpointDetector.gameObject)
                                .ToArray();
            RpcInvokeTrackGeneratedEvent();
            SentrySdk.AddBreadcrumb("Track generator finished.");
        }