Um código que controla um jogo rodando tem que ser capaz de alcançar todo objeto e componente para que seja possivel a movimentaçao, a alteração na escala de tamnho, a animação e alterações no que o player vê e escuta. O mecanismo de Endereçamento do Defold torna isso possível.
O Defold utiliza o Endereçamento (ou URLs, mas vamos ignorar por ora) para se referir a objetos no jogo e componentes. Esses endereçamentos consistem em identificadores. Os seguintes exemplos são sobre como o Defold usa o endereçamento. Dentro desse documento iremos examinar em detalhes como ele funciona:
local id = factory.create("#enemy_factory")
label.set_text("my_gameobject#my_label", "Hello World!")
local pos = go.get_position("my_gameobject")
go.set_position(pos, "/level/stuff/other_gameobject")
msg.post("#", "hello_there")
local id = go.get_id(".")
Vamos começar com um exemplo bem simples. Suponha que você tem um game object com um único sprite component. Você também tem um script component para controlar o game object. O setup no editor ficaria parecido com o exibido abaixo :
Agora você preccisa desabilitar o sprite quando o jogo iniciar para que ele possa aparecer tardiamente. Essa é uma tarefa facil, basta apenas colocar o seguinte código no “controller.script”:
function init(self)
msg.post("#body", "disable") -- <1>
end
Isso vai funcionar como esperado. Quando o jogo começar o script component addresses(Endereça) o sprite component pelo sseu identificador “body” e o utiliza esse Endereço para enviar uma message(Mensagem) com o “disable”. O efeito dessa mensagem especial da engine é que o sprite component esconde o sprite graphics Schematically, o setup fica assim:
Os identificadores no setup são arbitrários. Aqui nos escolhemos dar ao game object o nome de “bean”, o seu componente de sprite foi nominado “body”, e o script component que controla o personagem foi nomeado “controller”.
Se voce não escolher um nome o editor escolherá automaticamente. Sempre que você criar um object ou component ele terá um Id próprio que sera automaticamente gerado.
Você pode deixar o editor designar os nomes automaticamente, entretanto nos encorajamos você a utilizar nomes descritivos.
Agora vamos adicionar outro sprite component e dar ao bean um shield:
O novo component tem de ser identificado de forma única. se você fosse nomealo “body” o código do script seria ambiguo, assim também recebendo a mensagem de “disable” do sprite. Nesse caso selecionamos um identificador único e descritivo: “shield”. Agora podemos desabilitar o “body” e “shield” sprites separadamente.
Se voce tentar utilizar o mesmo identificador mais de uma vez o editor indicará erro, portanto isso não será um problema:
Agora vamos ver o que acontece se você adicionar mais de um game object. Suponha que você queira unir dois “beans” em um pequeno time. Você decide chamar um de “bean” e o outro de “buddy”. Futuramente, quando o “bean” estiver em idle por um tempo , o programa deve fazer com que o “buddy” comece a dançar. Isso é feito enviando uma mensagem personalizada chamada “dance” do “controller” script component em “bean” para o “controller” script no “buddy”:
Existem dois components chamados “controller”, um em cada game object, mas isso não causa problemas uma vez que cada game object cria um new naming context.
Desde que o endereçamento da mensagem está fora do game object enviando a mensagem (“bean”), o código precisa especificar qual “controller” deve receber a mensagem. Ele precisa especificar ambos, o target game id e o component id. O endereçamento completo fica "buddy#controller"
e ele consistem em duas partes.
Voltando ao último exemplo com um único game object nos vemos que deixando o object identifier sendo parte do target address, o código pode endereçar components no current game object.
Por exemplo, "#body"
demonstra o endereço do component “body” no atual game object. Isso é muito útil porque o código ira funcionar em any(qualquer) game object, enquanto existir um component “body” presente.
As coleções possibilitam a criação de grupos, ou hierarquias, de game objects ou reutiliza-los controladamente. Você utiliza arquivos de coleção como templates (ou “prototypes” ou “prefabs”) no editor quando voce popula o seu game.
Suponha que voce deseje criar um número maior de times de bean/buddy. A melhor maneira de fazer isso é criando um template em um novo collection file (coloque o nome de “team.collection”). Crie o game objects no arquivo de collection e o salve. Depois disso coloque uma instancia desse collection file’s contents no seu main bootstrap collection e dê a instancia um identificador (chame-o de “team_1”):
Com essa estrutura o “bean” ainda pode se referir ao “controller” component no “buddy” pelo endereço "buddy#controller"
.
E se você adicionar um segunda instancia de “team.collection” (chame-o de “team_2”), o código rodando dentro do “team_2” script components vai funcionar como esperado. O “bean” game object instance da collection “team_2” ainda pode endereçar o “controller” component no “buddy” pelo endereço "buddy#controller"
.
O endereçamento "buddy#controller"
funciona para game objects nas duas collections porque é um endereço relative(relativo). Cada uma das coleções “team_1” e “team_2” cria um novo contexto de nomeamento, ou “namespace” se voce preferir. Defold evita colisãp de nomes :levando em conta o naming context de uma collection para o endereçamento:
O endereçamento relativo funciona de forma que automaticamente prevê o atual naming context quando esta resolvendo o target adress. Isso novamente vem a ser funcional e importante, sendo que você pode criar grupos de game objects com código e reutiliza-los de forma eficiente.
Defold proporciona two handy shorthands que te possibilita enviar messagens sem especificar uma URL completa:
.
#
For example:
-- Let this game object acquire input focus
msg.post(".", "acquire_input_focus")
-- Post "reset" to the current script
msg.post("#", "reset")
Para entendermos o naming mechanism vamos ver o que acontece quando você cria e roda um projeto:
Para o exemplo acima, o jogo ira rodar com os 4 game objects seguintes:
Os identificadores são guardados como um valor de tamanho fixo. O runtime também guarda o hash state para cada collection identificada, que por sua vez é utilizada para continuar o processo de relative hashing de uma string para um id absoluto.
No runtime, o agrupamento da collection não existe. Não se pode identificar de qual collection um game object especifico pertencia antes da compilação. Também não é possivel manipular todos os objects de uma collections de uma vez. Se você precisar realizar essas operações você pode tentar achar no código. Cada id de object é estático, e é garantido ele se manter fixo durante a existência do object. Isso signigica que você pode de maneira segura guardar o identificador de um object e utiliza-lo tardiamente.
É possivel utilizar os full identifiers descritos acima ao endereçar. Na maioria dos casos o relative adressing é preferido por permitir a reutilização do conteudo, mas existem casos em que o absolutely adressing é necessário.
Por exemplo, supondo que voce queira um AI mananger que siga o estado de cada bean object. Você quer que os beans reportem a sua atividade ao mananger, e o manager toma as decisões táticas e e da ordens aos beans baseado em seus status atuais. Faria total sentido em criar um single manager game object com um script component e coloca-lo junto a team collections no bootstrap collection.
Cada bean é responsavel por enviar o seu status ao mananger: “contact” se encontrar um inimigo ou “ouch!” se for atingido. Para esse trabalho, o bean controller scrips utiliza absolute addressing para enviar mensagens ao component “controller” no “manager”.
Qualquer endereçamento que começar com ‘/’ sera “resolvido” a partir da root of the game world(raiz do jogo). Isso corresponde a raiz do bootstrap collection que é carregada com o início do jogo.
O absolute address do the manager script é "/manager#controller"
e esse absolute address vai até o componet correto idenpendentemente de onde foi utilizado.
A engine guarda all identificadores como hashed values. Todas as funções que aceitam como argumento componets or game objects aceitam também uma string, hash ou URL object. Nos vimos como usar strings para endereçamento acima.
Quando você pega o identificador de um game object, a engine vai sempre retornar um absolute path identifier que é hashed:
local my_id = go.get_id()
print(my_id) --> hash: [/path/to/the/object]
local spawned_id = factory.create("#some_factory")
print(spawned_id) --> hash: [/instance42]
Você pode usar um identificador no lugar de uma string id, ou construir um por conta propria. Uma hashed id corresponde ao path(caminho) ao object, i.e. um absolute address:
A rasão para os relative adresses terem de ser dados como strings é porque a engine vai computar um novo hash id baseado no hash state do atual naming context (collection) com a string adicionada ao hash.
local spawned_id = factory.create("#some_factory")
local pos = vmath.vector3(100, 100, 0)
go.set_position(pos, spawned_id)
local other_id = hash("/path/to/the/object")
go.set_position(pos, other_id)
-- This will not work! Relative addresses must be given as strings.
local relative_id = hash("my_object")
go.set_position(pos, relative_id)
Para completar a figura, vamo olhar para o formato completo do endereçamento do Defold: the URL.
Uma URL é um object, geralmente escritos como specially formatted strings, ou strings com formatação especial. Uma URL genérica consistem em tres partes:
[socket:][path][#fragment]
Assim como vimos acima, você pode deixar de fora parte, ou grande maioria das informações na maioria dos casos. Você quase nunca precisa especificar o socket, e você frequentemente, mas nem sempre, precisa especificar o path. Nesses casos em que você precisa endereçar coisas em um outro mundo do game, você necessitará especificar a parte do socket na URL. Na instancia, a URL string completa para o “controller” script no “manager” game object acima é:
"main:/manager#controller"
e o buddy controller in team_2 é:
"main:/team_2/buddy#controller"
Nos podemos enviar mensagens para eles:
-- Send "hello" to the manager script and team buddy bean
msg.post("main:/manager#controller", "hello_manager")
msg.post("main:/team_2/buddy#controller", "hello_buddy")
URL objects também podem ser construidos em forma de programação em código Lua:
-- Construct URL object from a string:
local my_url = msg.url("main:/manager#controller")
print(my_url) --> url: [main:/manager#controller]
print(my_url.socket) --> 786443 (internal numeric value)
print(my_url.path) --> hash: [/manager]
print(my_url.fragment) --> hash: [controller]
-- Construct URL from parameters:
local my_url = msg.url("main", "/manager", "controller")
print(my_url) --> url: [main:/manager#controller]
-- Build from empty URL object:
local my_url = msg.url()
my_url.socket = "main" -- specify by valid name
my_url.path = hash("/manager") -- specify as string or hash
my_url.fragment = "controller" -- specify as string or hash
-- Post to target specified by URL
msg.post(my_url, "hello_manager!")
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB