【问题标题】:Unity C# Class and ObjectsUnity C# 类和对象
【发布时间】:2018-08-08 21:32:51
【问题描述】:

Alight,我很抱歉这将是一个相当愚蠢的问题,但我正在努力全面了解对象的用途和用途。具体来说,我在 Unity 工作,但我不确定这是否重要。另外,我还应该提到,语言相当压倒性。但这是我正在处理的简单示例。

public class Player : MonoBehaviour 
{
    private string name = "Players Name";

    private void Start () {
        var nexus = new Player();
    }

    public static void Using() {
        // how are these different?
        Attack();
        this.Attack();
    }

    public static void Move() {
        print ("The player is moving"); 
    }

    private void Attack () {
        print ("The player is attacking"); 
    }
}

public class UsingStuff : MonoBehaviour 
{
    private void Start () {
        Player.Move();
    }
}

以下是问题:

  1. 调用函数 Attack() 与 this.Attack 有什么区别?从新手的角度来看,他们似乎在做同样的事情。但是,我假设我的示例过于简单了。
  2. 我使用随机名称 nexus 创建了 Player 类的对象。但是,当我从不同的类调用函数时,我似乎使用的是类名而不是这个变量名。那么创建变量名的目的是什么?

我现在将把它留在这两个方面,因为希望这将有助于解决一些其他事情的困惑。 感谢您的帮助。

【问题讨论】:

  • 1) 在这种情况下没有区别。 2) 您需要对变量、属性和范围有基本的了解。您应该有一个在Start() 中实例化的Player 类成员。相反,您正在创建一个播放器,然后将其丢弃,因为当Start() 返回时它超出了范围。
  • 对于#2,查看C# static vs instance methods 了解为什么有时需要类的实例(变量名)来调用方法,而其他时候只使用类名(用于静态方法)。
  • 你应该有 Update 功能它没有被使用,它的性能更差。此外,您不应将其包含在 Stack Overflow 帖子中,因为它与您的问题无关。
  • 我建议您在尝试制作游戏之前花大量时间自学 C# 和核心编程概念。我花了很长时间才终于准备好参加比赛。现在,您正在同时学习非常复杂的事物,这阻碍了您对这两件事的学习。

标签: c# oop unity3d


【解决方案1】:

调用函数 Attack() 与 this.Attack 有什么区别?

例如,当您编写 Player 类并且想要使用方法“Attack()”时,您可以通过键入 Attack() 或 this.Attack() 来实现。在这两种情况下它做同样的事情。

当你使用“这个”时。您保证您使用的方法是在您所在的类上实现的。例如:

public void GroupAttack(DifferentPlayer ally){
    Attack(); 
    ally.Attack();
}

public void GroupAttackWithThis(DifferentPlayer ally){
    this.Attack(); 
    ally.Attack();
}

方法 GroupAttack 和 GroupAttackWithThis 没有区别。

查看区别的有用链接: In Java, what is the difference between this.method() and method()?

我使用随机名称 nexus 创建了 Player 类的对象。但是,当我从不同的类调用函数时,我似乎使用的是类名而不是这个变量名。那么创建变量名的目的是什么?

发生这种情况是因为您将“Move”函数设为静态,这意味着对于该类的每个实例,它都会执行完全相同的操作。当您希望方法或变量对于类的每个实例每次都具有相同的值或具有相同的行为时,使用静态。 如果您的方法不是静态的,您将需要使用变量的名称来使用它,那是因为您希望您的方法更改每个实例的行为。 一个简单的例子:

public string GetName(){
    return name;
}

此方法将为每个玩家返回不同的名称(将在变量中返回玩家的名称)。所以如果你这样做:

Player jhon = new Player();
jhon.name = "Jhon";
String jhonsName = jhon.GetName();

变量“jhonsName”将包含一个值为“Jhon”的字符串。

我希望这对你有用,祝你的游戏好运。

【讨论】:

    【解决方案2】:

    当您执行Player nexus = new Player(); 时,您正在创建一个类型(/class)“Player”的新实例(/object)。变量nexus 是一个对象引用(即,它引用您刚刚创建的对象实例)。一旦你有了一个实例,你就可以在那个对象上调用实例方法,设置或读取它的属性(顺便说一句,你没有这些属性)。由于只有一个对该对象的引用(nexus 变量),当该引用超出范围(在函数末尾)时,该对象不再被任何对象引用,并且有资格进行垃圾回收。

    当您使用static 关键字声明方法(/function)时,您是在类级别而不是在实例级别创建它。如果你想调用非静态的UpdateAttack 方法,你必须说nexus.Updatenexus.Attack。要调用静态 UsingMove 方法,您可以使用类名(Player),如下所示:Player.UsingPlayer.Move

    您发布的代码无法编译,是吗?您不能从静态方法调用实例方法。您不能从静态方法Using 调用实例方法Attackthis.Attack(那里没有“this”(this 是与实例方法关联的当前对象实例))。最好发布至少可以编译的代码(除非你说(在评论中)//this is the code that doesn't compile

    您也只在Start 方法中创建了一个新的 Player 实例,这有点奇怪。通常,您在其他类或静态 factory 方法中创建对象的实例(并非总是如此,但通常如此)。

    阅读面向对象编程的本质、C# 的static 关键字和对象实例的本质(一般而言)。您可能还想了解 C# 的属性。

    哦,从长远来看,命名 Using 会受到伤害(小写 using 是 C# 关键字,已经有两个含义)。

    最后,回答您的问题,“以这种方式调用实例方法:Attack() 或以这种方式:this.Attack() 具有相同的语义。”当然,如上所述,如果您尝试从静态方法调用实例方法,则不能使用任何一种形式。

    【讨论】:

      【解决方案3】:

      Unitys MonoBehaviour 与普通 C# 类之间存在一些差异。我建议观看一些 Unity 教程。 Unity 脚本是面向场景的。

      1. 没有区别。 this.Something 可用于将某物标记为“来自类对象”(通常为属性或变量),以区别于参数。

      2. 编写 Player nexus = new Player(); 应该是一个警告,因为您不应该对 MonoBehaviour 执行此操作。

      通过将脚本“玩家”放在游戏对象上,您可以在场景中创建玩家。您应该从 GameObject 访问 Player 脚本。因此,您应该删除方法上的“静态”。然后将 public Player player; 放入 UsingStuff(在公共类 UsingStuff : MonoBehaviour { 下)。然后将您的播放器拖到播放器的占位符上。您将在场景中使用 UsingStuff-Script 的 GameObject 上找到它。现在您可以在 Start 方法中调用 player.Move()(带有小 p)。

      尝试为您的变量 name 命名。它已经存在于每个 MonoBehaviour 中。它指的是脚本所在的游戏对象的名称。您的命名也应该引发警告。

      【讨论】:

        【解决方案4】:
        1. this 关键字的意思是“对类的当前实例的引用”它确实是语法糖(它可以使您的代码更清晰易读)

        当你这样做时

        public static void Using() {
            Attack();
            this.Attack();
        }
        

        在幕后,编译器会这样执行:

        public static void Using() {
            this.Attack();
            this.Attack();
        }
        

        所以在您的上下文中,它们是相同的。

        有时它很有用,请参阅this post

        1. 如果我理解正确,这个问题与为什么需要一个名为 name 的变量而不是仅仅将对象命名为播放器有关 姓名。 变量“name”和对象的名称是两个不同的东西

        假设你想将玩家姓名输出到屏幕上

        void Start () {
            Player nexus = new Player();
        
        }
        

        在上面的方法中,我不能[简单地]这样做

        void Start () {
            Player p = new Player();
            p.name = "nexus";
            Console.WriteLine(p);
        }
        

        而在这里我输出名为 name 的变量中的值。变量基本上是一个在 (name) 上带有名称的框,其中包含值 (nexus)

        我建议你在处理 Unity 之前先看看 basic programming tutorials

        【讨论】:

        • 请注意,Using 函数中的代码不会编译,因为它是 static 函数,而 Attack 不是 static 函数。您不能static 函数调用非 static 函数。
        • 而且,第一个代码示例(从静态方法 Using 调用实例方法 Attack)不会编译。两个Start 方法示例也有点奇怪。正如@itsme86 所指出的,第一个创建了一个新的 Player 对象,但是,由于引用立即超出范围,该对象将永远丢失并且符合“垃圾收集”的条件。第二个例子只有在 Player 类有一个名为 name 的公共字符串属性时才有效(按照惯例,它实际上应该以大写字母开头)。 WriteLine 调用依赖于具有 ToString 方法 override 的 Player
        • 注意,仅引用 OPs 代码以进行解释。省略了 cmets re:代码质量和其他超出问题范围的因素
        【解决方案5】:

        除了其他答案之外,您还可以使用this 来消除歧义。常见例子:

        public MyType(this name) => this.name = name;
        

        或者使用扩展方法:

        public static void MyCustomExtensionMethod(this Foo foo) { }
        ...
        public class Bar() : Foo
        {
           // I did not create Foo and do not have access to Foo
           this.MyCustomExtensionMethod();
        }
        

        【讨论】:

          【解决方案6】:

          在深入研究代码之前,您可能只想阅读更多 OOP 理论。希望我能提出一些建议,帮助您走上正轨:)

          首先,从类内部调用 Attack() 与 this.Attack() 相同。这只是更明确一点,您说的是当前对象,而不是其他称为 Attack 的函数。

          您的 Player 类是 Player 应该喜欢的模板。您不应访问 Player。因为这是访问您的模板而不是播放器的对象。 E.G

          Player.Something() //Accesses a function inside the Player class.
          

          你很少会做上面这样的事情,这是一个更复杂的想法。这些被称为静态属性/函数并应用于类的模板。你这里的所有函数都是静态的,而它们不应该是:)

          所以你的类是玩家应该是什么样子的模板,但是我们如何制作玩家的对象呢?

          public class UsingStuff : MonoBehaviour 
          {
              private void Start () {
                  Player myPlayer = new Player(); //Makes an object called myPlayer
                  Player otherPlayer = new Player(); //Another player object
                  //For functions
                  myPlayer.Attack(); //myPlayer will attack
                  otherPlayer.Move(); //otherPlayer.move
                  //For variables
                  myPlayer.name = "Dave";
                  otherPlayer.name = "Emma";
                  //Now both our players have different names
              }
          }
          

          希望这更有意义,您的课程是玩家应该拥有/能够做什么的模板。要实际使用/制作玩家,您可以创建他们的对象,然后每个人都有自己的变量。例如,如果Ouch(){this.hp--); 那么myPlayer.Ouch() 会导致myPlayer 失去1 生命值,但otherPlayer 不会受到影响。

          【讨论】:

            【解决方案7】:

            好的,首先感谢大家指出不同的细节。我想我对事情有所了解。我现在有一个工作示例:

            public class Player : MonoBehaviour {
            
                public string name;
                public int power;
                public int speed;
            
                public void gameData() {
                    print ("Player name = " + name);
                    print ("Player power = " + power);
                    print ("Player speed = " + speed);   
                }
                private void Attack () { }
            }
            ...
            
            public class UsingStuff : MonoBehaviour {
            
            var P1 = new Player();
            var P2 = new Player();
            var P3 = new Player();
            
            private void Start () {
                P1.name  = "Bill";
                P1.power = 10;
                P1.speed = 30;
            
                P2.name  = "Bob";
                P2.power = 100;
                P2.speed = 3;
            
                P3.name  = "Jerry";
                P3.power = 50;
                P3.speed = 10;
            
                P1.gameData();
                P2.gameData();
                P3.gameData();
            }
            

            但是,即使程序可以运行,我也会收到一个特定于使用 Unity 和 MonoDevelop 的警告,所以这是一个完全不同的问题。谢谢大家的帮助。

            【讨论】:

            • 这个网站不像论坛。您不应将回复作为答案发布。您可以使用 cmets,或打开一个新问题。
            • 同意,最好在原始帖子的底部添加和编辑,因为它被归类为答案,因此会被遗漏
            【解决方案8】:

            我无法发表评论,所以我会在你的第二个帖子中这样回答:

            第一种情况:

            如果您 需要将播放器作为脚本与 GameObject 和 Unity 函数(如更新循环)连接:删除继承(来自 MonoBehaviour),它将起作用: public class Player { ... }

            第二种更常见的情况:

            如果您需要一个播放器作为场景中使用 Unity 函数的对象,请保留播放器脚本。错误在于使用 var P1 = new Player(); 您知道必须将脚本添加到 Unity 场景中的 GameObject 吗?观看一些基本的 Unity 教程以了解:https://unity3d.com/de/learn/tutorials/topics/interface-essentials/game-objects-and-components?playlist=17090

            这显示了如何从另一个脚本控制游戏对象脚本: https://unity3d.com/de/learn/tutorials/topics/scripting/activating-gameobjects?playlist=17117

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-07-02
              • 1970-01-01
              • 2011-10-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-10-31
              • 1970-01-01
              相关资源
              最近更新 更多