Gameduino Documentation (neoficiální)

license license license

Gameduino - shield, poskytuje možnost připojit Arduino k VGA monitoru a audio výstupu. Základem grafického zobrazení jsou tři nezávislé tile-based vrstvy.

Některé základní metody knihovny Gameduino

Jiné metody: http://excamera.com/sphinx/gameduino/GD/index.html

Vykreslování na obrazovku

Gameduino poskytuje rozlišení 400 * 300px přes VGA konektor. Celý výstup tvoří tři nezávislé vrstvy. Platí pravidlo, že pokud barva pixelu v určité vrstvě je nastavena na průhlednou, tak je vidět vrstva, jenž se nachází pod ní.

Background color

Nejspodnější vrstva. Tvoří ji jediná barva (16 bitů), jenž se zadává přímo do registru.

GD.wr16(BG_COLOR, RGB(255,0,0));

Background

Vrstva o rozměru 512 * 512px, ale ve skutečnosti můžeme vidět pouze 400 * 300px. Pokud není posunutá, pak pixely x:401-512 a y:301-512 se nachází mimo obrazovku. Posun vrstvy se zadává v pixelech v registru, každý s pomocí 16 bitů (0-511).

GD.wr16(SCROLL_X, 0);
GD.wr16(SCROLL_Y, 0);

Poznámka: character (znak) - základní jednotka pro vykreslování obrazovky, tvořen maticí pixelů 8x8

Celá vrstva background je vykreslována s pomocí 4096 znaků (64 řádků, 64 sloupců). Celkově může být definováno 256 různých znaků (v RAM_CHR). V rámci jednoho znaku lze použít nejvýše 4 unikátní barvy.

Vrstvu můžeme v paměti rozdělit do tří skupin: RAM_PIC, RAM_CHR, RAM_PAL. RAM_PIC přímo udává, co se bude vykreslovat, RAM_CHR a RAM_PAL slouží jako úložiště.

RAM_PIC

Do této vrstvy se umístí 4096 (64 * 64) pořadových čísel znaků (0-255, neboli 0x00-0xff), jenž se mohou libovolně opakovat. Hodnoty všech znaků jsou uloženy v RAM_CHR. Viditelná část vrstvy má 50 znaků na šířku a 37.5 znaků na výšku.

Příklad (viz tabulka):

V paměti by daná vrstva vypadala takto:

static flash_uint8_t pictureOfChars[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ... 0x00,
   0x00, 0xff, 0xff, 0xff, 0xff, 0x00, ... 0x00,
   0x00, 0xff, 0x00, 0x00, 0x00, 0x00, ... 0x00,
   0x00, 0xff, 0x00, 0xff, 0xff, 0x00, ... 0x00,
   0x00, 0xff, 0x00, 0x00, 0xff, 0x00, ... 0x00,
   0x00, 0xff, 0xff, 0xff, 0xff, 0x00, ... 0x00,
   ...
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ... 0x00
}

RAM_CHR

V této části paměti je definováno, jak mají vypadat jednotlivé znaky.

Znak je matice pixelů o rozměrech 8 * 8, každý pixel je popsán s pomocí 2 bitů (2^2 kombinací = 4 různé barvy), jenž uchovávají pořadové číslo barvy daného znaku (princip masky). 4 barvy pro každý znak jsou uloženy v RAM_PAL.

Příklad (viz tabulka): Tento znak tvoří čtyři barvy (00 = fialová, 01 = zelená, 10 = modrá, 11 = žlutá), další barvu nelze přidat (neexistují jiné kombinace dvou bitů). Využijeme tento znak a definujeme ho jako první a druhý znak v paměti:

// Definujeme znaky v AVR flash pameti (zbytek pameti je vyplnen 0x00, viz GB.begin())
static flash_uint8_t chars[] = {
   // 0. znak
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   // 1. znak
   0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0xaa, 0xff, 0xaa, 0xff, 0xaa, 0xff, 0xaa, 0xff,
   // 2. znak
   0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0xaa, 0xff, 0xaa, 0xff, 0xaa, 0xff, 0xaa, 0xff,
}

// Zkopirujeme z AVR flash pameti do pameti gameduina
GD.copy(RAM_CHR, chars, sizeof(chars));

RAM_PAL

Pamět pro uložení 4 barev (8 bytů) pro každý ze 256 znaků. Každá barva je uložena v little endian (viz barva).

Příklad: Využijeme znaku, jenž byl popsán v RAM_CHR, barvy definujeme jako:

Barva Big Endian Little Endian
fialová 0x4d17 0x174d
zelená 0x4f8b 0x8b4f
modrá 0x32db 0xdb32
žlutá 0x467b 0x467b
// Definujeme barvy v AVR flash pameti
static flash_uint8_t colors[] = {
   // Barvy 0. znaku (nedefinovane - hodnoty 0x00 )
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   // Barvy 1. znaku
   0x8b, 0x4f, 0xdb, 0x32, 0x46, 0x7b, 0x17, 0x4d,
   // Barvy 2. znaku
   0x8b, 0x4f, 0xdb, 0x32, 0x46, 0x7b, 0x17, 0x4d,
   ...
}

// Zkopirujeme z AVR flash pameti do pameti gameduina
GD.copy(RAM_PAL, colors, sizeof(colors));

Foreground

Vrstva je určena pro zobrazovaní spritů na monitoru, každý sprite má rozměry 16 * 16px (větší sprity lze skládat ze základních). Každému z možných 256 spritů na obrazovce je v RAM_SPR přiřazena právě jedna paleta a právě jedna z masek, podle které se budou zobrazovat barvy z vybrané palety.

Gameduino může v sobě uchovávat 64 256-barevných masek spritů, nebo 128 16-barevných masek spritů, nebo 256 4-barevných masek spritů. Nebo jejich libovolné kombinace.

Příklad: Do paměti uložíme 63 256-barevných, jednu 16-barevnou a dvě 4-barevné masky. Celkový počet různých masek je 66.

Příklad: Definujeme jeden ze 64 256-barevných spritů, můžeme ho vykreslit na obrazovku až 256krát.

Může být definováno 8 unikátních palet. Čtyři palety obsahující 256 barev v RAM_SPRPAL, dvě obsahující 16 barev v PALETTE16A a PALETTE16B a dvě obsahující 4 barvy v PALETTE4A a PALETTE4B.

Poznámka: Maximální počet různých spritů můžeme zvětšit takovým způsobem, že definujeme např. 64 různých 256-barevných postaviček (masek) s tím, že každá bude mít 4 vlastní různobarevné kopie (palety). První postavička bude nejdřív využívat první 256-barevné palety a bude zelená. Pak bude využívat druhé palety a bude fialová. Celkový počet kombinací 256-barevných postaviček bude 64*4=256.

RAM_SPR

Uchovává vlastnosti používaných spritů, každý je určen přesně 32 bity. Maximální počet spritů na obrazovce je 256. Zápis se provádí s pomocí metody GD.sprite(…). Číslo spritu (int spr) zárověn určuje z-index překrytí - větší pořadové číslo překrývá sprity s menšími čísly.

RAM_SPRIMG

Tato pamět obsahuje masky pořadových čísel barev 64 spritů, podle které se budou vybarvovat jednotlivé pixely v závislosti na barevné paletě vybrané v RAM_SPR.

Příklad (viz tabulka): Maska 256-barevného spritu. První sloupec a první řádek pixelů jsou vyplněny barvou s pořadovým číslem 1. Z barvy číslo 255 je napsaný symbol S, zbytek je vyplněn barvou číslo 0. Pokud na danou masku aplikujeme první 256-barevnou paletu, kde 0. barva bude bílá, 1. modrá a 255. šedá, tak dostaneme podobný vzhled, jako v tabulce. Pokud však aplikujeme na masku paletu, kde 255. barva je definována jako průhledná, tak získáme “děravý” sprite, přes který bude vidět background vrstva.

Jeden sprite 256 barev

Máme 16 * 16 = 256 hodnot typu 0b 0000 0000. Každá hodnota se čte jako celek a určuje pořadové číslo barvy (0-255).

Jeden sprite 16 barev

Pokud chceme vytvořit 16-barevný sprite, tak si teoreticky rozdělíme jeden 256-barevný sprite na dva. Pak máme 16 * 16 = 256 hodnot typu 0b 0000 0000, kde první čtveřice bitů určuje pořadové číslo barvy (0-15) prvního spritu a druhá čtveřice (0-15) druhého. To, jaká čtveřice se bude číst (o jaký ze dvou spritů se bude jednat), je určeno v RAM_SPR 4 bitama typu PALETTE (viz PALETY).

Jeden sprite 4 barev

Pro vytvoření spritu ze 4 barev uplatníme stejný postup, jako pro 16-barevný. Ted dělíme jeden 16-barevný na dva 4-barevné. Máme 16 * 16 = 256 hodnot typu 0b 00 00 00 00. První dva bity určují pořadového číslo barvy prvního spritu atd.

PALETY

Uživatel může definovat 8 unikátních palet pro vykreslování spritů.

Ukazatel Paměť Jedna hodnota Kapacita hodnot
RAM_SPRPAL 4 * 512 bytes 2 bytes 4 * 256
PALETTE16A 32 bytes 2 bytes 16
PALETTE16B 32 bytes 2 bytes 16
PALETTE4A 8 bytes 2 bytes 4
PALETTE4B 8 bytes 2 bytes 4

Palety obsahující 256 barev nemají každá svůj ukazatel, jsou definovány jen ukazatelem na první 256 barevnou paletu. Ukazatel na druhou je (RAM_SPRPAL + 256). Palety PALETTE16A, PALETTE16B, PALETTE4A a PALETTE4B se v rámci paměti nachází v registru.

Definovat barvy v paletách lze např. tímto způsobem:

// Definovat 4 palety po 256 barvach (vsechny barvy v palete jsou bile)
// (i << 1) jsou suda cisla, protoze zapisujeme po 2 bytech
for (i = 0; i < (4 * 256); i++)
   GD.wr16(RAM_SPRPAL + (i << 1), RGB(255,255,255));  

// Definovat 16 barev v paletach
for (i = 0; i < 16; i++) {
   GD.wr16(PALETTE16A + (i << 1), RGB(255,255,255));
   GD.wr16(PALETTE16B + (i << 1), RGB(255,255,255));
}

// Definovat 4 barvy v paletach
for (i = 0; i < 4; i++) {
   GD.wr16(PALETTE4A + (i << 1), RGB(255,255,255));
   GD.wr16(PALETTE4B + (i << 1), RGB(255,255,255));
}

Příklad: http://excamera.com/sphinx/gameduino/samples/palettes/

Přiřazení palety spritu (PALETTE)

BIT 3 2 1 0
256 barev 0 0 P P
16 barev 0 1 N A/B
4 barvy 0 N N A/B

Rotace spritu

Rotaci spritu zajištují 3 bity v paměti RAM_SPR. Podle osy x překlápí 11. bit, podle y 10. bit, 9. bit překlápí sprite diagonálně. Všechny možné typy rotací a jejich decimální hodnota jsou uvedeny v tabulce.

Kolize spritu

Všechny sprity se dělí na dva typy J (31.bit = 0) a K (31.bit = 1). Ke kolizi může dojít vždy pouze mezi dvěma spritama různých typů.

Barva

Každá definovaná barva zabírá 16 bitů paměti. Nejvyšší bit (typ A) určuje, zda tato barva je průhledná. Pokud ano, tak zbytek hodnot je ignorován, tzn. nelze vytvořit poloprůhlednou barvu.

Bity 0-14 jsou určeny pro ukládání odstínu. Většinou jsme zvyklí definovat každou z RGB barev hodnotou 8 bitů a mít 256 * 256 * 256 = 16 777 216 barevných odstínů. Zde je každá ze základních barev určena 5 bity. Při transformaci z normálního obrázkového formátu dochází k přemapování hodnoty z 0-255 na 0-31, což snižuje celkový počet barevných odstínů na 32 768.

Pro ukládání barvy se používá little endian. Příklad uložení zelené barvy:

Formát Hodnota
RGB (0, 255, 0)
Binární gameduino 0b 0000 0011 1110 0000
Hexadecimální gameduino 0x0BE0
Hexadecimální little endian 0xE00B

Kodér obrázků a (de)komprese

Téměř určitě vznikne potřeba použít vlastní grafiku pro zobrazení na monitoru. Převod do hexadecimální podoby zajistí Gameduino Encoder.

Background Encoder

Při standardním spuštění je vytvořen soubor image.h, jenž zabírá víc místa, ale popisuje všechny hodnoty v přesně zadaném pořadí. Takový soubor se do paměti gameduina ukládá jako:

// Nahrat poradova cisla znaku do pameti, pocatek v RAM_PIC (0x0000)
for (byte y = 0; y < 1; y++)
   GD.copy(RAM_PIC + y * 64, image_pic + y * 1, 1);
// Nahrat znaky do pameti, pocatek v RAM_CHR (0x1000)
GD.copy(RAM_CHR, image_chr, sizeof(image_chr));
// Nahrat barvy znaku do pameti, pocatek v RAM_PAL (0x2000)
GD.copy(RAM_PAL, image_pal, sizeof(image_pal));

Pokud je potřeba šetřit pamětí arduina, tak existuje možnost komprese obrázku (compress). Pak se do paměti gameduina musí nahrávat s dekompresí:

for (byte y = 0; y < 1; y++)
   GD.copy(RAM_PIC + y * 64, image_pic + y * 1, 1);
GD.uncompress(RAM_CHR, image_chr);
GD.uncompress(RAM_PAL, image_pal);

Pokud je počátek každé z pamětí již vyplněn daty, tak je potřeba posunout ukazatelé na vhodné místo.

Foreground Encoder

Oficiální stránky nefungují (500 Internal Server Error). Existuje možnost samostatně stáhnout python package a kodovat obrázky na vlastním počítači.

Gameduino: Python package

Python Gameduino

Existuje oficiální package gameduino v rámci python 2.7, jenž poskytuje některé funkce pro práci s shieldem. Je však zastaralý a po instalaci potřebuje manuálně upravit zdrojový kod.

Mezi užitečné funkce patří např. kodér background, kodér spritů, screenshot obrazovky (bez využití fyzického shieldu).

Install

Poznámka: Následující instrukce byly testovány pouze na macOS Sierra 10.12.4

# python 2.7 required
easy_install pip       # Install pip
pip install pillow     # Install lib for images
pip install gameduino  # Install gameduino

Ujistěte se, že Váš $PYTHONPATH je v pořádku.

# Launch python & check import Image
python
>>> from PIL import Image

Při spuštění ukázkových skriptů z ./python/ se budou generovat jednotlivé chyby. Především se to týká import Image, jenž se má v souborech gameduino nahradit za from PIL import Image, a různých názvů deprecated funkcí/metod, jenž se mají nahradit podle chybové zprávy (pouze název).

Skripty ./python/

Příklady

Font 8x16px (background)

Gameduino má standardní ascii font definovaný na background vrstvě jako znaky 8 * 8px. Pokud chceme využívat lépe vypadající font 8 * 16px, musíme nahrát do paměti již připravený obrázek prvních 128 znaků cp437 pro background vrstvu (bílá písmena na černém pozadí).

#include <SPI.h>     // SPI interface
#include <GD.h>      // Gameduino
#include "cp437.h"   // Hexadec hodnoty 128 symbolu, jejich palety

static int atxy(int x, int y) {
   // Zjistit poradove cislo pointeru z background vrstvy (z 4096 znaku)
   // Y urcuje y-sovou souradnici radku, jelikoz font je 16px na vysku, tak musi preskocit 1 radek znaku 8 * 8px
   //    "Hello":
   //       (0 << 7) + 0 = 0
   //    "This is the cp437"
   //       (1 << 7) + 2 = (64 + 64) + 2 = 130
   //       Preskocime 1. a 2. radek 64 znaku (muze byt zabrany jinym textem), jsme na 3. radku znaku, 3. znak zleva
   //
   // X urcuje x-sovou souradnici v kazdem radku
   return (y << 7) + x;
}

static void drawstr(uint16_t addr, const char *s) {
   while (*s) {                                       // Pro kazdy symbol
      uint16_t w = pgm_read_word(cp437_pic + 2 * *s); // Je urcen 16 bity (horni a dolni cast 8 * 8px kazdeho symbolu)
      GD.wr(addr, lowByte(w));                        // Horni cast 8 * 8px na urceny uzivatelem radek znaku
      GD.wr(addr + 64, highByte(w));                  // Dolni cast 8 * 8px na dalsi radek znaku
      s++, addr++;                                    // Bere dalsi symbol a dalsi pozici horniho znaku
   }
}

void setup() {
   GD.begin();                                   // Inicializace gameduina
   GD.uncompress(RAM_CHR, cp437_chr);            // Dekomprese znaku a ulozeni od prvniho pointeru v RAM_CHR
   GD.uncompress(RAM_PAL, cp437_pal);            // Dekomprese palety a ulozeni od prvniho pointeru v RAM_PAL
   drawstr(atxy(0, 0), "Hello");                 // Od pozice symbolu 8 * 16px [0:0] vypis text 
   drawstr(atxy(10, 2), "This is the cp437");    // Od pozice symbolu 8 * 16px [10:2] vypis text
   for (byte i = 0; i < 14; i++)
      drawstr(atxy(i, 4 + i), " *Gameduino* ");  // Od pozice symbolu 8 * 16px [0,4], [1,5] ... vypis text
}

void loop(){}

Licence

Toto dílo podléhá licenci Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Creative Commons License