您好我是Unity游戏引擎的新手,我正在创建一个3D射击游戏。在1级我想在一定时间内射击5个敌人,比如30秒。完成1级后,我想进入2级,我的总敌人数为10,并希望在60秒内杀死它,如果失败则会有游戏结束。我为它编写了一些脚本,它有点作用,但它并不完美,因为在开始第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";
}
}
}
答案 0 :(得分:2)
您的问题是您使用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
或static
也可以修复一些错误,但您仍然遇到static
s
这就是为什么我鼓励根本不使用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
或MaxTotalZombies
听起来有点开销,但您可以在这里进行大量额外检查。例如,检查计数器是否永远不会小于零。因为当它发生时,你的代码中有一个错误。例如,将您的TotalZombies意外地增加两个,或者将其减少两个,依此类推。您还可以实现status.IncreaTotalZombies();
status.IncreaTotalZombies();
属性,以确保您永远不会获得更多定义的僵尸。如果发生这种情况,它会抛出一个Exception,直接将代码指向你的代码。
识别错误也更容易。因为连续两次增加它看起来是错误的。
Status.TotalZombies += 2;
以下代码可以恰到好处
Status.TotalZombies
但是,如果您执行上述更改,您将看到当前的Status
不再有效。您还必须更改如何获取Status
课程的实例。为此,我们假设您在Unity中创建名为Generate
的GameObject。然后在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
值通过加载持续存在。但整个方法仍然不是很好。问题是你在代码中硬编码值。而且你似乎想要每个关卡都有不同数量的僵尸和时间。如果您需要,可以将属性添加到初始化变量的Status
类中,并且可以通过Unity IDE设置这些变量。例如,将以下属性添加到public int _StartZombies = 5;
public float _StartTime = 25f;
类。
Status
如果您现在在IDE中将其添加到Start Zombies
课程,则两个TextBox会显示为Start Time
和5
。在此框中,您现在可以输入您的关卡应该有多少个僵尸或开始时间。这些值的默认值为25
和Awake
。但是这些值并没有适用于加载你的等级。要在加载关卡时应用这些值,请将void Awake() {
this.TotalZombies = this._StartZombies;
this.TimeLeft = this._StartTime;
}
方法更改为。
this.TotalZombies
现在this.TimeLeft
和Application.LoadLevel("SomeLevel");
始终获取您在IDE中配置的值。你现在唯一需要做的就是写作。
[System.Serializable]
public class LoadLevelData {
public float TimeLeft;
public string LoadLevel;
}
您只需通过IDE配置僵尸的数量和时间!它还意味着您现在拥有可重用的组件。并配置它所属的东西!
您还描述了加载新级别需要不同的条件。例如,如果用户能够在特定时间内杀死所有僵尸,则他直接跳到3级而不是2级,依此类推。那么如何在不创建大量特殊类的情况下添加它呢?
首先,你需要一个只保存数据的类。在您的情况下,您需要一个特定的时间和一个定义哪个级别被加载。所以你可以写这样的东西。
Status
但是在我看来逻辑属于public LoadLevelData[] _NextLevels;
类,所以你现在要做的是将以下内容添加到这个类中。
Size
只要将其添加到代码中即可。在Unity IDE中,您将看到&#34; Next Levels&#34;使用&#34;光标&#34;。您现在可以展开此光标,并显示Element 0
字段。您现在可以将2写入其中,它会为您提供Element 1
和LoadNextLevel
。因此,Unity使您能够创建对象数组,并且您可以使用所需的任何值从IDE创建任意数量的条目!
现在你可以用这种方式编写public void LoadNextLevel() {
foreach ( var level in this._NextLevels ) {
if ( level.TimeLeft > this.TimeLeft ) {
Application.LoadLevel(level.LoadLevel);
}
}
}
方法了。
Element 0:
Time Left -> 20
Next Level -> "Level 3"
Element 1:
Time Left -> 10
Next Level -> "Level 2"
现在您可以在Unity IDE中进行配置
status.LoadNextLevel()
您只需在游戏结束时致电{{1}}。您可以从IDE配置所有内容。另请注意。填充_NextLevel数组的顺序非常重要。在这种情况下&#34;剩下的时间&#34; - &GT; 20必须在&#34; 10&#34;之前。