Как сделать падающие блоки лучше?

Я и мой друг пытаемся изучить Unity и C #, чтобы мы могли попробовать сделать игру. У моего друга проблемы с логическими операторами, поэтому я пытаюсь сделать простую программу, которая заставляет блоки падать, когда вы нажимаете определенные комбинации клавиш. (Он очень зацикливается на вещах без наглядного пособия) Мне удалось что-то собрать но это очень круто, и я чувствую, что есть намного лучшие способы сделать это. Вот то, что мне удалось написать методом проб и ошибок.

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

public class Falling : MonoBehaviour {

    public float fallspeed = 8.0f;
    bool a;
    bool b;
    private GameObject box_0;
    private GameObject box_2;
    private GameObject box_3;
    private GameObject box_4;
    private GameObject box_5;
    private GameObject box_6;
    private GameObject clone;
    private GameObject clone1;
    private GameObject clone2;
    private GameObject clone3;
    private GameObject clone4;
    private GameObject clone5;
    // Use this for initialization
    void Start () {
        a = false;
        b = false;
        box_0 = GameObject.Find("box_0");
        box_2 = GameObject.Find("box_2");
        box_3 = GameObject.Find("box_3");
        box_4 = GameObject.Find("box_4");
        box_5 = GameObject.Find("box_5");
        box_6 = GameObject.Find("box_6");
    }

    // Update is called once per frame
    void Update () {
        // makes box_0 fall if a key is pressed
        if (Input.GetKeyDown (KeyCode.A)) {
            a = true;
            if (a) {
                clone = Instantiate (box_0, transform.position, Quaternion.identity);
                a = false;
            }
        }
        if (Input.GetKeyDown (KeyCode.B) || Input.GetKeyDown (KeyCode.C)) {
            a = true;
            if (a) {
                clone1 = Instantiate (box_2, transform.position, Quaternion.identity);
                a = false;
            }
        }
        if (Input.GetKeyDown (KeyCode.Q) && Input.GetKeyDown (KeyCode.E)) {
            a = true;
            if (a) {
                clone2 = Instantiate (box_3, transform.position, Quaternion.identity);
                a = false;
            }
        }
        if (Input.GetKeyDown (KeyCode.R) || (Input.GetKey (KeyCode.LeftShift) && Input.GetKeyDown (KeyCode.S)) ) {
            a = true;
            if (a) {
                clone3 = Instantiate (box_4, transform.position, Quaternion.identity);
                a = false;
            }
        }
        if (Input.GetKeyDown (KeyCode.U)) {
            a = true;
            if (a) {
                clone4 = Instantiate (box_5, transform.position, Quaternion.identity);
                a = false;
            }
        }
        if (Input.GetKeyDown (KeyCode.I)) {
            a = true;
            if (a) {
                clone5 = Instantiate (box_6, transform.position, Quaternion.identity);
                a = false;
            }
        }
        //makes clones fall if they exist
        if (clone != null) {
            clone.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
        } else if (clone1 != null) {
            clone1.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
        } else if (clone2 != null) {
            clone2.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
        } else if (clone3 != null) {
            clone3.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
        } else if (clone4 != null) {
            clone4.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
        } else {
            if (clone5 != null) {
                clone5.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
            }
        }
    }
}

код работает, но есть проблемы. например, если я создаю более одного клона одного и того же клона, они застревают, и я не думаю, что вообще очень хорошо справился с нулевым игровым объектом. может ли кто-нибудь дать мне совет, как это сделать лучше?

(также в случае, если это уместно, есть шесть цветных блоков, выстроенных в линию в верхней части экрана, при нажатии определенных клавиш создается клон блока, на который ссылается, и заставляется упасть. Блок имеет коллайдер, и когда он ударяется внизу экрана он попадает в другой коллайдер и разрушается.)


person David Adams    schedule 31.10.2017    source источник
comment
Я считаю, что есть значительно более простые способы объяснения логических операций, чем в подобной программе, тем более, что я вообще вижу здесь очень мало логических операторов.   -  person Carcigenicate    schedule 31.10.2017
comment
Вы, наверное, правы, это просто идеал, который я придумал в своей голове, и, честно говоря, он даже не закончен полностью. Если у вас есть предложения о лучших способах решения этой проблемы, я более чем открыт. Я также очень новичок в программировании, поэтому любые советы о том, что я сделал не так или мог бы сделать лучше, также будут приветствоваться.   -  person David Adams    schedule 31.10.2017
comment
Я предлагаю создать класс коробки и создать его экземпляр. Либо прикрепите твердое тело и используйте гравитацию, чтобы переместить его вниз, либо поместите падающий код в функцию обновления этого класса. Это прояснило бы здесь примерно половину вашего кода. Просто пропустите игру с bools и просто создайте поле при нажатии клавиши, так как вы все равно устанавливаете его в false сразу после этого. Кроме того, если вы создаете более 1 бокса с именем clone1 или clone2 и т. Д., Вы теряете ссылку на него, и поэтому он перестает падать. Поместите новые поля в список и просто перебирайте их, если ничего больше.   -  person oxrock    schedule 31.10.2017
comment
@DavidAdams Просто покажите ему таблицу истинности и заставьте его составить по одной для каждого логического оператора. Вы не можете заниматься программированием, особенно в такой сложной области, как программирование игр, без глубокого понимания основных логических операций. Я думаю, вам, ребята, нужно сделать шаг назад и сначала поработать над более простыми вещами. В противном случае это окажется упражнением, а иначе - разочарованием.   -  person Carcigenicate    schedule 31.10.2017


Ответы (4)


Несколько вещей, которые вы определенно можете улучшить ...


  1. Когда у вас есть коллекция экземпляров, используйте структуру данных, подходящую для коллекций.

Вместо этого:

private GameObject box_0;
private GameObject box_2; //<<<That typo tho!
private GameObject box_3;
private GameObject box_4;
private GameObject box_5;
private GameObject box_6;
//...

Вы можете и должны это сделать:

private GameObject[] boxes = new GameObject[6];
private List<GameObject> clones = new List<GameObject>();

Это, соответственно, array и list; два из многих типов данных коллекции C #.

  • Массивы отлично подходят для фиксированного количества элементов определенного типа объекта.
  • Списки отлично подходят для динамического количества элементов.

Другие типы включают: Set<>, Dictionary<>, Queue<>, Stack<> и другие; у каждого есть свой оптимальный вариант использования.


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

Вместо этого:

box_0 = GameObject.Find("box_0");
box_2 = GameObject.Find("box_2"); //<<<That typo tho!
box_3 = GameObject.Find("box_3");
box_4 = GameObject.Find("box_4");
box_5 = GameObject.Find("box_5");
box_6 = GameObject.Find("box_6");

У вас может быть это:

for(int i=0; i<6; i++)
    boxes[i] = GameObject.Find("box_"+i);

Это называется циклом. Это способ сделать это, когда вам нужно выполнить повторяющуюся задачу несколько раз. В частности, это цикл for.

Это также:

  • while (while(/*condition evaluated before loop*/){/*some code*/})
  • do-while (do{/*some code*/}while(/*condition evaluated after loop*/))
  • for-each (foreach(var element in someCollection){/*some code*/})
  • И другие.

  1. Помните о дублировании!

Переменная a в:

if (Input.GetKeyDown (KeyCode.A)) {
    a = true;
    if (a) {
        clone = Instantiate (box_0, transform.position, Quaternion.identity);
        a = false;
    }
}
if (Input.GetKeyDown (KeyCode.B) || Input.GetKeyDown (KeyCode.C)) {
    a = true;
    if (a) {
        clone1 = Instantiate (box_2, transform.position, Quaternion.identity);
        a = false;
    }
}
//...

ничего! И b даже не используется!


  1. Если ваши клоны являются динамическими (иногда могут иметь значение NULL, а иногда нет), вы можете обрабатывать их динамически!

Если вы сохраните clones список как список существующих клонов, а не список возможных клонов, будет намного проще автоматизировать работу. потом!

if (Input.GetKeyDown (KeyCode.A))
    clones.Add(Instantiate(boxes[0], transform.position, Quaternion.identity));
if (Input.GetKeyDown (KeyCode.B) || Input.GetKeyDown (KeyCode.C))
    clones.Add(Instantiate(boxes[1], transform.position, Quaternion.identity);
//...

Позже вместо этого:

if (clone != null) {
    clone.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
} else if (clone1 != null) {
    clone1.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
} else if (clone2 != null) {
    clone2.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
} else if (clone3 != null) {
    clone3.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
} else if (clone4 != null) {
    clone4.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
} else {
    if (clone5 != null) {
        clone5.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
    }
}

Ты можешь сделать это:

foreach(var clone in clones)
    clone.transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);

  1. Вы должны быть последовательны.

private GameObject box_0;
private GameObject box_2; //<<<NOPE! should be box_1!
//And others should follow the sequence from 1!
private GameObject box_3;
private GameObject box_4;
private GameObject box_5;
private GameObject box_6;

private GameObject clone; //<<<NOPE! Should be 'clone_0', 'clone_1' etc...
private GameObject clone1;
private GameObject clone2;
private GameObject clone3;
private GameObject clone4;
private GameObject clone5;
person XenoRo    schedule 31.10.2017
comment
В значительной степени обобщил все, что я хотел прокомментировать ошибки ОП. Единственное, что я хотел бы упомянуть, это то, что в вашем 4-м пункте цикл foreach и OP if elses не идентичны. На самом деле, вещь, которая не идентична, лучше, так как OP жаловался, что если один упадет, другие замерзнут, ваше решение решит эту проблему (поскольку в его случае вызывается только один из переводов, а в вашем каждый вызывается в петля). - person Arman Papikyan; 31.10.2017
comment
IMHO может быть проблема во всех подходах на основе списков, поскольку в вопросе упоминалось, что у блока есть коллайдер, и когда он попадает в нижнюю часть экрана, он попадает в другой коллайдер и разрушается. Не зная, как обрабатывается уничтожение, мы можем получить список, содержащий уничтоженные игровые объекты. Я считаю, что лучше всего заставить каждую падающую коробку управлять своим движением. - person Mikko Koivisto; 31.10.2017
comment
@MikkoKoivisto В оригинальном подходе Op у вас может быть 6 переменных, содержащих ссылки на уничтоженные объекты. Не стесняйтесь спросить ОП о том, как он справляется с подобным сценарием, и если это так, как вы подозреваете, я с радостью отредактирую ответ, чтобы учесть это. Но пока это не будет подтверждено, я не собираюсь рассматривать то, что не является частью вопроса или частью предоставленного кода, а просто основано на предположении; если бы я делал это для каждого ответа, я сойду с ума. --- Однако в отношении того, как объекты должны управлять собой * (*: в отношении специфического для них поведения), я полностью согласен! +1 - person XenoRo; 01.11.2017

Во-первых, это слишком много переменных. Когда вы обнаруживаете, что копируете и вставляете свой код, вы, вероятно, делаете что-то не так. Просто поместите всех этих плохих парней в List<GameObject>, и вы достигнете того же результата.

Я бы порекомендовал то, что ответил Микко Койвисто, а именно создание другого сценария для обработки фактического эффекта падения ящиков. Но чтобы сохранить все в одном сценарии, я поступил иначе.

Я немного изменил ваш код, чтобы он стал намного чище и читабельнее. Комментарии в основном рассказывают обо всех изменениях.

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

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

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

public class Falling : MonoBehaviour
{

    public float fallspeed = 8.0f;
    //removed bool a, b because they never get used anymore

    //"boxes" is public so you can add the boxes in the editor instead of having to do it in your start function
    public List<GameObject> boxes;  //Instead of 6 different variables you could just throw them all in a list
    private List<GameObject> clones; //Same here

    // Update is called once per frame
    void Update()
    {

        // makes box_0 fall if a key is pressed
        //Not sure why you were checking if(a) constantly when you set it true 1 line ahead, how could it be false? I took the liberty to remove that
        if (Input.GetKeyDown(KeyCode.A))
        {
            clones.Add(Instantiate(boxes[0], transform.position, Quaternion.identity)); //No need for different variables, just throw them all in one list
        }
        if (Input.GetKeyDown(KeyCode.B) || Input.GetKeyDown(KeyCode.C))
        {
             clones.Add(Instantiate(boxes[1], transform.position, Quaternion.identity));
        }
        if (Input.GetKeyDown(KeyCode.Q) && Input.GetKeyDown(KeyCode.E))
        {
            clones.Add(Instantiate(boxes[2], transform.position, Quaternion.identity));
        }
        if (Input.GetKeyDown(KeyCode.R) || (Input.GetKey(KeyCode.LeftShift) && Input.GetKeyDown(KeyCode.S)))
        {
            clones.Add(Instantiate(boxes[3], transform.position, Quaternion.identity));
        }
        if (Input.GetKeyDown(KeyCode.U))
        {
            clones.Add(Instantiate(boxes[4], transform.position, Quaternion.identity));
        }
        if (Input.GetKeyDown(KeyCode.I))
        {
            clones.Add(Instantiate(boxes[5], transform.position, Quaternion.identity));
        }


        //Instead of checking if any of them exist you can just loop through the list with clones and make them all fall
        foreach (GameObject clone in clones)
        {
            clone.transform.Translate(Vector3.down * fallspeed * Time.deltaTime, Space.World);
        } 
    }
}
person Shogunivar    schedule 31.10.2017

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

Прикрепите это к каждому ящику и удалите часть clone.transform.Translate из сценария падения:

public class BoxScript : MonoBehaviour {

    public float fallspeed = 8.0f;
    public bool fall = false;
    void Update () {
        if (fall) {
            transform.Translate (Vector3.down * fallspeed * Time.deltaTime, Space.World);
        }
    }
}

... а затем при создании новых ящиков:

clone = Instantiate (box_0, transform.position, Quaternion.identity);
clone.fall = true;
person Mikko Koivisto    schedule 31.10.2017

Я думаю, вы можете вместо этого составить список блоков и клонов, чтобы вы могли факторизовать свой код (я не знаком с единством, поэтому могут быть опечатки, но общая идея будет такой:

        var clones = new List<GameObject>();
        GameObject box = null;
        if (Input.GetKeyDown(KeyCode.A))
        {
            box = box_0
        }
        // else if ... box = box_1 ...
        // end of your series of else if
        if (box != null)
        {
            clone = Instantiate(box, transform.position, Quaternion.identity);
            clones.Add(clone);
        }
        box = null;
        //   
        foreach (GameObject clone in clones)
        {
            clone.transform.Translate(Vector3.down * fallspeed * Time.deltaTime, Space.World)
        }
person Maxime    schedule 31.10.2017