Все еще приостановка / возобновление и запуск новой игры не работает хорошо. Не могу понять, что мне делать? - PullRequest
0 голосов
/ 04 октября 2019

Первый скриншот моей Иерархии:

Все внутри одной сцены Название:

Игра запускается, когда основная игра отключена. Внутри основной игры сидят все игровые объекты. Главное меню и Диспетчер игр включены.

При первом запуске игры, при запуске игры короткая анимация игрока на 5 секунд. Игрок начинает с нескольких вращающихся градусов по Z. Z = 50, когда x и y оба равны 0. Затем игрок медленно поворачивается по Z к 0.

Это похоже на то, что игрок спит и просыпается. Я использую стек постобработки на единицу.

И вот первая проблема. Пока игрок вращается по оси Z и эффект пост-обработки работает, если я нажимаю клавишу Escape, он возвращает меня в главное меню, но затем, если я снова нажимаю клавишу Escape, он начинает игру заново с самого начала.

Но если я жду в своей игре, чтобы игрок закончил вращение по Z, и эффект постобработки закончился, а затем нажав Escape, то откроется главное меню, и второй раз возобновит игру изта же точка.

Я не могу понять, почему, когда игрок вращается и пост-процесс работает, клавиша escape заставляет его начать игру заново с существа?

Hierarchy

Это скриншот игры при запуске и после завершения вращения и стека процессов:

On the left when the game start cutscene on the right starting a conversation

Еще одна проблема, которую я заметил сейчас. После начала игры с использованием пост-процесса и завершения вращения игрока, если я нажму Escape, он перейдет в главное меню и снова выйдет из игры, но вернется в игру, но, например, на втором скриншоте разговор не будет продолжен. Он вернется к тому же моменту в игре, но, похоже, все не будет продолжаться, как разговор.

К объекту «Вернуться к главному меню» мне прикреплен скрипт:

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

public class BackToMainMenu : MonoBehaviour
{
    private bool _isInMainMenu = false;
    public GameObject mainGame;
    public GameObject mainMenu;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            if (!_isInMainMenu)
            {
                // -- Code to freeze the game
                mainGame.SetActive(false);
                mainMenu.SetActive(true);
            }
            else
            {
                // -- Code to unfreeze the game
                mainGame.SetActive(true);
                mainMenu.SetActive(false);
            }

            _isInMainMenu = !_isInMainMenu;
        }
    }
}

К объекту главного меню в главном меню я прикрепил этот скрипт:

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

public class MainMenu : MonoBehaviour
{
    public GameObject mainGame;
    public GameObject mainMenu;

    public void PlayGame()
    {
        mainGame.SetActive(true);
        mainMenu.SetActive(false);
    }

    public void QuitGame()
    {
        Application.Quit();
    }
}

На кнопке PLAY я использую этот метод сценария PlayGame из сценария MainMenu.

В основной игреобъект на объекте Player, прикрепленный к W Player У меня есть несколько сценариев контроллера:

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

public class PlayerController : MonoBehaviour
{
    public float speed = 10.0f;

    // Update is called once per frame
    void Update()
    {
        float translatioin = Input.GetAxis("Vertical") * speed;
        float straffe = Input.GetAxis("Horizontal") * speed;
        translatioin *= Time.deltaTime;
        straffe *= Time.deltaTime;

        transform.Translate(straffe, 0, translatioin);
    }
}

Диспетчер блокировки игрока:

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

public class PlayerLockManager : MonoBehaviour
{
    public PlayerCameraMouseLook playerCameraMouseLook;
    public PlayerController playerController;

    // Start is called before the first frame update
    public void PlayerLockState(bool LockPlayer, bool LockPlayerCamera)
    {
        if (LockPlayer == true)
        {
            playerController.enabled = false;
        }
        else
        {
            playerController.enabled = true;
        }

        if (LockPlayerCamera == true)
        {
            playerCameraMouseLook.enabled = false;
        }
        else
        {
            playerCameraMouseLook.enabled = true;
        }
    }
}

И состояние блокировки мыши:

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

public class MouseLockState : MonoBehaviour
{
    public bool lockState = true;

    private void Start()
    {
        LockState(lockState);
    }

    private void Update()
    {

    }

    public void LockState(bool lockState)
    {
        if (lockState == false)
        {
            Cursor.visible = true;
            Cursor.lockState = CursorLockMode.None;
        }
        else
        {
            Cursor.visible = false;
            Cursor.lockState = CursorLockMode.Locked;
        }
    }
}

Под Player в качестве дочернего объекта у меня есть объект Player Camera и к нему также прикреплены некоторые сценарии:

Look Player Player Mouse Mouse:

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

public class PlayerCameraMouseLook : MonoBehaviour
{
    public float sensitivity = 5.0f;
    public float smoothing = 2.0f;

    private GameObject player;
    private Vector2 mouseLook;
    private Vector2 smoothV;

    // Use this for initialization
    void Start()
    {
        player = this.transform.parent.gameObject;
    }

    // Update is called once per frame
    void Update()
    {
        var md = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));

        md = Vector2.Scale(md, new Vector2(sensitivity * smoothing, sensitivity * smoothing));
        smoothV.x = Mathf.Lerp(smoothV.x, md.x, 1f / smoothing);
        smoothV.y = Mathf.Lerp(smoothV.y, md.y, 1f / smoothing);
        mouseLook += smoothV;

        mouseLook.y = Mathf.Clamp(mouseLook.y, -90f, 90f);

        transform.localRotation = Quaternion.AngleAxis(-mouseLook.y, Vector3.right);
        player.transform.localRotation = Quaternion.AngleAxis(mouseLook.x, Vector3.up);
    }
}

И сценарий глубины резкости:

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

public class DepthOfField : MonoBehaviour
{
    public GameObject player;
    public PostProcessingProfile postProcessingProfile;
    public bool dephOfFieldFinished = false;
    public PlayerLockManager playerLockManager;

    private Animator playerAnimator;
    private float clipLength;

    // Start is called before the first frame update
    void Start()
    {
        playerAnimator = player.GetComponent<Animator>();

        AnimationClip[] clips = playerAnimator.runtimeAnimatorController.animationClips;
        foreach (AnimationClip clip in clips)
        {
            clipLength = clip.length;
        }

        var depthOfField = postProcessingProfile.depthOfField.settings;
        depthOfField.focalLength = 300;
        StartCoroutine(changeValueOverTime(depthOfField.focalLength, 1, clipLength));
        postProcessingProfile.depthOfField.settings = depthOfField;
    }

    IEnumerator changeValueOverTime(float fromVal, float toVal, float duration)
    {
        playerLockManager.PlayerLockState(true, true);

        float counter = 0f;

        while (counter < duration)
        {
            var dof = postProcessingProfile.depthOfField.settings;

            if (Time.timeScale == 0)
                counter += Time.unscaledDeltaTime;
            else
                counter += Time.deltaTime;

            float val = Mathf.Lerp(fromVal, toVal, counter / duration);

            dof.focalLength = val;
            postProcessingProfile.depthOfField.settings = dof;

            yield return null;
        }

        playerAnimator.enabled = false;
        dephOfFieldFinished = true;
    }
}

У меня в основной игре есть также имя объекта Openning Scene и прикрепленное к нему:

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

public class OpeningCutscene : MonoBehaviour
{
    public NaviConversations naviConversation;
    public DepthOfField dephOfField;

    // Update is called once per frame
    void Update()
    {
        if (dephOfField.dephOfFieldFinished == true)
        {
            naviConversation.PlayNaviConversation(0);
            dephOfField.dephOfFieldFinished = false;
        }
    }
}

Это немного долго, но все подключено.

  1. Игра начинается с маяn menu.

  2. При нажатии на кнопку PLAY начинается новая игра.

  3. При запуске новой игры запускается сценарий Depth Of Fieldиспользуя пост-обработку. Также я блокирую плеер, чтобы игрок не мог двигать ни мышью, ни самим игроком.

  4. Когда сценарий глубины резкости закончил свою работу, разговор между Навии проигрыватель запускается. Когда разговор находится в действии, игрок может перемещать мышь на 360 градусов, но не может перемещать игрока в любом направлении.

  5. Когда разговор заканчивается, игрок также может двигаться в любом направлении.

Проблемы:

  1. При запуске новой игры, когда скрипт глубины резкости находится в работе, нажатие клавиши выхода приведет к открытию главного меню, а нажатие клавиши выхода - сноване возобновлять сценарий глубины резкости, но запустит его снова и снова.

  2. Когда вы ждете окончания разговора, если не нажимаете клавишу escape, пока разговор не закончится, то при перемещении и нажатии клавиши escape он будетпринесите главное меню и снова возобновите игру с того же пункта.

  3. Проблема в том, что когда игра делает что-то наподобие глубины резкости, она возобновляет игру, вместо этого возобновляя ее, или когда в середине разговора разговор никогда не будет продолжаться.

  4. Идентификаторы с помощью клавиши escape - один раз, чтобы попасть в главное меню, и второй, чтобы возобновить игру.

  5. Кнопка PLAY - это то, что должно начинать только новую игру, а не клавиша выхода.

Это немного долго, но все связано.

1 Ответ

0 голосов
/ 04 октября 2019

Я не вижу, какие объекты вы подключили до mainGame и mainMenu в BackToMainMenu из вашей иерархии, поэтому у меня есть некоторые предположения здесь. Звучит так, как будто вы хотите, чтобы клавиша Escape приостанавливала и отключала, а также вызывало меню, вы хотите, чтобы в меню была кнопка Play, чтобы перезапустить игру. Однако и в MainMenu, и в BackToMainMenu используется один и тот же код:

    mainGame.SetActive(true);
    mainMenu.SetActive(false);

Это просто включает и выключает объекты в иерархии. Это означает, что в первый раз вы выполните одно из следующих действий: все объекты, включенные в игровой объект, на который ссылается mainGame, будут запускать свои методы Awake и Start, но не во второй раз. Кроме того, в зависимости от того, какие объекты активны (как я уже сказал, я не могу полностью увидеть, какой компонент на каком объекте и на какие объекты ссылается какое сериализованное поле), вы можете внести ошибку состояния в BackToMainMenu._isInMainMenu, потому что это поле не является 'т изменился, когда вы нажмете играть. Вот фантастическое изображение, показывающее временную шкалу выполнения в Unity: Временная шкала монобихи поведения

В итоге:

  1. При втором запуске игры DepthOfField не будетпозвоните Start во второй раз. Вместо этого попробуйте OnEnable.
  2. Беседа и игровая логика не показаны в вашем посте, но, скорее всего, она также инициализируется в Start и не запустится во второй раз.
  3. AЗдесь, скорее всего, виновата хитрость вокруг сопрограмм. Когда DepthOfField запускается, он также запускает сопрограмму. Эта сопрограмма продолжает работать, даже если вы отключите игровой объект . Таким образом, вы запускаете игру один раз, запускается сопрограмма, вы выключаете объект, когда нажимаете escape, сопрограмма завершается, вы снова запускаете игру, но DepthOfField.dephOfFieldFinished == true и ваш playerAnimator отключен и не включается снова.
  4. Также 5. Такое поведение может быть хитрым и обычно зависит от того, что еще происходит в вашей сцене. Поскольку у вас есть одна сцена со всем сразу, вам нужно следить за тем, чтобы что-то добавлялось в Awake и Start, так как это будет выполняться только один раз. Вместо этого вы можете попробовать несколько вещей, моим любимым обычно является создание одноэлементного или статического класса, который работает как конечный автомат для всей сцены. Он будет вызывать пользовательские функции Initialize в вашем поведении вместо использования Awake и Start.

Сохраняйте ссылки на свои сопрограммы, выполняя такие действия, как:

    private Coroutine depthOfFieldRoutineRef;

    private OnEnable()
    {
        if (depthOfFieldRoutineRef != null)
        {
            StopCoroutine(depthOfFieldRoutineRef);
        }

        depthOfFieldRoutineRef = 
            StartCoroutine(changeValueOverTime
            (depthOfField.focalLength, 1, clipLength));
        // Don't forget to set depthOfFieldRoutineRef to null again at the end of routine!
    }

Для простого режима приостановки вы также можете попробовать установить Time.timescale = 0; и вернуться к 1, когда вы хотите возобновить воспроизведение.

Меньше вопрос, чем набор проблем, надеюсь, этот набор решений поможет!

Учебное пособие по Пробуждение и запуск

Документы по Сопрограммы

Документы на Time.timescale

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...