如何制作更好的落块?

How to make better falling blocks?

我和一个朋友正在尝试学习 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);
            }
        }
    }
}

代码可以正常运行,但有问题。一方面,如果我生成多个相同的克隆体,它们就会卡住,而且我认为我根本没有很好地处理空游戏对象。谁能给我一些关于如何做得更好的建议?

(如果相关的话,屏幕顶部排列着六个彩色块,当按下某些键时,将创建一个正在引用的块的克隆并使其下降。该块有一个对撞机,当它撞到屏幕底部时,它会撞到另一个对撞机并被摧毁。)

"for one if I spawn more then one of the same clone they get stuck and I dont think I handled the null gameobject very well at all."

将此附加到每个框并从 Falling-script 中删除 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;

我认为你可以制作一个盒子和克隆的列表,这样你就可以分解你的代码(我不熟悉 unity 所以可能会有拼写错误,但总体思路是:

        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)
        }

首先,变数太多了。当您发现自己复制并粘贴代码时,您可能做错了什么。只需将所有这些坏男孩放在一些 List<GameObject> 中,您就可以获得相同的结果。

我推荐的是 Mikko Koivisto 回答的内容,那就是制作另一个脚本来处理盒子的实际掉落效果。但是为了将所有内容都保存在一个脚本中,我采用了不同的处理方式。

我稍微更改了您的代码,使其更加清晰易读。评论基本上说出了所有的变化。

至于为什么老盒子卡住了。当你创建一个新的克隆时,你一直在覆盖你的克隆变量。因此,在更新中,您的旧克隆将不再更新,因为它们不再在变量中定义,因此它们停止下降。

Note: In order for this to work you'll have to add the boxes to your script in the editor, you could also do it in code but it's a lot messier. Just click on the script in the editor, open the boxes list, put size on six and drag them all in there.

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);
        } 
    }
}

您绝对可以改进的几件事...


  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>();

分别是arraylist;许多 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. 如果你克隆是动态的(有时可以是空的,有时不是),你可以动态地处理它们!

如果您将 clones list 作为 existing 克隆的列表,而不是 可能个克隆,以后自动化东西会容易得多!

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;