繁体   English   中英

C# Unity 我的代码不允许一个类编辑另一个类中的变量

[英]C# Unity My code won't allow for one class to edit the variables in another

我有一个小游戏,玩家将火箭飞到绿色平台上以进步。 如果他们崩溃,他们会重新开始。 关卡简单循环,游戏玩法正常。 我遇到的问题是计时器。 我正在尝试制作一个系统来跟踪玩家通关所需的时间,然后将他们最快的时间保存在记录变量下。 我尝试在 Timer 脚本中添加一个热键来重置计时器,但是如果我从 Rocket 脚本运行相同的 ResetTimer 函数,则 currentTime 变量不会改变。 它仍然打印“计时器重置”,但变量保持不变。 有任何想法吗?

Rocket.cs

using UnityEngine.SceneManagement;

public class Rocket : MonoBehaviour
{
    Timer timer = new Timer();


    [SerializeField] float rotationThrust = 100f;
    [SerializeField] float mainThrust = 50f;
    [SerializeField] float levelLoadDelay = 2f;

    [SerializeField] AudioClip mainEngine;
    [SerializeField] AudioClip success;
    [SerializeField] AudioClip death;

    [SerializeField] ParticleSystem mainEngineParticles;
    [SerializeField] ParticleSystem successParticles;
    [SerializeField] ParticleSystem deathParticles;

    Rigidbody rigidBody;
    AudioSource audioSource;

    enum State { Alive, Dying, Transcending }
    State state = State.Alive;

    bool collisionsEnabled = true;

    void Start()
    {
        rigidBody = GetComponent<Rigidbody>();
        audioSource = GetComponent<AudioSource>();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.Escape))
        {
            Application.Quit();
        }
        if (state == State.Alive)
        {
            RespondToThrustInput();
            RespondToRotateInput();
        }
        if (Debug.isDebugBuild)
        {
            RespondToDebugKeys();
        }
    }

    private void RespondToDebugKeys()
    {
        if (Input.GetKeyDown(KeyCode.L))
        {
            LoadNextLevel();
        }
        else if (Input.GetKeyDown(KeyCode.K))
        {
            LoadFirstLevel();
        }
        else if (Input.GetKeyDown(KeyCode.C))
        {
            collisionsEnabled = !collisionsEnabled;
        }
        else if (Input.GetKeyDown(KeyCode.P))
        {
            timer.ResetRecord();
        }
    }

    private void RespondToThrustInput()
    {
        if (Input.GetKey(KeyCode.Space) || Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
        {
            ApplyThrust();
        }
        else
        {
            audioSource.Stop();
            mainEngineParticles.Stop();
        }
    }

    private void ApplyThrust()
    {
        rigidBody.AddRelativeForce(Vector3.up * mainThrust);
        if (!audioSource.isPlaying)
        {
            audioSource.PlayOneShot(mainEngine);
        }
        mainEngineParticles.Play();
    }

    private void RespondToRotateInput()
    {
        rigidBody.freezeRotation = true;
        float rotationSpeed = rotationThrust * Time.deltaTime;

        if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
        {
            transform.Rotate(Vector3.forward * rotationSpeed);
        }
        else if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
        {
            transform.Rotate(Vector3.back * rotationSpeed);
        }

        rigidBody.freezeRotation = false;
    }

    void OnCollisionEnter(Collision collision)
    {
        if (state != State.Alive || !collisionsEnabled)
        {
            return;
        }

        switch (collision.gameObject.tag)
        {
            case "Friendly":
                break;
            case "Finish":
                StartSuccessSequence();
                break;
            default:
                StartDeathSequence();
                break;
        }
    }

    private void StartSuccessSequence()
    {
        state = State.Transcending;
        audioSource.PlayOneShot(success);
        successParticles.Play();
        Invoke("LoadNextLevel", levelLoadDelay);
    }

    private void StartDeathSequence()
    {
        state = State.Dying;
        audioSource.Stop();
        deathParticles.Play();
        audioSource.PlayOneShot(death);
        Invoke("LoadFirstLevel", levelLoadDelay);
    }

    public void LoadFirstLevel()
    {
        timer.ResetTimer();
        SceneManager.LoadScene(1);
    }

    private void LoadNextLevel()
    {
        int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
        if (currentSceneIndex + 1 == SceneManager.sceneCountInBuildSettings)
        {
            timer.Save();
            LoadFirstLevel();
        }
        else
        {
            SceneManager.LoadScene(currentSceneIndex + 1);
        }

    }

}

定时器.cs

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
using UnityEngine.UI;
using System;

public class Timer : MonoBehaviour
{

    float currentTime;
    float record = -1f;
    public Text currentText;
    public Text recordText;


    private void Start()
    {
        DontDestroyOnLoad(gameObject);
    }

    public void Save()
    {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Create(Application.persistentDataPath + "/playerInfo.dat");

        PlayerData data = new PlayerData();
        if (RecordTest() == true)
        {
            if (currentTime < record)
            {
                record = currentTime;
                data.recordSave = record;
            }
        }
        else record = currentTime;
        data.recordSave = record;

        bf.Serialize(file, data);
        file.Close();
    }

    public void Load()
    {
        if(File.Exists(Application.persistentDataPath + "/playerInfo.dat"))
        {
            BinaryFormatter bf = new BinaryFormatter();
            FileStream file = File.Open(Application.persistentDataPath + "/playerInfo.dat", FileMode.Open);
            PlayerData data = (PlayerData)bf.Deserialize(file);
            file.Close();

            record = data.recordSave;
        }
    }


    private void Update()
    {
        CheckForResetInput();
        SetCurrentTimeText();
        SetRecordTimeText();
    }

    private void SetRecordTimeText()
    {
        if (record == -1)
        {
            recordText.text = "N/A";
        }
        else
        {
            string minutesRecord = Mathf.Floor(record / 60).ToString("00");
            string secondsRecord = Mathf.Floor(record % 60).ToString("00");

            recordText.text = minutesRecord + ":" + secondsRecord;
        }
    }

    private void CheckForResetInput()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            ResetTimer();
        }
    }

    private void SetCurrentTimeText()
    {
        currentTime += Time.deltaTime;

        string minutes = Mathf.Floor(currentTime / 60).ToString("00");
        string seconds = (currentTime % 60).ToString("00");

        currentText.text = minutes + ":" + seconds;
    }

    public void ResetTimer()
    {
        print("Timer reset");
        currentTime = 0;
    }

    public void ResetRecord()
    {
        record = -1f;
    }

    public bool RecordTest()
    {
        if (record == -1)
        {
            return false;
        }
        else return true;
    }
    public void Collided()
    {
        print("Ouch");
    }
}

[Serializable]
class PlayerData
{
    public float recordSave;
}

没有其他修饰符的序列化字段对于它们所在的类是私有的。 序列化字段纯粹是为了让它们在统一检查器中显示,它们需要是公开的,以便您从其他类访问它们

所以

[SerializeField] float rotationThrust = 100f; 对 Rocket 来说是私有的

[SerializeField] public float rotationThrust = 100f; 将通过引用 Rocket 实例对其他类可见 - 从技术上讲,您不需要序列化该字段,因为 public 将始终显示,但是如果它们全部对齐,它会使您的代码看起来很好:D

您正在为 Rocket 组件创建一个新的 Timer 实例。

Timer timer = new Timer();

这意味着当火箭调用 timer.ResetTimer() 时正在重置新实例化的 Timer 类,而不是附加到场景对象的 MonoBehaviour。

与其在你的火箭类中声明一个新的计时器,不如在检查器中通过以下方式创建一个对它的引用:

[SerializeField] Timer timer;

并将您的 Timer 对象拖放到该引用。


上面的解决方案可能是最好的模式,但是如果您想要一个快速简便的解决方案,请将 currentTime 更改为静态:

private static float currentTime

将此值设为静态意味着所有 Timer 实例将共享相同的值。 由于变量是静态的,因此它不特定于单个类实例。 使用此解决方案,您不会消除 Timer 的多个实例,而是强制它们共享一个 currentTime 变量。 这并不能解决导致问题的设计,但确实可以解决症状。 这就是为什么我说这不是最好的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM