Os arquivos layout¶
Introdução¶
Os arquivos layout [1] são usados para informar ao MAME o que exibir
enquanto a emulação de um sistema estiver rodando e também como
organizá-los na tela. O MAME pode renderizar a emulação das telas
originais dos sistemas, as imagens, os textos, as formas e objetos
especiais na saída comum dos dispositivos.
Os elementos podem ser estáticos ou se atualizar de forma dinâmica para
refletir a condição das entradas e das saídas.
Os layouts podem ser gerados automaticamente com base no número ou no
tipo da tela que será emulada, ser construído e conectado internamente
ao binário do MAME ou sendo disponibilizado externamente. Para o MAME os
arquivos layout são interpretados como arquivos XML porém utilizam a
extensão .lay
.
Conceitos básicos¶
Números¶
Os layouts do MAME possuem dois tipos de números, números inteiros e os de ponto flutuante.
Os números inteiros podem ser usados com notação decimal ou hexadecimal. Um número decimal inteiro consiste num prefixo opcional # (hash [2]), um caractere opcional +/- (mais ou menos) ou uma sequência de dígitos entre 0-9.
Um número hexadecimal consiste em que um dos prefixos seja o $ (cifrão) ou 0x (zero xis) seguido por uma sequência de números decimais entre 0-9 em conjunto com as letras entre A-F. Não há diferenciação entre as letras maiúsculas e as letras minúsculas no índice e nos dígitos dos números hexadecimais.
Os números de ponto flutuante podem ser usados com decimal de ponto fixo ou com notação científica. Observe que os prefixos do número inteiro e os valores hexadecimais não são aceitos caso um número de ponto flutuante seja esperado.
São permitidos como atributos ambos os números inteiros e os números de ponto flutuante. Nesses casos a presença de um prefixo # (hash), $ (cifrão) ou 0x (zero xis) faz com que o valor seja interpretado como um número inteiro. Caso nenhum prefixo de número inteiro, o ponto decimal ou a letra E (maiúsculo ou minusculo) seja encontrado na introdução de um expoente, este será interpretado como um número de ponto flutuante. Caso nenhum prefixo de número inteiro, ponto decimal ou a letra E seja encontrado, o número será interpretado como um número inteiro.
Os números são analisados usando uma acentuação de caracteres em C [3] por questões de portabilidade.
Coordenadas¶
As coordenadas do layout são representadas internamente através da norma IEEE754 como um número binário de 32-bit de ponto flutuante (também conhecido como "precisão simples"). O incremento das coordenadas se dão nas direções da direita e para baixo. A origem (0,0) não possui um significado em particular e valores negativos podem ser usados.
O MAME pressupõe que as coordenadas da visualização possuem a mesma proporção de aspecto com relação aos pixels gerados pelo dispositivo (janela ou nativa). Considerando que sejam pixels quadrados e sem rotação, isso significa que a distância seja igual nos eixos X e Y o que corresponde a distâncias iguais na vertical e na horizontal que for gerado pela renderização.
Todos os elementos, os grupos e as visualizações possuem os seus sistemas internos de coordenadas. Quando um elemento ou um grupo é referenciado a partir de uma visualização ou de um outro grupo, as suas coordenadas são dimensionadas de acordo com a necessidade para que os limites sejam definidos.
Os objetos são posicionados e dimensionados através do elemento
bounds
que define os seus limites e também as suas fronteiras.
A posição horizontal e o seu tamanho podem ser definidos de três
maneiras:
A borda esquerda e a largura usando atributos
x
ewidth
.O eixo horizontal centralizado onde c significa que a referência usada será o centro do objeto/imagem e a largura usando atributos
xc
ewidth
.As bordas esquerda e direita usando atributos
left
eright
.De maneira semelhante a posição vertical e o seu tamanho podem ser definidos através da borda superior e a altura usando atributos
y
eheight
.O eixo vertical centralizado e a altura usando atributos
yc
eheight
.As bordas superiores e inferiores usando atributos
top
ebottom
.
No exemplo abaixo estes três elementos bounds
são equivalentes:
<bounds x="455" y="120" width="12" height="8" />
<bounds xc="461" yc="124" width="12" height="8" />
<bounds left="455" top="120" right="467" bottom="128" />
É possível utilizar diferentes esquemas nas direções horizontal e
vertical. Por exemplo, estes elementos bounds
equivalentes também
são válidos:
<bounds x="455" top="120" width="12" bottom="128" />
<bounds left="455" yc="124" right="467" height="8" />
Caso nenhum valor seja informado, é predefindo que seja 1.0 para o
atributo width
/height
ou right
/bottom
.
O MAME irá considerar como um erro caso os atributos width
ou
height
tenham valores negativos, right
tenha um valor menor que
left
ou caso bottom
tenha um valor menor que top
.
Cores¶
As cores são definidas no espaço RGBA. O MAME não trabalha com todo o leque da gama de cores, portanto, as cores serão interpretadas como sRGB em conjunto da definição do gamma do seu sistema que geralmente é 2.2. Os valores dos canais são definidos como números de ponto flutuante. Os valores dos canais vermelho, verde e azul variam entre 0.0 (desligado) até 1.0 (intensidade plena). Os valores alfa variam entre 0.0 (transparência absoluta) até 1.0 (opaco). Os valores dos canais das cores não são previamente multiplicadss pelo valor alfa.
O componente e a cor do item da visualização são definidas através dos
elementos color
.
Os atributos relevantes são vermelho red
, verde green
,
azul blue
e alpha
. Este exemplo do elemento color
determina
todos os valores dos canais:
<color red="0.85" green="0.4" blue="0.3" alpha="1.0" />
Qualquer atributo omitido do canal terá o seu valor predefinido para 1.0 (intensidade absoluta ou opaca). Será considerado como um erro caso os valores do canal estejam fora do intervalo entre de 0.0 até 1.0.
Nem toda a ferramenta de edição de imagens trabalhe com o mesmo sistema que o MAME, assim sendo, utilize esta calculadora para converter um valor RGB hexadecimal usado em HTML ou RGB por exemplo, para o formato que o MAME aceita.
Parâmetros¶
Os parâmetros funcionam como variáveis que podem ser utilizadas para
substituir o valor dos atributos, basta cercar o seu nome com caracteres
til (~).
Nenhuma substituição será feita caso nenhum parâmetro seja definido.
No exemplo abaixo é possível ver como os valores dos parâmetros
digitno
e do x
substituirão o ~digitno~
e o ~x~
:
<repeat count="8">
<param name="digitno" start="1" increment="1" />
<param name="x" start="0" increment="114" />
<element name="digit~digitno~" ref="digit">
<bounds x="~x~" y="80" width="25" height="40" />
</element>
Um nome para o parâmetro é uma sequência de letras maiúsculas das letras
A-Z, das letras minusculas a-z, dígitos decimais 0-9, ou
caracteres subtraço (_).
As letras maiúsculas e as letras minúsculas são levadas em consideração
nos nomes dos parâmetros. Durante a procurar por um parâmetro o motor do
layout começando a trabalhar da parte mais interna do escopo atual até a
sua parte mais externa. O nível mais periférico do escopo corresponde ao
elemento do primeiro nível mamelayout
. Cada elemento repeat
,
group
ou view
cria um novo nível de encadeamento do escopo.
Internamente um parâmetro pode conter uma string, números inteiros ou números de ponto flutuante, porém esta é bem mais óbvia. Os números inteiros são armazenados como 64-bit signed com dois valores complementares, já os números de ponto flutuante são armazenados como binários IEEE754 com 64-bit, estes números também são conhecido como "precisão dupla". Os números inteiros são substituídos em notação decimal, já os números de ponto flutuante são substituídos pelo seu formato padrão que pode ser um decimal de ponto fixo ou dependendo do valor pode ser uma notação científica. Não há nenhuma maneira de substituir a formatação predefinida dos parâmetros de um número inteiro ou de ponto flutuante.
Existem dois tipos de parâmetros: os valores e os geradores. O parâmetro "value" mantém o seu valor atribuído até que eles sejam alterados, já o parâmetro "gerador" possui um valor inicial, um incremento e/ou um deslocamento [4] aplicado em cada interação.
Os valores dos parâmetros são atribuídos através do elemento param
junto com os elementos name
e value
, os seus valores podem
aparecer de dentro de um elemento de primeiro nível mamelayout
e
dentro dos elementos repeat
, view
assim como dentro da definição
dos elementos group
(isso é, elementos group
dentro do nível
superior do elemento mamelayout
, ao contrário dos elementos
group
dentro de elementos view
definidos por outros elementos
group
.
O valor do parâmetro pode ser reatribuído a qualquer momento.
Aqui está um exemplo de como atribuir o valor "4" para o parâmetro "firstdigit":
<param name="firstdigit" value="4" />
Os parâmetros dos geradores são atribuídos através do elemento param
em conjunto com os atributos name
, start
, increment
,
lshift
e rshift
.
Os parâmetros dos geradores só podem aparecer de dentro dos elementos
repeat
(consulte Repetindo os blocos para obter mais
informações) e também não devem ser reatribuídos dentro do mesmo escopo
(um parâmetro com um nome idêntico pode ser atribuído num escopo
através da sua ramificação). Abaixo alguns parâmetros de exemplos dos
geradores:
<param name="nybble" start="3" increment="-1" />
<param name="switchpos" start="74" increment="156" />
<param name="mask" start="0x0800" rshift="4" />
O parâmetro
nybble
geram os valores 3, 2, 1...O parâmetro
switchpos
geram os valores 74 (74
), 230 (74 + 156
), 386 (230 + 156
)...O parâmetro
mask
geram os valores 2048 (0x0800
), 128 (0x0800 >> 4
), 8 (0x80 >> 4
)...
O atributo increment
deve ser um número inteiro ou de ponto
flutuante que será adicionado ao valor do parâmetro. Os atributos
lshift
e rshift
devem ser números positivos e inteiros pois
definem a quantidade dos bits que serão aplicados aos parâmetros. O
deslocamento (shift) e o incremento são aplicados no final do bloco que
está sendo repetido antes do inicio da próxima iteração.
O valor do parâmetro poderá ser interpretado como um número de ponto
flutuante ou um número inteiro antes que o incremento ou o deslocamento
seja aplicado. Caso informe ambos os valores para incremento e para o
deslocamento, então o valor do incremento será aplicado primeiro e
depois o valor deslocado.
Caso o atributo increment
esteja presente e seja um número de
ponto flutuante, o seu valor será convertido para um número de ponto
flutuante caso seja necessário antes que o incremento seja adicionado.
Caso o atributo increment
esteja presente e seja um valor inteiro
enquanto o valor do parâmetro seja um número de ponto flutuante, o valor
do incremento será convertido para um número de ponto flutuante antes
que o valor seja adicionado.
Caso os atributos lshift
ou rshift
estejam presentes porém não
sejam iguais, o valor do parâmetro será convertido para um número
inteiro e deslocado conforme a necessidade. O deslocamento para a
esquerda é definido como um deslocamento feito em direção ao bit de
maior importância.
Caso ambos os parâmetros lshift
e rshift
sejam passados, estes
serão compensados antes dos valores serem aplicados. Significa que
não é possível usar atributos iguais tanto para o lshift
como para o
rshift` por exemplo para limpar os bits num valor do final do
parâmetro após a primeira iteração.
Será considerado um erro caso o elemento param
não esteja em
qualquer um dos atributos value
ou start
, será também
considerado um erro caso ambos os elementos param
tiverem os mesmos
atributos value
ou qualquer um dos mesmos atributos start
,
increment
, lshift
, ou rshift
.
Um elemento param
define ou reatribui o seu valor num parâmetro no
escopo atual mais interno. Não é possível definir ou reatribuir os
parâmetros num escopo de contenção.
Parâmetros já predefinidos¶
Uma certa quantidade de valores predefinidos nos parâmetros já estão disponíveis e fornecem informações sobre o sistema que está em execução:
devicetag
Um exemplo do caminho completo da etiqueta [9] dispositivo que será responsável pela leitura do layout, seria
:
para o driver do controlador do dispositivo raiz ou:tty:ie15
para o terminal conectado numa porta. Este parâmetro é uma sequência de caracteres definida no escopo global de visualização do layout.
devicebasetag
A base da etiqueta do dispositivo que será responsável pela leitura do layout, como por exemplo
root
para o driver do dispositivo raiz ouie15
para o terminal que estiver conectado numa porta. Este parâmetro é uma sequência de caracteres definida no escopo global do layout.
devicename
O nome completo (descrição) do dispositivo que será responsável pela leitura do layout, como por exemplo os terminais
AIM-65/40
ouIE15
. Este parâmetro é uma sequência de caracteres definida no escopo global do layout.
deviceshortname
Um nome curto do dispositivo que será responsável pela leitura do layout, como por exemplo os terminais
aim65_40
ouie15
. Este parâmetro é uma sequência de caracteres definida no escopo global do layout.
scr0physicalxaspect
A parte horizontal da relação de aspecto físico da primeira tela (caso esteja presente). A relação de aspecto físico é fornecida como uma fração impropriamente reduzida. Observe que este é o componente horizontal aplicado antes da rotação. Este parâmetro é um número inteiro definido no escopo global do layout.
scr0physicalyaspect
A parte vertical da relação de aspecto físico da primeira tela (caso esteja presente). A relação de aspecto físico é fornecida como uma fração impropriamente reduzida. Observe que este é o componente vertical aplicado antes da rotação. Este parâmetro é um número inteiro definido no escopo global do layout.
scr0nativexaspect
A parte horizontal da relação de aspecto do pixel visível na região da primeira tela (caso esteja presente). A relação de aspecto do pixel é fornecida como uma fração impropriamente reduzida. Observe que este é o componente horizontal aplicado antes da rotação. Este parâmetro é um número inteiro definido no escopo global do layout.
scr0nativeyaspect
A parte vertical da relação de aspecto do pixel visível na região da primeira tela (caso esteja presente). A relação de aspecto do pixel é fornecida como uma fração impropriamente reduzida. Observe que este é o componente vertical aplicado antes da rotação. Este parâmetro é um número inteiro definido no escopo global do layout.
scr0width
A largura da região visível da primeira tela (se houver) nos pixels emulados. Observe que a largura é aplicada antes da rotação. Este parâmetro é um número inteiro definido no escopo global do layout.
scr0height
A altura da região visível da primeira tela (se houver) nos pixels emulados. Observe que a altura é aplicada antes da rotação. Este parâmetro é um número inteiro definido no escopo global do layout.
scr1physicalxaspect
A parte horizontal da relação de aspecto físico da primeira tela (caso esteja presente). Este parâmetro é um número inteiro definido no escopo global do layout.
scr1physicalyaspect
A parte vertical da relação de aspecto físico da segunda tela (caso esteja presente). Este parâmetro é um número inteiro definido no escopo global do layout.
scr1nativexaspect
A parte horizontal da relação de aspecto do pixel visível na região da segunda tela (caso esteja presente). Este parâmetro é um número inteiro definido no escopo global de visualização do layout.
scr1nativeyaspect
A parte vertical da relação de aspecto do pixel visível na região da segunda tela (caso esteja presente). Este parâmetro é um número inteiro definido no escopo global de visualização do layout.
scr1width
A largura da região visível da segunda tela (se houver) nos pixels emulados. Este parâmetro é um número inteiro definido no escopo global do layout.
scr1height
A altura da região visível da segunda tela (se houver) nos pixels emulados. Este parâmetro é um número inteiro definido no escopo global do layout.
scr*N*physicalxaspect
A parte horizontal da relação de aspecto físico da tela (base-zero) Nth (caso esteja presente). Este parâmetro é um número inteiro definido no escopo global do layout.
scr*N*physicalyaspect
A parte vertical da relação de aspecto físico da tela (base-zero) Nth (caso esteja presente). Este parâmetro é um número inteiro definido no escopo global do layout.
scr*N*nativexaspect
A parte horizontal da relação de aspecto da parte visível da tela (base-zero) Nth (caso esteja presente). Este parâmetro é um número inteiro definido no escopo global do layout.
scr*N*nativeyaspect
A parte vertical da relação de aspecto da parte visível da tela (base-zero) Nth (caso esteja presente). Este parâmetro é um número inteiro definido no escopo global do layout.
scr*N*width
A largura da região visível da tela (base-zero) Nth (se presente) nos pixels emulados. Este parâmetro é um número inteiro definido no escopo da visualização do layout.
scr*N*height
A largura da região visível da tela (base-zero) Nth (se presente) nos pixels emulados. Este parâmetro é um número inteiro definido no escopo de visualização do layout.
viewname
O nome da visualização atual. Este parâmetro é uma sequências de caracteres definido no escopo de visualização. Não é definido fora do campo de visão.
Para parâmetros relacionados à tela, elas são numeradas do zero na ordem em que aparecem na configuração do sistema. Todas as telas estão inclusas (não apenas nos sub-dispositivos do dispositivo que fizeram com que o layout fosse carregado). X/width e Y/height referem-se as dimensões horizontal e vertical da tela antes da rotação ser aplicada. Os valores baseados na região visível são calculados no final da configuração. Caso o sistema não reconfigure a tela durante a execução os valores dos parâmetros não serão atualizados assim como os layouts não serão recalculados.
As partes de um layout¶
Uma visualização define a disposição de um objeto gráfico a ser exibido. O arquivo layout do MAME pode conter diversas visualizações. As visualizações são construídas a partir de elementos elements e telas screens. Para simplificar a organização dos layouts complexos são compatíveis entre si a repetição dos blocos e dos grupos que podem ser reutilizados.
O primeiro elemento do cabeçalho de um arquivo layout do MAME deve
ser um elemento chamado mamelayout
junto com um atributo
version
. O atributo version
deve ser um valor inteiro.
Atualmente, o MAME suporta apenas a versão 2 e não carregará qualquer
outra versão diferente.
Este é um exemplo de uma tag inicial para um elemento mamelayout
:
<mamelayout version="2">
Para fins de compatibilidade na identificação do arquivo com diversos editores de texto é possível declarar que o mesmo é um arquivo XML, logo, o MAME também aceita o arquivo com uma declaração XML:
<?xml version="1.0"?>
<mamelayout version="2">
Da mesma maneira que é possível usar o identificador XML, também é possível identificar a codificação do arquivo caso seja necessário:
<?xml version="1.0" encoding="UTF-8"?>
<mamelayout version="2">
Os comentários podem ser adicionados em qualquer parte do arquivo desde
que estejam entre <!--
e -->
:
<?xml version="1.0"?>
<!-- Este é um comentário -->
<!--
Este tipo de comentário também é válido.
-->
<!--
Também é possível incluir longas instruções ou informações
relevantes no seu arquivo layout para que as pessoas saibam
o que fazer ou como prosseguir caso seja necessário.
Identifique os seus arquivos, utilize estes espaços para deixar
o seu nome ou apelido, a versão, a data que o layout foi criado
ou que o arquivo foi alterado, a descrição das alterações, os
direitos autorais, etc.
The Alpha Betas and the Lambda Lambda Lambda Fraternity
Versão: 1.0
Criado em: 10/11/2020
Licença: CC by 4.0
-->
<mamelayout version="2">
Algumas regras devem ser observadas ao adicionar os comentários:
Os comentários não devem aparecer antes da declaração XML.
Os comentários não devem aparecer dentro da etiqueta de um elemento.
Os comentários não devem aparecer dentro do valor de um atributo.
Os comentários não pode ter a sequência de caracteres
--
.
Em geral, as ramificações do primeiro elemento mamelayout
são
processados na ordem em que eles chegam, de cima para baixo, exceto as
visualizações que são processadas por último.
Isso significa que as visualizações veem os valores finais de todos os
parâmetros no final do elemento mamelayout
e pode se referir a
elementos e grupos que possam aparecer depois deles.
Os seguintes elementos são permitidos dentro do primeiro elemento
mamelayout
:
param
Define ou reatribui um valor ao parâmetro. Consulte Parâmetros para mais informações.
element
Define um elemento, um dos objetos primários a serem organizados numa Visualização. Consulte Os elementos para obter mais informações.
group
Define um grupo dos elementos ou das telas que possam ser reutilizáveis e que também possam ser usados como referência numa visualização ou nos outros grupos.
Consulte Grupos reutilizáveis para obter mais informações.
repeat
Um grupo de elementos repetidos que podem conter os elementos
param
,element
,group
erepeat
. Consulte Repetindo os blocos para obter mais informações.
view
Um arranjo dos elementos ou das telas que podem ser exibidos na saída de um dispositivo (uma janela ou uma tela do host). Consulte As visualizações para obter mais informações.
script
Permite que scripts lua sejam usados num layout aprimorado ainda mais a interação.
Os elementos¶
Os elementos são um dos objetos visuais mais básicos que podem ser organizados em conjunto com as telas na composição de uma visualização. Os elementos podem ser construídos com um ou mais componentes porém um elemento é tratado como uma única superfície na composição do gráfico da cena e da sua renderização. Um elemento pode ser usado em diversas visualizações e pode também serem utilizadas várias vezes dentro da visualização.
A aparência de um elemento depende do seu estado. O estado é um valor inteiro que geralmente vem de uma região da porta E/S ou da emulação gerada (consulte O Estado do elemento para obter mais informações de como conectar um elemento numa porta ou na saída E/S de uma emulação). Qualquer componente de um elemento pode estar restrito apenas ao desenho quando o estado do elemento tiver um valor em particular. Alguns componentes (como os mostradores com múltiplos segmentos por exemplo) que usam diretamente o seu estado para determinar a sua aparência final.
Cada elemento possui o seu próprio sistema interno de coordenadas. Os limites dos elementos dos sistema de coordenadas são computados através da união dos limites individuais dos componentes que ele é composto.
Todo elemento deve ter o seu nome definido através do atributo name
.
Os elementos são mencionados através do nome quando forem solicitados
nos grupos ou nas visualizações. Haverá um erro caso o arquivo de
layout tenha vários elementos name
com valores iguais.
Os elementos podem de forma opcional, ser utilizado para informar um
valor padrão do seu estado através do atributo defstate
caso esteja
conectado numa saída emulada ou numa porta E/S. O valor do atributo
defstate
deve possuir um valor inteiro e positivo, os valores
negativos geram erros e fazem com que o layout não seja mais carregado.
As ramificações do elemento element
instanciam componentes que são
desenhados na textura do elemento na ordem de leitura a partir do
primeiro ao último elemento utilizando alpha blending (os componente são
desenhados por cima e podem se sobrepor aos componentes que venham antes
dele). Todos os componentes são compatíveis com algumas características
em comum:
Os componentes podem ser desenhados de forma condicional dependendo da condição do elemento ao informar os atributos
state
oustatemask
. Caso estejam presentes, estes atributos devem ser inteiros com valores positivos. Caso apenas o atributostate
esteja presente, então o componente só será desenhado na tela quando o elementostate
coincidir com o seu valor. Caso apenas o atributostatemask
esteja presente, então o componente só será desenhado na tela caso todos os bits estejam definidos e os seus valores estejam definidos através do atributostate
.Na existência de ambos os atributos
state
estatemask
, então o componente só será desenhado na tela quando os bits no elementostate
corresponderem ao bit que estiver definido no atributostatemask
e também corresponder com os bits do valor do atributostate
.O componente sempre será desenhado na ausência de ambos os atributos
state
oustatemask
ou caso o valor do atributostatemask
for zero.
Cada componente pode ter um sub-elemento
bounds
definindo a sua posição e o seu tamanho (consulte Coordenadas). Na ausência de tal elemento os limites serão predefinidos a uma unidade quadrada com o valor igual à 1.0 tanto para a largura quanto para a altura e com o canto superior esquerdo com valor 0.0.A posição ou o tamanho de um componente pode ser animado de acordo com o estado do elemento ao prover diversos elementos
bounds
em conjunto com atributosstate
. O atributostate
de cada ramificação do elementobounds
deve ser um número inteiro e positivo. Os atributosstate
não devem ser iguais para quaisquer um dos dois elementosbounds
que estiverem dentro de um componente.Caso o estado do elemento seja inferior que o valor do atributo
state
de qualquer uma das ramificações do elementobounds
, será utilizada a posição/tamanho definido através do elementobounds
com o menor valor do atributostate
. Já quando o estado do elemento for maior que o valor do atributostate
de qualquer elementobounds
, será utilizada a posição/tamanho especificado através do elementobounds
com o maior valor do atributostate
. Se o estado do elemento estiver entre os valores do atributostate
dos dois elementosbounds
, a posição/tamanho será interpolada de forma linear.Cada componente de cor pode ter um elemento
color
definindo uma cor RGBA (Consulte Cores para obter mais informações). Isto pode ser usado para controlar a geometria da cor dos componentes desenhados de forma algorítmica ou textual. Para os componentesimage
, a cor dos pixels da imagem são multiplicadas através da cor que foi definida. Caso tal elemento não esteja presente, será usada uma cor branca opaca já predefinida.A cor do componente pode ser animada de acordo com o estado do elemento ao prover diversos elementos
color
em conjunto com os atributosstate
. Os atributosstate
não devem ser iguais em qualquer um dos dois elementoscolor
internos de um componente.Caso o estado do elemento seja inferior ao valor do atributo
state
de qualquer elementocolor
, será utilizada a cor especificada através do elementocolor
com o menor valor do atributostate
.Caso o estado do elemento seja superior ao valor do atributo
state
de qualquer elementocolor
, será utilizada a cor especificada através do elementocolor
com o maior valor do atributostate
. Caso o estado do elemento estiver entre os valores do atributostate
de dois elementoscolor
, os componentes de cor RGBA serão interpolados de forma linear.
Há suporte para os seguintes componentes:
rect
Desenha um retângulo colorido uniforme com as suas bordas preenchidas.
disk
Desenha uma elipse (círculo) colorido e uniforme.
image
Exibe uma imagem na tela a partir de um arquivo PNG, JPEG, Window DIB (BMP) ou um arquivo SVG. O nome do arquivo que será carregado (incluindo a sua extensão) é definido usando o atributo
file
. Adicionalmente, é possível utilizar um atributo opcional chamadoalphafile
para determinar o nome de um arquivo PNG (incluindo a sua extensão) para ser carregada dentro do canal alfa.Alternativamente, os dados da imagem podem ser informados no próprio arquivo layout utilizando um sub-elemento
data
. Isto pode ser útil para oferecer gráficos SVG simples e legíveis. Será considerado como um erro caso nenhum atributofile
oudata
seja informado.O arquivo usado como
alphafile
deve ter as mesmas dimensões (em pixels) que o arquivo do atributofile
e a sua profundidade de bits por pixel da imagem não deve ser maior que 8 bits por canal. A intensidade de brightness desta imagem é copiada para o canal alfa com total intensidade (branco em escala de cinza) o que corresponde a um opaco completo e o preto uma total transparência.O atributo
alphafile
será ignorado caso o atributofile
aponte para um arquivo SVG ou um sub-elementodata
contendo dados SVG, o atributo é apenas utilizado com imagens do tipo bitmap.O(s) arquivo(s) da(s) imagem(s) devem ser colocados no mesmo diretório que o arquivo layout. Os formatos da imagem são detectados durante a analise do conteúdo dos arquivos, os nomes das extensões dos arquivos não são levados em consideração. Note porém que nos sistemas *nix o nome dos aquivos com maiúsculas e com minúsculas são levadas em consideração quando não estiverem dentro de um arquivo
.zip
ou.7z
.É possível identificar quando o MAME não conseguir carregar as imagens pois aparecem uma sequência de pequenas bolinhas cinzas na tela, isso mostra que ou o MAME não encontrou os arquivos ou houve algum outro erro com o formato do arquivo.
text
Desenha o texto usando a fonte da interface e na cor definida pelo usuário. O texto que será desenhado deve ser informado através do atributo
string
. Um atributoalign
pode ser usado para definir o alinhamento do texto. Se presente, o atributoalign
deve ser um valor inteiro onde (zero) significa centralizado, 1 (um) alinhado à esquerda e 2 (dois) alinhado à direita. Caso o atributoalign
esteja ausente o texto será centralizado automaticamente.
led7seg
Desenha um mostrador LED ou fluorescente alfanumérico comum com dezesseis segmentos e o mostrador numa determinada cor. Os oito bits baixos do estado do elemento controlam quais os segmentos estarão acesos. Começando pelo bit de menor importância a sequência de atualização dos bits correspondentes começam no segmento superior, superior direito, depois continuando no sentido horário para o segmento superior esquerdo, a barra central e o ponto decimal. Os pixels que estiverem apagados são desenhados com uma intensidade menor (0x20/0xff).
led14seg
Desenha um mostrador LED ou fluorescente alfanumérico padrão com catorze segmentos numa determinada cor. Os 14 bits mais baixos do controle de estado do elemento determinam quais os segmentos estarão acesos. Começando pelo bit com menor importância, os bits correspondentes ao segmento superior, o segmento superior direito, continuando no sentido horário para o segmento superior esquerdo, as metades esquerda e direita da barra central horizontal, as metades superior e inferior do meio vertical da barra, e as barras diagonais no sentido horário da parte inferior esquerda para a direita inferior. Os pixels que estiverem apagados são desenhados com uma intensidade menor (0x20/0xff).
led14segsc
Desenha um mostrador LED ou fluorescente alfanumérico padrão com catorze segmentos com ponto decimal/vírgula numa determinada cor. Os 16 bits baixos do elemento controlam quais segmentos estarão acesos. Os 14 bits baixos correspondem aos mesmos segmentos que no componente
led14seg
. Os dois bits adicionais correspondem ao ponto decimal e a vírgula. Os pixels que estiverem apagados são desenhados com uma intensidade menor (0x20/0xff).
led16seg
Desenha um mostrador LED ou fluorescente alfanumérico padrão com dezesseis segmentos numa determinada cor. Os 16 bit baixos do elemento controlam quais os elementos que estarão acesos. Começando pelo bit de menor importância a sequência de atualização dos bits correspondentes começam da metade esquerda da barra superior, a metade direita da barra superior, continuando no sentido horário para o segmento superior esquerdo, as metades esquerda e direita da barra central e horizontal, as metades superior e inferior da barra do meio vertical, e as barras diagonais no sentido horário a partir do canto inferior esquerdo até a parte inferior direito. Os pixels que estiverem apagados são desenhados com uma intensidade menor (0x20/0xff).
led16segsc
Desenha um mostrador LED ou fluorescente alfanumérico padrão com dezesseis segmentos e o ponto decimal numa determinada cor. Os 16 bits baixos do elemento controlam quais os segmentos estarão acesos. Os 18 bits inferiores correspondem aos mesmos controles do estado dos segmentos que em
led16seg
. Os dois bits adicionais correspondem ao ponto decimal e a vírgula. Os pixels que estiverem apagados são desenhados com uma intensidade menor (0x20/0xff).
simplecounter
Exibe o valor numérico do estado do elemento usando a fonte do sistema numa determinada cor. O valor é formatado em notação decimal. Um atributo
digits
pode ser informado para definir a quantidade mínima de dígitos que serão exibidos. Se presente, o atributodigits
deve ser um número inteiro, na sua ausência será exibido um dígito com no mínimo dois dígitos.O atributo
maxstate
pode ser informado para definir o valor máximo do estado que será exibido. Se presente, o atributomaxstate
deve ser um número positivo; na sua ausência o valor predefinido é 999. Um atributoalign
pode ser usado para determinar o alinhamento do texto através do atributoalign
que deve ser um número inteiro onde0
significa alinhar ao centro,1
alinhar à esquerda e2
alinhar à direita. Na sua ausência, o texto será centralizado automaticamente.
Um exemplo de um elemento que desenha um texto estático do lado esquerdo da tela:
<element name="label_reset_cpu">
<text string="CPU" align="1"><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
Um exemplo de um elemento que mostra um LED redondo onde a intensidade do seu brilho depende do nível do seu estado na saída:
<element name="led" defstate="0">
<disk state="0"><color red="0.43" green="0.35" blue="0.39" /></disk>
<disk state="1"><color red="1.0" green="0.18" blue="0.20" /></disk>
</element>
Um exemplo de um elemento para um botão que retorna um efeito visual quando ele for clicado:
<element name="btn_rst">
<rect state="0"><bounds x="0.0" y="0.0" width="1.0" height="1.0" /><color red="0.2" green="0.2" blue="0.2" /></rect>
<rect state="1"><bounds x="0.0" y="0.0" width="1.0" height="1.0" /><color red="0.1" green="0.1" blue="0.1" /></rect>
<rect state="0"><bounds x="0.1" y="0.1" width="0.9" height="0.9" /><color red="0.1" green="0.1" blue="0.1" /></rect>
<rect state="1"><bounds x="0.1" y="0.1" width="0.9" height="0.9" /><color red="0.2" green="0.2" blue="0.2" /></rect>
<rect><bounds x="0.1" y="0.1" width="0.8" height="0.8" /><color red="0.15" green="0.15" blue="0.15" /></rect>
<text string="RESET"><bounds x="0.1" y="0.4" width="0.8" height="0.2" /><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
Um exemplo de um elemento que desenha um LED com sete segmentos usando imagens externas:
<element name="digit_a" defstate="0">
<image file="a_off.png" />
<image file="a_a.png" statemask="0x01" />
<image file="a_b.png" statemask="0x02" />
<image file="a_c.png" statemask="0x04" />
<image file="a_d.png" statemask="0x08" />
<image file="a_e.png" statemask="0x10" />
<image file="a_f.png" statemask="0x20" />
<image file="a_g.png" statemask="0x40" />
<image file="a_dp.png" statemask="0x80" />
</element>
Um exemplo de um gráfico com barras que crescem verticalmente e mudam da cor verde, passando pelo amarelo e para o vermelho à medida que o nível for aumentando:
<element name="pedal">
<rect>
<bounds state="0x000" left="0.0" top="0.9" right="1.0" bottom="1.0" />
<bounds state="0x610" left="0.0" top="0.0" right="1.0" bottom="1.0" />
<color state="0x000" red="0.0" green="1.0" blue="0.0" />
<color state="0x184" red="1.0" green="1.0" blue="0.0" />
<color state="0x610" red="1.0" green="0.0" blue="0.0" />
</rect>
</element>
Um exemplo de um gráfico com barras que crescem horizontalmente para a esquerda ou para a direita e muda de cor do verde, passando pelo amarelo e para o vermelho à medida que o nível muda da posição neutra:
<element name="wheel">
<rect>
<bounds state="0x800" left="0.475" top="0.0" right="0.525" bottom="1.0" />
<bounds state="0x280" left="0.0" top="0.0" right="0.525" bottom="1.0" />
<bounds state="0xd80" left="0.475" top="0.0" right="1.0" bottom="1.0" />
<color state="0x800" red="0.0" green="1.0" blue="0.0" />
<color state="0x3e0" red="1.0" green="1.0" blue="0.0" />
<color state="0x280" red="1.0" green="0.0" blue="0.0" />
<color state="0xc20" red="1.0" green="1.0" blue="0.0" />
<color state="0xd80" red="1.0" green="0.0" blue="0.0" />
</rect>
</element>
As visualizações¶
Uma visualização (elemento view
) define um arranjo dos elementos ou
das imagens exibidas da tela emulada numa janela ou numa tela.
As exibições também conectam os elementos, as entradas E/S e as saídas
emuladas.
Um arquivo layout pode conter vários elementos view
, caso uma
delas corresponda a uma tela inexistente, esta se torna inválida.
O MAME exibirá uma mensagem de aviso ignorando toda a visualização que for considerada inválida e continuará a carregar aquelas que estiverem corretas. Isso é muito útil nos sistemas onde uma tela seja opcional, como computadores que tenham apenas controles no painel frontal e onde um terminal serial seja opcional.
As visualizações são identificadas através do nome na interface de
usuário do MAME ou na linha de comando. Para os arquivos dos layouts que
sejam associados aos dispositivos ou a outros onde o dispositivo do
controlador principal, os nomes das visualizações dos dispositivos sejam
precedidos por uma tag (com os dois pontos iniciais omitidos) por
exemplo, para exibir um dispositivo chamado "Keyboard LEDs" vindo do
dispositivo :tty:ie15
, ele deve ser associado como tty:ie15
Keyboard LEDs.
As visualizações são exibidas na ordem em que forem sendo carregadas.
As visualizações são criadas com elementos view
dentro de um
atributo do primeiro nível do elemento mamelayout
. É obrigatório que
cada elemento view
tenha um atributo name
informando um nome
único que será disponibilizado na interface do usuário e nas opções da
linha de comando. Este é um exemplo de um atributo válido para um
elemento view
:
<view name="Painel de controle">
O elemento "view" cria uma seção visível do mamelayout
. Os elementos
view
apenas são processados depois que todas as outras
ramificações dos outros elementos do mamelayout
forem corretamente
carregadas. Isso significa que uma visualização pode fazer referência a
elementos e aos grupos que apareçam posteriormente naquele arquivo assim
como os valores finais dos parâmetros que estejam anexados ao escopo do
mamelayout
.
Um elemento view
pode ter um atributo showpointers
para definir
se os ponteiros do mouse e da caneta devem ser exibidos na visualização.
Quando o atributo estiver presente, o valor deverá ser yes
ou
no
, quando não estiver, os ponteiros do mouse e da caneta serão
mostrados para as exibições que contêm itens vinculados as portas de
E/S, exemplo:
<view name="Painel de controle" showpointers="no">
Nota
A partir da versão 0.265 do MAME é preciso definir o
showpointers
como no em seu arquivo de layout, caso
contrário, o ponteiro laranja do mouse irá aparecer nas suas
ilustrações gráficas (artwork) ainda que ele não seja necessário para
aquela ilustração. A predefinição é exibir o ponteiro, caso esta seja
a sua intenção, não é preciso alterar nada.
As seguintes ramificações dos elementos são permitidos dentro de um
elemento view
:
bounds
Define a origem e o tamanho da visualização através das coordenadas interna do sistema caso um esteja presente. Consulte Coordenadas para obter mais informações. Em sua ausência os limites da visualização serão computadas unindo os limites de todas as telas e dos elementos dentro da região exibida. Só faz sentido ter um elemento
bounds
caso seja uma ramificação direta de um elementoview
. Qualquer conteúdo fora dos limites da visualização ficarão recortados e a visualização será redimensionada de forma proporcional para que se ajuste aos limites da tela ou da janela.
param
Define ou reatribui um valor no parâmetro do escopo da visualização. Consulte Parâmetros para obter mais informações.
element
Adiciona um elemento à visualização (consulte Os elementos) através do atributo do elemento obrigatório
ref
. Haverá um erro caso nenhum elementoref
seja definido no arquivo layout.Opcionalmente pode estar conectada numa porta E/S emulada através dos atributos
inputtag
e oinputmask
ou através da emulação de uma saída usando um atributoname
. Consulte Itens que podem ser clicados e também O Estado do elemento para obter mais detalhes sobre como informar o valor de uma condição/estado para o elemento que for solicitado.
screen
Adiciona uma imagem emulada da tela na visualização. A tela deve ser identificada através do atributo
index
ou do atributotag
(um elementoscreen
não pode ter ambos os atributosindex
etag
). Caso esteja presente, o atributoindex
deve ter um valor inteiro e positivo. As telas são numeradas através da ordem em que aparecem na configuração do sistema, começando com zero (0
). Caso o atributotag
esteja presente, este deve ser o caminho da etiqueta para a tela com relação ao dispositivo para que provoque a leitura do layout. As telas são desenhadas na ordem em que aparecem no arquivo layout.Pode opcionalmente estar conectada numa porta E/S emulada através dos atributos
inputtag
einputmask
ou através de uma saída emulada através do atributoname
. Consulte Itens que podem ser clicados para obter mais informações.
collection
Adiciona as telas ou os itens numa coleção de itens que poderão ser exibidos ou escondidos pelo usuário (consulte Coleções). O nome da coleção é definida através do atributo
name
. Há um limite de até 32collection
por visualização.
group
Adiciona o conteúdo do grupo na visualização (consulte Grupos reutilizáveis). O nome do grupo que será adicionado pode ser definido através do atributo
ref
. Haverá um erro caso nenhum grupo com este atributo seja definido no arquivo layout. Veja abaixo para mais informações sobre a questão de posicionamento.
repeat
Repete seu conteúdo pela quantidade de vezes que estiver definida no atributo
count
. O atributocount
deve ser um número inteiro e positivo. O elementorepeat
aceita os elementoselement
,screen
,group
mais os elementosrepeat
que funcionam da mesma maneira que quando colocados numa visualização direta. Consulte Repetindo os blocos para saber como usar os elementosrepeat
.
As telas com os elementos screen
e os elementos element
do
layout podem ter um atributo id
. Quando presente o atributo
id
deve ser único entre os elementos view
e seu valor não pode
ser vazio, isso incluí telas (screens) e os elementos instanciados
através dos grupos reutilizáveis e da repetição dos blocos. Os elementos
de tela e layout com o atributo id
são identificados através de
scripts Lua (consulte Usando scripts no layout do MAME).
As telas com os elementos screen
, elementos do layout element
e
os elementos de grupo group
, podem ter a sua orientação alterada
usando o elemento orientation
.
Para as telas, os modificadores de orientação são aplicados em conjunto
com os modificadores de orientação definido na tela do dispositivo e no
sistema.
O elemento orientation
suporta os seguintes atributos opcionais:
rotate
Se presente, aplica rotação no sentido horário em incrementos de
90
graus. Deve ser um número inteiro igual à0
90
,180 (90 + 90)
ou270 (180 + 90)
.
swapxy
Permite que a tela, elemento ou grupo seja espelhado ao longo de uma linha em 45 graus na vertical, da esquerda para a direita. Se presente o seu valor deve ser
yes
ouno
. O espelhamento se aplica logicamente após a rotação.
flipx
Permite que a tela, elemento ou grupo sejam espelhados à partir de uma linha com 45 graus em torno de seu eixo vertical, vindo da quina superior esquerda até a quina inferior direita. Se presente o seu valor deve ser
yes
ouno
. O espelhamento ocorre após a rotação.
flipy
Permite que a tela, elemento ou grupo sejam espelhados ao redor do seu eixo horizontal de cima para baixo. Se presente o seu valor deve ser
yes
ouno
. O espelhamento ocorre após a rotação.
As telas (elementos screen
) e os elementos do layout (elementos
element
) podem conter um atributo blend
para determinar o modo
de mesclagem dos elementos gráficos. Os valores válidos são
none
(sem mesclagem), alpha
(transparência) [5],
multiply
(soma dos valores RGB) [6] e
add
(soma das camadas) [7]. A predefinição para a tela é alpha
permitindo que o driver defina a mesclagem dos elementos do layout
através de camadas.
As telas (elementos screen
), elementos do layout (elementos
element
) e elementos de grupo (group
) podem ser posicionados e
redimensionados usando um elemento bounds
(consulte Coordenadas para mais informações).
Na ausência do sub-elemento bounds
os elementos "screen" e "layout"
retornam aos valores predefinidos em unidades quadradas (origem em
0,0 e ambos os valores de altura e largura serão igual à 1
Na ausência do elemento bounds
, os grupos são expandidos sem
qualquer tradução ou redimensionamento (note que os grupos podem
posicionar as telas ou elementos fora dos seus limites. Este exemplo
mostra uma visualização com referência a posição da tela com um elemento
individual do layout e dois grupos de elementos:
<view name="LED Displays, Terminal and Keypad" showpointers="no">
<screen index="0"><bounds x="0" y="132" width="320" height="240" /></screen>
<element ref="beige"><bounds x="320" y="0" width="172" height="372" /></element>
<group ref="displays"><bounds x="0" y="0" width="320" height="132" /></group>
<group ref="keypad"><bounds x="336" y="16" width="140" height="260" /></group>
</view>
As telas (elementos screen
), os elementos do layout (element
) e
os elementos do grupos (group
) podem ter um sub-elemento color
(consulte Cores) ao definir uma cor
modificadora. O valor dessa cor será usada como multiplicador para
alterar as cores componentes da tela ou dos elementos do layout.
As telas (elementos screen
) e os elementos do layout (element
)
podem ter a sua cor, posição e tamanho animados ao informar diversos
elementos color
e/ou sub-elementos bounds
em conjunto com o
atributo state
. Consulte A visualização de um item animado para obter
mais informações.
Coleções¶
As coleções das telas ou dos elementos do layout que são agrupados de
maneira que possam ser exibidos ou não pelo usuário conforme a sua
necessidade caso este esteja definido pelo autor do layout. Numa
visualização única, é possível ambas as visualizações e um teclado
numérico (keypad) selecionável por exemplo, permitir que o usuário
esconda o teclado numérico deixando visível apenas a visualização. As
coleções são criadas através do elemento collection
dentro dos
elementos view
, group
e dos outros elementos collection
.
Um elemento collection
deve ter um atributo name
informando o
nome da visualização. Os nomes destinados para collection
devem ser
únicos. A visualização inicial da coleção deve ser definida através do
atributo visible
. Defina o atributo visible
para yes
caso a
coleção deva estar visível desde o inicio ou no
caso queira
escondê-la. É predefinido que as coleções estejam visíveis.
Aqui um exemplo demonstrando a utilização de um collection
permitindo que partes de uma visualização possam ser escondidas pelo
usuário:
<view name="Telas LED, CRT e Teclado Numérico" showpointers="no">
<collection name="Telas LED">
<group ref="displays"><bounds x="240" y="0" width="320" height="47" /></group>
</collection>
<collection name="Teclado numérico">
<group ref="keypad"><bounds x="650" y="57" width="148" height="140" /></group>
</collection>
<screen tag="screen"><bounds x="0" y="57" width="640" height="480" /></screen>
</view>
Uma coleção cria um escopo de parâmetros agrupados. Qualquer elemento
param
que estiver dentro do elemento de coleção define os parâmetros
no escopo local para a coleção. Para mais detalhes sobre os parâmetros
consulte Parâmetros. Observe que o nome da coleção e
a visualização predefinida não fazem parte do seu conteúdo, quaisquer
referências dos parâmetros nos atributos name
e visible
serão
substituídos usando os valores dos parâmetros a partir da origem do
escopo relacionado com a coleção.
Para mais informações consulte Desativando objetos na tela.
Grupos reutilizáveis¶
Os grupos permitem que um arranjo das telas ou dos elementos do layout
sejam usados várias vezes numa visualização ou outros grupos. Os
grupos podem ser de grande ajuda mesmo que seja usado o arranjo apenas
uma vez, pois eles podem ser usados para agregar parte de um layout
complexo.
Os grupos são definidos usando elementos group
dentro de elementos
mamelayout
de primeiro nível e representados ao usar elementos
group
dentro de elementos view
e outros elementos group
.
Cada definição de grupo deve ter um atributo name
informando um
identificador único. Será considerado um erro caso o arquivo layout
tenham várias definições de grupos usando um atributo name
idêntico.
O valor do atributo name
é usado quando for justificar a
visualização de um grupo ou outro. Este é um exemplo da abertura da
etiqueta para definir o grupo de um elemento dentro do primeiro elemento
mamelayout
:
<group name="panel">
Este grupo pode então ser justificado numa visualização ou em outro
elemento group
usando um elemento de grupo como referência.
Opcionalmente os limites de destino, a orientação e as modificações
das cores poderão ser informados também.
O atributo ref
identifica o grupo a qual faz referência, neste
exemplo são informados os limites dos valores:
<group ref="panel"><bounds x="87" y="58" width="23" height="23.5" /></group>
Os elementos de definição dos grupos permitem que todos os elementos filhos que forem iguais, sejam exibidos. O posicionamento e as orientações das telas, os elementos do layout e o arranjo destes grupos para que funcionem da mesma maneira que as visualizações. Veja As visualizações para mais informações. Um grupo pode justificar outros grupos, porém loops recursivos não são permitidos. Será considerado um erro caso um grupo represente a si mesmo de forma direta ou indireta.
Os grupos possuem seus próprios sistemas de coordenadas internas.
Caso um elemento de definição de grupo não tenha um elemento limitador
bounds
como filho direto, os seus limites serão computados junto com
a união dos limites de todas as telas, os elementos do layout ou dos
grupos relacionados.
Um elemento filho bounds
pode ser usado para definir
explicitamente grupos limitadores
(consulte Coordenadas para mais informações).
Observe que os limites dos grupos são usados com a única justificativa
para calcular as coordenadas de transformação quando forem relacionados
a um grupo. Um grupo pode posicionar as telas ou os elementos fora dos
seus limites sem que sejam cortados.
Para demonstrar como o cálculo dos limites funcionam, considere este exemplo:
<group name="autobounds">
<!-- limites automaticamente calculados com sua origem em (5,10), largura 30, e altura 15 -->
<element ref="topleft"><bounds x="5" y="10" width="10" height="10" /></element>
<element ref="bottomright"><bounds x="25" y="15" width="10" height="10" /></element>
</group>
<view name="Teste" showpointers="no">
<!--
Os grupos limitadores são traduzidos e escalonados para preencher 2/3 da escala
horizontal e o dobro verticalmente.
O elemento superior esquerdo posicionado em (0,0) com 6.67 de largura e 20 de altura
O elemento inferior direito posicionado em (13.33,10) com 6.67 de largura e 20 de altura
Os elementos de visualização calculado com origem em (0,0) 20 de largura e 30 de altura
-->
<group ref="autobounds"><bounds x="0" y="0" width="20" height="30" /></group>
</view>
Como todos os elementos inerentemente caem dentro dos limites calculados ao grupo de forma automática. Agora, considere o que acontece caso a posição dos elementos de um grupo estejam fora dos seus limites:
<group name="periphery">
<!-- os limites dos elementos estão acima da quina superior e à direita da quina direita -->
<bounds x="10" y="10" width="20" height="25" />
<element ref="topleft"><bounds x="10" y="0" width="10" height="10" /></element>
<element ref="bottomright"><bounds x="30" y="20" width="10" height="10" /></element>
</group>
<view name="Test" showpointers="no">
<!--
Os grupos limitadores são traduzidos e escalonados para preencher 2/3 da escala
horizontal unido verticalmente.
O elemento superior esquerdo posicionado em (5,-5) com 15 de largura e 10 de altura
O elemento inferior direito posicionado em (35,15) com 15 de largura e 10 de altura
Os elementos de visualização calculado com origem em (5,-5) 45 de largura e 30 de altura
-->
<group ref="periphery"><bounds x="5" y="5" width="30" height="25" /></group>
</view>
Os elementos de grupo são traduzidos e escalonados conforme sejam necessários para distorcer os limites internos dos grupos para o limite de visualização final. O conteúdo dos grupos não ficam restritos aos seus limites. A visualização considera os limites dos elementos atuais ao calcular os seus próprios limites e não aos limites do destino definido para o grupo.
Quando um grupo é instanciado [10], ele cria um escopo agrupado
do parâmetro.
A lógica do escopo principal é o escopo do parâmetro de visualização,
do grupo ou do bloco de repetição onde o grupo for instanciado (não é
um parente léxico ao elemento de primeiro nível mamelayout
).
Qualquer elemento param
dentro da definição do conjunto, estabelece
os parâmetros dos elementos no escopo local para o grupo instanciado.
Os parâmetros locais não se preservam através das várias instancias.
Consulte Parâmetros para obter mais informações sobre
os parâmetros. (Observe que o nome dos grupos não fazem parte do seu
conteúdo e qualquer referência de parâmetro no próprio atributo name
será substituído no ponto onde a definição do grupo aparecer no primeiro
nível do elemento de escopo mamelayout
.)
Repetindo os blocos¶
A repetição dos blocos fornecem uma maneira concisa de gerar ou para organizar uma grande quantidade de elementos iguais. A repetição dos blocos são geralmente usados em conjunto com o gerador de parâmetros (consulte Parâmetros). A repetição dos blocos podem ser agrupados para criar arranjos mais complexos.
Os blocos repetidos são criados através do elemento repeat
.
Cada elemento repeat
requer um atributo count
definindo uma
quantidade de iterações que serão geradas.
O atributo count
deve ser um número inteiro e positivo. A repetição
dos blocos é permitida dentro do elemento de primeiro nível
mamelayout
, dentro dos elementos group
e view
assim como
dentro dos outros elementos repeat
. O exato sub-elemento permitido
dentro do elemento repeat
depende de onde ele for aparecer:
Um bloco repetido dentro do elemento de primeiro nível
mamelayout
podem conter os seguintes elementosparam
,element
,group
(definição) erepeat
.Um bloco repetido dentro de um elemento
group
ouview
podem conter os seguintes elementos,param
,element
(referência),screen
,group
(referência) erepeat
.
Um bloco de repetição repete o seu conteúdo diversas vezes dependendo do
valor definido no atributo count
. Consulte as seções relevantes para
obter mais informações de como os sub-elementos são usados
(As partes de um layout, Grupos reutilizáveis
e As visualizações). Um bloco que se repete cria um escopo de
parâmetros agrupados dentro do escopo do parâmetro do seu elemento
léxico principal (DOM).
O exemplo abaixo geram rótulos numéricos em branco a partir de 0
até
11
com o nome label_0
, label_1
e assim por diante (dentro do
elemento de primeiro nível mamelayout
):
<repeat count="12">
<param name="labelnum" start="0" increment="1" />
<element name="label_~labelnum~">
<text string="~labelnum~"><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
</repeat>
Uma fileira horizontal com 40 mostradores digitais, separadas por cinco
unidades de espaço entre elas, controladas pelas saídas digit0
até
digit39
(dentro de um elemento group
ou view
):
<repeat count="40">
<param name="i" start="0" increment="1" />
<param name="x" start="5" increment="30" />
<element name="digit~i~" ref="digit">
<bounds x="~x~" y="5" width="25" height="50" />
</element>
</repeat>
Oito mostradores com matrix de ponto medindo cinco por sete numa
linha, com pixels controlados por Dot_000
até Dot_764
(dentro de um elemento group
ou view
):
<!-- 8 dígitos -->
<repeat count="8">
<param name="digitno" start="1" increment="1" />
<!-- a distância entre os dígitos ((111 * 5) + 380) -->
<param name="digitx" start="0" increment="935" />
<!-- 7 linhas para cada dígito -->
<repeat count="7">
<param name="rowno" start="1" increment="1" />
<!-- a distância vertical entre os LEDs -->
<param name="rowy" start="0" increment="114" />
<!-- 5 colunas em cada dígito -->
<repeat count="5">
<param name="colno" start="1" increment="1" />
<!-- a distância horizontal entre os LEDs -->
<param name="colx" start="~digitx~" increment="111" />
<element name="Dot_~digitno~~rowno~~colno~" ref="Pixel" state="0">
<!-- o tamanho de cada LED -->
<bounds x="~colx~" y="~rowy~" width="100" height="100" />
</element>
</repeat>
</repeat>
</repeat>
Dois teclados que podem ser clicados, separados horizontalmente por um
teclado numérico quatro por quatro (dentro de um elemento group
ou
view
):
<repeat count="2">
<param name="group" start="0" increment="4" />
<param name="padx" start="10" increment="530" />
<param name="mask" start="0x01" lshift="4" />
<repeat count="4">
<param name="row" start="0" increment="1" />
<param name="y" start="100" increment="110" />
<repeat count="4">
<param name="col" start="~group~" increment="1" />
<param name="btnx" start="~padx~" increment="110" />
<param name="mask" start="~mask~" lshift="1" />
<element ref="btn~row~~col~" inputtag="row~row~" inputmask="~mask~">
<bounds x="~btnx~" y="~y~" width="80" height="80" />
</element>
</repeat>
</repeat>
</repeat>
Os botões são desenhados usando os elementos btn00
na parte superior
esquerda, btn07
na parte superior direita, btn30
na parte
inferior esquerda e btn37
na parte inferior direita contando entre
eles. As quatro colunas são conectadas às portas E/S row0
, row1
,
row2
, and row3
de cima para baixo.
As colunas consecutivas são conectadas aos bits das portas E/S começando
com o bit de menor importância do lado esquerdo.
Observe que o parâmetro mask
no elemento mais interno repeat
recebe o seu valor inicial a partir do parâmetro correspondentemente
nomeado no delimitador do escopo, porém sem alterá-lo.
Gerando um tabuleiro de xadrez com valores alfa alternados entre 0.4
e 0.2
(dentro de um elemento group
ou view
):
<repeat count="4">
<param name="pairy" start="3" increment="20" />
<param name="pairno" start="7" increment="-2" />
<repeat count="2">
<param name="rowy" start="~pairy~" increment="10" />
<param name="rowno" start="~pairno~" increment="-1" />
<param name="lalpha" start="0.4" increment="-0.2" />
<param name="ralpha" start="0.2" increment="0.2" />
<repeat count="4">
<param name="lx" start="3" increment="20" />
<param name="rx" start="13" increment="20" />
<param name="lmask" start="0x01" lshift="2" />
<param name="rmask" start="0x02" lshift="2" />
<element ref="hl" inputtag="board:IN.~rowno~" inputmask="~lmask~">
<bounds x="~lx~" y="~rowy~" width="10" height="10" />
<color alpha="~lalpha~" />
</element>
<element ref="hl" inputtag="board:IN.~rowno~" inputmask="~rmask~">
<bounds x="~rx~" y="~rowy~" width="10" height="10" />
<color alpha="~ralpha~" />
</element>
</repeat>
</repeat>
</repeat>
O elemento repeat
mais externo gera um grupo com duas colunas em
cada interação; o próximo elemento repeat
gera uma coluna individual
em cada interação; o elemento repeat
interno produz dois recortes
horizontais adjacentes em cada interação.
As colunas são conectadas às portas E/S através do board:IN.7
no topo do board.IN.0
na parte inferior.
Interatividade¶
As visualizações com interatividade são suportadas através da permissão dos itens que serão vinculados nas saídas e nas portas E/S. Há suporte para cinco tipos de interatividades:
Itens que podem ser clicados
Caso um item numa visualização esteja vinculado com uma região dos interruptores da porta E/S, será possível clicar no item para ativar o interruptor ou um botão emulado.
Componentes que dependam de uma condição
Dependendo do estado do elemento que o contiver, alguns componentes serão desenhados de forma diferente. Isso inclui a matriz de pontos, o display de LEDs com vários segmentos, os contadores simples e os elementos com mostradores rotativos. Consulte Os elementos para obter mais detalhes.
Componentes desenhados de forma condicional
Os componentes podem ser desenhados de forma condicional ou escondidos dependendo da condição do conteúdo do elemento a partir da informação dos valores para os elementos
state
e/oustatemask
. Consulte Os elementos para obter mais detalhes.
Parâmetros para a animação dos componentes
A posição, tamanho e a cor dos componentes contido em seus elementos talvez possam ser animados de acordo com a condição do elemento a partir da informação dos diversos elementos
color
e/oubounds
em conjunto com os atributos de condiçãostate
. Consulte Os elementos para obter mais detalhes.
Parâmetros para a animação dos itens
A cor, a posição e o tamanho dos itens restritos ao seu espaço de visualização podem ser animados de acordo com a sua condição.
Itens que podem ser clicados¶
Caso um item de visualização (elemento element
ou screen
) tenham
atributos inputtag
e inputmask
com valores que correspondam a
uma região com interruptores digitais no sistema emulado, será possível
clicar no elemento para que determinado interruptor seja ativado. O
interruptor permanecerá ativo enquanto o botão o botão primário estiver
pressionado e o ponteiro estiver dentro dos limites do item.
(Observe que os limites podem mudar dependendo da condição do estado de
animação do item, consulte A visualização de um item animado).
O atributo inputtag
determina o caminho do identificador de uma
porta E/S relativa ao dispositivo responsável pelo carregamento do
arquivo layout. O atributo inputmask
deve ser um valor inteiro
definindo os bits da região da porta de E/S que o item deve ativar.
Este exemplo demonstra a instanciação dos botões que podem ser clicados:
<element ref="btn_3" inputtag="X2" inputmask="0x10">
<bounds x="2.30" y="4.325" width="1.0" height="1.0" />
</element>
<element ref="btn_0" inputtag="X0" inputmask="0x20">
<bounds x="0.725" y="5.375" width="1.0" height="1.0" />
</element>
<element ref="btn_rst" inputtag="RESET" inputmask="0x01">
<bounds x="1.775" y="5.375" width="1.0" height="1.0" />
</element>
Ao lidar com o retorno das informações vindas da entrada, o MAME trata todos os elementos do layout como sendo um retângulo.
Para bloquear o elemento de ser clicado na tela crie uma camada vazia com as mesmas dimensões do item bloqueado antes do item que você deseja bloquear. Primeiro crie-o no inicio do layout:
<element name="cobertura" defstate="0">
<text string=" " />
</element>
Em seguida use-o antes do elemento que será protegido. A organização
precisa ser feita desta maneira pois é nesta ordem que o MAME renderiza
os elementos do layout na tela, primeiro vem o elemento "cobertura"
seguido por outros elemento abaixo dele. O atributo bounds
definirá a sua posição na tela (x
e y
), width
a largura da
"cobertura" (em pixels) e height
a altura:
<element ref="cobertura" blend="add" inputtag="IN0" inputmask="0x0" inputraw="yes">
<bounds x="1783" y="3919" width="270" height="270" />
</element>
<element ref="controle" inputtag="IN0" inputmask="0xf" inputraw="yes">
<bounds x="1783" y="3919" width="270" height="270" />
</element>
Na versão 0.265 do MAME em diante, o atributo clickthrough
controla se os cliques podem passar através do item de visualização para
outros itens desenhados acima dele. Caso esteja presente, o valor do
atributo clickthrough
deve ser yes
ou no
. A predefinição é
no
(os cliques não atravessam) nos itens de visualização com
atributos inputtag
e inputmask
. Já a predefinição se torna
yes
(os cliques atravessam) para os outros itens de visualização.
Da mesma maneira que o exemplo anterior, agora podemos utilizar o
atributo clickthrough
para bloquear os cliques.
Primeiro definimos um retângulo transparente:
<element name="cobertura" defstate="0">
<rect><color alpha="0" /></rect>
</element>
Agora nós invocamos a nossa "cobertura" de proteção na região onde queremos que ela seja aplicada:
<element ref="cobertura" clickthrough="no">
<bounds x="1783" y="3919" width="270" height="270" />
</element>
<element ref="controle" inputtag="IN0" inputmask="0xf" inputraw="yes">
<bounds x="1783" y="3919" width="270" height="270" />
</element>
No exemplo acima estamos criando uma "cobertura" de proteção para o "controle", repare que a nossa cobertura utiliza exatamente os mesmos parâmetros de posição e tamanho do elemento "controle" justamente para cobrir toda a área que ele estiver ocupando. Não é obrigatório que a nossa "cobertura" tenha exatamente o mesmo tamanho da região que desejamos proteger. Dependendo da ilustração e do sistema onde esta ilustração será utilizada, talvez seja interessante ir um pouco além dos limites do "controle" e cobrir uma área um pouco maior evitando falsos cliques, caso seja necessário.
Consulte também Adicionando diagonais nos controles com 8 direções usando o inputraw.
O Estado do elemento¶
Um item de visualização que instancie um elemento (elemento element
)
pode fornecer um valor da sua condição para o elemento a partir de uma
porta emulada de E/S ou para a saída. Consulte
Os elementos para obter mais detalhes sobre como o
estado de um elemento afeta sua aparência.
O valor do estado do elemento será obtido através do valor da saída
emulada que corresponda a tal nome caso o elemento element
tenha um
atributo name
. Observe que os nomes das saídas são globais
e podem se tornar um problema quando um sistema utilizar várias
instâncias do mesmo tipo do dispositivo. Este exemplo mostra como
os monitores digitais podem ser conectados na saída emulada:
<element name="digit6" ref="digit"><bounds x="16" y="16" width="48" height="80" /></element>
<element name="digit5" ref="digit"><bounds x="64" y="16" width="48" height="80" /></element>
<element name="digit4" ref="digit"><bounds x="112" y="16" width="48" height="80" /></element>
<element name="digit3" ref="digit"><bounds x="160" y="16" width="48" height="80" /></element>
<element name="digit2" ref="digit"><bounds x="208" y="16" width="48" height="80" /></element>
<element name="digit1" ref="digit"><bounds x="256" y="16" width="48" height="80" /></element>
O valor do estado do elemento será obtido a partir do valor da porta
correspondente ao E/S mascarado com o valor do inputmask
caso o
elemento element
tenha os atributos inputtag
e inputmask
porém não tenha um atributo name
. O atributo inputtag
determina o caminho do identificador de uma porta E/S relativa ao
dispositivo responsável pelo carregamento do arquivo layout. O
atributo inputmask
deve ser um valor inteiro para definir os bits da
região da porta E/S que o item deve ativar.
O valor da porta E/S é mascarado com o valor do inputmask
e feito
uma operação XOR [11] com o valor predefinido da região da porta E/S
caso o elemento element
não tenha qualquer atributo inputraw
ou
caso o valor do atributo inputraw
seja no
. Em geral é utilizado
para fornecer um retorno visual para os botões que sejam clicáveis como
valores normais para os interruptores alto-ativo e baixo-ativo.
O estado do elemento será obtido a partir dos valores da porta E/S
mascarado com o valor do inputmask
e deslocada para a direita
para remover os zeros restantes caso o elemento element
tenha um
atributo inputraw
com o valor yes
(por exemplo, uma máscara com
o valor 0x5
não terá deslocamento algum enquanto uma máscara com o
valor 0xb0
resultará num deslocamento com quatro bits à direita).
É útil para obter os valores analógicos das entradas ou das posições.
A visualização de um item animado¶
A cor, a posição e o tamanho dos itens que estejam dentro dos limites da
visualização poderão ser animados. Isso é feito através da definição dos
diversos sub-elementos color
ou bounds
com atributos state
.
O atributo state
deve ser um número inteiro positivo para cada
elemento color
ou sub-elemento bounds
. Dentro do item de
visualização os dois elementos color
e os dois elementos bounds
não podem ter os mesmos atributos state
com os mesmos valores.
Para definir a posição ou o tamanho do item através do sub-elemento
bounds
será usado o menor valor do atributo state
caso o estado
de animação do item seja menor que o valor do atributo state
de
qualquer um dos sub-elementos bounds
. Já a posição ou o tamanho
definido pelo sub-elemento bounds
será utilizado com o maior valor
do atributo state
caso o estado da animação do item seja maior que o
valor do atributo state
de qualquer um dos sub-elementos bounds
.
No entanto a posição ou o tamanho será interpolada de forma linear caso
o estado da animação do item esteja entre os valores do atributo
state
dos dois sub-elementos bounds
.
A cor será atribuída através do sub-elemento color
com o menor valor
do atributo state
caso o estado de animação do item seja menor do
que o valor do atributo state
de qualquer sub-elemento color
.
O mesmo princípio é usado com o maior valor do atributo state
.
Os componentes da cor RGBA serão interpolados de forma linear caso o
estado da animação do item esteja entre os valores do atributo state
dos dois sub-elementos color
.
O estado da animação de um item pode estar limitada a uma saída emulada
ou a entrada de uma porta durante o fornecimento de um sub-elemento
animate
. Quando estiver presente o elemento animate
deve possuir
ou um atributo inputtag
ou um atributo name
(porém não ambos).
Na ausência do sub-elemento animate
o estado de animação do item
será idêntico ao estado do seu elemento (consulte
O Estado do elemento).
Quando um sub-elemento animate
estiver presente e tiver um atributo
inputtag
, o estado da animação do item será obtido a partir do valor
correspondente à porta E/S. O atributo inputtag
determina o caminho
da etiqueta de uma porta E/S relativa ao dispositivo que provoque a
leitura do arquivo layout. São utilizados os valores brutos da porta
da entrada, os valores baixo-ativo do interruptor não são normalizados.
Na presença de um sub-elemento animate
com o atributo name
o
estado da animação do item será obtido através do valor do nome
correspondente a saída emulada. Observe que os nomes das saídas são
globais e podem se tornar um problema quando um sistema utilizar várias
instâncias do mesmo tipo do dispositivo.
O estado da animação será mascarado com o valor mask
e deslocada
para a direita para remover os zeros restantes caso um sub-elemento
animate
tenha um atributo mask
(por exemplo, uma máscara com o
valor 0x5
não terá deslocamento algum enquanto uma máscara com o
valor 0xb0
resultará num deslocamento com quatro bits à direita).
Observe que o atributo mask
aplica o valor da saída (determinado
através do atributo inputtag
). Na presença do atributo mask
o
seu valor deve ser inteiro, na ausência, é equivalente a todas as
definições com 32 bits.
Este exemplo exibe elementos com estado independente para o elemento e para a animação obtendo o estado da animação a partir das saídas emuladas para controlar a sua posição:
<repeat count="5">
<param name="x" start="10" increment="9" />
<param name="i" start="0" increment="1" />
<param name="mask" start="0x01" lshift="1" />
<element name="cg_sol~i~" ref="cosmo">
<animate name="cg_count~i~" />
<bounds state="0" x="~x~" y="10" width="6" height="7" />
<bounds state="255" x="~x~" y="48.5" width="6" height="7" />
</element>
<element ref="nothing" inputtag="FAKE1" inputmask="~mask~">
<animate name="cg_count~i~" />
<bounds state="0" x="~x~" y="10" width="6" height="7" />
<bounds state="255" x="~x~" y="48.5" width="6" height="7" />
</element>
</repeat>
Assim como no exemplo anterior porém agora usa o estado da emulação a partir da posição emulada da entrada para controlar as suas posições:
<repeat count="4">
<param name="y" start="1" increment="3" />
<param name="n" start="0" increment="1" />
<element ref="ledr" name="~n~.7">
<animate inputtag="IN.1" mask="0x0f" />
<bounds state="0" x="0" y="~y~" width="1" height="1" />
<bounds state="11" x="16.5" y="~y~" width="1" height="1" />
</element>
</repeat>
Lidando com erros¶
Para os arquivos internos do layout (fornecidos pelo desenvolvedor), os erros são detectados através script
complay.py
durante uma falha de compilação.O MAME irá parar de carregar um arquivo layout caso encontre um erro de sintaxe, fazendo assim com que nenhuma visualização do layout fique disponível. Alguns exemplos de erros de sintaxe incluem referências para elementos ou grupos indefinidos, limites inválidos, cores inválidas, grupos recursivamente emaranhados e a redefinição do gerador dos parâmetros.
O MAME exibirá uma mensagem de aviso e continuará caso uma visualização faça referência à uma tela inexistente durante o carregamento de um layout. Visualizações apontando para telas não existentes não são exibidas, elas são consideradas inviáveis e tão pouco estarão disponíveis para o usuário.
As visualizações que são geradas automaticamente¶
Após o carregamento interno dos layouts (fornecido pelo desenvolvedor) e do layout externo (fornecido pelo usuário). As seguintes visualizações são geradas automaticamente:
Será exibido a mensagem "No screens Attached to the system" ou "Sem telas anexadas ao sistema" caso o sistema não possua telas e tão pouco sejam encontradas visualizações viáveis no sistema interno ou externo do layout.
A tela será exibida com a sua proporção física e com a rotação aplicada em cada tela que for emulada.
A tela será exibida numa proporção onde os pixels sejam quadrados e com a rotação aplicada para cada tela emulada onde a proporção configurada para o pixel não corresponda a proporção física.
Serão exibidos duas cópias da imagem da tela uma sobreposta a outra com um pequeno espaço entre elas caso o sistema emule apenas uma tela. A cópia da parte de cima será rotacionada em 180 graus. Esta visão pode ser usada num cabine tipo cocktail, que disponibiliza uma mesa onde os jogadores se sentam frente a frente e cada um com a sua tela, ou alternando os jogos que não girem automaticamente a tela para o segundo jogador.
As telas serão organizadas horizontalmente da esquerda para a direita e verticalmente de cima para baixo, ambos com e sem as pequenas lacunas entre elas caso o sistema tenha exatamente duas telas emuladas e nenhuma visualização no layout interno ou no layout externo exibindo todas as telas, ou caso o sistema tenha mais de duas telas emuladas.
As telas serão exibidas em formato de grade em ambas as fileiras principais (da esquerda para a direita e de cima para baixo) e o pilar principal (de cima para baixo e depois da esquerda para a direita). As visualizações são geradas com e sem intervalos entre as telas.
Usando o complay.py¶
No código-fonte do MAME existe um script Python chamado complay.py, encontrado no subdiretório scripts/build. Como parte do processo de compilação do MAME esse script é usado para reduzir o tamanho dos dados dos layouts internos e para convertê-los de maneira que possam ser anexados dentro do executável.
O script pode também detectar muitos erros comuns de formatação exibindo mensagens de erro com mais informações das que o MAME exibe.
Observe que o script não executa todo o mecanismo do layout e portanto não tem a capacidade de detectar erros nos parâmetros usados como referências para elementos indefinidos ou para agrupamentos dos grupos organizados de forma recursiva. O script complay.py é compatível com os interpretadores Python a partir das versões 2.7, 3 ou mais recente, ele usa três parâmetros, um nome do arquivo na entrada, um nome do arquivo na saída e um nome base para as variáveis na saída:
python scripts/build/complay.py <input> [<output> [<varname>]]
É obrigatório o uso de um arquivo na entrada. Caso nenhum nome de arquivo
seja usado na saída, o complay.py irá analisar e verificar apenas o
arquivo da entrada, informando quaisquer erros que forem encontrados e
não gerando qualquer tipo de arquivo na saída.
Caso nenhum varname
seja informado, o complay.py irá
gerar um com base no nome do arquivo da entrada. Isso não garante a
geração de identificadores válidos.
O script retorna as seguintes condições:
0
(zero) quando for concluído com êxito.1
quando houver um erro durante a invocação através da linha de comando.2
caso haja erro no arquivo de entrada.3
caso seja um erro de E/S.
Ao definir um arquivo na saída, este será criado ou substituído caso seja concluído com sucesso ou será removido caso haja um erro.
Para aferir e testar um arquivo layout, execute o script apontando o caminho completo do arquivo como mostra o exemplo abaixo:
python scripts/build/complay.py artwork/dino/default.lay
Criando diferentes arquivos layout na prática¶
Neste capítulo criaremos um layout do zero para o sistema Galaxian demonstrando como definir todos os parâmetros para que todos os objetos apareçam na tela em seus devidos lugares e com o tamanho correto, no final será possível ver a capacidade do MAME de apresentar o design completo na tela, com a devida animação dos controles e dos botões e com todos os botões clicáveis.
Ferramentas necessárias¶
Para esta tarefa precisamos dos seguintes itens:
Um editor de texto da sua preferência, recomendo o Notepad++ no Windows ou o Geany para *nix e macOS.
Gimp.
A versão básica do layout do sistema Galaxian usada neste documento.
A versão básica do layout do sistema Galaxian usando o método inputraw.
A versão avançada do layout com diferentes versões do sistema Galaxian.
O layout modelo criado para identificar as posições do controle para 2 e 4 jogadores.
A arte utilizada aqui foi criada por Etienne MacGyver.
Uma planilha feita com LibreOffice para facilitar o cálculo da relação de aspecto da tela.
Os botões foram criados pela minha amiga u/cd4053b.
A rom do sistema Galaxian.
O MAME configurado e instalado no seu computador.
A versão básica do Galaxian já deve ter um arquivo default.lay
montado e funcionando, porém vamos descrever como encontramos cada um
dos valores utilizados nele. Os arquivos vêm com os respectivos nomes
basic_galaxian.zip
, inputraw_galaxian.zip
e
advanced_galaxian.zip
, para melhor acompanhar o andamento dos
capítulos faça a descompressão dos arquivos dentro do diretório
artwork onde cada um esteja dentro do seu próprio diretório ou seja
basic_galaxian
, inputraw_galaxian
e advanced_galaxian
.
Quando quiser avaliar qualquer um deles basta renomear um deles para
galaxian
Identificando as partes¶
No diretório onde o seu MAME está instalado vá até artwork, dentro dele crie outro diretório chamado galaxian extraia o conteúdo do arquivo de imagens dentro deste diretório. Abra o seu editor de texto e adicione as duas primeiras linhas:
<?xml version="1.0"?>
<mamelayout version="2">
Salve o arquivo como default.lay
.
O próximo passo é definir um nome para a nossa imagem de fundo, estamos usando o nome "Italiano" pois é a versão italiana do sistema Galaxian e também precisamos anexar junto ao nome a imagem que servirá como o fundo do nosso sistema:
<element name="Italiano">
<image file="arte.png" />
</element>
Todas as imagens em grupos, é importante utilizar nomes bem específicos
para cada uma elas. O defstate
define a sua condição inicial e o
state
define o seu o estado em cada condição onde 0
(zero)
significa quando o botão não estiver pressionado e 1
quando
estiver, observe que a imagem usada para os direcionais e para o disparo
pode ser a mesma:
<element name="J1" defstate="0">
<image file="btn0.png" state="0" />
<image file="btn1.png" state="1" />
</element>
<element name="J2" defstate="0">
<image file="btn0.png" state="0" />
<image file="btn1.png" state="1" />
</element>
<element name="esquerda" defstate="0">
<image file="vermelho0.png" state="0" />
<image file="vermelho1.png" state="1" />
</element>
<element name="direita" defstate="0">
<image file="vermelho0.png" state="0" />
<image file="vermelho1.png" state="1" />
</element>
<element name="disparo" defstate="0">
<image file="vermelho0.png" state="0" />
<image file="vermelho1.png" state="1" />
</element>
<element name="pisca" defstate="0">
<image file="pisca1.png" state="1" />
</element>
Usamos o exemplo abaixo para definir o nome da visualização que vai aparecer na interface do MAME para ser selecionada na opção Vídeo (Tab --> Opções do vídeo):
<view name="Galaxian Italiano" showpointers="no">
Precisamos informar ao MAME o tamanho exato da imagem, para ver estas
informações clique com o mouse direito do mouse em cima dela e
selecione Propriedades ou abra o arquivo arte.png
no Gimp e
selecione Imagem --> Propriedades da imagem para
identificar que a imagem tem 3296
x 4093
:
<element ref="Italiano">
<bounds x="0" y="0" width="3296" height="4093" />
</element>
A Posição e a proporção de tela¶
Para aqueles que nunca trabalharam com gráficos de linhas o eixo x vai lidar com as coordenadas da posição horizontal e o eixo y da vertical.
Para descobrir os valores x e y abra o arquivo arte.png no Gimp, na parte de baixo da tela próximo ao zoom ficam as coordenadas x,y como mostra a imagem abaixo. É dali que obtemos os valores e eles aparecem conforme movimentamos o mouse:
O espaço quadriculado ao centro é a área vazia da imagem, dê um zoom na
imagem na casa dos 300% ou mais posicione o mouse na borda entre o
quadriculado e a parte preta da imagem do lado ESQUERDO para
encontrar o valor de x e em CIMA para encontrar o valor de
y, para facilitar foram posicionados duas linhas azuis na imagem
abaixo indicando a posição que o mouse deve estar para obter as
coordenadas que são 817
e 575
:
Uma maneira ainda mais fácil de se obter estes valores é pressionando a
tecla U do seu teclado para selecionar a "varinha" ou a
Ferramenta de seleção contígua, clique dentro da área
quadriculada da imagem para selecioná-la. Em seguida pressione a tecla
R ou a Ferramenta de seleção retangular e clique em
qualquer região da área quadriculada, no painel à esquerda deve aparecer
os mesmos valores para a coordenada x (817
) e y (575
):
Observe que nem sempre haverá um vazio selecionável na imagem para ser mensurado, assim sendo, utilize a técnica que funcionar melhor com o desenho ou a arte que estiver utilizando.
Com a posição da tela definida agora é necessário dimensioná-la mantendo a sua proporção 4:3 que é o padrão para a maioria dos arcades e telas CRT da época. A tela está invertida com proporção 3:4 então pegue o valor da largura como mostra a imagem acima e faça as contas:
x = 1660 * (4 / 3)
x = 1660 * 1,333333333
x = 2213
Ou utilize a ferramenta disponibilizada em Ferramentas necessárias para facilitar o cálculo destas dimensões exibindo o mesmo resultado no campo 3:4.
Insira os valores no campo verde, o primeiro campo verde no topo serve como uma fácil identificação da relação de aspecto da tela de um valor qualquer, caso um valor seja inserido no segundo campo será feito o cálculo da largura com base nos dados da primeira linha, se nenhum valor for inserido, nada será calculado. O segundo campo em verde serve para situações como demonstrada acima onde você identifica uma área qualquer e quer saber qual seria a proporção 4:3 ideal para ela, você insere o valor da altura quando a tela for horizontal ou largura quando a tela estiver na vertical para que a planilha calcule os valores.
A planilha também faz o cálculo da largura com o valor do SAR (Storage Aspect Ratio ou Relação de Aspecto da Origem), este valor é vulgarmente conhecido como pixel perfect. Supondo que você vá fazer um layout para um jogo de um determinado console e queira o tal "pixel perfect", insira uma das resoluções do console no primeiro campo para obter o SAR e descobrir o valor da largura com base neste SAR em vez de utilizar o DAR (Display Aspect Ratio ou a Proporção da Imagem na Tela).
Esta planilha foi criada com a intenção de facilitar os cálculos e para ser usada no desenvolvimento dos layouts, ela não serve para nada muito técnico ou avançado, porém a planilha está aberta, podendo ser alterada para atender qualquer outra necessidade que você venha a ter.
Nota
Sempre que possível, procure usar valores inteiros na definição da resolução da sua tela. Em certos sistemas como a do exemplo acima não haverá qualquer diferença visível, contudo, certos sistemas não são tão tolerantes assim e podem apresentar pixels distorcidos na tela ou até mesmo artefatos estranhos durante o uso de shaders como o CRT-geom por exemplo. Assim, aumente o valor da altura na planilha até quem um valor inteiro seja encontrado.
Se fosse o caso do exemplo acima, o valor ideal seria
1662
x 1899
.
Aviso
Em alguns sistemas que usam diferentes resoluções (como o Neo Geo por exemplo) o design da sua ilustração pode ficar fora das proporções ideais, assim, em vez de utilizar os valores DAR, experimente usar os valores SAR. Nestes casos muito específicos, pode ser que o mecanismo de ajuste de proporção de tela do MAME faça com que haja bordas pretas em quaisquer um dos lados da tela ou partes da tela podem ficar ocultas pela sua ilustração.
Com o valor calculado em mãos, a resolução final será
1660
x 2213
. Assim temos todos os valores para posicionarmos a
nossa tela emulada na tela (screen) e a sua correta proporção:
<screen index="0">
<bounds x="817" y="575" width="1660" height="2213" />
</screen>
É possível organizar este layout de duas maneiras diferentes dependendo do efeito que você queira dar ao seu design. O MAME organiza o layout em camadas obedecendo a ordem em que elas forem definidas no arquivo de layout, então a composição da sua tela emulada e o gráfico pode começar com a ilustração no fundo e a tela emulada em cima desta arte:
<element ref="Italiano">
<bounds x="0" y="0" width="3296" height="4093" />
</element>
<screen index="0">
<bounds x="817" y="575" width="1660" height="2213" />
</screen>
Ou ao contrário, com a arte gráfica por cima da tela:
<screen index="0">
<bounds x="817" y="575" width="1660" height="2213" />
</screen>
<element ref="Italiano">
<bounds x="0" y="0" width="3296" height="4093" />
</element>
Os motivos de se escolher um ou outro depende do efeito final que você queira dar na tela. A arte que estamos utilizando tem uma área preta na região da tela fazendo com que ambos se misturem quase que criando uma composição de um fundo infinito, porém caso eu queira dar um efeito de tela recortada como uma tela CRT eu usaria a segunda opção para aproveitar o recorte do design.
Na Galaxian não dá para perceber este recorte da tela pois tanto a arte quanto a tela são pretas, porém com outros sistemas é possível por exemplo, colocar uma moldura em volta da tela com efeitos de sombra, como mostra este exemplo:
Neste caso nós definimos a tela primeiro, a arte e por último a moldura (screen_bezel) da tela já com alguns efeitos de sombra e transparência:
<screen index="0">
<bounds x="292" y="43" width="1340" height="997" />
</screen>
<element ref="Artwork_1">
<bounds x="0" y="0" width="1920" height="1080" />
</element>
<element ref="screen_bezel">
<bounds x="272" y="0" width="1376" height="1080" />
</element>
Repare que os efeitos da sombra e da transparência da moldura aparecem perfeitamente sobre a tela emulada. Neste caso específico a tela foi um pouco esticada horizontalmente para cobrir as barras pretas que aparecem nos cantos da tela.
Posicionando os botões na tela¶
Para posicionar qualquer outra imagem na tela como botões, controles ou
o que quer que seja, será preciso estar com a arte que será usada no
MAME já aberta no Gimp, pegue a imagem de um botão por exemplo e
arraste para a tela do Gimp, ele deverá aparecer como uma camada,
posicione-o na região desejada e se for o caso redimensione-o usando a
ferramenta de redimensionamento Shift + S, para o botão
escolhi o tamanho de 190
px. Quando concluir selecione a camada
do botão com o botão direito do mouse e escolha Alfa para a
seleção, em seguida pressione R ou escolha Ferramenta
de seleção retangular e clique na imagem do botão, na barra à esquerda
já deve aparecer as informações do tamanho e a posição na tela que seria
x 1105
e y 3314
com o tamanho de 190
x 190
.
Com todos os botões posicionados e com os valores em mãos, temos então a
seguinte configuração para o botão esquerda
, direita
e
disparo
:
<element ref="esquerda">
<bounds x="867" y="3313" width="190" height="190" />
</element>
<element ref="direita">
<bounds x="1105" y="3313" width="190" height="190" />
</element>
<element ref="disparo">
<bounds x="1819" y="3313" width="190" height="190" />
</element>
Outra maneira de se posicionar os botões na tela é utilizar os valores centralizados, no modo descrito anteriormente a referência utilizada como coordenadas é o limite da imagem do lado esquerdo ou x e o limite do topo da imagem ou y. Os valores centralizados utilizam exatamente a posição do ponteiro do mouse na tela e por isso tais coordenadas são definidas como xc e yc como mostra a figura abaixo:
Posicione o ponteiro do mouse bem em cima onde as linhas se cruzam para ver as coordenadas na parte de baixo da tela do Gimp, fica mais fácil fazer um zoom com 300% ou mais, assim os valores podem ser encontrados de forma mais precisa, para isso, posicione o ponteiro no ponto desejado, mantenha pressionado Ctrl e movimente a roda do mouse para cima para aplicar o zoom na região do ponteiro do mouse:
<element ref="esquerda">
<bounds xc="962" yc="3407" width="190" height="190" />
</element>
<element ref="direita">
<bounds xc="1201" yc="3407" width="190" height="190" />
</element>
<element ref="disparo">
<bounds xc="1913" yc="3407" width="190" height="190" />
</element>
Conectando os botões e ativando as suas funções lógicas¶
A referência "ref" esquerda
, direita
e disparo
são os nomes
dos conjuntos das imagens definidos lá no começo, durante o inicio da
emulação o MAME identifica o defstate
(condição/estado
inicial) inicial, como o seu valor é 0
(zero), a primeira imagem que
aparece será aquela que estiver definida como state=0
. Quando
acionarmos o botão e a sua condição mudar para state=1
o MAME
carregará a imagem definida como state=1
, a mecânica para todo este
processo é bem simples:
<element name="disparo" defstate="0">
<image file="vermelho0.png" state="0" />
<image file="vermelho1.png" state="1" />
</element>
Para conectar os botões e para dar as suas funções lógicas é necessário
encontrar os valores para inputtag
e inputmask
onde inputtag
é o nome da porta usada pelo sistema para os controles e botões do
jogador 1, 2, etc. Já inputmask
é o valor usado pelo sistema para
definir os valores hexadecimais dos comandos, botões, etc.
Para encontrar estes valores, inicie o sistema Galaxian:
mame galaxian
Pressione Tab e vá em Atribuições da entrada (este sistema). Selecione P1 Left, no teclado clique na tecla Del para excluir o valor e usando o seu controle, joystick ou teclado, clique no direcional para o lado esquerdo, faça o mesmo para P1 Right e P1 Button 1, quando terminar, pressione Tab seguido de Esq para encerrar a emulação.
Será criado dentro do diretório cfg um arquivo chamado
galaxian.cfg
, abra-o num editor de texto e veja que para cada
configuração feita para os controles e para o botão há um valor
específico para eles, aqui um exemplo usando um controle de
Playstation 2:
<input>
<port tag=":IN0" type="P1_JOYSTICK_LEFT" mask="4" defvalue="0">
<newseq type="standard">
JOYCODE_1_XAXIS_LEFT_SWITCH
</newseq>
</port>
<port tag=":IN0" type="P1_JOYSTICK_RIGHT" mask="8" defvalue="0">
<newseq type="standard">
JOYCODE_1_XAXIS_RIGHT_SWITCH
</newseq>
</port>
<port tag=":IN0" type="P1_BUTTON1" mask="16" defvalue="0">
<newseq type="standard">
JOYCODE_1_BUTTON3
</newseq>
</port>
</input>
Para o botão de disparo P1_BUTTON1 por exemplo, temos o
valor da porta tag=":IN0"
que usaremos em inputtag
e o valor
mask="16"
que usaremos em inputmask
, assim a nossa configuração
fica assim:
<element ref="esquerda" inputtag="IN0" inputmask="4">
<bounds x="867" y="3313" width="190" height="190" />
</element>
<element ref="direita" inputtag="IN0" inputmask="8">
<bounds x="1105" y="3313" width="190" height="190" />
</element>
<element ref="disparo" inputtag="IN0" inputmask="16">
<bounds x="1819" y="3313" width="190" height="190" />
</element>
Na primeira definimos que vamos associar a imagem esquerda na
entrada IN0
e que seu código (máscara) para este botão é 4 e
assim sucessivamente, com isso nós conectamos e damos funções para as
imagens na parte lógica do sistema fazendo com que o MAME passe a
interpretá-las de forma animada na tela quando o botão for pressionado
no seu joystick ou seja acionado na tela quando for clicado pelo mouse.
Observe que não é preciso copiar os dois pontos iniciais existentes
em tag=":IN0"
, copie apenas o seu valor IN0
. Cada sistema possuí
a sua configuração específica, no caso dos sistemas Neo Geo por exemplo
a tag aparece como tag=":edge:joy:JOY1"
, apenas ignore os dois
pontos iniciais e copie todo o resto, ou seja, a nossa inputtag
para os sistemas Neo Geo ficaria inputtag="edge:joy:JOY1"
.
Alertas e retornos externos¶
O sistema Galaxian possui uma porta que sinaliza através de um sinal luminoso quando 1 crédito é inserido, o sinal da lâmpada passa a piscar indicando que o jogador deve clicar nele para iniciar a partida para o jogador 1 e o mesmo ocorre quando dois créditos são inseridos indicando o inicio da partida para o jogador 1 ou para o jogador 2. A versão do sistema Galaxian Italiana não possui tais luzes porém vamos adicioná-las mesmo assim para fins didáticos.
Usando o Gimp, posicione os botões para o jogador 1 e para o jogador 2,
escolhemos o tamanho de 200
x 200
px e então chegamos na
seguinte configuração:
<element ref="J1" inputtag="IN1" inputmask="1">
<bounds xc="2670" yc="3408" width="200" height="200" />
</element>
<element ref="J2" inputtag="IN1" inputmask="2">
<bounds xc="2790" yc="3753" width="200" height="200" />
</element>
Execute o MAME no terminal ou prompt de comando com a opção abaixo:
mame -output console galaxian
Insira 2 créditos na sistema, repare no terminal que é possível ver os
valores lamp0
e lamp1
se alternando entre 0
(desligado) e
1
(ligado), são estes sinais que usaremos para fazer piscar uma
imagem na tela simulando uma lâmpada e indicando os créditos para os
respectivos jogadores.
É possível montar este layout de duas maneiras, posicionando a nossa
lâmpada uma a uma ou utilizando cont
em conjunto com os
Parâmetros. Imagine o seguinte cenário, você está
montando um layout onde precisa colocar lamp0 até lamp100 na tela, é
possível fazer isso de forma manual ou usar parâmetros para facilitar
escrevendo poucas linhas e deixando que o MAME lide com o resto.
Dada a simplicidade do sistema Galaxian não é preciso usar qualquer parâmetro porém usaremos mesmo assim para que fique fácil a compreensão de como isso funciona.
São duas lâmpadas lamp0
e lamp1
então contamos até 2:
<repeat count="2">
No parâmetro nós definimos que a variável i
inicie uma contagem, que
comece a partir do 0
(zero) e termine em 1
.
<param name="i" start="0" increment="1" />
A nossa "lâmpada" será um anel azul que vai aparecer e sumir na tela
simulando um pisca-pisca. Definimos os parâmetros para a posição deste
primeiro anel azul (pisca) no eixo x (horizontal), x é o nome da
nossa variável e esta pode ter qualquer nome, escolhemos x para
facilitar a identificação do eixo horizontal, start
define a posição
inicial do anel que fica bem em cima da posição do botão J1
(Jogador 1) como descrito na configuração alguns parágrafos atrás, não
precisamos fazer nenhum deslocamento no eixo x, portanto o valor de
increment
é zero.
<param name="x" start="2790" increment="0" />
Fazemos o mesmo para o eixo y (vertical), para que o segundo anel azul
possa ficar alinhado exatamente onde queremos realizado o cálculo
abaixo.
O cálculo serve para descobrir quantos pixels a mais eu preciso para
deslocar o anel azul da primeira coordenada no eixo y a partir da
posição do J1 (3408
) até a segunda coordenada vertical do eixo y
(3753
) do J2. A conta foi organizada para que o minuendo tenha o
maior valor que o subtraendo para que se obtenha um número inteiro
positivo:
increment = J2 y - J1 y
increment = 3753 - 3408
increment = 345
Definimos uma variável y para facilitar a identificação do eixo y
(vertical), nós começamos na coordenada 3408
e para que o anel azul
fique em cima do botão J2 precisamos deslocá-lo 345
pixels.
<param name="y" start="3408" increment="345" />
Aqui é onde toda a mágica acontece, usaremos as variáveis que definimos
para que elas sejam substituídas automaticamente pelos seus respectivos
valores, aqui o valor da variável ~i~
será automaticamente
substituído por 0
e 1
, então lamp~i~
se transforma em
lamp0
e lamp1
respectivamente, repare que utilizamos apenas 1
linha para isso, se fosse o caso para 100 lamps seria a mesma coisa:
<element name="lamp~i~" ref="pisca">
Para ficar fácil a compreensão do resultado, a linha acima se traduziria desta forma para o MAME:
<element name="lamp0" ref="pisca">
<element name="lamp1" ref="pisca">
Pisca está definido com os parâmetros:
<element name="pisca" defstate="0">
<image file="pisca1.png" state="1" />
</element>
O valor de lamp0
sempre retorna 0
(zero) quando estiver
desligado e 1
(um) quando estiver ligado, o mesmo acontece com
lamp1
, como não é preciso definir nada para quando lamp0
e
lamp1
estiverem desligados basta definir apenas a condição de
ligado, portanto defstate="0"
estabelece a sua condição inicial é
0
ou não faça nada, o anel azul só vai aparecer caso a sua condição
mude para 1 através do state="1"
.
As variáveis ~x~
e ~y~
são substituídas pelos seus respectivos
valores que foram definidos anteriormente fazendo com que a imagem do
anel azul seja posicionada corretamente no eixo x (horizontal) e no
eixo y (vertical), assim como definimos que o seu tamanho será pouca
coisa maior que o desenho do botão para que ele cubra um pouco a mais
dos limites do botão, assim o valor seria de 250
pixels de
largura por 250
pixels de altura.
...
<bounds xc="~x~" yc="~y~" width="250" height="250" />
</element>
O atributo abaixo encerra as definições para repeat
</repeat>
Finalizamos o nosso layout com os dois últimos itens:
</view>
</mamelayout>
Este é o nosso arquivo completo:
<?xml version="1.0"?>
<!-- Layout file created by: Wellington Terumi Uemura
Bezel design by: Etienne MacGyver
Round Buttons design by: u/cd4053
License: CC by 4.0
https://mamedoc.readthedocs.io/pt/latest/techspecs/layout_files.html
-->
<mamelayout version="2">
<element name="Italiano">
<image file="arte.png" />
</element>
<element name="J1" defstate="0">
<image file="btn0.png" state="0" />
<image file="btn1.png" state="1" />
</element>
<element name="J2" defstate="0">
<image file="btn0.png" state="0" />
<image file="btn1.png" state="1" />
</element>
<element name="disparo" defstate="0">
<image file="vermelho0.png" state="0" />
<image file="vermelho1.png" state="1" />
</element>
<element name="esquerdo" defstate="0">
<image file="vermelho0.png" state="0" />
<image file="vermelho1.png" state="1" />
</element>
<element name="direito" defstate="0">
<image file="vermelho0.png" state="0" />
<image file="vermelho1.png" state="1" />
</element>
<element name="pisca" defstate="0">
<image file="pisca1.png" state="1" />
</element>
<view name="Galaxian Italiano" showpointers="no">
<element ref="Italiano">
<bounds x="0" y="0" width="3296" height="4093" />
</element>
<screen index="0">
<bounds x="817" y="575" width="1660" height="2213" />
</screen>
<element ref="J1" inputtag="IN1" inputmask="1">
<bounds xc="2790" yc="3408" width="200" height="200" />
</element>
<element ref="J2" inputtag="IN1" inputmask="2">
<bounds xc="2790" yc="3753" width="200" height="200" />
</element>
<element ref="esquerdo" inputtag="IN0" inputmask="4">
<bounds xc="962" yc="3407" width="190" height="190" />
</element>
<element ref="direito" inputtag="IN0" inputmask="8">
<bounds xc="1201" yc="3407" width="190" height="190" />
</element>
<element ref="disparo" inputtag="IN0" inputmask="16">
<bounds xc="1913" yc="3407" width="190" height="190" />
</element>
<repeat count="2">
<param name="i" start="0" increment="1" />
<param name="x" start="2790" increment="0" />
<param name="y" start="3408" increment="345" />
<element name="lamp~i~" ref="pisca">
<bounds xc="~x~" yc="~y~" width="250" height="250" />
</element>
</repeat>
</view>
</mamelayout>
Colocando para funcionar e adicionando efeitos na tela¶
Para ver a nossa criação funcionar, dentro do diretório do MAME existe
o diretório artwork, dentro deste diretório crie um novo chamado
galaxian, dentro deste diretório coloque o arquivo acima com o nome
default.lay
e todos os arquivos *.png. Rode o mame com o
comando:
mame -window galaxian
A tela deve aparecer com a arte e todos os botões em seus respectivos lugares, experimente clicar nos botões com o mouse e verá que eles reagem ao seu clique, ao adicionar 2 créditos o nosso anel azul deve aparecer simulando o piscar de uma luz.
O MAME aceita trabalhar com o diretório como está ou então comprimindo-o em formato .zip ou .7zip. Ao usar a arte como um diretório permite que você altere alguma coisa enquanto realiza testes, caso altere o arquivo ou alguma imagem, é possível recarregar todos eles sem precisar encerrar o MAME pressionando Shift + F3.
Caso queira aplicar shaders na tela para dar um efeito de uma tela CRT
como mostra a foto, baixe este arquivo, descompacte-o dentro
de um diretório chamado glsl. Vá até o diretório ini e crie o
arquivo galaxian.ini
e dentro dele adicione estas opções:
video opengl
gl_glsl 1
gl_glsl_filter 1
# No Windows use
glsl_shader_mame0 glsl\osd\CRT-geom
# No Linux use
glsl_shader_mame0 glsl/osd/CRT-geom
Ao rodar o galaxian novamente a tela terá uma aparência de um CRT,
caso queira manter a aparência porém eliminando um pouco a curvatura da
tela, edite o arquivo CRT-geom.vsh
e use estas configurações:
// START of parameters
// gamma of simulated CRT
CRTgamma = 2.2;
// gamma of display monitor (typically 2.2 is correct)
monitorgamma = 2.2;
// overscan (e.g. 1.02 for 2% overscan)
overscan = vec2(1.01,1.01);
// aspect ratio
aspect = vec2(1.0, 0.75);
// lengths are measured in units of (approximately) the width of the monitor
// simulated distance from viewer to monitor
d = 2.0;
// radius of curvature
R = 10.0;
// tilt angle in radians
// (behavior might be a bit wrong if both components are nonzero)
const vec2 angle = vec2(0.01,0.01);
// size of curved corners
cornersize = 0.03;
// border smoothness parameter
// decrease if borders are too aliased
cornersmooth = 1000.0;
// END of parameters
Altere o valor do overscan
de vec2(1.01,1.01)
para
vec2(1.00,1.00)
para que a tela apareça completa na tela.
Adicionando diagonais nos controles com 8 direções usando o inputraw¶
Alguns sistemas possuem controles simples como a própria Galaxian
tem apenas 2 direções, esquerda e direita, outras como
Pacman possuem 4 direções, cima, baixo, esquerda,
direita. Nestes casos a criação de um layout animado para o controle
por exemplo é bem simples, basta adicionar uma imagem para cada posição
associando-a com os seus respectivos inputtag
e inputmask
para
que seja possível ver a sua animação na tela quando cada uma das
posições forem acionadas, no entanto a coisa muda um pouco quando se
trata de um sistema com um joystick com 8 direções (ou mais).
Nestes casos, quando qualquer uma das diagonais é acionada o MAME sobrepõem outras imagens uma em cima da outra, caso a diagonal superior direita seja acionada no controle, o MAME irá exibir a imagem para a diagonal superior direita em cima da imagem cima e em cima da imagem direita ou seja, em vez de aparecer apenas uma imagem para a diagonal escolhida irão aparecer outras duas. Na internet diferentes pessoas encontraram diferentes soluções para o problema, algumas criaram imagens na diagonal com um tamanho suficiente para cobrir as duas outras imagens dentre diversas outras soluções.
Outra maneira de lidar com o problema é utilizar o inputraw
, com
esta opção o MAME faz a leitura dos dados diretamente da entrada do
controle, vamos aproveitar o layout que acabamos de criar para o sistema
Galaxian e ver como isso ficaria modificando a lógica para os
controles e adicionando 3 imagens para simular um joystick:
<element name="controle" defstate="0xc">
<image file="esquerda.png" state="0x1" />
<image file="direita.png" state="0x2" />
<image file="centro.png" state="0x0" />
</element>
Assim como foi feito anteriormente, usamos o Gimp para posicionar o joystick na região que queremos que ele apareça, com as coordenadas em mãos nós montamos toda a lógica dele:
<element ref="controle" inputtag="IN0" inputmask="0xc" inputraw="yes">
<bounds x="1010" y="3260" width="250" height="250" />
</element>
Agora em vez de 2 botões vermelhos nós temos um joystick preto na posição que escolhemos e se move conforme nós mexemos nos controles. Fica claro que a quantidade de linhas necessárias para que esta animação aconteça se reduz deixando o layout mais enxuto e simplificado.
Note que ao clicar com o mouse em cima do controle ele faz um movimento indesejado, para evitar essa anomalia adicionamos uma camada de "nada" com o mesmo tamanho da imagem do controle e exatamente na mesma posição:
<element name="nada" defstate="0">
<text string=" " />
</element>
E aplicamos isso antes do controle com inputmask="0x00"
pois o
MAME interpreta estes elementos na ordem que eles aparecem no layout,
assim temos a camada "nada" sendo construída primeiro seguida do
controle logo abaixo desta camada, bloqueando qualquer interação do
mouse:
<element ref="nada" blend="add" inputtag="IN0" inputmask="0x00" inputraw="yes">
<bounds x="1010" y="3260" width="250" height="250" />
</element>
<element ref="controle" inputtag="IN0" inputmask="0xc" inputraw="yes">
<bounds x="1010" y="3260" width="250" height="250" />
</element>
A partir da versão 0.265 do MAME prefira fazer desta maneira, exemplo:
<element name="cobertura" defstate="0">
<rect><color alpha="0" /></rect>
</element>
...
...
<element ref="cobertura" clickthrough="no">
<bounds x="1010" y="3260" width="250" height="250" />
</element>
<element ref="controle" inputtag="IN0" inputmask="0xc" inputraw="yes">
<bounds x="1010" y="3260" width="250" height="250" />
</element>
Ambos funcionam mas o atributo clickthrough
deixam as coisas mais
fáceis. Para obter mais detalhes consulte
Itens que podem ser clicados.
Ao executar o sistema novamente o controle não mais responde aos cliques do mouse.
Como estamos trabalhando com dados vindos diretamente dos controles, é
necessário encontrar os valores para defstate
, inputmask
e para
state
.
O defstate
e o inputmask
utilizam o mesmo valor, este valor
precisa ser calculado, para isso acessamos o
código-fonte do driver Galaxian, bem na linha L3011
de
cara já temos o nosso inputtag
com o valor IN0
que utilizamos
acima, observe quem nem sempre o valor do inputtag
está disponível
assim tão fácil, o driver Playstaion (psx) por exemplo utiliza
port1:digital_pad:PSXPAD0
, já o sistema Neo Geo usa
edge:joy:JOY1
e assim por diante. Para casos como estes é preferível
neste caso é preferível utilizar a maneira descrita no capítulo
Conectando os botões e ativando as suas funções lógicas.
A Galaxian tem apenas duas direções, esquerda e direita, preste atenção
ao valor 0x04
para IPT_JOYSTICK_LEFT
e 0x08
para
IPT_JOYSTICK_RIGHT
. Abra a calculadora do seu celular ou do seu
sistema operacional em modo programador ou qualquer função que consiga
somar valores em hexadecimais e some os valores 4
+ 8
para
obter c
ou 0xc
, este é o valor do nosso defstate
e
inputmask
específicos para este sistema, dependendo do sistema
este valor pode mudar, é possível ter uma ideia vendo a tabela logo
abaixo.
Já os valores para state
vão de 0x0
até 0xf
nos sistemas
mais comuns, porém em outros sistemas mais complexas como os sistemas
de corrida de carro e de moto que usam um volante ou guidão, os valores
vão muito além disso. O assunto foge ao escopo do que estamos
apresentando aqui então recomendo o belo trabalho do
Mr.Do e de
todas as pessoas que colaboram com aquele projeto, baixe o arquivo
hangon.ZIP e veja um exemplo de até onde o nível de complexidade
pode chegar.
Até o presente momento não há uma ferramenta que ajude a obter os
valores para state
e portanto eles devem ser encontrados de forma
manual o que dá um certo trabalho, para facilitar, veja o exemplo da
tabela abaixo com os valores para alguns sistemas:
Driver |
defstate |
|||||||||
cps1 |
|
|
|
|
|
|
|
|
|
|
cps2 |
|
|
|
|
|
|
|
|
|
|
cps3 |
|
|
|
|
|
|
|
|
|
|
ddragon |
|
|
|
|
|
|
|
|
|
|
dkong |
|
|
|
|
|
|
||||
galaxian |
|
|
|
|
||||||
kinst |
|
|
|
|
|
|
|
|
|
|
m72/m92 |
|
|
|
|
|
|
|
|
|
|
midtunit |
|
|
|
|
|
|
|
|
|
|
midwunit |
|
|
|
|
|
|
|
|
|
|
model1 |
|
|
|
|
|
|
|
|
|
|
nemesis |
|
|
|
|
|
|
|
|
|
|
neogeo |
|
|
|
|
|
|
|
|
|
|
pacman |
|
|
|
|
|
|
||||
psx |
|
|
|
|
|
|
|
|
|
|
segas16a |
|
|
|
|
|
|
|
|
|
|
segas18 |
|
|
|
|
|
|
|
|
|
|
seibuspi |
|
|
|
|
|
|
|
|
|
|
simpsons |
|
|
|
|
|
|
|
|
|
|
stv |
|
|
|
|
|
|
|
|
|
|
tmnt |
|
|
|
|
|
|
|
|
|
|
toaplan2 |
|
|
|
|
|
|
|
|
|
|
Aqui vai a dica de ouro para quem for criar os seus próprios layouts
com o método inputraw
, se ao carregar um layout a imagem usada para
o centro (posição neutra do controle) não aparecer ou se durante o
acionamento do direcional uma das posições sumir da tela, significa que:
O valor do
inputtag
está errado.O valor do
state
está errado.O valor do
defstate
não foi calculado corretamente.
Todos estes valores variam de sistema para sistema, em determinados
sistemas por exemplo, ainda que o valor inputtag
esteja incorreto a
imagem do controle relacionada ao ponto neutro ou centro poderá
aparecer ou não e em outras vezes apesar da imagem do ponto neutro
aparecer, não haverá qualquer animação do controle na tela. Para a
maioria dos sistemas avaliadas o state
para o ponto neutro funciona
com o valor 0xf
, contudo há alguns sistemas onde o valor precisa
ser alterado para 0x0
para que funcione ou algum outro valor onde
este irá depender do tipo do sistema emulado. Quando você movimenta os
direcionais e nota que o controle fica piscando na tela, este é um bom
indicativo que você está no caminho certo pois indica que a combinação
inputtag
e inputmask
está correta e apenas os valores para o
state
estão errados.
Nos casos onde a animação aparece invertida na tela como por exemplo se
ao clicar para cima o controle na tela aparece para baixo e assim por
diante, basta inverter os valores dos seus respectivos state
e
clicar em Shift + F3 para recarregar o arquivo e ver como
ficou.
Há casos onde nenhum dos valores da tabela mostrada acima vai funcionar
com o sistema que você estiver desenvolvendo o layout, mesmo que os
valores estejam corretos para inputtag
, defstate
e
inputmask
. Nestes casos inicie a sua configuração com todos os
valores state
zerados:
<element name="controle" defstate="0xf">
<image file="cima.png" state="0x0" />
<image file="baixo.png" state="0x0" />
<image file="esquerda.png" state="0x0" />
<image file="direita.png" state="0x0" />
<image file="centro.png" state="0xf" />
</element>
Comece alterando o valor state
para o comando cima, comece com
0x1
e vá subindo, clique Shift + F3 a cada alteração
para ver se alteração surtiu algum efeito. É muito comum encontrar um
determinado valor onde você aciona o controle para cima e ocorre uma
animação para qualquer outra direção, supondo que a animação que
apareceu foi para baixo, altere o valor do state
relacionado com
baixo.png e continue incrementando o valor até chegar em 0xf
,
depois comece novamente a partir de 0x1
. Observe que alguns valores
já testados e que não retornaram nenhuma animação agora passam a
responder, continue definindo os valores encontrados para as suas
respectivas posições até encontrar todos os valores.
Sistemas com dois ou mais controles¶
Em sistemas com dois ou mais controles como a Neo Geo por exemplo, nós fazemos assim:
<element name="controle" defstate="0xf">
<image file="baixodireito.png" state="0x5" />
<image file="cimadireita.png" state="0x6" />
<image file="direita.png" state="0x7" />
<image file="baixoesquerdo.png" state="0x9" />
<image file="cimaesquerda.png" state="0xa" />
<image file="esquerda.png" state="0xb" />
<image file="baixo.png" state="0xd" />
<image file="cima.png" state="0xe" />
<image file="centro.png" state="0xf" />
</element>
</element>
<element ref="cobertura" clickthrough="no">
<bounds x="158" y="794" width="150" height="150" />
</element>
<element ref="controle" inputtag="edge:joy:JOY1" inputmask="0xf" inputraw="yes">
<bounds x="158" y="794" width="150" height="150" />
</element>
<element ref="cobertura" clickthrough="no">
<bounds x="665" y="794" width="150" height="150" />
</element>
<element ref="controle" inputtag="edge:joy:JOY2" inputmask="0xf" inputraw="yes">
<bounds x="665" y="794" width="150" height="150" />
</element>
Ambos os controles utilizam os mesmos valores para state
e
defstate
, o que muda é o valor do inputtag
onde
edge:joy:JOY1
define que este é o controle do jogador 1 e que
edge:joy:JOY2
é o controle do jogador 2.
Contudo, há sistemas na CPS2 que apresentam o mesmo valor IN0
no inputtag
para ambos os jogadores, nestes casos devemos recorrer
novamente ao código-fonte do MAME para este driver, observando a linha
#1501 nós temos os 4 valores para a porta do 2º jogador
PORT_PLAYER(2)
, temos o valor 100
para a direita, 200
para a
esquerda, 400
para baixo e 800
para cima. Assim foi feito
anteriormente, some todos os valores hexadecimais com uma calculadora
compatível para obter o valor f00
ou seja 100
+ 200
+
400
+ 800
= f00
.
Este é o valor que deve ser usado no inputmask
da configuração do
controle do 2º jogador como mostra o exemplo abaixo que também funcionam
para todas as outros sistemas existentes no driver CPS2 do MAME:
<element name="controle" defstate="0xf">
<image file="cimaesquerda.png" state="0x5" />
<image file="cimadireita.png" state="0x6" />
<image file="cima.png" state="0x7" />
<image file="baixoesquerda.png" state="0x9" />
<image file="baixodireita.png" state="0xa" />
<image file="baixo.png" state="0xb" />
<image file="esquerda.png" state="0xd" />
<image file="direita.png" state="0xe" />
<image file="centro.png" state="0xf" />
</element>
<element ref="cobertura" clickthrough="no">
<bounds x="158" y="794" width="150" height="150" />
</element>
<element ref="controle_J1" inputtag="IN0" inputmask="0xf" inputraw="1">
<bounds x="158" y="794" width="150" height="150" />
</element>
<element ref="cobertura" clickthrough="no">
<bounds x="665" y="794" width="150" height="150" />
</element>
<element ref="controle_J2" inputtag="IN0" inputmask="0xf00" inputraw="1">
<bounds x="665" y="794" width="150" height="150" />
</element>
Utilize o layout modelo disponibilizado em Ferramentas necessárias para
realizar testes em diferentes sistemas e obter uma visualização simples
e rápida dos controles, depois de baixar e extrair o arquivo, copie-o
para o diretório artwork e o renomeie com o nome do sistema que será
testada, para o exemplo usado na foto o nome do diretório é
ssriders. Rodando o comando mame -window ssriders
irá
aparecer uma tela com um design branco genérico, será possível escolher
um modelo para 2 ou 4 jogadores através do menu
Opções do Vídeo (TAB --> Opções
do vídeo), faça as alterações necessárias no arquivo de layout e para
visualizar na tela pressione Shift + F3.
Abaixo as teclas predefinidas do MAME para os 4 jogadores, para mais informações consulte Predefinições para o jogador 1.
1º Jogador |
Direcional cima |
Direcional baixo |
Direcional esquerda |
Direcional direita |
2º Jogador |
R |
F |
G |
D |
3º Jogador |
I |
K |
J |
L |
4º Jogador |
Teclado num. 8 |
Teclado num. 2 |
Teclado num. 4 |
Teclado num. 6 |
Um único arquivo para diferentes visualizações¶
Com os layouts também é possível montar diferentes visualizações não ficando limitado a apenas uma, ou seja, é possível dar a opção ao jogador para escolher aquilo que ele possa querer ver na tela como por exemplo, ter diferentes versões de visualização para o sistema Galaxian. No arquivo da versão completa (disponível em Ferramentas necessárias) temos o exemplo da Galaxian Italiana, Americana e a Japonesa com diferentes configurações para os botões para o 1º e 2º jogador, algumas utilizam botões simples e outras usam os botões iluminados da NAMCO.
As configurações para as diferentes visualizações devem ficar entre os
elementos view
. Assim como é feito com um layout simples, primeiro
todas as imagens que serão utilizadas são definidas, depois cada
visualização fica separada através dos elementos view
e cada um com
seus respectivos nomes:
<view name="Nome da visualização 1" showpointers="no">
...
...
</view>
<view name="Nome da visualização 2" showpointers="no">
...
...
</view>
<view name="Nome da visualização 3" showpointers="no">
...
...
</view>
Estas visuzalizações ficam acessíveis através do menu Opções do Vídeo (TAB --> Opções do vídeo).
Desativando objetos na tela¶
Assim como foi descrito em Coleções isso é
possível organizando os objetos da tela dentro dos elementos
collections
, quando a visualização tiver esta opção, o jogador
poderá desligar qualquer objeto na tela desde que tenha sido organizado
desta maneira pelo autor da arte. Use visible
yes
ou no
(também funciona com 1
ou 0
) dentro do elemento collection
caso queira que ele já inicie ligado ou desligado. Baixe este
arquivo para ver como funciona na prática com o sistema
mspacman.
<collection name="Botão para o inicio da partida para o 1º Jogador" visible="no">
<element ref="btn-namco">
<bounds x="2605" y="3217" width="350" height="291" />
</element>
<element name="lamp0" ref="J1JP" inputtag="IN1" inputmask="1">
<bounds x="2666" y="3299" width="230" height="126" />
</element>
</collection>
Criando as posições de um controle arcade¶
Para animar os movimentos de um joystick é preciso antes definir os limites de onde ele vai operar para que quando tudo estiver pronto a sua animação se comporte como a movimentação de um controle arcade tradicional. Usando as referências deste limite, nós podemos definir a posição de cada movimento do controle.
No Inkscape, crie um novo arquivo, pressione Ctrl + Shift + D ou File --> Document properties, em Page defina Display units como PX.
Crie um círculo preto, defina o seu tamanho em W: e H: com
7,700
px, este será aquela peça preta que fica no fim do eixo do joystick, ele vai ajudar a dar a ilusão de movimento.Vá em Layers ou Shift + Ctrl + L, altere o nome deste primeiro layer para centro base clique no + para adicionar um layer chamado centro e um outro chamado quadrado, este layer quadrado deve ficar abaixo do layer centro base.
Clique no círculo preto para selecioná-lo, pressione Ctrl + D para duplicá-lo, escolha uma cor vermelha qualquer, defina o seu tamanho para
9,700
px. Clique com o botão direito em cima deste círculo vermelho e selecione a opção Move to Layer... e mova este círculo para o layer centro.
Exportando arquivos SVG¶
Para definir o limite eu multiplico 1.8
vezes o valor do
diâmetro círculo do controle para determinar o tamanho total desse
quadrado. Este valor funciona bem ao fazer um joystick genérico,
dependendo do design pode ser que um valor multiplicado por 1.5
ou
por 2
funcione melhor dependendo do modelo/tipo do joystick que você
está querendo imitar, ajuste e faça testes para ver qual se adapta
melhor ao seu design e veja se a animação fica boa na tela.
Fazendo os cálculos,
9,700
*1,8
=17,46
, vá em File --> Document Properties (Shift + Ctrl + D), em Custom size defina as unidades para PX e defina também o Width/Height para17,46
, pressione Enter para aplicar e feche a janela.Vá em Object --> Align and distribute, clique no círculo preto, na aba Align -> Relative To: escolha Page, clique no botão logo abaixo (Center on vertical axis), depois (Center on horizontal axis) para centralizar o objeto. Clique no círculo vermelho e faça o mesmo com ele. * Pressione o 3 no teclado numérico (lado direito do teclado) para que o círculo apareça no centro da tela.
Clique 2x no sinal de - no teclado numérico ou pressione Ctrl e gire a roda do mouse para traz para recuar um pouco.
Crie um quadrado com
17,46
x17,46 px
.Pressione Ctrl + Shift + F para abrir o Fill and Stroke, ainda com o quadrado selecionado, em Fill clique no X para remover o preenchimento, mantenha o Shift pressionado e com o mouse, clique na cor preta na parte debaixo da tela.
Novamente na aba Fill and Stroke, defina Width: para
0,005
, isso cria uma borda bem fina no nosso quadrado, fina o suficiente para que ele possa ser selecionado, isso será útil mais adiante.Selecione o quadrado e mova ele para o layer quadrado.
Na aba Align and distribute, clique nos mesmos botões para centralizar o quadrado na página.
Até aqui nós temos uma imagem como esta:
Em caso de problema, basta baixar a imagem acima (caso esteja lendo o documento no formato HTML) e abra ele no Inkscape para seguir os próximos passos e recrie todos layers quadrado, centro base e centro, depois mova círculo vermelho para o layer centro (clique no ícone do olho para escondê-lo) e faça o mesmo com o círculo preto e mova ele para o layer centro base, faça o mesmo com o quadrado, sempre deixando o layer do quadrado abaixo de todos os outros layers.
Pressione Shift + Ctrl + A para abrir a aba Align and distribute, em Relative To: escolha Last Selected para alinhar com último objeto que for selecionado.
Na aba Layers crie as camadas cima, baixo, esquerda e direita, clique no olho para escondê-las.
Deixe o círculo do centro visível, clique nele e pressione Ctrl + D para duplicar.
Com o Shift pressionado clique no quadrado na borda da página, solte o Shift e na aba de alinhamento escolha o ícone com a seta para a direita (Align right sides), isso alinha o nosso círculo para os limites da página do lado direito.
Clique em qualquer ponto vazio da tela para remover a seleção, clique no círculo vermelho e com o botão direito do mouse, escolha a opção para movê-lo para a camada direita, o círculo do lado direito deve desaparecer logo em seguida.
Clique no círculo vermelho ao centro, pressione Ctrl + D para duplicar, mantenha Shift pressionado, clique no quadrado, solte o Shift, na aba de alinhamento clique no ícone a seta para a esquerda (Align left sides) para alinhar o círculo para o lado esquerdo, repita o passo anterior e faça o mesmo para as posições cima e baixo.
Temos agora as quatro primeiras posições:
Na questão das diagonais do joystick, seguindo a mesma lógica, elas
seriam alinhadas bem nos cantos do quadrado assim com foi feito com as
outras posições, porém a animação fica estranha, parece que o eixo
oculto do controle permite que o círculo vá muito além do que deveria.
Na imagem abaixo temos duas opções, na esquerda as diagonais foram
alinhadas no limite do quadrado e depois recuados cerca de 5 pontos
(clicando no + e - do X: e
Y: trazendo o círculo mais para dentro).
Já na imagem da direita, foi criado um círculo nas mesmas dimensões
do quadrado (17,46
x 17,46
px) e as diagonais foram
alinhadas no limite do círculo:
A decisão de usar um ou outro vai depender de como ele se comporta no seu design e na tela, o alinhamento com base num círculo pode parece "curto" em determinados designs e assim por diante, por isso que não há uma regra, cabe ao artista/designer testar e avaliar o que fica melhor. No nosso exemplo usamos o alinhamento com base no círculo.
Caso use a imagem acima, é preciso criar todos os layers novamente e mover cada posição do círculo para cada um dos seus respectivos layers, isso é importante para isolar o elemento que será exportado depois.
Depois que todas as posições já estiverem nas suas respectivas camadas (layers), começamos o processo de exportação:
Clique em qualquer um dos layers com o botão direito do mouse e escolha a opção Hide all layers para ocultar tudo.
Ative (torne visível) apenas os layers, centro base e centro.
Vá em File --> Save a Copy ou Shift + Ctrl + Alt + S, escolha o caminho MAME\Artwork\sfa3, ou a mesma pasta onde está o
default.lay
que criamos, no nome insira centro.svg e na parte debaixo desta janela troque a opção Inkscape SVG por Optimized SVG, clique em Save e clique em OK na próxima janela que aparecer.Esconda o layer centro e deixe apanas o layer direita visível e repita o procedimento anterior, faça o mesmo para cada uma das posições.
As imagens das diagonais têm os seguintes nomes:
Diagonal superior direita
cimad
Diagonal superior esquerda
cimae
Diagonal inferior direita
baixod
Diagonal inferior esquerda
baixoe
Exportando arquivos PNG¶
Existe um pequeno truque que precisa ser feito para exportar imagens
como estas que precisam estar dentro de algo invisível, pois diferente
da exportação em SVG, não basta simplesmente deixar visível o que se
deseja exportar, também é preciso exportar o quadrado junto e aqui
está o "pulo do gato", definimos o Stroke (como descrito
no capítulo anterior) com 0,005
ou até mesmo 0,001
para que este
quadrado possa ser selecionável no Inkscape e que quando exportado, a
borda não apareça quando for carregado pelo MAME.
No final, a imagem exportada ficará dentro de uma área invisível onde ocorrerá a animação dos movimentos. É preciso fazer assim pois caso contrário, em vez de uma animação de movimentos nós teremos o controle sendo jogado de um lado para o outro.
Usando o mesmo arquivo que você usou acima:
Pressione Shift + Ctrl + E para abrir a aba Export PNG image.
Deixe apenas o layout centro e quadrado visível.
Clique no círculo, mantenha o Shift pressionado e clique no quadrado fazendo com que o círculo e o quadrado fiquem selecionados.
Em Export PNG image, na aba Export area o Selection vai estar selecionado.
Em Image size defina ambos para
174
.Em Filename defina o caminho completo e o nome do arquivo (
centro.png
), no caso, exportaremos essa imagem para dentro da pasta artwork\sfa3.Faça o mesmo com as outras posições e respeitando o mesmo nome usado anteriormente, porém, com a extensão .png,
centro.png
,cima.png
,baixo.png
, etc.
Trabalhando com grupos¶
Trabalhar com grupos é como trabalhar com camadas em programas como o Adobe Illustrator ou o Inkscape, você desenha, posiciona os seus objetos onde eles precisam ficar e no final você agrupa tudo para que o seu design composto de diferentes pedaços fiquem fixos e você possa movimentá-los livremente.
O sistema de layout do MAME trabalha da mesma maneira e possui também a mesma vantagem, primeiro você define o tamanho da sua área de trabalho, define o tamanho e a posição de todos os seus elementos dentro deste grupo e faz o que quiser com ele depois.
Sem organizar o seu layout dentro de um grupo, você precisaria posicionar todos os seus elementos num ponto da tela e mais tarde caso queira movê-los para um outro ponto, você teria que literalmente ajustar cada elemento na nova posição da tela.
Exportaremos os gráficos tanto em .PNG (gráficos matriciais/pixels) quanto em .SVG (gráficos vetoriais), o suporte o MAME ao formato .SVG é simples, ele consegue carregar arquivos simples porém ele não aceita designs complexos e cheio de efeitos. Por isso dependendo da complexidade do design é preferível exportar tais gráficos no formato .PNG.
Os gráficos vetoriais por serem coordenadas em texto e cálculos matemáticos para criar as formas geométricas, tamanho, cor, etc; tais gráficos não se deterioram quando eles são expandidos, eles mantém as suas bordas lisas e o seu arquivo final é extremamente leve, já os gráficos matriciais quando a aproximação ultrapassa o seu tamanho original ele se deteriora, os pixels que compõem essa imagem começa a ficar mais evidente, nas bordas começa a aparecer um serrilhamento, etc. Para compensar tal limitação é preciso exportar um gráfico cada vez maior e com isso tais imagens ficam cada vez mais pesadas. Quando for possível, use SVG, caso o seu design seja mais complexo, use PNG.
Colocaremos um pequeno joystick animado sobreposto num canto da tela,
porém antes precisamos saber de duas coisas, o tamanho da tela e o
tamanho do objeto que será colocado nela. Para saber o tamanho da tela,
inicie um sistema qualquer como a sfa3 (mame sfa3
), pressione
Tab --> Informação do sistema, na parte de Vídeo
vai estar listado 384
x 224
(valor em pixels).
Abra o Inkscape, vá em File --> Document properties... ou Shift + Ctrl + D, em Page --> Units alterne de mm para px (pixels), em Width defina
384,0
e em Height defina224,0
, feche a janela.
Abaixo temos uma imagem para ilustrar o tamanho, a área em azul é a área de segurança de quem usa shaders para deixar a tela curvada e com scanlines.
Com a nossa área de trabalho definida, precisamos definir o tamanho e a posição do nosso joystick na tela, como é um controle animado, ele não precisa ser muito grande pois a região já é pequena. No nosso exemplo, este controle só vai nos servir como uma referência para o que está acontecendo na tela, assim, ele não precisa de muitos detalhes. Na imagem abaixo tem uma ilustração da posição que foi escolhida e do tamanho.
Para o joystick da nossa tela, foi criado um objeto com 60,017
px de largura por 30,760
px de altura com uma cor qualquer e
com a mesma cor na porém porém mais escura.
Dentro dessa área posicionaremos o joystick, os botões, a arte de fundo (se for o caso), etc. Depois obtemos as coordenadas X (horizontal), Y (vertical), W (largura) e H (altura), como o tamanho da tela e dos elementos são pequenos, todos os elementos e as suas bordas tem um tamanho um pouco exagerado e com bordas bem grossas, porém pois quando tudo isso for projetado na tela o resultado final ficará dentro do desejado.
Com todos os elementos posicionados nós temos:
Joystick, X =
2,397
, Y =6,378
, W =18
, H =18
Botão 1, X =
21,201
, Y =4,337
, W =9,6
, H =9,6
Botão 2, X =
33,641
, Y =4,292
, W =9,6
, H =9,6
Botão 3, X =
46,126
, Y =4,292
, W =9,6
, H =9,6
Botão 4, X =
21,201
, Y =16,820
, W =9,6
, H =9,6
Botão 5, X =
33,641
, Y =16,820
, W =9,6
, H =9,6
Botão 6, X =
46,126
, Y =16,820
, W =9,6
, H =9,6
Usando os botões gerados pelo MAME e os arquivos SVG¶
Na pasta do MAME vá em Artwork, crie uma pasta chamada sfa3,
dentro dela crie um novo arquivo texto chamado default.lay
, tenha
certeza de estar criando um arquivo default.lay
e não
default.lay.txt
, no Windows é preciso alterar as configurações da
pasta para ver a extensão dos arquivos.
Abra o arquivo num editor de texto, iniciamos o nosso layout pelo cabeçalho e colocando todas as informações que achamos relevantes:
<?xml version="1.0"?>
<mamelayout version="2">
<!--
Joystick for CPS2
Created by: Wellington Terumi Uemura
License: CC by 4.0
https://mamedoc.readthedocs.io/
Date: October 02, 2021
Download: https://www.mediafire.com/file/lk1veez63081xy2/sfa3-v1.zip
-->
Aqui nós definimos os tipos (círculos) e as cores de todos os objetos
que usaremos neste design, vamos criar os círculos coloridos (azul,
amarelo, vermelho) e um círculo branco ao centro para indicar um botão
na sua condição normal, quando o jogador pressionar um destes botões
definidos em hit azul
, hit amarelo
e hit vermelho
a cor vai
se alterar para um tom mais escuro das cores do botão:
<!-- elementos gerados -->
<!-- Aqui definimos a cor azul e o mesmo é feito com as outras cores -->
<element name="azul">
<disk><color red="0" green="0.549" blue="0.831" /></disk>
</element>
<!-- definimos um tom mais escuro da cor do botão, o mesmo é feito com as outras cores -->
<element name="hit azul" defstate="1">
<disk state="1"><color red="0" green="0.369" blue="0.6" /></disk>
</element>
<element name="amarelo">
<disk><color red="1" green="0.933" blue="0" /></disk>
</element>
<element name="hit amarelo" defstate="1">
<disk state="1"><color red="0.639" green="0.596" blue="0" /></disk>
</element>
<element name="vermelho">
<disk><color red="1" green="0.007" blue="0.105" /></disk>
</element>
<element name="hit vermelho" defstate="1">
<disk state="1"><color red="0.6" green="0" blue="0.058" /></disk>
</element>
<!-- Este é o elemento branco que fica em cima dos círculos coloridos -->
<element name="branco">
<disk><color red="0.925" green="0.925" blue="0.925" /></disk>
</element>
<!-- Definimos um elemento vazio que será usado para evitar os cliques do mouse -->
<element name="tampa" defstate="0">
<text string=" " />
</element>
<!-- Agrupamos as imagens que formam o movimento do controle num único elemento -->
<element name="comandos" defstate="0xf">
<image file="cima.svg" state="0x7" />
<image file="cimad.svg" state="0x6" />
<image file="direita.svg" state="0xe" />
<image file="baixod.svg" state="0xa" />
<image file="baixo.svg" state="0xb" />
<image file="baixoe.svg" state="0x9" />
<image file="esquerda.svg" state="0xd" />
<image file="cimae.svg" state="0x5" />
<image file="centro.svg" state="0xf" />
</element>
<!-- Carregamos a imagem da base do nosso controle -->
<element name="base">
<image file="base.svg" />
</element>
Organizamos tudo isso dentro de um grupo e usamos o count
para
duplicar os nossos botões no eixo vertical. Aqui nós também conectamos
os respectivos botões ao driver, no caso, a lógica vai funcionar com
qualquer sistema dentro do driver CPS2. Alguns valores aparecem
diferente daqueles que nós obtemos, é um ajuste fino necessário para
alinhar os objetos na tela:
<!-- Nome do grupo, este é o joystick do jogador 1 -->
<group name="Joystick J1">
<!-- Definimos o tamanho do grupo -->
<bounds x="0" y="0" width="60.017" height="30.760" />
<!-- Usamos a nossa base do joystick e definimos o seu tamanho -->
<element ref="base">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<!-- São 2 botões no eixo Y (vertical) iniciando na posição 4.292.-->
<!-- Incremente 12.528 para definir a posição do segundo botão. -->
<!-- Para encontrar o valor 12.528, use o Inkscape para posicionar o botão onde deseja -->
<!-- pegue o valor de Y do segundo botão (16,82) e subtraia com o valor -->
<!-- de Y do primeiro botão (4,292), ou seja, 16,82 - 4,292 = 12,528. -->
<!-- No eixo X inicie em 21.202 e use o valor calculado para definir Y. -->
<!-- Crie o nosso elemento com 9.7px x 9.7px -->
<!-- O mesmo é feito com os outros botões -->
<repeat count="2">
<param name="y" start="4.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="azul"><bounds x="21.202" y="~y~" width="9.7" height="9.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="5.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="branco"><bounds x="22.202" y="~y~" width="7.7" height="7.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="4.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="amarelo"><bounds x="33.641" y="~y~" width="9.7" height="9.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="5.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="branco"><bounds x="34.641" y="~y~" width="7.7" height="7.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="4.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="vermelho"><bounds x="46.129" y="~y~" width="9.7" height="9.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="5.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="branco"><bounds x="47.129" y="~y~" width="7.7" height="7.7" />
</element>
</repeat>
<!-- Aqui a tampa cobre toda a área do joystick para evitar o click do mouse -->
<element ref="tampa" blend="add" inputtag="IN0" inputmask="0x00" inputraw="yes">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<!-- Invocamos os nossos comandos, definimos a sua posição e tamanho na base do joystick -->
<element ref="comandos" inputtag="IN0" inputmask="0xf" inputraw="yes">
<bounds x="2.397" y="6.378" width="18" height="18" />
</element>
<!-- Aqui conectamos toda a lógica dos botões -->
<element ref="hit azul" inputtag="IN0" inputmask="0x10">
<bounds x="22.202" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit azul" inputtag="IN1" inputmask="0x1">
<bounds x="22.202" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="hit amarelo" inputtag="IN0" inputmask="0x20">
<bounds x="34.641" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit amarelo" inputtag="IN1" inputmask="0x2">
<bounds x="34.641" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="hit vermelho" inputtag="IN0" inputmask="0x40">
<bounds x="47.129" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit vermelho" inputtag="IN1" inputmask="0x4">
<bounds x="47.129" y="17.820" width="7.7" height="7.7" />
</element>
</group>
Como já explicado em A Posição e a proporção de tela, as telas CRT da
época tinham uma proporção de 4:3, assim sendo, precisamos ajudar a
resolução de tela. Use a planilha disponibilizada em
Ferramentas necessárias, no campo Digite a altura insira
224
, note que o cálculo retorna um DAR com 298,666666666667
,
isso não é bom pois causa
aliasing, então, precisamos
de um valor inteiro.
Substitua 224
por 225
, note que os cálculos retornam valores
inteiros, é este valor que usaremos para definir o tamanho da nossa
tela 300
x 225
, procure sempre utilizar valores pares no eixo
horizontal. Logo abaixo nós definimos a posição do nosso joystick e o
seu respectivo tamanho.
<!-- Definimos o nome que vai aparecer na seleção -->
<view name="Controle" showpointers="no">
<!-- Não precisamos da posição, só do tamanho da tela -->
<screen index="0">
<bounds x="0" y="0" width="300" height="225" />
</screen>
<!-- Aqui definimos um nome que vai aparecer nas opções para -->
<!-- tirar o joystick da tela, será possível ligar e desligar esta opção. -->
<!-- Já fica predefinido que quando a emulação começar, o joystick já apareça na tela. -->
<collection name="Joystick do jogador 1" visible="yes">
<!-- Invocamos o nome do grupo, definimos a posição e o seu tamanho -->
<group ref="Joystick J1">
<bounds x="1.5" y="169.239" width="60.017" height="30.760" />
</group>
</collection>
<!-- Fim do layout -->
</view>
</mamelayout>
Salve o arquivo, execute o comando no terminal/prompt de comando
mame sfa3
e veja que todos os botões e o controle reagem ao que você
fizer no seu controle. Pressione
Tab --> Opções do vídeo --> Tela #0, veja
que há a opção Joystick do jogador 1 que pode ser ligada
ou desligada.
Para adicionar o controle para o jogador 2, basta agora adicionar a lista abaixo depois do grupo Joystick J1 e adaptar a lógica para o controle 2.
<!-- Nome do grupo, este é o joystick do jogador 2 -->
<group name="Joystick J2">
<bounds x="0" y="0" width="60.017" height="30.760" />
<element ref="base">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<repeat count="2">
<param name="y" start="4.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="azul"><bounds x="21.202" y="~y~" width="9.7" height="9.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="5.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="branco"><bounds x="22.202" y="~y~" width="7.7" height="7.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="4.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="amarelo"><bounds x="33.641" y="~y~" width="9.7" height="9.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="5.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="branco"><bounds x="34.641" y="~y~" width="7.7" height="7.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="4.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="vermelho"><bounds x="46.129" y="~y~" width="9.7" height="9.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="5.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="branco"><bounds x="47.129" y="~y~" width="7.7" height="7.7" />
</element>
</repeat>
<element ref="tampa" blend="add" inputtag="IN0" inputmask="0x00" inputraw="yes">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<element ref="comandos" inputtag="IN0" inputmask="0xf00" inputraw="yes">
<bounds x="2.397" y="6.378" width="18" height="18" />
</element>
<!-- Aqui conectamos toda a lógica dos botões para o J2-->
<element ref="hit azul" inputtag="IN0" inputmask="0x1000">
<bounds x="22.202" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit azul" inputtag="IN1" inputmask="0x10">
<bounds x="22.202" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="hit amarelo" inputtag="IN0" inputmask="0x2000">
<bounds x="34.641" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit amarelo" inputtag="IN1" inputmask="0x20">
<bounds x="34.641" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="hit vermelho" inputtag="IN0" inputmask="0x4000">
<bounds x="47.129" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit vermelho" inputtag="IN2" inputmask="0x4000">
<bounds x="47.129" y="17.820" width="7.7" height="7.7" />
</element>
</group>
Para que funcione, nós adicionamos a opção "Controle" e adicionamos a opção para o jogador 2 logo depois das configurações do jogador 1.
<collection name="Joystick do jogador 1" visible="yes">
<group ref="Joystick J1" blend="add">
<bounds x="1.5" y="169.239" width="60.017" height="30.760" />
</group>
</collection>
<collection name="Joystick do jogador 2" visible="no">
<group ref="Joystick J2" blend="add">
<bounds x="238.4" y="169.239" width="60.017" height="30.760" />
</group>
</collection>
Salve e rode o sfa3 novamente, parece que nada mudou, porém ao
entrar nas opções do vídeo novamente, verá que está disponível a opção
para o Joystick do jogador 2 e ele está
desligado. Quem define isso é a opção visible="no"
,
porém para deixar ele sempre ativo sem ter que mexer no arquivo layout,
basta deixar o segundo joystick visível e encerrar a emulação, isso
salva as definições este sistema e na próxima vez o joystick vai
aparecer na tela.
Simplificando o layout¶
Uma outra maneira de se lidar com grupos e evitar a duplicidade de configuração como foi feito para o controle do jogador 1 e 2, seria organizar a parte visual primeiro num grupo que poderia ser usado por ambos e deixar a parte lógica dos botões de fora, invocando a lógica individual de cada controle separadamente.
A parte visual do nosso controle ficaria assim:
<group name="Joystick">
<bounds x="0" y="0" width="60.017" height="30.760" />
<element ref="base">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<repeat count="2">
<param name="y" start="4.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="azul"><bounds x="21.202" y="~y~" width="9.7" height="9.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="5.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="branco"><bounds x="22.202" y="~y~" width="7.7" height="7.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="4.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="amarelo"><bounds x="33.641" y="~y~" width="9.7" height="9.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="5.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="branco"><bounds x="34.641" y="~y~" width="7.7" height="7.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="4.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="vermelho"><bounds x="46.129" y="~y~" width="9.7" height="9.7" />
</element>
</repeat>
<repeat count="2">
<param name="y" start="5.292" increment="12.528" />
<param name="i" start="0" increment="1" />
<element ref="branco"><bounds x="47.129" y="~y~" width="7.7" height="7.7" />
</element>
</repeat>
<element ref="tampa" blend="add" inputtag="IN0" inputmask="0x00" inputraw="yes">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
</group>
Aqui a parte lógica para o jogador 1.
<group name="Logic J1">
<bounds x="0" y="0" width="60.017" height="30.760" />
<element ref="hit azul" inputtag="IN0" inputmask="0x10">
<bounds x="22.202" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit azul" inputtag="IN1" inputmask="0x1">
<bounds x="22.202" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="hit amarelo" inputtag="IN0" inputmask="0x20">
<bounds x="34.641" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit amarelo" inputtag="IN1" inputmask="0x2">
<bounds x="34.641" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="hit vermelho" inputtag="IN0" inputmask="0x40">
<bounds x="47.129" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit vermelho" inputtag="IN1" inputmask="0x4">
<bounds x="47.129" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="comandos" inputtag="IN0" inputmask="0xf" inputraw="yes">
<bounds x="2.397" y="6.378" width="18" height="18" />
</element>
</group>
Aqui a parte lógica para o jogador 2.
<group name="Logic J2">
<bounds x="0" y="0" width="60.017" height="30.760" />
<element ref="hit azul" inputtag="IN0" inputmask="0x1000">
<bounds x="22.202" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit azul" inputtag="IN1" inputmask="0x10">
<bounds x="22.202" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="hit amarelo" inputtag="IN0" inputmask="0x2000">
<bounds x="34.641" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit amarelo" inputtag="IN1" inputmask="0x20">
<bounds x="34.641" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="hit vermelho" inputtag="IN0" inputmask="0x4000">
<bounds x="47.129" y="5.292" width="7.7" height="7.7" />
</element>
<element ref="hit vermelho" inputtag="IN2" inputmask="0x4000">
<bounds x="47.129" y="17.820" width="7.7" height="7.7" />
</element>
<element ref="comandos" inputtag="IN0" inputmask="0xf00" inputraw="yes">
<bounds x="2.397" y="6.378" width="18" height="18" />
</element>
</group>
Aqui alteramos a parte final do layout, invocamos primeiro a parte visual e depois a parte lógica.
<view name="Controle" showpointers="no">
<screen index="0">
<bounds x="0" y="0" width="300" height="225" />
</screen>
<collection name="Joystick do jogador 1" visible="yes">
<group ref="Joystick" blend="add">
<bounds x="1.5" y="169.239" width="60.017" height="30.760" />
</group>
<group ref="Logic J1">
<bounds x="1.5" y="169.239" width="60.017" height="30.760" />
</group>
</collection>
<collection name="Joystick do jogador 2" visible="no">
<group ref="Joystick" blend="add">
<bounds x="238.4" y="169.239" width="60.017" height="30.760" />
</group>
<group ref="Logic J2">
<bounds x="238.4" y="169.239" width="60.017" height="30.760" />
</group>
</collection>
Ao rodar o sistema novamente verá que toda a parte funcional e das opções continuam os mesmos, porém, o nosso arquivo layout está mais organizado.
Usando imagens PNG para o mesmo tipo de layout¶
Ao trabalhar com imagens PNG é preciso exportar CADA elemento do nosso joystick, além de todas as posições do controle é preciso exportar todo o resto, além disso, é preciso levar em consideração o seu tamanho final pois como foi explicado antes, a imagem matricial se deteriora caso ela seja expandida para dimensões maiores do que ela foi projetada.
Neste exemplo usaremos design abaixo como referência:
Abra a imagem no Inkscape, pressione Ctrl + A para selecionar todos os elementos, em seguida faça Shift + Ctrl + G para separar todos os elementos do grupo.
Faça Shift + Ctrl + E ou File --> Export PNG Image para abrir a aba de exportação.
Clique nos números e pressione Del para excluí-los.
Clique no círculo azul com o centro escuro, na aba Export PNG Image em Image size defina ambos Width/Height como
100
(poderia ser maior mas serve para o nosso exemplo).Em Filename exporte para a pasta artwork/sfa3 com o nome
azul1.png
.Clique no outro botão azul com o centro claro e faça o mesmo, porém exporte com o nome
azul0.png
.Faça o mesmo com os outros botões, no final você deverá ter os arquivos
azul0.png
,azul0.png
,amarelo0.png
,amarelo1.png
,vermelho0.png
evermelho1.png
.Clique na base do nosso joystick, exporte ele com o tamanho
575
x294
e defina o seu nome comobase.png
.
Com todas as imagens em mãos podemos começar a montar o nosso layout:
<?xml version="1.0"?>
<mamelayout version="2">
<!--
Joystick for CPS2
Created by: Wellington Terumi Uemura
License: CC by 4.0
https://mamedoc.readthedocs.io/
Date: October 02, 2021
Download: https://www.mediafire.com/file/3dd1dtn114ochi2/sfa3-v3.zip
-->
Em seguida definimos as condicionais dos botões e a nossa tampa:
<element name="painel">
<image file="base.png" />
</element>
<element name="azul" defstate="0">
<image file="azul0.png" state="0" />
<image file="azul1.png" state="1" />
</element>
<element name="amarelo" defstate="0">
<image file="amarelo0.png" state="0" />
<image file="amarelo1.png" state="1" />
</element>
<element name="vermelho" defstate="0">
<image file="vermelho0.png" state="0" />
<image file="vermelho1.png" state="1" />
</element>
<element name="tampa" defstate="0">
<text string=" " />
</element>
Aqui nós definimos todas as posições do joystick:
<element name="comandos" defstate="0xf">
<image file="cima.png" state="0x7" />
<image file="cimad.png" state="0x6" />
<image file="direita.png" state="0xe" />
<image file="baixod.png" state="0xa" />
<image file="baixo.png" state="0xb" />
<image file="baixoe.png" state="0x9" />
<image file="esquerda.png" state="0xd" />
<image file="cimae.png" state="0x5" />
<image file="centro.png" state="0xf"/>
</element>
Neste primeiro momento, usamos a área que nós definimos para o controle
60,017
x 30,760
px e dentro dessa área posicionamos todas
as partes do controle.
<group name="controle J1">
<bounds x="0" y="0" width="60.017" height="30.760" />
<element ref="painel">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<!-- aqui cobrimos toda a área do joystick para não interagir com o mouse -->
<element ref="tampa" blend="add" inputtag="IN0" inputmask="0x00" inputraw="yes">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<!-- Botão 1 -->
<element ref="azul" inputtag="IN0" inputmask="16">
<bounds x="21.201" y="4.292" width="9.6" height="9.6" />
</element>
<!-- Botão 4 -->
<element ref="azul" inputtag="IN1" inputmask="1">
<bounds x="21.201" y="16.820" width="9.6" height="9.6" />
</element>
<!-- Botão 2 -->
<element ref="amarelo" inputtag="IN0" inputmask="32">
<bounds x="33.641" y="4.292" width="9.6" height="9.6" />
</element>
<!-- Botão 5 -->
<element ref="amarelo" inputtag="IN1" inputmask="2">
<bounds x="33.641" y="16.820" width="9.6" height="9.6" />
</element>
<!-- Botão 3 -->
<element ref="vermelho" inputtag="IN0" inputmask="64">
<bounds x="46.126" y="4.292" width="9.6" height="9.6" />
</element>
<!-- Botão 6 -->
<element ref="vermelho" inputtag="IN1" inputmask="4">
<bounds x="46.126" y="16.820" width="9.6" height="9.6" />
</element>
<element ref="comandos" inputtag="IN0" inputmask="0xf" inputraw="yes">
<bounds x="2.397" y="6.378" width="18" height="18" />
</element>
</group>
<!-- Aqui os parâmetros para o joystick para o segundo jogador -->
<group name="controle J2">
<bounds x="0" y="0" width="60.017" height="30.760" />
<element ref="painel">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<element ref="tampa" blend="add" inputtag="IN0" inputmask="0x00" inputraw="yes">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<!-- Botão 1 -->
<element ref="azul" inputtag="IN0" inputmask="0x1000">
<bounds x="21.201" y="4.337" width="9.6" height="9.6" />
</element>
<!-- Botão 4 -->
<element ref="azul" inputtag="IN1" inputmask="0x10">
<bounds x="21.156" y="16.820" width="9.6" height="9.6" />
</element>
<!-- Botão 2 -->
<element ref="amarelo" inputtag="IN0" inputmask="0x2000">
<bounds x="33.641" y="4.292" width="9.6" height="9.6" />
</element>
<!-- Botão 5 -->
<element ref="amarelo" inputtag="IN1" inputmask="0x20">
<bounds x="33.641" y="16.820" width="9.6" height="9.6" />
</element>
<!-- Botão 3 -->
<element ref="vermelho" inputtag="IN0" inputmask="0x4000">
<bounds x="46.126" y="4.292" width="9.6" height="9.6" />
</element>
<!-- Botão 6 -->
<element ref="vermelho" inputtag="IN2" inputmask="0x4000">
<bounds x="46.126" y="16.820" width="9.6" height="9.6" />
</element>
<element ref="comandos" inputtag="IN0" inputmask="0xf00" inputraw="yes">
<bounds x="2.397" y="6.378" width="18" height="18" />
</element>
</group>
O mesmo caso anterior, definimos o nome e uma resolução 4:3, no
caso, 300
x 225
, posicionamos os controles na tela e encerramos
o layout.
<view name="Controles" showpointers="no">
<screen index="0">
<bounds x="0" y="0" width="300" height="225" />
</screen>
<collection name="Joystick do jogador 1" visible="yes">
<group ref="controle J1">
<bounds x="1.5" y="169.239" width="60.017" height="30.760" />
</group>
</collection>
<collection name="Joystick do jogador 2" visible="no">
<group ref="controle J2">
<bounds x="238.4" y="169.239" width="60.017" height="30.760" />
</group>
</collection>
</view>
</mamelayout>
Ao testar o layout com as imagens o controle funciona normalmente, contudo, repare que nos botões amarelos e nos botões vermelhos, parece que há uma borda preta neles.
Parece que outra pessoa já identificou este problema e ele ainda
não foi corrigido,
a solução parece que é criar um quadrado do mesmo tamanho do círculo (ou
outra cor qualquer menos preto), alinhe com a página, preencha com o
mesmo amarelo do círculo ffee00ff
, baixe o alpha para zero
ficando ffee0000
e exporte a imagem .png novamente.
Simplificando o layout novamente¶
Usando o conhecimento aprendido em Usando os botões gerados pelo MAME e os arquivos SVG, podemos fazer o mesmo com imagens PNG, dando a mesma funcionalidade porém deixando o layout mais enxuto, separamos o que é visual do lógico e depois montamos.
<?xml version="1.0"?>
<mamelayout version="2">
<!--
Joystick for CPS2
Created by: Wellington Terumi Uemura
License: CC by 4.0
https://mamedoc.readthedocs.io/
Date: October 02, 2021
Download: https://www.mediafire.com/file/8w6fk6ogo0z17q2/sfa3-v4.zip
-->
<element name="painel">
<image file="base.png" />
</element>
<element name="azul" defstate="0">
<image file="azul0.png" state="0" />
<image file="azul1.png" state="1" />
</element>
<element name="amarelo" defstate="0">
<image file="amarelo0.png" state="0" />
<image file="amarelo1.png" state="1" />
</element>
<element name="vermelho" defstate="0">
<image file="vermelho0.png" state="0" />
<image file="vermelho1.png" state="1" />
</element>
<element name="tampa" defstate="0">
<text string=" " />
</element>
<element name="comandos" defstate="0xf">
<image file="cima.png" state="0x7" />
<image file="cimad.png" state="0x6" />
<image file="direita.png" state="0xe" />
<image file="baixod.png" state="0xa" />
<image file="baixo.png" state="0xb" />
<image file="baixoe.png" state="0x9" />
<image file="esquerda.png" state="0xd" />
<image file="cimae.png" state="0x5" />
<image file="centro.png" state="0xf"/>
</element>
<group name="Joystick">
<bounds x="0" y="0" width="60.017" height="30.760" />
<element ref="painel">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
<element ref="tampa" blend="add" inputtag="IN0" inputmask="0x00" inputraw="yes">
<bounds x="0" y="0" width="60.017" height="30.760" />
</element>
</group>
<group name="Logic J1">
<bounds x="0" y="0" width="60.017" height="30.760" />
<element ref="azul" inputtag="IN0" inputmask="0x10">
<bounds x="21.202" y="4.292" width="9.7" height="9.7" />
</element>
<element ref="azul" inputtag="IN1" inputmask="0x1">
<bounds x="21.202" y="16.820" width="9.7" height="9.7" />
</element>
<element ref="amarelo" inputtag="IN0" inputmask="0x20">
<bounds x="33.641" y="4.292" width="9.7" height="9.7" />
</element>
<element ref="amarelo" inputtag="IN1" inputmask="0x2">
<bounds x="33.641" y="16.820" width="9.7" height="9.7" />
</element>
<element ref="vermelho" inputtag="IN0" inputmask="0x40">
<bounds x="46.129" y="4.292" width="9.7" height="9.7" />
</element>
<element ref="vermelho" inputtag="IN1" inputmask="0x4">
<bounds x="46.129" y="16.820" width="9.7" height="9.7" />
</element>
<element ref="comandos" inputtag="IN0" inputmask="0xf" inputraw="yes">
<bounds x="2.397" y="6.378" width="18" height="18" />
</element>
</group>
<group name="Logic J2">
<bounds x="0" y="0" width="60.017" height="30.760" />
<element ref="azul" inputtag="IN0" inputmask="0x1000">
<bounds x="21.202" y="4.292" width="9.7" height="9.7" />
</element>
<element ref="azul" inputtag="IN1" inputmask="0x10">
<bounds x="21.202" y="16.820" width="9.7" height="9.7" />
</element>
<element ref="amarelo" inputtag="IN0" inputmask="0x2000">
<bounds x="33.641" y="4.292" width="9.7" height="9.7" />
</element>
<element ref="amarelo" inputtag="IN1" inputmask="0x20">
<bounds x="33.641" y="16.820" width="9.7" height="9.7" />
</element>
<element ref="vermelho" inputtag="IN0" inputmask="0x4000">
<bounds x="46.129" y="4.292" width="9.7" height="9.7" />
</element>
<element ref="vermelho" inputtag="IN2" inputmask="0x4000">
<bounds x="46.129" y="16.820" width="9.7" height="9.7" />
</element>
<element ref="comandos" inputtag="IN0" inputmask="0xf00" inputraw="yes">
<bounds x="2.397" y="6.378" width="18" height="18" />
</element>
</group>
<view name="Controle" showpointers="no">
<screen index="0">
<bounds x="0" y="0" width="300" height="225" />
</screen>
<collection name="Joystick do jogador 1" visible="yes">
<group ref="Joystick" blend="add">
<bounds x="1.5" y="169.239" width="60.017" height="30.760" />
</group>
<group ref="Logic J1">
<bounds x="1.5" y="169.239" width="60.017" height="30.760" />
</group>
</collection>
<collection name="Joystick do jogador 2" visible="no">
<group ref="Joystick" blend="add">
<bounds x="238.4" y="169.239" width="60.017" height="30.760" />
</group>
<group ref="Logic J2">
<bounds x="238.4" y="169.239" width="60.017" height="30.760" />
</group>
</collection>
</view>
</mamelayout>
Dividindo a tela com um controle ou outro tipo de informação¶
Usar uma tela dividida com um controle pode ser útil para jogos do tipo Mahjong por exemplo, o uso do teclado do PC ou até mesmo um joystick comum em tais jogos pode ser confuso, deixando o controle visível e com a possibilidade de poder clicar nos botões na tela, assim você se preocupa com o jogo e não em lidar com o mapeamento do teclado.
Neste exemplo usaremos o sistema Lovely Pop Mahjong JangJang Shimasho (Japan), ela usa o driver ssv.cpp, precisamos acessá-lo para identificar todas as suas entradas e posteriormente, mapear todos os botões.
Para os controles usaremos uma variação do controle do capítulo Teclas predefinidas para Mahjong e Hanafuda:
Crie a pasta artwork\janjans1
, dentro dela copie o arquivo .svg
acima, crie também o arquivo default.lay
e abra-o num editor de
texto.
Iniciamos o layout com o básico:
<?xml version="1.0"?>
<mamelayout version="2">
<!--
Joystick Mahjang for janjans1
Created by: Wellington Terumi Uemura
License: CC by 4.0
https://mamedoc.readthedocs.io/
Date: October 25, 2021
Download: https://www.mediafire.com/file/4m6di0smdiqj8wf/janjans1.zip
-->
<!-- Aqui carregamos o nosso controle -->
<element name="base">
<image file="mahjongpanel2.svg" />
</element>
<!--
Aqui deixamos reservado para uso futuro, como iluminar as teclas
depois de serem clicadas por exemplo, porém, distrai demais.
Assim deixamos ele vazio.
-->
<element name="btn" defstate="1">
<text string=" ">
<color red="0.0" green="0.0" blue="0.0" />
<bounds x="0" y="0" width="58.038" height="58.038" />
</text>
</element>
Agora precisamos ver no driver como os botões estão definidos para poder conectá-los na imagem do nosso controle, na data de criação deste texto, essa informação começa na linha 1028:
<!-- Definimos o tamanho da nossa área de trabalho dentro de um grupo -->
<group name="Controle_mah">
<bounds x="0" y="0" width="1136" height="216.63" />
<!-- Informamos o tamanho original da imagem do nosso controle -->
<element ref="base">
<bounds x="0" y="0" width="1136" height="216.63" />
</element>
<!--
Aqui mapeamos todos os botões segundo o driver, abrindo a nossa
imagem no Inkscape e desagrupando todos os elementos
(Shift + Ctrl + G), ao clicar na região amarelo claro do botão
"Start" por exemplo, nós temos as coordenadas e o seu respectivo
tamanho no topo da tela (lembrando que os valores devem estar em
px!).
É assim que obtemos os valores de posição e tamanho que usamos
abaixo.
-->
<!-- PON -->
<element ref="btn" inputtag="KEY0" inputmask="0x4">
<bounds x="442.969" y="159.961" width="58.038" height="58.038" />
</element>
<!-- L -->
<element ref="btn" inputtag="KEY0" inputmask="0x8">
<bounds x="882.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- H -->
<element ref="btn" inputtag="KEY0" inputmask="0x10">
<bounds x="562.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- D -->
<element ref="btn" inputtag="KEY0" inputmask="0x20">
<bounds x="242.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- RON -->
<element ref="btn" inputtag="KEY1" inputmask="0x2">
<bounds x="715.014" y="159.961" width="58.038" height="58.038" />
</element>
<!-- CHI -->
<element ref="btn" inputtag="KEY1" inputmask="0x4">
<bounds x="522.965" y="159.961" width="58.038" height="58.038" />
</element>
<!-- K -->
<element ref="btn" inputtag="KEY1" inputmask="0x8">
<bounds x="802.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- G -->
<element ref="btn" inputtag="KEY1" inputmask="0x10">
<bounds x="482.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- C -->
<element ref="btn" inputtag="KEY1" inputmask="0x20">
<bounds x="162.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- BET -->
<element ref="btn" inputtag="KEY2" inputmask="0x1">
<bounds x="122.981" y="159.961" width="58.038" height="58.038" />
</element>
<!-- REACH -->
<element ref="btn" inputtag="KEY2" inputmask="0x2">
<bounds x="602.965" y="159.961" width="58.038" height="58.038" />
</element>
<!-- N -->
<element ref="btn" inputtag="KEY2" inputmask="0x4">
<bounds x="1075" y="47.961" width="58.038" height="58.038" />
</element>
<!-- J -->
<element ref="btn" inputtag="KEY2" inputmask="0x8">
<bounds x="722.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- F -->
<element ref="btn" inputtag="KEY2" inputmask="0x10">
<bounds x="402.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- B -->
<element ref="btn" inputtag="KEY2" inputmask="0x20">
<bounds x="82.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- START -->
<element ref="btn" inputtag="KEY3" inputmask="0x1">
<bounds x="42.980" y="159.961" width="58.038" height="58.038" />
</element>
<!-- KAN -->
<element ref="btn" inputtag="KEY3" inputmask="0x2">
<bounds x="362.965" y="159.961" width="58.038" height="58.038" />
</element>
<!-- M -->
<element ref="btn" inputtag="KEY3" inputmask="0x4">
<bounds x="962.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- I -->
<element ref="btn" inputtag="KEY3" inputmask="0x8">
<bounds x="642.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- E -->
<element ref="btn" inputtag="KEY3" inputmask="0x10">
<bounds x="322.979" y="47.960" width="58.038" height="58.038" />
</element>
<!-- A -->
<element ref="btn" inputtag="KEY3" inputmask="0x20">
<bounds x="2.979" y="47.960" width="58.038" height="58.038" />
</element>
</group>
Resolvida a questão dos controles, agora é preciso definir como vamos
disponibilizar os objetos na tela. Ao iniciar o sistema com
mame janjans1
e ao clicar em
Tab --> Informação do sistema, o driver informa
que o tamanho da tela tem 336
x 240
.
Inicie o Inkscape, defina o Units e a página em px, defina o tamanho da página como
336
por240
px.Importe para esta página a imagem do controle fornecida acima, durante a importação da imagem deve aparecer uma tela, em DPI for rendered SVG mantenha 96 DPI.
Clique no cadeado entre os valores W: e H: para travar a proporção.
Em W: coloque
336,0
e pressione Enter para aplicar, agora temos um objeto na tela com336
por63,904
.Somando a altura do objeto com a altura da tela
63,904
+240
nós temos uma altura total de303,904
.Pressione Shift + Ctrl + D e na altura da página coloque
303,904
px.Desenhe um quadrado que vai simular a nossa tela com
336
por240
alinhado no Topo da página.Posicione a imagem do controle logo abaixo e alinhe, isso lhe dará uma visão geral daquilo que pretende fazer.
Como o controle fica muito colado com a tela, descemos o controle cerca de um toque do direcional para baixo.
Faça Ctrl + A para selecionar tudo seguido de Ctrl + G para agrupar ambos os objetos, agora temos uma altura final com
305,904
.Faça novamente Shift + Ctrl + D e atualize a altura da página em px, isso é importante para obter as devidas coordenadas para o posicionamento dos elementos na tela.
Se deu tudo certo até aqui, você deve ter algo semelhante ao exemplo abaixo:
Agora faça Ctrl + A seguido de Shift + Ctrl + G para desagrupar o objeto tela do controle, repare que ao clicar no objeto tela e depois no controle, nós obtemos as coordenadas e seus respectivos tamanhos, é com estas informações que usamos para concluir o nosso layout:
<!-- Nome -->
<view name="Controle" showpointers="no">
<!-- Delimitamos a nossa área de exibição -->
<bounds x="0" y="0" width="336" height="305.904" />
<screen index="0">
<!-- O tamanho original da tela -->
<bounds x="0" y="0" width="336" height="240" />
</screen>
<!-- Invocamos o nosso controle -->
<group ref="Controle_mah">
<bounds x="0.4" y="242" width="335" height="63.904" />
</group>
</view>
</mamelayout>
Observe que os valores X, Y e width de Controle_mah
estão um pouco diferentes do que obtivemos com o Inkscape, não é algo
comum, porém, algumas vezes é necessário para fazer um ajuste fino de
alinhamento para que os elementos apareçam em seus devidos lugares na
tela do MAME.
Com tudo pronto, rodamos o sistema novamente com mame janjans1
e
entramos no modo de serviço (Tab --> Chaves DIP -->
Service Mode --> On, depois
Redefine. Clique no botão A na tela uma vez para pular o
teste de cores, clique novamente para entrar no modo de teste e
pressione Tab para fechar o quadro de informação na tela).
Ao clicar nos botões na tela, os botões da emulação deverão se alternar entre ON e OFF indicando que todos os botões estão funcionando. Retorne ao Service Mode, mude a chave para Off e clique em Redefine para reiniciar o sistema.
Exemplos de outros arquivos layout¶
Estes arquivos layout demonstram as várias características do sistema de visualização, todo eles estão embutidos no MAME.
-
Um caso simples da utilização de transparências coloridas para visualizar a separação e o destaque dos elementos numa tela preto e branco.
-
Este sismtema utiliza lâmpadas para os elementos importantes do jogo. Os modos de mesclagem são utilizados para a transparência colorida que é colocada na frente do monitor. Também utiliza
collections
permitindo que partes do layout sejam desativadas seletivamente. -
A tela deste jogo é vista diretamente através de uma transparência colorida em vez de ser refletida a partir de dentro do gabinete. Isso significa que a transparência reflete a luz ambiente assim como afeta a cor da imagem do vídeo.
-
Uma tela e um teclado alfanumérico com vários segmentos. As teclas são clicáveis e fornecem feedback visual quando pressionadas.
-
Constrói uma matriz de caracteres com múltiplas linhas. As repetições são usadas para evitar a repetição das linhas num caractere, dos caracteres numa linha e das linhas numa página. As cores de grupo permitem que um único elemento seja usado para todas as quatro cores da tela.
-
Anima a posição dos elementos para simular um jogo de tiro eletromecânico. Também demonstra o uso eficaz dos componentes para a construção de gráficos complexos.
-
Exibe a posição de um controle deslizante com LEDs.
-
Usa grupos de forma efetiva como uma linguagem de programação para construir a imagem de um protoboard.
-
Faz o uso de scripts com base em eventos para posicionar dinamicamente os elementos e automaticamente desenhar o conteúdo dos elementos.