×

Langue

Fermer
Atelier 801
  • Forums
  • Dev Tracker
  • Connexion
    • English Français
      Português do Brasil Español
      Türkçe Polski
      Magyar Română
      العربية Skandinavisk
      Nederlands Deutsch
      Bahasa Indonesia Русский
      中文 Filipino
      Lietuvių kalba 日本語
      Suomi עברית
      Italiano Česky
      Hrvatski Slovensky
      Български Latviešu
      Estonian
  • Langue
  • Forums
  • /
  • Transformice
  • /
  • Modules
  • /
  • [Script] Class.lua - handler de classes
[Script] Class.lua - handler de classes
Jp_darkuss
« Citoyen »
1630624380000
    • Jp_darkuss#4806
    • Profil
    • Derniers messages
#1
  1
  • Apresentação
  • Class.lua X Common Class
  • Como aplicar
  • Métodos/variáveis privadas, métodos estáticos/get
  • Classes estendidas
  • Informações adicionais
  • Catálogo de métodos (resumo)
  • Tree
Eae galera, firmeza? Então... Faz tempo que não jogo Transformice (infelizmente não pretendo voltar a jogar por causa de alguns probleminhas...), mas nesse meio tempo que adquiri tempo livre para fazer algo diferente, decidi trazer algo novo para a comunidade br.

Na verdade, é algo que já tinha pensado há muito tempo desde que aprendi a mexer com classes em Lua, só nunca tentei tirá-lo do papel. Nos finalmentes, estou trazendo uma classe que auxilia a criação de classes em Lua, o Class.lua!

Mas qual a diferença que há entre a classe e o sistema de classes nativo da linguagem? É isso que vocês verão nos próximos tópicos!

Entretanto de antemão já pus aqui em baixo o link para vocês adquirirem o código:

Class.lua (acesse o script aqui!)

Class.lua by Jp_darkuss#4806

Observações

O objetivo para a criação desse script é adicionar novas funcionalidades na hora de criar uma classe, tornando o programa mais elegível a problemas mais complexos que exijem ferramentas diferentes. O script não foi criado com o objetivo de simplificar a criação de classes (apesar de que é possível usar menos linhas com ele), portanto este não é recomendado se você necessita criar objetos simples ou se você é iniciante na área da programação.

A classe inserida nesse tópico pode ser usado em qualquer plataforma que use Lua e aceite metamétodos.


Deus seja louvado!
Pra quem caiu de paraquedas nessa linguagem, apresentarei as formas mais conhecidas de se criar uma classe na linguagem Lua:

  • Através de metatabelas

    View
    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    local Entity= {}

    do
    Entity.__index= Entity

    function Entity:new(x, y)
    return setmetatable({
    x= x,
    y= y
    }, self)
    end

    function Entity:move(x, y)
    self.x= x
    self.y= y
    end
    function Entity:printPosition()
    print("x: "..self.x)
    print("y: "..self.y.."\n")
    end
    end

    local entidade= Entity:new(100, 100)
    entidade:printPosition()
    entidade:move(200, 200)
    entidade:printPosition()

    --[[

    Output

    >
    x: 100
    y: 100

    x: 200
    y: 200

    ]]--


  • Através do desing pattern "Factory"

    View

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    local Entity= function(x, y)
    local e= {
    x= x,
    y= y
    }

    function move(x, y)
    e.x= x
    e.y= y
    end
    function printPosition()
    print("x: "..e.x)
    print("y: "..e.y.."\n")
    end

    e.move= move
    e.printPosition= printPosition

    return e
    end

    local entidade= Entity(100, 100)
    entidade.printPosition()
    entidade.move(200, 200)
    entidade.printPosition()

    --[[

    Output

    >
    x: 100
    y: 100

    x: 200
    y: 200

    ]]--


    Entretanto, em nenhumas dessas formas existem variáveis ou métodos privadas já fornecidos pela Linguagem Lua, você mesmo teria que criá-las. De fato, se estivermos trabalhando com a Factory, privacidade não é um grande problema, mas e se for uma metatabela? E se você quiser uma função do tipo "get" (pesquise isso em Javascript para mais informações). Por causa desses problemas decidi criar essa classe para facilitar tudo.

    Antes de passarmos para o tutorial, eis aqui o exemplo dessa mesma classe Entity usando o Class.lua:

    Class.lua

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    local Entity= Class.new("x,y")
    :method("move", function(self, x, y)
    self.x= x
    self.y= y
    end)
    :method("printPosition", function(self)
    print("x: "..self.x)
    print("y: "..self.y.."\n")
    end)

    local entidade= Entity:new(100, 100)
    entidade.printPosition()
    entidade.move(200, 200)
    entidade.printPosition()

    --[[

    Output

    >
    x: 100
    y: 100

    x: 200
    y: 200

    ]]--

  • Para aplicar o construtor de classes em seu script é muito simples. Inicialmente, obviamente, certifique-se de que incluiu o código-fonte no seu script ;)

    Vamos criar um exemplo simples, uma classe para guardar informações sobre players e agir sobre eles!

    Primeiramente teremos que construir nossa classe; para isso, usaremos a funcão Class.new, que nos ajudará a construí-la:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    local _, err= pcall(nil)
    local admName, adm

    local Player= Class.new() --Player será a variável que receberá a classe

    do
    admName= err:match("(.-)%.")
    end

    O método Class.new pode receber somente um argumento (vars), uma string onde você indicará quais variáveis você quer criar dentro de sua classe. A formatação é simples, basta por na string o nome da variável, e caso queira por mais, é só separar por vírgulas (não ponha espaços!).

    Por exemplo, se quiséssemos só o nome, a formatação seria: "name"; e se fosse nome - posição x - posição y, poderíamos impôr: "name,x,y". Nesse exemplo só usaremos o nome:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    local _, err= pcall(nil)
    local admName, adm

    local Player= Class.new("name") --Requisitando a criação da variável "name"

    do
    admName= err:match("(.-)%.")
    end

    Você já poderia utilizar a classe, mas ela seria só um pequeno objeto com uma propriedade simples (Table {name= String}). Vamos adicionar um método? Que tal um para mover o player?

    Para isso usaremos o método :method, que recebe como primeiro parâmetro o nome do método, e em seguida uma callback; vale lembrar que a callback recebe os parâmetros self (para acessar os dados do objeto) e a variável args para acessar dados passados para a função (self, ...):

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    local _, err= pcall(nil)
    local admName, adm

    local Player= Class.new("name")
    :method("move", function(self, x, y) --criando o método "move", com a callback que receberá a posição x e y de onde o alvo deverá estar
    --acessando o nome através do parâmetro self
    tfm.exec.movePlayer(self.name, x, y) --movendo o player para a posição designada
    end)

    do
    admName= err:match("(.-)%.")
    end

    Agora que temos nossa classe pronta, vamos criar um objeto para o administrador da sala (com aquele velho método conhecido). Para fazermos isso, usaremos a função construtora (:new) da classe criada; ela receberá os argumentos referente às variáveis na ordem de declaração no método Class.new:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    local _, err= pcall(nil)
    local admName, adm

    local Player= Class.new("name")
    :method("move", function(self, x, y)
    tfm.exec.movePlayer(self.name, x, y)
    end)

    do
    admName= err:match("(.-)%.") --capturando o nome de quem executou o script

    adm= Player:new(admName) --definindo a variável adm como um objeto que receberá os dados do administrador da sala
    end

    Pronto! Com isso você poderá movê-lo usando o método move criado, vamos fazer teletransportes aleatórios?

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    local _, err= pcall(nil)
    local admName, adm

    local Player= Class.new("name")
    :method("move", function(self, x, y)
    tfm.exec.movePlayer(self.name, x, y)
    end)

    eventLoop= function()
    local x= math.random(0, 400)
    local y= math.random(0, 100)
    adm.move(x, y) --movendo o adm para posição x e y aleatória
    end

    do
    admName= err:match("(.-)%.")

    adm= Player:new(admName)
    end
    Como dito anteriormente, essa classe tem suporte a métodos/variáveis privadas e a métodos estáticos/get. Vocês verão como criá-las aqui:

  • Métodos/variáveis privadas

    Vamos reutilizar parte da classe anterior para fazer esse exemplo:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    local _, err= pcall(nil)
    local admName, adm

    local Player= Class.new("name")

    do
    admName= err:match("(.-)%.")

    adm= Player:new(admName)
    end

    Vamos colocar dados fictícios no nome desse jogador, como quantia de dinheiro e senha, coisas geralmente privadas.

    No atual código, esses dados poderia simplesmente serem acessados com o objeto gerado, o que seria uma violação da privacidade:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    local _, err= pcall(nil)
    local admName, adm

    local Player= Class.new("name,password,money")

    do
    admName= err:match("(.-)%.")

    adm= Player:new(admName, "bolo_de_cenoura", 1000)
    print(adm.password)
    print(adm.money)
    end

    Vamos tornar essa variáveis privadas agora! Para fazer isso, é só escrever "priv:" na frente das variáveis que você quer privatizar. Com isso, somente métodos do objeto poderão acessá-la, veja:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    local _, err= pcall(nil)
    local admName, adm

    local Player= Class.new("name,priv:password,priv:money")
    :method("getMoney", function(self) -- método que retorna a quantia de dinheiro
    return self.money
    end)
    :method("getPassword", function(self) -- método que retorna a senha
    return self.password
    end)

    do
    admName= err:match("(.-)%.")

    adm= Player:new(admName, "bolo_de_cenoura", 1000)

    print(adm.name)
    print(adm.password) -- nil
    print(adm.money) -- nil

    print(adm.getMoney()) -- "bolo_de_cenoura"
    print(adm.getPassword()) -- 1000
    end

    Você poderá privatizar suas métodos também também, colocando "priv:" no nome deles:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    local Classe_qualquer= Class.new()
    :method("metodo_comum", function()
    print("Common")
    end)
    :method("priv:metodo_privado", function()
    print("Private")
    end)
    :method("executar_methd_privado", function(self)
    self.metodo_privado()
    end)

    local objeto= Classe_qualquer:new()

    objeto.metodo_comum() -- Common
    objeto.metodo_privado() -- Não acontece nada
    objeto.executar_methd_privado() -- Private

  • Métodos estáticos/get

    Métodos estáticos são aqueles que chamamos diretamente através da classe criada. Geralmente são usadas quando queremos uma mesma ação para vários objetos, mas não haveria a necessiade de injetarmos o método no objeto.

    Vamos criar uma classe Block e testemos as propriedades x e y para verificarmos! Mas antes, usaremos o método setDefaultValues para darmos um valor padrão às variáveis. Funciona de forma parecida com a função construtora, os valores são dados na ordem de declaração:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    local Block= Class.new("x,y")
    :setDefaultValues(100, 200) -- x = 100, y = 200

    local meu_bloco= Block:new()

    print(meu_bloco.x) -- 100
    print(meu_bloco.y) -- 200

    Para criarmos a função estática move (vamos mudar sua posição), usaremos a função ?static, que receberá o nome da função e uma callback. Lembrando que a callback também suportr a var args:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    local Block= Class.new("x,y")
    :setDefaultValues(100, 200) -- x = 100, y = 200
    :static("move", function(b, x, y) -- criando a função estática move, que receberá o bloco e as suas novas posições x, y
    -- alterando as posições do objeto
    b.x= x
    b.y= y
    end)

    local meu_bloco= Block:new()

    print(meu_bloco.x) -- 100
    print(meu_bloco.y) -- 200

    -- chamando o método move diretamente pela classe
    Block.move(meu_bloco, 500, 100) -- movendo meu_bloco para as posições (500, 100)

    print(meu_bloco.x) -- 500
    print(meu_bloco.y) -- 100

    -- verificando que o método só existem em Block ;)
    print(meu_bloco.move) -- nil
    print(Block.move) -- function: ####

    Outro tipo de método que você pode criar é o do tipo get. O Método Get é aquele que você não chama como se fosse uma função, mas como se fosse uma variável. Assim, ele poderá executar algo e retorná-lo sem se preocupar com parâmetros.

    Para criar um método get é só você adicionar a palavra "get:" antes do nome do método, conforme o exemplo:

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    local players= {} --guarda o dados dos players

    local Player= Class.new("name") -- declarando o construtor de player
    :method("get:hashtag", function(self) -- criando o método get "hashtag"
    return self.name:match("#(%d+)") -- retorna todos os números após "#"
    end)

    local getAllHashtags= function() -- printa as hashtags de todos os players
    for pos in next, players do
    local name= players[pos].name -- obtendo o nome (var name)
    local hashtag= players[pos].hashtag -- obtendo a hashtag com o método get "hashtag" (with no "()")

    print("Full name: "..name..", hash tag: "..hashtag.."\n")
    end
    end

    eventNewPlayer= function(player)
    players[#player + 1]= Player:new(player) -- declarando os players
    end

    do
    table.foreach(tfm.get.room.playerList, eventNewPlayer)
    getAllHashtags()
    end
  • Você também pode criar classes extendidas. Elas possuem muitas funções, e sua principal é criar várias classes filho que procedem de uma classe pai, por possuírem características semelhantes.

    Para fazer isso, use o método Class.extended, que receberá a classe pai e os argumentos da nova classe. Os argumentos, isto é, a string para criação de variáveis, e os métodos da classe pai será herdados pelo filho, assim como os métodos estáticos. Todos os métodos de criação do Class.new são acessíveis pelo Class.extended.

    Veja abaixo um exemplo genérico do que é possível fazer.

    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    local _, err= pcall(nil)
    local adm

    local TextArea= Class.new("id,text,target,x,y")
    :method("init", function(self)
    ui.addTextArea(self.id, self.text, self.target, self.x, self.y)
    end)
    :method("move", function(self, x, y)
    self.x= x
    self.y= y

    self.update()
    end)
    :method("update", function(self)
    ui.addTextArea(self.id, self.text, self.target, self.x, self.y)
    end)
    local Button= Class.extended(TextArea, "event") -- criando a classe Button, que terá todos os métodos, variáveis e métodos estáticos da classe TextArea
    :method("draw", function(self)
    self.text= "<a href='event:"..self.event.."'>"..self.text.."</a>"
    self.init()
    end)
    :method("on", function()
    print("Movido!")
    end)

    local txtArea, btn

    eventTextAreaCallback= function(_, _, event)
    if event=="move" then
    txtArea.move(200, 100)
    btn.move(245, 120)
    btn.on()
    end
    end

    do
    adm= err:match("(.-)%.")

    txtArea= TextArea:new(0, "Clique aí em baixo :)", adm, 400, 100)
    btn= Button:new(1, "Mover", adm, 445, 120, "move")

    txtArea.init()
    btn.draw()
    end
    Veja aqui as informações adicionais sobre o script:

    Informações
  • Tamanho em linhas: 198
  • Tamanho em bytes: 7 Kb
  • Velocidade de inicialização no Transformice: ~ 1.71 - 2 ms (de 14 testes)

  • Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    --[[
    Veja as explicações de cada método nas páginas anteriores!

    (Method) Class.new(string vars) => Class_instance (retorna uma instância criadora de configuração de classes)

    (Method) Class.extended(Class_instance class, string vars) => Class_instance (retorna uma instância de configuração de classes extendida)

    (Method) Class_instance:method(string name, function callback) => nil || any (cria um novo método possível na classe)

    (Method) Class_instance:static(string name, function callback) => nil (cria um método estático na classe)

    (Method) Class_instance:new(...) => table (retorna um objeto da instância de configuração de classes)

    Para criar métodos/variáveis privados(as): priv:name
    Para criar métodos "get": get:name
    ]]
    Code Lua

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    --[[

    Class
    .instance
    .prototype
    .vars
    [1...]
    .name
    .isGet
    .isPriv
    .methods
    [1...] => function()
    :_new => new table
    :method => nil || any
    :static => nil
    .new => new Class.instance
    .extended => new Class.instance

    ]]
    Belizard
    « Censeur »
    1630625820000
      • Belizard#6173
      • Profil
      • Derniers messages
    #2
      1
    Que shoow! Seja bem-vindo novamente ;)
    Ninguem
    « Consul »
    1630633500000
      • Ninguem#0095
      • Profil
      • Derniers messages
      • Tribu
    #3
      1
    Ficou muito bom, parabéns!
    Jp_darkuss
    « Citoyen »
    1630685580000
      • Jp_darkuss#4806
      • Profil
      • Derniers messages
    #4
      0
    Belizard a dit :
    Que shoow! Seja bem-vindo novamente ;)

    Obrigado :), talvez ainda faça mais coisas rsrsrs

    Dernière modification le 1630685640000
    Jp_darkuss
    « Citoyen »
    1630685640000
      • Jp_darkuss#4806
      • Profil
      • Derniers messages
    #5
      0
    Ninguem a dit :
    Ficou muito bom, parabéns!

    Vlw, espero que ela ajude com seus projetos futuros ;)
    Gamedroit
    « Citoyen »
    1631029680000
      • Gamedroit#3347
      • Profil
      • Derniers messages
      • Tribu
    #6
      2
    Parabéns! Mas eu não chamaria o uso de metatabelas e funções de classe nativa, é bem diferente da realidade. Outra coisa, o problema de privacidade que você apontou pode sim ser resolvido em metatabelas usando o metamethod __index para controle de saída de informação, e você pode usar o metamethod __newindex para controle de entrada de informação.

    Com inspiração no que você fez, e para provar que com metatabelas você pode sim criar variáveis privadas, eu decidi fazer meu próprio sistema:
    https://pastebin.com/baGsL6nL ou https://pastebin.com/raw/baGsL6nL

    PS: Foi testado em Lua 5.3, não sei se teria algum problema em versões anteriores.
    PS2: Talvez não seja o melhor método para criar classes, porém seria útil em sistemas pequenos ao qual você se preocupa em manter algumas variáveis invisíveis ao utilizador.

    Dernière modification le 1631208600000
    Jp_darkuss
    « Citoyen »
    1632186840000
      • Jp_darkuss#4806
      • Profil
      • Derniers messages
    #7
      0
    Gamedroit a dit :
    Parabéns! Mas eu não chamaria o uso de metatabelas e funções de classe nativa, é bem diferente da realidade. Outra coisa, o problema de privacidade que você apontou pode sim ser resolvido em metatabelas usando o metamethod __index para controle de saída de informação, e você pode usar o metamethod __newindex para controle de entrada de informação.

    Com inspiração no que você fez, e para provar que com metatabelas você pode sim criar variáveis privadas, eu decidi fazer meu próprio sistema:
    https://pastebin.com/baGsL6nL ou https://pastebin.com/raw/baGsL6nL

    PS: Foi testado em Lua 5.3, não sei se teria algum problema em versões anteriores.
    PS2: Talvez não seja o melhor método para criar classes, porém seria útil em sistemas pequenos ao qual você se preocupa em manter algumas variáveis invisíveis ao utilizador.

    É verdade, não pensei por esse lado. Gostei do código :)
    • Forums
    • /
    • Transformice
    • /
    • Modules
    • /
    • [Script] Class.lua - handler de classes
    © Atelier801 2018

    Equipe Conditions Générales d'Utilisation Politique de Confidentialité Contact

    Version 1.27