Нужна логика для моей игры на андроид

Привет, я новичок в игровом движке Unity, и я создаю 3D-стрелялку. На уровне 1 я хочу застрелить 5 врагов за определенное время, скажем, 30 секунд. После завершения уровня 1 я хочу перейти на уровень 2, где у меня всего 10 врагов, и я хочу убить его за 60 секунд, и в случае неудачи игра будет окончена. Я написал для него некоторый скрипт, он немного работает, но он не идеален, потому что после запуска уровня 2 игра становится медленнее, и после того, как игра на уровне 2 перезапускается снова, но не со значением по умолчанию 10 врагов, а начинается с нет. который достигает во время игры. нужна идея, хорошая логика и сценарий для моей игры. вот мой код.

 public class Status : MonoBehaviour
{

    public static int TotalZombies=5;
    public static float timeLeft=25.0f; 

 // destry this game object.
            Destroy (this.gameObject);
            TotalZombies--;
            }

и вот мой другой скрипт, в котором я управляю своими уровнями, временем и т. д.

using UnityEngine;
using System.Collections;

public class Generate : MonoBehaviour {
    public GUIText Zombiesobject;
    public string zombiesscore;
    public GUIText countdown;
    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {
        zombiesscore = "Zombies Left: " + Status.TotalZombies.ToString ();
        Zombiesobject.text = (zombiesscore);

        Status.timeLeft -= Time.deltaTime;
        if (Status.timeLeft <= 0.0f && Status.TotalZombies > 0) 
        {
            countdown.text = "Game Over";
            Application.LoadLevel(Application.loadedLevel);
} 
        else if (Status.timeLeft <= 10.0f && Status.TotalZombies > 0) 
        {
            countdown.text = "Time left = " + (int)Status.timeLeft + " seconds" + "  \n  You are running out of time!";
        } 
        else if (Status.timeLeft > 0.0f && Status.TotalZombies <= 0) {
                countdown.text = "You win!";
                Application.LoadLevel("level 2");
                Status.TotalZombies=10;
                Status.timeLeft=59.0f;
        }
        else
        {
            countdown.text = "Time left = " + (int)Status.timeLeft + " seconds";
        }
    }
}

person user3866627    schedule 08.11.2014    source источник
comment
так же зависает после окончания игры.   -  person user3866627    schedule 08.11.2014


Ответы (1)


Ваша проблема в том, что вы используете static переменных. static точно такие же, как и глобальные переменные. Единственное отличие состоит в том, что вам нужно получить к ним доступ через имя класса.

Так что же это означает? Когда вы загружаете свою игру и когда загружается сам класс, создаются и инициализируются статические переменные. В вашем случае TotalZombies установлено на 5, а timeLeft на 25f.

Но эти переменные сохраняются и никогда не инициализируются повторно, пока работает ваша игра. Даже если вы сделаете Application.LoadLevel эти переменные и их значения сохраняются.

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

Из-за этого я призываю никогда не использовать static переменных. Они легко внедряют трудно обнаруживаемые ошибки. Давайте предположим простое исправление вашего кода.

Вы дополнительно добавляете инициализацию в свой метод Start(). Например, в вашем классе Status вы добавляете.

void Start() {
    TotalZombies = 5;
    timeLeft     = 25.0f;
}

В вашем случае это может полностью решить проблему, но вы также можете сказать, что это просто случайность или удача.

В Unity не существует порядка, в котором вызывается Start(). Например, все еще может случиться так, что метод Start в вашем классе Generate вызывается первым при загрузке сцены. Если вы использовали Status.TotalZombies или Status.timeleft в Start для инициализации чего-либо в Generate, у вас все еще есть ошибка, заключающаяся в том, что ваша инициализация неверна, поскольку она использует переменные из предыдущего запуска. Проблема в том, что иногда Unity может сначала выполнить Status.Start() перед Generate.Start(), а иногда и наоборот. Это привело бы к ошибке, которая возникает только sometimes и которую чрезвычайно сложно отладить.

Если бы вы знали вышеизложенное, вы также могли бы поместить свою инициализацию в метод Awake. Потому что методы Awake будут вызываться перед любым методом Start. Так что это будет лучшее решение.

Но существуют и другие проблемы. Например, давайте рассмотрим ваш метод Generate.Update(). Например, вы напрямую выполняете Status.timeLeft -= Time.deltaTime; в своем методе Update. Но когда, например, в вашей игре есть несколько GameObjects с компонентом Generate, это означает, что timeLeft будет уменьшено несколько раз в одном кадре. Если у вас есть два компонента Generate, это означает, что ваше время будет истекать в два раза быстрее.

Таким образом, даже установка инициализации в Start или Awake может исправить некоторые ошибки, но у вас все еще будут другие проблемы с statics

Это причина, по которой я рекомендую вообще не использовать static. Итак, как решить эту проблему? Вместо статического вы должны создать атрибуты класса. И, кроме того, вы должны сделать все свои атрибуты доступными только для вашего собственного класса. Это также влияет на другой код. Например, вы больше не могли уменьшить атрибут timeLeft с Generate. Звучит как недостаток, но заставляет задуматься о том, как правильно изменить timeLeft. В вашем случае вы не очень хотите, чтобы любой класс отовсюду мог изменить timeLeft. Это время, которое следует постоянно сокращать, и сокращать его в несколько раз просто ошибка. Результат того есть. Ваш класс Status должен изменить только timeLeft в Update. То же самое касается TotalZombies. Было бы лучше просто иметь такой метод, как IncrementTotalZombies и DecrementTotalZombies, вместо того, чтобы делать Status.TotalZombies++ и так далее. Например, ваш класс Status теперь должен выглядеть так:

public class Status : MonoBehaviour {
    public int   TotalZombies { get; private set; }
    public float TimeLeft     { get; private set; }

    void Awake() {
        this.TotalZombies = 5;
        this.TimeLeft     = 25f;
    }

    void Update() {
        this.TimeLeft -= Time.deltaTime;
    }

    public void IncreaseTotalZombies() {
        this.TotalZombies++;
    }

    public void DecreaseTotalZombies() {
        if ( this.TotalZombies <= 0 ) {
            throw new ApplicationException("Cannot decrease TotalZombies. Already 0. Possible Bug in your code.");
        }
        this.TotalZombies--;
    }
}

Теперь IncreaseTotalZombies или DecreaseTotalZombies звучат как накладные расходы, но здесь вы можете сделать много дополнительных проверок. Например, проверьте, не становится ли счетчик никогда меньше нуля. Потому что когда это происходит, у вас где-то в коде есть ошибка. Например, случайно увеличить TotalZombies на два или где-то еще уменьшить его на два и так далее. Вы также можете реализовать атрибут MaxTotalZombies, который гарантирует, что вы никогда не получите больше зомби, как определено. И если это произойдет, он выдаст исключение, указывающее на ваш код непосредственно там, где это произошло.

Также легче выявить ошибки. Потому что увеличение его два раза подряд выглядит неправильно.

status.IncreaTotalZombies();
status.IncreaTotalZombies();

где следующий код может выглядеть правильно

Status.TotalZombies += 2;

Но если вы сделаете вышеуказанные изменения, вы увидите, что ваш текущий Status.TotalZombies больше не будет работать. Вы также должны изменить способ получения экземпляра вашего класса Status. Для этого предположим, что вы создали GameObject в Unity с именем Status. Затем в свой класс Generate вы должны добавить следующее.

private Status status;
void Awake() {
    this.status = GameObject.Find("Status").GetComponent<Status>();
}

Теперь вы можете заменить Status.TotalZombies++ и так далее на status.IncreaseTotalZombies(). Если вы просто хотите получить значения, вы все равно можете написать status.TimeLeft, но установка значения status.TimeLeft -= Time.deltaTime теперь вызовет ошибку. И вам больше не нужно его устанавливать, потому что это поведение, которое класс Status уже обрабатывает в своем методе Update.

Кроме того, в вашем классе Generate у вас был такой код.

Application.LoadLevel("level 2");
Status.TotalZombies=10;
Status.timeLeft=59.0f;

Это не сработало, как ожидалось. Потому что, когда вы вызываете Application.LoadLevel(), вызывается ваша новая сцена, а строки за ней никогда не вызывались. Вы можете исправить это, изменив порядок.

Status.TotalZombies=10;
Status.timeLeft=59.0f;
Application.LoadLevel("level 2");

Потому что ваш статус, где static значение сохраняется при загрузке. Но весь подход по-прежнему не очень хорош. Проблема в том, что вы жестко задаете значения в своем коде. И, похоже, вам нужно разное количество Зомби и Времени для каждого уровня. Если вы хотите, вы можете просто добавить атрибуты в свой класс Status, которые инициализируют ваши переменные, и эти переменные можно установить через вашу среду разработки Unity. Например, добавьте следующие атрибуты к вашему классу Status.

public int   _StartZombies = 5;
public float _StartTime    = 25f;

Если вы добавите это в свой класс Status сейчас, в вашей IDE появятся два текстовых поля с именами Start Zombies и Start Time. В этих полях теперь вы можете указать, сколько Зомби или сколько стартового времени должно быть у вашего уровня. Значения по умолчанию — 5 и 25 для этих значений. Но эти значения не применялись при загрузке вашего уровня. Чтобы также применять эти значения при загрузке вашего уровня, измените метод Awake на.

void Awake() {
    this.TotalZombies = this._StartZombies;
    this.TimeLeft     = this._StartTime;
}

Теперь this.TotalZombies и this.TimeLeft всегда получают значения, которые вы настроили в своей среде IDE. Единственное, что вам сейчас нужно сделать, это написать.

Application.LoadLevel("SomeLevel");

И вы просто можете настроить количество зомби и время через вашу IDE! Это также означает, что теперь у вас есть повторно используемые компоненты. И вы настраиваете вещи там, где они принадлежат!


Вы также описали, что вам нужны разные условия для загрузки нового уровня. Например, если пользователь может убить всех зомби за определенное время, он сразу переходит на уровень 3 вместо уровня 2 и так далее. Итак, как вы можете добавить это, не создавая множество специальных классов?

Сначала вам нужен отдельный класс, который просто хранит данные. В вашем случае вам нужно конкретное время и определение, какой уровень загружается. Итак, вы можете написать что-то вроде этого.

[System.Serializable]
public class LoadLevelData {
    public float  TimeLeft;
    public string LoadLevel;
}

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

public LoadLevelData[] _NextLevels;

Как только вы добавите это в свой код. В Unity IDE вы увидите «Следующие уровни» с «Курсором». Теперь вы можете развернуть этот курсор, и появится поле Size. Теперь вы можете, например, записать в него 2, и это даст вам Element 0 и Element 1. Таким образом, Unity дает вам возможность создавать массив объектов, и вы можете создавать столько записей, сколько хотите, из IDE с любыми значениями, которые вы хотите!

Теперь вы можете написать метод LoadNextLevel таким образом.

public void LoadNextLevel() {
    foreach ( var level in this._NextLevels ) {
        if ( level.TimeLeft > this.TimeLeft ) {
            Application.LoadLevel(level.LoadLevel);
        }
    }
}

Теперь вы можете настроить в Unity IDE

Element 0:
    Time Left  -> 20
    Next Level -> "Level 3"

Element 1:
    Time Left -> 10
    Next Level -> "Level 2"

Вам нужно только позвонить status.LoadNextLevel(), когда ваша игра закончится. И вы можете настроить все из IDE. Также обратите внимание. Порядок, в котором вы заполняете массив _NextLevel, важен. В этом случае «Оставшееся время» -> 20 должно стоять перед «10».

person David Raab    schedule 08.11.2014
comment
спасибо ... есть ли способ, где в классе Generate я могу контролировать все уровни игры, а также количество и время моих врагов? например перед переходом на 3 уровень я хочу изменить своих врагов на 15 и время на 15.0f? - person user3866627; 08.11.2014
comment
@user Я добавил это в конце. Лучше всего, если вы не используете static и инициализируете их другими значениями, такими как _StartZombies. Таким образом, вы можете настроить время и зомби из Unity IDE, а не через код. Установка его через код с инициализацией и по-прежнему использование статики - не лучший способ. Потому что при каждой загрузке ваши Awake или Start сбрасывают значения по умолчанию. Это будет работать только в том случае, если вы установите статику до LoadLevel и никогда не инициализируете значения с помощью Start или Awake. - person David Raab; 08.11.2014
comment
это хорошая идея использовать разные классы генерации для разных уровней? например, сгенерировать уровень 1, сгенерировать уровень 2, где я устанавливаю своего врага и время индивидуально? - person user3866627; 08.11.2014
comment
@user Когда вы делаете то же самое в своих классах Generate, только с другими значениями, такими как зомби и время, это не очень хорошая идея. Потому что у вас много дублированного кода. Поиск DRY-принципа. Если меняются только значения, просто сделайте их настраиваемыми через Unity IDE! Поэтому вам не нужно писать класс Generate для каждого уровня. Я имею в виду, это экономит время, верно? Но если вы будете следовать всем инструкциям, которые я вам дал, вы точно закончите с одним классом Status и Generate, который можно настроить таким образом. Без этого вам нужно несколько классов Generate1, Generate2 и так далее. - person David Raab; 08.11.2014
comment
но я не могу понять, как я могу перейти со второго уровня со значениями зомби 15 и оставшимся временем 40 секунд? Поскольку я установил различные временные условия, например, если (timeLeft › 0 && Totalzombies ‹= 0), то он загрузит уровень 2, а не мой новый уровень 3? - person user3866627; 08.11.2014
comment
Если вы создадите классы, как я объяснил, у вас будет Status GameObject на каждом уровне. Когда вы открываете свой уровень 2 в Unity, вы можете просто щелкнуть GameObject, который содержит компонент Status, и вы можете настроить количество зомби и время для этого уровня. Вы можете сделать то же самое со всеми своими уровнями. Таким образом, вам больше не нужно устанавливать правильное количество Зомби и время для каждого уровня. Вы просто выполняете Application.LoadLevel("Level 2"), и в самой вашей Сцене Уровня 2 настраивается, сколько у нее Зомби/Времени. - person David Raab; 08.11.2014
comment
@user Я добавил дополнительный код, который делает то, что вы описали. - person David Raab; 08.11.2014