---
title: 'Rajzok és grafikus képernyők'
url: 'https://wiki.teletypegames.org/hu/engines/rubbs/art'
markdown: 'https://wiki.teletypegames.org/hu/engines/rubbs/art.md'
lang: hu
date: '2026-11-01'
description: 'Egy képekre épülő board darabjai, mint a Teletype BBS TTG Klubház külseje: olvasható YAML-ben tartott rajzok, a modern arcon 256 színű ANSI-val, a Commodore 64-arcon nyers PETSCII-vel rajzolva; kurzoros menü a C64-es képen; widgetek, amelyek hotspotjai a board életét mutatják; és a napszak meg az …'
taxonomy:
  tag:
    - reference
---

# Rajzok és grafikus képernyők

> Egy képekre épülő board darabjai, mint a Teletype BBS TTG Klubház külseje: olvasható YAML-ben tartott rajzok, a modern arcon 256 színű ANSI-val, a [Commodore 64](https://wiki.teletypegames.org/hu/engines/rubbs/commodore)-arcon nyers PETSCII-vel rajzolva; kurzoros menü a C64-es képen; widgetek, amelyek hotspotjai a board életét mutatják; és a napszak meg az évszak a kép rétegeinek kiválasztásához. A 0.30.0 óta érhető el; minden osztály benne van az **[API-referenciában](https://wiki.teletypegames.org/rdoc/bbs/latest/)**.

| Darab | Mit csinál |
|---|---|
| `BBS::Petscii::Canvas` | A C64 képernyője 40×25 cellaként a nagybetűs/grafikus készletben, nyers PETSCII-ként ki: egy teljes kép, aztán foltok |
| `BBS::Art` | YAML-rajz: karakterek, színek, reverse, hátterek, hotspotok, rétegek; ki ANSI-ra, FrameBufferre vagy Canvasra |
| `BBS::Petscii::CanvasMenu` | A `BBS::Selector` billentyűi és elemei Canvasra rajzolva |
| `BBS::Widgets::Scene` | Egy Art widgetként a modern arcon, a hotspotjai adatból kitöltve |
| `BBS::Season` | Hajnal, nappal, alkony vagy éjjel a nap szerint; hó, tök, tűzijáték, zászlófüzér |
| `art-preview` | Ellenőrzi és megmutatja a rajzot: ANSI, PETSCII-bájtok, C64-képernyőkép |

# A C64-vászon

A C64-nek két karakterkészlete van. A kisbetűsben, amelyet a `session.petscii!` és a `BBS::Petscii::Screen` beszél, mindkét betűméret megvan, de grafika nincs. A nagybetűsben vannak a kártyaszínek, golyók, lekerekített sarkok, átlók és negyedblokkok, ezért a kép ott készül. A `BBS::Petscii::Canvas` cellákként tartja a képernyőt és nyers PETSCII-t ír, amely a `session.write_raw`-val megy ki, a kisbetűs enkóder mellett:

```ruby
C = BBS::Petscii::Canvas
canvas = C.new
canvas.text(0, 1, 'TTG CLUBHOUSE', :yellow)
canvas.text(2, 1, '╭──╮', :light_blue)
canvas.text(3, 1, '█▀▀█', :orange)                 # reversed glyphs work too
session.write_raw(canvas.frame)                    # the whole screen

before = canvas.dup
canvas.text(2, 2, '●', :red)
session.write_raw(canvas.update(before))           # a patch: only the cell that changed
```

- A cellák Unicode-ból rajzolódnak: a betűk (nagybetűként), számjegyek és jelek úgy, ahogy vannak, a grafika aszerint, aminek látszik (`Canvas::GLYPHS`); a `█ ▀ ▐ ◢ ◣` fordított kódok (`GLYPHS_REVERSED`). A `Canvas.glyph?(ch)` megmondja, meg tudja-e mutatni a készlet a karaktert.
- A színek kódok (`Canvas::YELLOW`), nevek (`:light_blue`, `'dark grey'`), vagy az 1.0.0 óta a paletta színei is: `Canvas.color(YE) == Canvas::YELLOW`, így ugyanazok a `BBS::Petscii::Colors` konstansok szolgálnak ki egy Screent és egy vásznat.
- Az 1.0.0 óta egyetlen PETSCII-jeltábla van: a lekerekített sarkok (`╭╮╰╯`) ebben a nagybetűs készletben a `Petscii::ROUNDED_CORNERS`, és a negyedblokkokat (`▖▗▘▝▚`) is megrajzolja; egy Screen kisbetűs készlete a sarkokat szögletesen mutatja.
- A `frame` egyszer küldi el a képernyőt, minden sort az utolsó látható cellája után vágva; a `span(row, col, width)` egy sor egy szakaszát; a `patch(positions)` szétszórt cellákat, a kurzor a legközelebbihez lépve; az `update(before)` a folt és a teljes kép közül az olcsóbbat. Lassú vonalon cél: kb. 1,2 KB egy képernyő, 200 bájt alatt egy animációs lépés.
- A `feed(bytes)` úgy játssza rá a PETSCII-t a vászonra, ahogy a C64 képernyőszerkesztője kiírja: tesztben az, amit a hívó lát a bájtjaid után (a `canvas.changes(seen)` üres, ha egyeznek).

A vászon a TTG Gate BBS-ből jött; a bájtjai ugyanazok, mint a gate saját osztályáéi voltak.

# Rajzok YAML-ben

A rajz szöveg, kódként írjuk és nézzük át. A `chars` a kép, a `colors` cellánként egy palettabetű (`.` = nincs szín), a `reverse` opcionális `R`-ek, a `background` opcionális (csak cp437: a félblokk második színe), a `hotspots` a cellák, amelyeket a board az adataiból tölt ki, a `layers` a ráhelyezhető további fájlok. Minden sor pontosan `size[0]` széles, a sorvégi szóközökkel együtt. A rácsokat `|2`-vel írd: behúzásjelző nélkül a YAML az első sorból veszi a behúzást, és a szóközzel kezdődő sor szétveri a blokkot.

PETSCII-rajz a C64-arcra:

```yaml
# art/house.yml
size: [20, 6]
charset: petscii-upper     # only glyphs the uppercase set shows
palette:                   # C64 colour names, 256-colour indexes or '#rrggbb'
  y: yellow
  r: red
  b: brown
  g: dark grey
  w: white
chars: |2
       ◢████◣    ●    
      ◢██████◣        
      │ ▒  ▒ │        
      │ ▒  █ │  TTG   
  ▁▁▁▁│▁▁▁▁█▁│▁▁▁▁▁▁▁ 

colors: |2
  .....rrrrrr....y....
  ....rrrrrrrr........
  ....b.y..y.b........
  ....b.y..b.b..www...
  ggggbggggbgbggggggg.
  ....................
hotspots:
  window_1: [6, 2]         # [col, row], 0-based
  window_2: [9, 2]
  sign: [14, 3, 3, 1]      # [col, row, width, height]
layers: [snow]             # art/snow.yml
```

Egy réteg: a szóköz átlátszó, az `at` helyezi el, a palettája és a hotspotjai hozzáadódnak a rajzéhoz.

```yaml
# art/snow.yml: a layer; spaces are transparent, 'at' places it
size: [10, 1]
at: [4, 0]
palette:
  w: white
chars: |2
   ▄▄▄▄▄▄   
colors: |2
  .wwwwww...
```

cp437-rajz a modern arcra, hátterekkel:

```yaml
size: [24, 4]
charset: cp437
palette: { y: yellow, o: 208, n: '#203060', w: white }
chars: |2
  ╔══════════════════════╗
  ║ ▄▀▀▄ TTG CLUBHOUSE ☼ ║
  ║ ▀▄▄▀  open tonight   ║
  ╚══════════════════════╝
colors: |2
  oooooooooooooooooooooooo
  oyyyyywwwwwwwwwwwwwwwyyo
  oyyyyyyyyyyyyyyyyyyyyyyo
  oooooooooooooooooooooooo
background: |2
  ........................
  .nnnnnnnnnnnnnnnnnnnnnn.
  .nnnnnnnnnnnnnnnnnnnnnn.
  ........................
hotspots:
  tonight: [8, 2, 13, 1]
```

Használat:

```ruby
house = BBS::Art.load('art/house.yml').with(*BBS::Season.now.layers)  # only the layers it lists
session.write(house.ansi)                        # modern: 256-colour ANSI lines
house.draw(frame, x: 1, y: 2)                    # … or into a BBS::FrameBuffer
session.write_raw(house.petscii)                 # C64: a whole frame
house.draw_canvas(canvas, row: 1, col: 10)       # … or onto a canvas
house.hotspot(:window_1)                         # => Art::Hotspot col=6 row=2 …
```

A `with` csak a fájlban felsorolt rétegeket rakja rá, így a `with(*season.layers)` bármely rajznál biztonságos. A hiba `BBS::Art::Error`-t dob a fájl, a rács, a sor és az oszlop nevével: rossz szélességű sor, a palettában nem szereplő betű, a karakterkészletben nem megjeleníthető jel, a rajzon kívüli hotspot. A palettaszínek C64-nevek, 256-os indexek vagy `#rrggbb`; mindegyikhez a másik arc legközelebbi színe tartozik, így a PETSCII-rajz ANSI-ban is megnézhető, és fordítva.

# Kurzoros menü a vásznon

A `BBS::Petscii::CanvasMenu` egy vászonra rajzoló `BBS::Selector`: ugyanazok az elemek, billentyűk és opciók (CRSR, RETURN, az elem betűje, RUN/STOP vagy ← vissza, számok, oszlopok, lapok, az about- és a hint-sor), a kiválasztott elem fordított sáv a saját színében. Minden lépés a megváltozott cellák foltjaként megy ki: egy 16 elemes, kétoszlopos menüben kb. 140 bájt.

```ruby
S = BBS::Selector
items = [S.item('CORK BOARD', key: 'm', value: :boards, color: :light_blue, about: 'READ AND POST'),
         S.item('GAMES ROOM', key: 'r', value: :doors, color: :orange),
         S.item('LEAVE',      key: 'q', value: :quit)]
menu = BBS::Petscii::CanvasMenu.new(session, items, canvas: canvas, row: 12, col: 1, width: 36,
                                    columns: 2, hint: 'CRSR: CHOOSE  RETURN: OPEN', idle: 0.5) do
  smoke.step(canvas)                 # bytes for an animation step while the caller thinks
end
menu.paint
session.write_raw(canvas.frame)      # the picture with the menu on it
choice = menu.run(drawn: true)       # without drawn: it paints and sends its rows itself
```

Az `idle:` másodperccel és egy blokkal a blokk akkor fut, amíg a hívó gondolkodik, és egy animációs lépés bájtjait adja vissza; egy leütés azonnal véget vet a várakozásnak.

A menü színszerepei a Selectoréi, a `BBS::Selector::COLORS` (`text key bar heading about hint`), amelyeket a vásznon C64-kódokká alakít; a `colors:` ezek közül írhat felül néhányat. (A `CanvasMenu::COLORS` az 1.0.0-ban megszűnt.)

# Jelenetek a modern arcon

A `BBS::Widgets::Scene` egy rajz widgetként: a rajz, majd minden hotspot a blokkjával kitöltve minden renderkor. A blokk Stringet ad (ANSI-színekkel, magasabb hotspotnál sorokkal) vagy `nil`-t, ha marad a rajz; amit visszaad, a hotspotjára vágódik.

```ruby
scene = BBS::Widgets::Scene.new(art: house, align: :center)
scene.fill(:window_1) { node_busy?(1) ? "\e[38;5;220m▓" : nil }
scene.fill(:sign)     { |spot| "\e[1;37m#{inside_count}".ljust(spot.width) }
```

A rajznál kisebb terminálon a jelenet a határaira vágódik; `crop: false`-szal semmit sem rajzol, és a `fits?` megmondja egy dashboardnak, mutassa-e egyáltalán a képet.

# Napszak és évszak

A `BBS::Season.now(tz: 'Europe/Budapest', at: Time.now, jam: nil)` megadja a napszakot a nap állása szerint (a `:dawn` és a `:dusk` a napkelte és napnyugta körüli 45-45 perc, a `latitude:` és `longitude:` helyre számolva, alapból Budapest), az évszakot és az érvényes rétegeket:

| Réteg | Mikor |
|---|---|
| `:snow` | decembertől februárig |
| `:pumpkin` | október 24–31. |
| `:fireworks` | december 31., és újév napján 6:00-ig |
| `:bunting` | a `jam:` napjain (Date-ek Range-e) |

```ruby
s = BBS::Season.now(jam: Date.new(2027, 1, 8)..Date.new(2027, 1, 10))
s.time_of_day          # => :night
s.layers               # => [:snow]
art = BBS::Art.load("art/house_#{s.night? ? 'night' : 'day'}.yml").with(*s.layers)
```

Tiszta függvény: a teszt átadja az `at:`-et. Az EU-s időzónák és a fix eltolások (`'+01:00'`) más gem nélkül működnek; más IANA-névhez a `tzinfo` kell.

# art-preview

A gem futtatható programja (`bundle exec art-preview`, vagy rubbs-checkoutban `bin/art-preview`) a rajz ellenőrzésére rajzolás közben:

```
art-preview art/house.yml                   # ANSI here, then size, palette, hotspots, PETSCII bytes
art-preview art/house.yml -l snow -s        # a layer stacked, the hotspots marked
art-preview art/house.yml --hex             # the PETSCII frame as a hex dump
art-preview art/house.yml --petscii h.seq   # the raw frame, e.g. for a C64 terminal program
art-preview art/house.yml --png h.png       # a C64 screenshot of the frame (VICE's character ROM)
```

A `--png` a PETSCII-képet egy vászonra játssza, és a VICE által telepített C64-karakter-ROM-mal rajzolja ki (más ROM: `--chargen FILE` vagy `$C64_CHARGEN`); a ROM nem része a gemnek. Az emulátoron belüli ellenőrzéshez töltsd be a `--petscii` fájlt egy C64-es terminálprogramba, vagy nézd a boardot VICE-ban.

'; var prevLevel = 2; for (var i = 0; i < headings.length; i++) { var h = headings\[i\]; var text = h.textContent.trim(); var slug = text.toLowerCase() .replace(/\[áàâä\]/g,'a').replace(/\[éèêë\]/g,'e').replace(/\[íìîï\]/g,'i') .replace(/\[óòôöő\]/g,'o').replace(/\[úùûüű\]/g,'u') .replace(/\[^a-z0-9\]+/g, '-').replace(/^-|-$/g, ''); if (!slug) slug = 'heading-' + i; if (used\[slug\]) { slug += '-' + i; } used\[slug\] = true; h.id = slug; var level = parseInt(h.tagName.charAt(1)); var cls = level > 2 ? ' class="toc-child"' : ''; html += '- [' + text + '](#'%20+%20slug%20+%20')
'; } html += '
'; toc.innerHTML = html; // Scroll-spy: highlight current heading var links = toc.querySelectorAll('a'); var ids = \[\]; for (var j = 0; j < links.length; j++) ids.push(links\[j\].getAttribute('href').slice(1)); function updateActive() { var current = ''; for (var k = 0; k < ids.length; k++) { var el = document.getElementById(ids\[k\]); if (el && el.getBoundingClientRect().top <= 80) current = ids\[k\]; } for (var m = 0; m < links.length; m++) { links\[m\].parentNode.classList.toggle('active', links\[m\].getAttribute('href') === '#' + current); } } window.addEventListener('scroll', updateActive, { passive: true }); updateActive(); })();

---

## Navigation

- Parent: [RuBBS](https://wiki.teletypegames.org/hu/engines/rubbs.md)
- Previous: [Application](https://wiki.teletypegames.org/hu/engines/rubbs/application.md)
- Next: [Változásnapló](https://wiki.teletypegames.org/hu/engines/rubbs/changelog.md)
