2

私の例と並行して、私はゲームを構築していて、player.lua というクラスを持っています。

Lua がどのように機能するかをよく知らなかった数週間前にこれをコーディングしたので、プレイヤー用のテーブルを作成していませんでした。

self.speed や self.strength など、さまざまな属性をプレイヤーに割り当てました。私は、これらの属性がすべてプレイヤーのものになることを望んでいました (そしてうまくいきました)。

論理的で直感的な答えが見つからないように見える質問があります。

player.lua 内でテーブル (player = {}) を宣言した場合、player.speed は player テーブルの「speed」キーを参照します。しかし、そのようなテーブルがなければ、私は現在実際に何をしているのでしょうか?

player.lua で self.speed の代わりに player.speed を使用するとどうなりますか?

将来、複数のプレイヤーを同時に同じゲームに参加させたい場合はどうすればよいですか? Javaの場合と同じように、「同じクラスの複数のインスタンスをインスタンス化」するにはどうすればよいですか? これは基本的に、中央のゲーム lua ファイル (たとえば、main.lua または game.lua) を用意してから、各要素がプレーヤー テーブル自体であるプレーヤーのテーブルを構築することを伴うのでしょうか?

たとえば、listofPlayers = {} とすると、次のようになります: table.insert(listOfPlayers, player:new()) ここで、player:new() はプレーヤーのすべてのデフォルト属性で新しいテーブルをインスタンス化し、そのテーブルを返しますか?

では、いつメタテーブルを使用するのでしょうか?

4

3 に答える 3

4

メタテーブル

メタテーブルは、言語の一般的な操作をオーバーロードするために使用されます。これらの操作には、加算、乗算、等価比較、および (名前が示すように) キーを介して値にアクセスするなどの表のような操作が含まれますtable[key]

メタテーブルは、Lua でオブジェクト指向プログラミングを実装するためによく使用されます。これを駆動する主なメカニズムは、 の使用です__index。この例は、これを最も基本的な形式で示しています。

>>> parent = {parentID = 'Secret'}
>>> child = {}
>>> setmetatable(child,{__index=parent})
>>> =child.parentID
Secret

キーparentIDは子内に実際には存在しないため、子のどこにも次のようなものはありません。

child = {
        parentID = 'Secret'
    }

代わりに、誰かが 内に存在しないキーを探すときにchild、 に移動して調べるようにしました。これは、テーブル に割り当てたメタparentテーブルに設定されています。

>>> setmetatable(child,{__index=parent})

したがって、プログラムで要求するときのイベントの流れchild.parentIDは次のとおりです。

  1. childキーが であるキーと値のペアが含まれていますか"parentID"? いいえ、したがって 2 に進みます。
  2. メタテーブルで定義されていますchildか? __indexはい、3へ。
  3. によって参照されるテーブル__indexを調べて、キーを確認します"parentID"
  4. 親に発見!の値を返すparent["parentID"]

これにより、テーブル間の関係を作成できます。__index次のようなメタテーブル メソッドを使用して、すべてのプレイヤーの情報を表すテーブルと各プレイヤー自身との間の関係を作成できます。

Player = { }
    Player_metatable = {
        __index = Player --look for the missing key in the Player table
    }

    function Player.new(name)
        aPlayer = { name = name } 
        setmetatable(aPlayer,Player_metatable)
        return aPlayer
    end 

    function Player:rotate()
        print("I'M ROTATING",tostring(self))
    end 

    henry =  Player.new("Henry")
    henry:rotate()

を呼び出すと、最初の例でメタテーブルを設定するのと同じようPlayer.new("Henry")に、テーブルを作成し、そのメタテーブルを に設定します。ただし、すぐに実行するのではなく、関数内で実行しているだけなので、違いはありません!Player_metatablechild

を呼び出すとhenry:rotate()、上で概説したように次のようになります: でキーを探します"rotate"henry、何も見つからないので調べますPlayer(これがメタテーブルで__index指し示すテーブルであるため)。そのキーに関連付けられた関数があります。そのため、その関数を呼び出して、t:function構文のために自分自身を渡します。

クラスのインスタンスを作成するには、クラスの動作を定義したテーブルを指すメタテーブルであるテーブルを割り当てるだけです。したがって、次のようにして、必要な数の Player を作成できます。

my_player_name = Player.new(...)

テーブルの値を変更するPlayerと、関連付けられたメタテーブルを持つすべてのテーブルに反映されます。

于 2013-06-23T13:50:15.270 に答える
2

HennyH には、メタテーブルの使用方法とそれらが提供するものについての適切な説明がありますが、質問に直接答えるために、Player テーブルに 1 つのメソッドが含まれているとしますname

local Player = {}
local name
function Player:name()
  return self.name -- #1
  return Player.name -- #2
  return name -- #3
end
  • #1はテーブルnameのフィールドを使用しselfます。これは、メタテーブルを使用してクラス継承を実装できるため、ほとんどの場合使用するものです。呼び出されたselfテーブル メソッドを参照します (メソッドが参照を通じて見つかった場合はそうではない可能性があります) 。namePlayer__index
  • #2は、実際のオブジェクトへの参照を無視してname、テーブルのフィールドを使用します。これは、継承されたクラスの影響を受けないプレーヤー固有のデータが必要な場合に役立ちます。Playerself
  • #3 はローカル変数を使用します。多くの点で似てPlayer.nameいますが、大きな違いが 1 つあります。それは、「クラス」のプライベート要素を実装できることです。クラスを使用する一部のコードは、必要に応じてPlayerアクセスおよび変更できることに注意してくださいPlayer.name。これは、このメソッドがローカル変数であるときにアクセスする唯一の方法として使用される場合ではありません。これは、nameこの変数を上位値として持つクロージャーを介することです。変更する方法を提供しない場合、読み取り専用になります。

各オプションには独自の用途がありますが、#1 はおそらく最も頻繁に目にするものです。

于 2013-06-23T20:16:48.500 に答える
1

別のクラスから継承する別の方法は、すべてのメソッドをコピーすることです。おそらく「新しい」メソッドを使用します。これで、多くのクラスを組み合わせることができます。Lua はメソッドへの参照をコピーするだけなので、メモリのオーバーヘッドは最小限に抑えられます

prototype={}
--add some methods to prototype
function prototype:new()
 local newtable={}
 for k,v in pairs(self) do
   newtabke[k]=v
 end
 return newtable
end
于 2013-06-23T18:59:57.190 に答える