Os arquivos layout

Introdução

Os arquivos layout [1] são usados para informar ao MAME o que exibir durante a emulação de um sistema e também para organizá-los na tela. O MAME pode renderizar a emulação das telas originais dos sistemas, incluindo imagens, textos, formas e objetos especiais, na saída comum dos dispositivos. Esses elementos podem ser estáticos ou se atualizar de maneira dinâmica refletindo a condição das entradas e das saídas. Os layouts podem ser gerados automaticamente com base na quantidade ou no tipo da tela que será emulada, construídas e conectadas internamente ao binário do MAME ou disponibilizados externamente. Para o MAME, os arquivos layout são interpretados como arquivos XML, mas 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 utilizados com notação decimal ou hexadecimal. Um número decimal inteiro consiste num prefixo opcional # (hash [2]), um caractere opcional +/- (sinal de mais ou de menos) ou uma sequência de números entre 0 e 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 e 9, juntamente com as letras entre A e F. Não há diferenciação entre as letras maiúsculas e minúsculas no índice e nos dígitos dos números hexadecimais.

Os números de ponto flutuante podem ser usados com notação decimal de ponto fixo ou com notação científica. Observe que os prefixos com número inteiro e valores hexadecimais não são aceitos caso um número de ponto flutuante for esperado.

São permitidos como atributos tanto números inteiros quanto números de ponto flutuante. Nesses casos a presença de um prefixo, como # (hash), $ (cifrão) ou 0x (zero xis), faz com que o valor seja interpretado como um número inteiro. Se nenhum prefixo de número inteiro, o ponto decimal ou a letra E (tanto maiúscula quanto minúscula) seja encontrado na introdução de um expoente, o número será interpretado como um número de ponto flutuante.Se nenhum prefixo de número inteiro, ponto decimal ou a letra E for encontrado, o número será interpretado como um número inteiro.

Os números são analisados usando a localidade "C" por questões de portabilidade.

Coordenadas

As coordenadas do layout são representadas internamente como um número binário IEEE754 de 32 bits de ponto flutuante (também conhecido como "precisão simples"). O incremento das coordenadas ocorre nas direções da direita e para baixo. A origem (0,0) não tem um significado específico e valores negativos podem ser utilizados.

O MAME pressupõe que as coordenadas da visualização tenham a mesma proporção de aspecto dos pixels gerados pelo dispositivo (janela ou nativos). Considerando que sejam pixels quadrados e sem rotação, isso significa que a distância é igual nos eixos X e Y, o que corresponde a distâncias iguais na vertical e na horizontal geradas pela renderização.

Todos os elementos, grupos e visualizações possuem seus próprios sistemas de coordenadas. Quando um elemento ou um grupo é referenciado a partir de uma visualização ou de outro grupo, suas coordenadas são dimensionadas conforme a necessidade para que os limites sejam definidos.

Os objetos são posicionados e dimensionados pelo elemento bounds, que define seus limites e fronteiras. A posição horizontal e o seu tamanho podem ser definidos de três maneiras:

  • A borda esquerda e a largura são definidas por atributos x e width.

  • O eixo horizontal centralizado é indicado pela letra c, que significa que a referência usada será o centro do objeto/imagem, e a largura é definida pelos atributos xc e width.

  • As bordas esquerda e direita são definidas por atributos left e right.

  • Da mesma forma, a posição vertical e o seu tamanho podem ser definidos pela borda superior e pela altura, usando atributos y e height.

  • O eixo vertical centralizado e a altura são definidos por atributos yc e height.

  • As bordas superior e inferior são definidas pelos atributos top e bottom.

Os três elementos bounds demonstrados abaixo 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, esses 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, o valor predefinido para o atributo width/height ou right/bottom é 1.0. O MAME considerará um erro se os atributos width ou height tiverem valores negativos, o atributo right tiver um valor menor que left ou o atributo bottom tiver um valor menor que o atributo top.

Cores

As cores são definidas no espaço RGBA. Como o MAME não trabalha com toda a gama de cores, as cores serão interpretadas como sRGB junto com a 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 de 0,0 (desligado) a 1,0 (intensidade plena). Os valores alfa variam de 0,0 (transparência absoluta) a 1,0 (opaco). Os valores dos canais de cores não são previamente multiplicados pelo valor de alpha.

O componente e a cor do item visualizado são definidos por meio dos elementos color. Os atributos relevantes são vermelho (red), verde (green), azul (blue) e alfa (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á seu valor predefinido como 1.0 (intensidade absoluta ou opaca). Será considerado um erro se os valores do canal estiverem fora do intervalo entre 0,0 e 1,0.

Nem toda ferramenta de edição de imagens trabalha 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 aceito pelo MAME.

Parâmetros

Os parâmetros funcionam como variáveis que podem substituir o valor dos atributos; basta cercar o nome da variável 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~, respectivamente:

<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 (A-Z) ou minúsculas (a-z), números (0-9) ou caracteres subtraço (_). São levadas em consideração letras maiúsculas e minúsculas nos nomes dos parâmetros. Durante a procura por um parâmetro, o motor de layout começa 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 de mapeamento. 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 essa é uma informação bem mais óbvia. Os números inteiros são armazenados como 64 bits signados, com dois valores complementares, enquanto os números de ponto flutuante são armazenados como binários IEEE754, também conhecidos como "precisão dupla". Os números inteiros são substituídos em notação decimal, enquanto 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, uma notação científica. Não é possível 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 ele seja alterado, enquanto o parâmetro "generador" possui um valor inicial, um incremento e/ou um deslocamento [3] aplicado em cada interação.

Os valores dos parâmetros são atribuídos pelo elemento param junto com os elementos name e value. Esses valores podem aparecer dentro de um elemento de primeiro nível mamelayout, dentro dos elementos repeat e view, assim como dentro da definição dos elementos group (isto é, elementos group dentro do primeiro nível 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 ao parâmetro "firstdigit":

<param name="firstdigit" value="4" />

Os parâmetros dos geradores são atribuídos pelo elemento param, juntamente com os atributos name, start, increment, lshift e rshift. Esses parâmetros só podem aparecer dentro dos elementos repeat (consulte o capítulo Repetindo os blocos para obter mais informações) e não devem ser reatribuídos dentro do mesmo escopo (um parâmetro com um nome idêntico pode ser atribuído em um escopo por meio de sua ramificação). Abaixo, alguns exemplos de parâmetros de 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 gera os valores 3, 2, 1...

  • O parâmetro switchpos gera os valores 74 (74), 230 (74 + 156), 386 (230 + 156)...

  • O parâmetro mask gera 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 de 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 início da próxima iteração. Antes que o incremento ou o deslocamento seja aplicado, o valor do parâmetro poderá ser interpretado como um número de ponto flutuante ou um número inteiro. Se ambos os valores forem informados para incremento e deslocamento, então o valor do incremento será aplicado primeiro, e depois o valor deslocado.

Se o atributo increment estiver presente e for um número de ponto flutuante, seu valor será convertido para um número de ponto flutuante, se necessário, antes que o incremento seja adicionado. Se o atributo increment estiver presente e o valor do parâmetro for inteiro, o valor do incremento será convertido para um número de ponto flutuante antes de ser adicionado.

Se os atributos lshift ou rshift estiverem presentes, mas não forem iguais, o valor do parâmetro será convertido para um número inteiro e deslocado conforme necessário. O deslocamento para a esquerda é definido como um deslocamento feito em direção ao bit de maior importância. Se ambos os parâmetros lshift e rshift forem informados, eles serão compensados antes dos valores serem aplicados. Isso significa que não é possível usar atributos iguais tanto para o lshift quanto para o rshift, por exemplo, para limpar os bits no final do parâmetro após a primeira iteração.

Será considerado um erro se o elemento param não estiver presente em qualquer um dos atributos value ou start. Da mesma forma, será considerado um erro se 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 seu valor em um parâmetro no escopo atual mais interno. Não é possível definir ou reatribuir parâmetros em um escopo de contenção.

Parâmetros já predefinidos

Uma certa quantidade de valores já predefinidos nos parâmetros está disponível e fornece informações sobre o sistema em execução:

devicetag

Um exemplo do caminho completo da etiqueta [10] do dispositivo responsável pela leitura do layout seria : para o driver do controlador do dispositivo raiz ou :tty:ie15 para o terminal conectado a uma porta. Esse parâmetro é uma sequência de caracteres definido no escopo global 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 ou ie15 para o terminal conectado a uma porta. Este parâmetro é uma sequência de caracteres definido no escopo global do layout..

devicename

O nome completo (descrição) do dispositivo responsável pela leitura do layout, como, por exemplo, os terminais AIM-65/40 ou IE15, é definido como um parâmetro, isto é, uma sequência de caracteres definido 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 ou ie15. Este parâmetro é uma sequência de caracteres definido 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 (caso esteja presente). É calculada 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 (caso esteja presente). É calculada 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 (caso esteja presente) 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 (caso esteja presente) 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 (caso esteja 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 (caso esteja 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 os 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 incluídas (não apenas nos subdispositivos do dispositivo que carregaram o layout). Os valores de X/width e Y/height referem-se às dimensões horizontal e vertical da tela antes da rotação ser aplicada. Os valores baseados na região visível são calculados ao término da configuração. Se o sistema não reconfigurar 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 vários tipos de visualizações. As visualizações são construídas a partir de elementos (elements) e telas (screens). Para simplificar a organização de layouts complexos, é compatível 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, seguido do atributo version. Esse atributo 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 o elemento mamelayout:

<mamelayout version="2">

Com o intuito de assegurar a compatibilidade na identificação do arquivo com diversos editores de texto, é possível declarar que o mesmo é um arquivo XML. Dessa forma, o MAME também aceita o arquivo com uma declaração XML.

<?xml version="1.0"?>
<mamelayout version="2">

Assim como é possível empregar o identificador XML, também é viável identificar a codificação do arquivo e fazer sua devida declaração, caso seja necessário:

<?xml version="1.0" encoding="UTF-8"?>
<mamelayout version="2">

Ademais, comentários podem ser adicionados em qualquer parte do arquivo, desde que estejam entre os caracteres <!-- e -->, exemplo:

<?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 layout para que as pessoas saibam o que fazer ou
        como prosseguir, se necessário. Identifique seus arquivos e
        utilize esses espaços para deixar seu nome ou apelido, a versão,
        a data de criação ou de alteração do layout, 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.

  • A sequência de caracteres -- não pode ser usada dentro de um comentário.

Em geral, as ramificações do primeiro elemento mamelayout são processadas 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 podem se referir a elementos e grupos que apareçam depois dele.

Os seguintes elementos são permitidos dentro do primeiro elemento mamelayout:

param

Define ou reatribui um valor ao parâmetro. Consulte o capítulo Parâmetros para obter mais informações.

element

Define um elemento, um dos objetos primários a serem organizados na visualização. Consulte o capítulo Os elementos para obter mais informações.

group

Define um grupo de elementos ou telas que possam ser reutilizados e que também possam ser usados como referência em uma visualização ou nos outros grupos. Consulte o capítulo Grupos reutilizáveis para obter mais informações.

repeat

Define um grupo de elementos repetidos que podem conter os elementos param, element, group e repeat. Consulte o capítulo 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 o capítulo As visualizações para obter mais informações.

script

Permite que scripts Lua sejam usados em um layout que aprimora ainda mais a interação.

Os elementos

Os elementos são um dos objetos visuais mais básicos, podendo ser organizados em conjunto com as telas na composição de uma visualização. Eles podem ser construídos com um ou mais componentes, porém, na composição do gráfico da cena e em sua renderização, um elemento é tratado como uma única superfície. Um elemento pode ser usado em diversas visualizações e ser utilizado várias vezes dentro de uma única visualização.

A aparência de um elemento depende de 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 capítulo O Estado do elemento para obter mais informações sobre como conectar um elemento a uma porta ou à 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 específico. Alguns componentes (como mostradores com múltiplos segmentos, por exemplo) usam diretamente o estado para determinar sua aparência final.

Cada elemento possui seu próprio sistema interno de coordenadas. Os limites dos elementos desse sistema de coordenadas são computados pela união dos limites individuais dos componentes que o compõem.

Todo elemento deve ter seu nome definido pelo atributo name. Os elementos são mencionados pelo nome quando são solicitados nos grupos ou nas visualizações. Haverá um erro se o arquivo layout tiver vários elementos name com o mesmo valor. De forma opcional, os elementos podem ser utilizados para informar um valor predefinido do seu estado por meio do atributo defstate, caso estejam conectados a uma saída emulada ou a uma porta E/S. O valor do atributo defstate deve ser um número inteiro e positivo; 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, do primeiro ao último, utilizando uma transparência de mesclagem alpha blending (os componentes são desenhados por cima e podem se sobrepor aos componentes anteriores). 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 ou statemask. Caso estejam presentes, esses atributos devem ser inteiros e com valores positivos. Se apenas o atributo state estiver presente, o componente só será desenhado na tela quando o elemento state coincidir com seu valor. Se apenas o atributo statemask estiver presente, o componente só será desenhado na tela se todos os bits estiverem definidos e os seus valores estiverem definidos pelo atributo state.

    Se ambos os atributos state e statemask estiverem presentes, o componente só será desenhado na tela se os bits do elemento state corresponderem ao bit definido no atributo statemask e também corresponderem aos bits do valor do atributo state.

    O componente sempre será desenhado na ausência de ambos os atributos state ou statemask ou caso o valor do atributo statemask for zero.

  • Cada componente pode ter um subelemento bounds, que define sua posição e tamanho (consulte o capítulo Coordenadas para obter mais informações). 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, quando vários elementos bounds são providos em conjunto com os atributos state. O atributo state de cada ramificação do elemento bounds deve ser um número inteiro e positivo. Os atributos state não devem ser iguais para quaisquer um dos dois elementos bounds que estiverem dentro de um componente.

    Se o estado do elemento for menor que o valor do atributo state de qualquer uma das ramificações do elemento bounds, será utilizada a posição/tamanho definida pelo elemento bounds com o menor valor do atributo state. Se o estado do elemento for maior que o valor do atributo state de qualquer elemento bounds, será utilizada a posição/tamanho especificada pelo elemento bounds com o maior valor do atributo state. Se o estado do elemento estiver entre os valores do atributo state dos dois elementos bounds, a posição/tamanho será interpolada linearmente.

  • Cada componente de cor pode ter um elemento color definindo uma cor RGBA (consulte o capítulo Cores para obter mais informações). Isso pode ser usado para controlar a geometria da cor dos componentes desenhados de forma algorítmica ou textual. Para os componentes image, a cor dos pixels da imagem é multiplicada pela cor definida. Caso esse 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, por meio da combinação de diversos elementos color junto com os atributos state. Os atributos state não devem ser iguais em qualquer um dos dois elementos color internos de um componente.

    Se o estado do elemento for inferior ao valor do atributo state de qualquer elemento color, será utilizada a cor especificada pelo elemento color com o menor valor do atributo state.

    Se o estado do elemento for superior ao valor do atributo state de qualquer elemento color, será utilizada a cor especificada pelo elemento color com o maior valor do atributo state. Se o estado do elemento estiver entre os valores do atributo state de dois elementos color, os componentes de cor RGBA serão interpolados linearmente.

Há suporte para os seguintes componentes:

rect

Desenha um retângulo colorido e uniforme com as bordas preenchidas.

disk

Desenha uma elipse colorida 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 chamado alphafile para determinar o nome de um arquivo PNG (incluindo a sua extensão) para ser carregado do canal alfa.

Alternativamente, os dados da imagem podem ser informados no próprio arquivo layout utilizando um subelemento data. Isto pode ser útil para oferecer gráficos SVG simples e legíveis. A falta de qualquer atributo file ou data é considerada como um erro.

O arquivo usado como alphafile deve ter as mesmas dimensões (em pixels) que o arquivo do atributo file 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 (cor branca na escala de cinza) o que corresponde a opacidade total, e o preto, a transparência total.

O atributo alphafile será ignorado caso o atributo file aponte para um arquivo SVG ou um subelemento data contendo dados SVG, pois o atributo é utilizado apenas com imagens do tipo bitmap.

O(s) arquivo(s) da(s) imagem(s) deve(m) ser colocado(s) no mesmo diretório do arquivo layout. Os formatos das imagens são detectados durante a análise do conteúdo dos arquivos, e os nomes das suas extensões não são levados em consideração. Note, porém, que nos sistemas *nix os nomes dos aquivos em maiúsculas e minúsculas, assim como, caracteres acentuados, 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 consegue carregar as imagens, pois uma sequência de bolinhas cinzas ou traços diagonais aparecem na tela. Isso indica que o MAME não encontrou os arquivos ou houve algum outro erro com o arquivo.

text

Gera texto na cor especificada, utilizando a fonte da ilustração. O texto a ser gerado deve ser fornecido utilizando um atributo string. Um atributo align pode ser fornecido para definir o alinhamento do texto. Se presente, o atributo align deve ser um número inteiro, onde 0 (zero) significa centralizado, 1 (um) significa alinhado à esquerda, 2 (dois) significa alinhado à direita e 3 (três) significa que o texto será esticado horizontalmente para preencher seus limites. Se o atributo align estiver ausente, o texto será centralizado automaticamente.

led7seg

Desenha um display digital LED/fluorescente padrão de sete segmentos (incluindo o ponto decimal) na cor especificada. Os oito bits inferiores do estado do elemento controlam quais segmentos estão acesos. Iniciando pelo bit de menor importância, começando pelos bits que correspondem ao segmento de cima, do superior direito, e assim por diante, até o segmento superior esquerdo, a barra do meio e o ponto decimal. Os segmentos não iluminados são desenhados com baixa intensidade (0x20/0xff).

led14seg

Desenha um display alfanumérico padrão de LED/fluorescente com quatorze segmentos na cor especificada. Os quatorze bits inferiores do estado do elemento controlam quais segmentos estão acesos. Iniciando pelo bit de menor importância, começando pelos bits que correspondem ao segmento de cima, do superior direito, continuando no sentido horário até o segmento superior esquerdo, às metades esquerda e direita da barra central horizontal, às metades de cima, de baixo e abaixo da barra central vertical e às barras diagonais no sentido horário, da esquerda para a direita. Os segmentos não iluminados são desenhados com baixa intensidade (0x20/0xff).

led14segsc

Desenha um display alfanumérico padrão de LED/fluorescente com quatorze segmentos incluindo o ponto decimal/vírgula na cor especificada. Os dezesseis bits inferiores do estado do elemento controlam quais segmentos estão acesos. Os quatorze bits inferiores correspondem aos mesmos segmentos do dispositivo led14seg. Dois bits adicionais correspondem ao ponto decimal e à vírgula. Os segmentos não iluminados são desenhados com baixa intensidade (0x20/0xff).

led16seg

Desenha um display alfanumérico de LED/fluorescente padrão de dezesseis segmentos na cor especificada. Os dezesseis bits inferiores do estado do elemento controlam quais segmentos estão acesos. Começando pelo bit de menor importância, os bits correspondem à metade esquerda e direita da barra superior, e assim por diante, no sentido horário, até o segmento superior esquerdo, às metades esquerda e direita da barra central horizontal, às metades superior e inferior da barra central vertical e às barras diagonais no sentido horário, da esquerda para a direita. Os segmentos não iluminados são desenhados com baixa intensidade (0x20/0xff).

led16segsc

Desenha um display alfanumérico padrão de LED/fluorescente com dezesseis segmentos incluindo o ponto decimal/vírgula na cor especificada. Os dezoito bits inferiores do estado do elemento controlam quais segmentos estão acesos. Os dezesseis bits inferiores correspondem aos mesmos segmentos do dispositivo led16seg. Dois bits adicionais correspondem ao ponto decimal e ao ponto decimal. Os segmentos não iluminados são desenhados com baixa intensidade (0x20/0xff).

simplecounter

Exibe o valor numérico do estado do elemento na cor especificada, usando a fonte da ilustração final. O valor é formatado em notação decimal. Um atributo digits pode ser fornecido para especificar a quantidade mínima de dígitos que serão mostrados na tela. Se presente, o atributo digits deve ser um número inteiro positivo; caso contrário, serão exibidos no mínimo dois dígitos.

Um atributo maxstate pode ser fornecido para especificar o valor máximo do estado a ser exibido. Se presente, o atributo maxstate deve ser um número não negativo; na sua ausência, o padrão será 999. Um atributo align pode ser fornecido para definir o alinhamento do texto. Se presente, o atributo align deve ser um número inteiro, onde 0 (zero) significa centralizado, 1 (um) significa alinhado à esquerda e 3 (três) significa que o texto será esticado horizontalmente para preencher seus limites. Na sua ausência, o texto será centralizado automaticamente.

Um exemplo de elemento estático posicionado à esquerda 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 elemento que mostra um LED redondo. A intensidade do brilho desse LED depende do seu estado.

<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>

O gráfico a seguir ilustra uma representação gráfica com barras que se expandem horizontalmente para a esquerda ou para a direita. À medida que o nível muda da posição neutra, a cor das barras muda, começando pelo verde, passando pelo amarelo e chegando ao vermelho:

<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>

Para mais informações, consulte o capítulo Criando controles visuais para Daytona USA.

As visualizações

Uma visualização define um arranjo de elementos emulados e/ou imagens na tela que podem ser exibidos em uma janela ou na tela inteira. As visualizações também conectam elementos emulados a portas e/ou saídas de entrada/saída. Um arquivo layout pode conter várias visualizações. Se uma visualização fizer referência a uma tela inexistente, ela será considerada inválida. Nesse caso, o MAME imprimirá um aviso, ignorará a visualização inviável e continuará a carregar as visualizações do arquivo layout. Isso é particularmente útil para sistemas nos quais uma tela é opcional, como sistemas de computador com controles na parte frontal da máquina e um terminal serial opcional.

As visualizações são identificadas por nome na interface do usuário do MAME e nas opções da linha de comando. Para arquivos layout associados a dispositivos que não sejam o dispositivo de driver raiz, os nomes das visualizações são prefixados com a etiqueta (tag) do dispositivo (com os dois pontos iniciais omitidos); por exemplo, uma visualização chamada “Teclado de LEDs” carregada para o dispositivo :tty:ie15 receberá o nome "tty:ie15 Keyboard LEDs” na interface do usuário do MAME. As visualizações são listadas na tela pela ordem em que são carregadas. Em um arquivo layout, as visualizações são carregadas pela ordem em que aparecem, começando de cima para baixo.

As visualizações são criadas com elementos view, começando pelo primeiro elemento mamelayout. Cada elemento de visualização deve ter um atributo name com um nome legível para utilização na interface do usuário e nas opções de linha de comando. A seguir, um exemplo de uma etiqueta (tag) válida para um elemento view:

<view name="Painel de controle">

Um elemento view cria um escopo de parâmetro aninhado dentro do escopo de parâmetro iniciando pelo elemento mamelayout. Por motivos históricos, os elementos view são processados depois de todos os outros elementos relacionados do elemento mamelayout. Isso significa que um elemento view pode fazer referência a elementos e grupos que aparecem depois dela no arquivo, e os parâmetros do escopo envolvente obtêm seus valores finais a partir do último elemento mamelayout.

Um elemento view pode ter um atributo showpointers para indicar se os ponteiros do mouse ou da caneta devem ser exibidos na tela. Se estiver presente, o valor deve ser yes ou no. Se o atributo showpointers não estiver presente, os ponteiros de mouse e da caneta serão exibidos para visualizações que contenham elementos vinculados a 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 atributo showpointers como "no" no arquivo layout, caso contrário, o ponteiro laranja do mouse aparecerá nas suas ilustrações gráficas (artwork), ainda que ele não seja necessário para aquela ilustração. Se essa for a sua intenção, não é preciso alterar nada, pois a predefinição é exibir o ponteiro.

As seguintes ramificações dos elementos são permitidos dentro de um elemento view:

bounds

Define a origem e o tamanho da visualização por meio das coordenadas interna do sistema caso um esteja presente. Consulte o capítulo 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 elemento view. 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 o capítulo Parâmetros para obter mais informações.

element

Adiciona um elemento à visualização (consulte o capítulo Os elementos para obter mais informações) por meio atributo do elemento obrigatório ref. Haverá um erro caso nenhum elemento ref seja definido no arquivo layout.

Opcionalmente pode estar conectada a uma porta E/S emulada por meio dos atributos inputtag e o inputmask ou por meio da emulação de uma saída usando um atributo name. Consulte o capítulo 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 por meio do atributo index ou do atributo tag (não é possível ter ambos os atributos index e tag em um mesmo elemento screen). Se estiver presente, o atributo index deve ter um valor inteiro e positivo. As telas são numeradas de acordo com a ordem de aparecimento na configuração do sistema, começando com zero (0). Se o atributo tag estiver presente, ele deve ser o caminho da etiqueta para a tela em relação ao dispositivo, para que a leitura do layout seja provocada. As telas são desenhadas na ordem em que aparecem no arquivo layout.

Opcionalmente, pode estar conectada a uma porta E/S emulada por meio dos atributos inputtag e inputmask ou a uma saída emulada por meio do atributo name. Consulte o capítulo Itens que podem ser clicados para obter mais informações.

collection

Adiciona as telas ou os itens a uma coleção, que poderá ser exibida ou escondida manualmente pelo usuário (consulte o capítulo Coleções para obter mais informações). O nome da coleção é definido pelo atributo name. Há um limite de até 32 coleções por visualização.

group

Adiciona o conteúdo do grupo na visualização (consulte o capítulo Grupos reutilizáveis para obter mais informações). O nome do grupo a ser adicionado pode ser definido por meio do atributo ref. Haverá um erro caso nenhum grupo com esse atributo seja definido no arquivo layout. Veja abaixo para mais informações sobre posicionamento.

repeat

Repete seu conteúdo pela quantidade de vezes que estiver definida no atributo count. O atributo count deve ser um número inteiro positivo. O elemento repeat aceita os elementos element, screen e group, além dos elementos repeat, que funcionam da mesma maneira que quando colocados em uma visualização direta. Consulte o capítulo Repetindo os blocos para saber como utilizar os elementos repeat.

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, o que se aplica a telas (screen) e elementos instanciados por meio dos grupos reutilizáveis e de repetição de blocos. Os elementos de tela e layout com o atributo id são identificados por meio de scripts Lua (consulte o capítulo Usando scripts no layout do MAME para obter mais informações).

É possível alterar a orientação dos elementos screen, element e group usando o elemento orientation. Para as telas, os modificadores de orientação são aplicados em conjunto com os modificadores de orientação definidos no dispositivo e no sistema. O elemento orientation é compatível com os seguintes atributos opcionais:

rotate

Se presente, permite aplicar uma rotação no sentido horário em incrementos de 90 graus. O número deve ser inteiro, igual a 0, 90, 180 (90 + 90) ou 270 (180 + 90).

swapxy

Permite espelhar a tela, um elemento ou um grupo ao longo de uma linha em 45 graus na vertical, da esquerda para a direita. Se o valor estiver presente, o valor deve ser yes ou no. O espelhamento se aplica logicamente após a rotação.

flipx

Permite espelhar a tela, um elemento ou um grupo à partir de uma linha com 45 graus em torno de seu eixo vertical, vindo da quina superior esquerda até a quina inferior direita. Se o valor estiver presente, deve ser yes ou no. O espelhamento ocorre após a rotação.

flipy

Permite espelhar a tela, um elemento ou um grupo ao redor do eixo horizontal, de cima para baixo. Se o valor estiver presente, o valor deve ser yes ou no. 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) [4], multiply (soma dos valores RGB) [5] e add (soma das camadas) [8]. A predefinição para a tela é alpha permitindo que o driver defina a mesclagem dos elementos do layout por meio de camadas.

É possível posicionar e redimensionar as telas (elementos screen), elementos do layout (elementos element) e elementos de grupo (group) usando um elemento bounds (consulte o capítulo Coordenadas para obter mais informações). Na ausência do subelemento bounds, os elementos screen e layout retornam aos valores predefinidos em unidades quadradas (com origem em 0,0 e ambos os valores de altura e largura serão igual a 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 de seus limites). Este exemplo mostra uma visualização com referência à 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 grupo (group) podem ter um subelemento color (consulte o capítulo Cores para obter mais informações) ao definir uma cor modificadora. O valor dessa cor será usado 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 subelementos bounds em conjunto com o atributo state. Consulte o capítulo A visualização de um item animado para obter mais informações.

Coleções

As coleções de telas ou de elementos do layout são agrupadas de maneira que o usuário decide se podem ser exibidos ou não, conforme a sua necessidade caso esta tenha sido definida pelo autor do layout. Em uma visualização única, é possível utilizar, por exemplo, uma visualização e um teclado numérico (keypad) selecionável, permitindo que o usuário esconda o teclado e deixe visível apenas a visualização da tela. As coleções são criadas por meio do elemento collection dentro dos elementos view, group bem como dos elementos collection.

Um elemento collection deve ter um atributo name, que informa o nome da visualização. Os nomes destinados para collection devem ser únicos. A visualização inicial da coleção deve ser definida pelo 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. As coleções estão predefinidas para estarem visíveis.

A seguir, um exemplo que demonstra a utilização de um collection permitindo que partes de uma visualização sejam ocultadas 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. Consulte o capítulo Parâmetros para obter mais informações. Observe que o nome da coleção e a visualização predefinida não fazem parte do seu conteúdo. Qualquer referência aos parâmetros nos atributos name e visible será substituída usando os valores dos parâmetros a partir da origem do escopo relacionado à coleção.

Consulte o capítulo Desativando objetos na tela.

Grupos reutilizáveis

Os grupos permitem que um arranjo de telas ou elementos do layout seja usado várias vezes em uma visualização ou em outros grupos. Mesmo que o arranjo seja usado apenas uma vez, os grupos podem ser de grande ajuda, 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 por meio de elementos group dentro de elementos view e outros elementos group.

Cada definição de grupo deve ter um atributo name que forneça um identificador único. É considerado um erro se o arquivo layout contiver várias definições de grupos com um atributo name idêntico. O valor do atributo name é usado para justificar a visualização de um grupo ou outro. A seguir, um exemplo de como abrir a etiqueta para definir o grupo de um elemento dentro do primeiro elemento mamelayout:

<group name="panel">

Então, este grupo pode ser justificado em uma visualização ou em outro elemento group usando um elemento de grupo como referência. Opcionalmente, também poderão ser informados os limites de destino, a orientação e as modificações das cores. Neste exemplo, os limites dos valores são informados por meio do atributo ref, que identifica o grupo a que se refere:

<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 a orientação das telas, os elementos do layout e o arranjo desses grupos devem ser configurados da mesma maneira que as visualizações. Veja As visualizações para mais informações. Um grupo pode justificar outros grupos, mas loops recursivos não são permitidos. Será considerado um erro se um grupo se representar diretamente ou indiretamente.

Os grupos possuem seus próprios sistemas de coordenadas internas. Se um elemento de definição de grupo não tiver um elemento limitador bounds como relação direta, 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. É possível usar um elemento relacionado bounds para definir explicitamente grupos limitadores (consulte o capítulo Coordenadas para obter mais informações). Observe que os limites dos grupos são usados apenas para calcular as coordenadas de transformação quando forem relacionados a um grupo. Um grupo pode posicionar as telas ou os elementos fora de seus limites sem que sejam cortados.

Para demonstrar como o cálculo dos limites funciona, considere o seguinte 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 [11], 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 por meio das várias instancias.

Consulte o capítulo 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 o capítulo Parâmetros para obter mais informações). A repetição dos blocos podem ser agrupados para criar arranjos mais complexos.

Os blocos repetidos são criados por meio 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 subelemento 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 elementos param, element, group (definição) e repeat.

  • Um bloco repetido dentro de um elemento group ou view podem conter os seguintes elementos, param, element (referência), screen, group (referência) e repeat.

Um bloco de repetição repete o seu conteúdo diversas vezes dependendo do valor definido no atributo count. Consulte as seções relevantes dos capítulos As partes de um layout, Grupos reutilizáveis e As visualizações para obter mais informações de como os subelementos são usados. 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 por meio do board:IN.7 no topo do board.IN.0 na parte inferior.

Interatividade

As visualizações com interatividade são suportadas por meio 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 em uma 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 o capítulo Os elementos para obter mais informações.

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/ou statemask. Consulte o capítulo Os elementos para obter mais informações.

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/ou bounds em conjunto com os atributos de condição state. Consulte o capítulo Os elementos para obter mais informações.

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 o capítulo A visualização de um item animado para obter mais informações).

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 por meio 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 o capítulo 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 o capítulo 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 por meio 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 [12] 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 por meio da definição dos diversos subelementos color ou bounds com atributos state. O atributo state deve ser um número inteiro positivo para cada elemento color ou subelemento 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 por meio do subelemento 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 subelementos bounds. Já a posição ou o tamanho definido pelo subelemento 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 subelementos 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 subelementos bounds.

A cor será atribuída por meio do subelemento 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 subelemento 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 subelementos 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 subelemento 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 subelemento animate o estado de animação do item será idêntico ao estado do seu elemento (consulte o capítulo O Estado do elemento para obter mais informações).

Quando um subelemento 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 subelemento animate com o atributo name o estado da animação do item será obtido por meio 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 subelemento 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 por meio 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 por meio do 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.

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:

  • o MAME carregará uma visualização que mostra a mensagem “Sem saída visual” se o sistema não tiver telas e nenhuma visualização viável for encontrada nos layouts interno e externo;

  • 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 em uma 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 em uma cabine do tipo cocktail, que disponibiliza uma mesa onde os jogadores se sentam frente a frente e cada um com a sua própria 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 por meio 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 elemento defstate define a sua condição inicial e o elemento 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:

Coordenadas

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:

Coordenadas

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):

Seleção

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.

Seleção

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>
Tela por baixo da arte

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>
Tela por baixo da arte

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:

Tela com arte, efeitos e moldura

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.

Seleção

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:

Centralizado

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 por meio 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 por meio 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.

"Tela do Galaxian"

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. Consulte o capítulo Itens que podem ser clicados para obter mais informações.

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:

Códigos dos direcionais de alguns drivers

Driver

defstate

cima

sdir

dire

idir

baix

iesq

esqu

sesq

cent

cps1

0xf

0x7

0x6

0xe

0xa

0xb

0x9

0xd

0x5

0xf

cps2

0xf

0x7

0x6

0xe

0xa

0xb

0x9

0xd

0x5

0xf

cps3

0xf

0xe

0x6

0x7

0x5

0xd

0x9

0xb

0xa

0xf

ddragon

0xf

0xb

0xa

0xe

0x6

0x7

0x5

0xd

0x9

0xf

dkong

0xf

0x4

0x1

0x8

0x2

0x0

galaxian

0xc

0x1

0x2

0x0

kinst

0x3c0

0xe

0x6

0x7

0x5

0xd

0x9

0xb

0xa

0xf

m72/m92

0xf

0x7

0x6

0xe

0xa

0xb

0x9

0xd

0x5

0xf

midtunit

0xf

0xe

0x6

0x7

0x5

0xd

0x9

0xb

0xa

0xf

midwunit

0xf

0xe

0x6

0x7

0x5

0xd

0x9

0xb

0xa

0xf

model1

0xf0

0xd

0x9

0xb

0xa

0xe

0x6

0x7

0x5

0xf

nemesis

0xf

0xb

0x9

0xd

0x5

0x7

0x6

0xe

0xa

0xf

neogeo

0xf

0xe

0x6

0x7

0x5

0xd

0x9

0xb

0xa

0xf

pacman

0xf

0xe

0xb

0x7

0xd

0xf

psx

0xf0

0xe

0xc

0xd

0x9

0xb

0x3

0x7

0x6

0xf

segas16a

0xf0

0xd

0x9

0xb

0xa

0xe

0x6

0x7

0x5

0xf

segas18

0xf0

0xd

0x9

0xb

0xa

0xe

0x6

0x7

0x5

0xf

seibuspi

0xf

0xe

0x6

0x7

0x5

0xd

0x9

0xb

0xa

0xf

simpsons

0xf

0xb

0x9

0xd

0x5

0x7

0x6

0xe

0xa

0xf

stv

0xf0

0xd

0x9

0xb

0x6

0xe

0x6

0x7

0x5

0xf

tmnt

0xf

0xb

0x9

0xd

0x5

0x7

0x6

0xe

0xa

0xf

toaplan2

0xf

0x1

0x9

0x8

0xa

0x2

0x6

0x4

0x5

0x0

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 dos elementos inputtag e inputmask estão corretos e apenas os valores para o elemento state está errado.

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 por meio 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.

Modelo com 4 controles

Abaixo as teclas predefinidas do MAME para os 4 jogadores. consulte o capítulo Predefinições para o jogador 1 para obter mais informações.

Teclas predefinidas do MAME para o controle dos jogadores

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 por meio 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 por meio do menu Opções do Vídeo (TAB --> Opções do vídeo).

As diferentes versões do sistema Galaxian

Desativando objetos na tela

Assim como foi descrito em Coleções isso é possível organizando os objetos da tela dentro dos elementos collection, 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>
Opções selecionáveis

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 para 17,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 x 17,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 defina 224,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.

Baixe este arquivo completo aqui.

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 e vermelho1.png.

  • Clique na base do nosso joystick, exporte ele com o tamanho 575 x 294 e defina o seu nome como base.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:

Controle de Mahjong

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 por 240 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 com 336 por 63,904.

  • Somando a altura do objeto com a altura da tela 63,904 + 240 nós temos uma altura total de 303,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 por 240 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:

Tamanho da tela com o controle

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).

Tamanho da tela com o controle

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.

Criando controles visuais para Daytona USA

Os controles do Daytona USA são bastante sensíveis, de modo que movimentos simples são interpretados de maneira extrema pelo jogo, como se você instantaneamente movesse a roda do carro para qualquer um dos lados ou acelerasse de zero a 100% em um instante. Com isso, você perde o controle e a tração na pista (na tela você vê fumaça saindo pelos pneus indicando que eles estão patinando). Essa ilustração te ajuda a ter um retorno visual para que voce possa fazer o ajuste fino nas configurações de sensibilidade do controle.

Crie uma pasta em artwork\daytona e salve o arquivo abaixo como default.lay:

<?xml version="1.0"?>
<mamelayout version="2">
<!--
        Controles virtuais para Daytona USA
        Criado por: Wellington Terumi Uemura
        Licença: CC0
        Fonte: Documentação Oficial do MAME:
        https://docs.mamedev.org/techspecs/layout_files.html
        Download: https://pastebin.com/TjTx5895
        https://mamedoc.readthedocs.io/
        Data de criação: December 28, 2025
-->

<!-- Elemento de texto "WHEEL" (volante) -->
<element name="volante" defstate="1">
        <text string="WHEEL"></text>
</element>

<!-- Elemento de texto "AC" (acelerador) -->
<element name="gas" defstate="1">
        <text string="AC"></text>
</element>

<!-- Elemento de texto "BK" (freio) -->
<element name="brake" defstate="1">
        <text string="BK"></text>
</element>

<!-- Para bloquear cliques do mouse -->
<element name="cover" defstate="0">
        <rect><color alpha="0" /></rect>
</element>

<!--
https://github.com/mamedev/mame/blob/master/src/mame/sega/model2.cpp#L1765

        PORT_START("STEER")
        PORT_BIT(0xff, 0x80, IPT_PADDLE) PORT_SENSITIVITY(30) PORT_KEYDELTA(10)

        É daqui de onde obtemos os valores para o volante e aplicamos as cores
        da posição neutra até o extremo. O mecanismo interno do MAME faz o
        resto para transicionar entre as cores.
-->

<element name="wheel">
        <rect>
                <bounds state="0xff" left="0.475" top="0.0" right="1.0" bottom="1.0" /><!-- Direita -->
                <bounds state="0x80" left="0.475" top="0.0" right="0.525" bottom="1.0" /><!-- Centro -->
                <bounds state="0x0" left="0.0" top="0.0" right="0.525" bottom="1.0" /><!-- Esquerda -->
                <color state="0x80" red="0.0" green="0.0" blue="0.8" /><!-- neutro/centro -->
                <color state="0x40" red="0.709" green="0.0" blue="0.709" /><!-- intermediário -->
                <color state="0x0" red="1.0" green="0.0" blue="0.0" /><!-- Left -->
                <color state="0xbf" red="0.709" green="0.0" blue="0.709" /><!-- intermediário -->
                <color state="0xff" red="1.0" green="0.0" blue="0.0"  /><!-- Right-->
        </rect>
</element>

<!--
https://github.com/mamedev/mame/blob/master/src/mame/sega/model2.cpp#L1765

        PORT_START("ACCEL")
        PORT_BIT(0xff, 0x00, IPT_PEDAL)  PORT_SENSITIVITY(30) PORT_KEYDELTA(10)

        PORT_START("BRAKE")
        PORT_BIT(0xff, 0x00, IPT_PEDAL2) PORT_SENSITIVITY(30) PORT_KEYDELTA(10)

        É daqui de onde obtemos os valores para o acelerador e freio, aplicamos
        as cores da posição neutra até o extremo. O mecanismo interno do MAME
        faz o resto para transicionar entre as cores.

-->

<element name="pedal">
        <rect>
                <bounds state="0x00" left="0.0" top="0.9" right="1.0" bottom="1.0" /><!-- Neutro -->
                <bounds state="0xff" left="0.0" top="0.0" right="1.0" bottom="1.0" /><!-- Socado -->
                <color state="0x00" red="0.0" green="0.0" blue="0.8" /><!-- Neutro -->
                <color state="0x7f" red="0.709" green="0.0" blue="0.709" /><!-- 50% -->
                <color state="0xff" red="1.0" green="0.0" blue="0.0" /><!-- Socado -->
        </rect>
</element>

<!-- Define o nome da nossa ilustração para ser selecionada na interface do MAME-->
<view name="Controles Analógicos" showpointers="no">
<screen index="0">
        <bounds x="0" y="0" width="4" height="3" />
</screen>

<!-- Define a posição e tamanho do texto "WHEEL" na tela -->
<element ref="volante" align="3">
        <bounds x="1.870" y="2.91" width="0.261" height="0.052" />
        <color red="1.0" green="1.0" blue="1.0" />
</element>

<!-- Define a posição e tamanho inicial para a animação do volante na tela -->
<element ref="wheel" inputtag="STEER" inputmask="0xff" inputraw="yes">
        <bounds x="1" y="2.8" width="2" height="0.1" />
        <color alpha="0.7" />
</element>

<!-- Bloqueia o clique no volante -->
<element ref="cover" clickthrough="no">
        <bounds x="0.9" y="2.8" width="2.1" height="0.2" />
</element>

<!-- Define a posição e tamanho do texto "AC" na tela -->
<element ref="gas" align="3">
        <bounds x="3.075" y="2.91" width="0.1" height="0.053" />
        <color red="1.0" green="1.0" blue="1.0" />
</element>

<!-- Define a posição e tamanho inicial para a animação do acelerador na tela -->
<element ref="pedal" inputtag="ACCEL" inputmask="0xff" inputraw="yes">
        <bounds x="3.075" y="1.9" width="0.1" height="1" />
        <color alpha="0.7" />
</element>

<!-- Define a posição e tamanho do texto "BK" na tela -->
<element ref="brake" align="3">
        <bounds x="3.274" y="2.911" width="0.1" height="0.051" />
        <color red="1.0" green="1.0" blue="1.0" />
</element>

<!-- Define a posição e tamanho inicial para a animação do freio na tela -->
<element ref="pedal" inputtag="BRAKE" inputmask="0xff" inputraw="yes">
        <bounds x="3.275" y="1.9" width="0.1" height="1" />
        <color alpha="0.7" />
</element>

<!-- Bloqueia o clique nos pedais -->
<element ref="cover" clickthrough="no">
        <bounds x="3.02" y="1.862" width="0.4" height="1.1" />
</element>

</view>
</mamelayout>

Ao iniciar o sistema, será possível ver os controles na parte inferior da tela com as descrições WHEEL, AC e BK. Experimente mover os controles para ver a animação acontecer em tempo real na tela.

Daytona USA

Acesse o link abaixo para assistir a ilustração em ação:

https://www.youtube.com/watch?v=2MVQd1KaGOU

Problema de sensibilidade

Aparentemente, até a versão 0.284 do MAME, parece que há um bug para controles analógicos. Você notará que, mesmo tentando fazer os ajustes dos controles em Tab --> Configuração da entrada --> Ajustes da entrada analógica e alterando os parâmetros da opção aumenta/reduz velocidade e velocidade da centralização automática eles não terão efeito, seja definindo-os como mínimo ou máximo.

Uma solução paliativa para esse problema é alterar o padrão JOYCODE_1_ para qualquer outro, como JOYCODE_2_ por exemplo. Antes de mais nada, identifique o ID do seu joystick. Com o jogo ainda em execução, pressione Tab e acesse Configuração da entrada --> Dispositivos da entrada --> entre no item listado em joystick. E seguida, clique duas vezes em Copia a ID do dispositivo. Cole essa informação em um arquivo de texto para usá-la mais tarde.

Também é possível obter essa ID por meio da linha de comando. No Windows ele aparece assim:

mame daytona -v | find "Adding joystick"
Input: Adding joystick #1: Playstation 4 ...
...

Já no Linux, macOS e outros sistemas SDL, aparece assim:

./mame daytona -v | grep "Game Controller:"
Game Controller: PS4 Controller [GUID 03008fe54c050000c405000000016800] ...
...

Para sistemas Windows, crie o arquivo ctrlr\daytona.cfg com o seguinte conteúdo:

<?xml version="1.0"?>
<mameconfig version="10">
    <system name="daytona">
        <input>
<!-- Joystick ID Windows-->
            <mapdevice device="Playstation 4" controller="JOYCODE_2" />
        </input>
    </system>
</mameconfig>

Para sistemas Linux crie o arquivo ctrlr/daytona.cfg com o seguinte conteúdo:

<?xml version="1.0"?>
<mameconfig version="10">
    <system name="daytona">
        <input>
<!-- Joystick ID Linux -->
            <mapdevice device="03008fe54c050000c405000000016800" controller="JOYCODE_2" />
        </input>
     </system>
 </mameconfig>

Crie o arquivo ini\daytona.ini (Windows) ou ini/daytona.ini (Linux) com o seguinte conteúdo:

ctrlr daytona

Se a emulação ainda estiver rodando pressione Shift + F3 para forçar uma reinicialização completa ou inicie a emulação novamente caso ela não esteja rodando. Repare que agora os controles aparentam estar mais pesados e não respondem mais de maneira instantânea.

Faça a personalização do mapeamento dos botões e do volante ao seu gosto pressionando a tecla Tab e indo em Configurações da entrada --> Atribuições da entrada (este sistema).

As configurações utilizadas naquela gameplay foram (Tab --> Configurações da entrada --> Ajustes da entrada analógica)

P1 pedal 1 velocidade da centralização automática 30
...
Acionador aumenta/reduz a velocidade 12
Acionador velocidade da centralização automática 13

No nosso caso, o acelerador (P1 Pedal 1) acelera devagar e, ao tirar o dedo do botão, retorna mais rápido à posição neutra. Aumente o valor para acelerar esse movimento. Já o volante (Acionador), eu deixei o movimento mais lento (aumente caso queira mais lento) e acelerei um pouco o retorno ao centro. Faça os ajustes de acordo com o seu tipo de jogabilidade e o controle utilizado.

Repare que, com a ajuda da nossa ilustração, é possível ver os efeitos das alterações em tempo real tanto do volante quanto dos pedais.

Migrando o layout do formato antigo para o novo

Ainda é possível encontrar na internet arquivos artwork que utilizam o formato antigo. O MAME ainda não abandonou completamente o formato antigo, mas poderá fazê-lo a qualquer momento, isso significa que o seus arquivos artwork poderão parar de funcionar. Para saber quais estão desatualizados, basta executar um sistema qualquer com artwork no formato antigo para que o MAME o alerte imediatamente por meio do terminal ou do prompt de comando informando que formato do arquivo é obsoleto, exemplo:

Warning: layout view 'Upright_Artwork' contains deprecated bezel element

Para resolver o problema, substitua estes itens:

  • bezel name= com element name=;

  • bezel element= com element ref=;

  • backdrop element= com element ref=;

  • </backdrop> com </element>;

  • overlay element= com element ref= e adicione blend="multiply" após o nome do elemento;

  • </overlay> com </element>;

  • </bezel> com </element>.

Fontes: Setembro 6, 7 e 9 de 2020.

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.

  • sstrangr.lay

    Um caso simples da utilização de transparências coloridas para visualizar a separação e o destaque dos elementos em uma tela preto e branco.

  • seawolf.lay

    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.

  • armora.lay

    A tela deste jogo é vista diretamente por meio 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.

  • tranz330.lay

    Uma tela e um teclado alfanumérico com vários segmentos. As teclas são clicáveis e fornecem feedback visual quando pressionadas.

  • esq2by16.lay

    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 em uma linha e das linhas em uma página. As cores de grupo permitem que um único elemento seja usado para todas as quatro cores da tela.

  • cgang.lay

    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.

  • minspace.lay

    Exibe a posição de um controle deslizante com LEDs.

  • md6802.lay

    Usa grupos de forma efetiva como uma linguagem de programação para construir a imagem de um protoboard.

  • beena.lay

    Faz o uso de scripts com base em eventos para posicionar dinamicamente os elementos e automaticamente desenhar o conteúdo dos elementos.