ANSI エスケープシーケンスについて

投稿者: | 2014年4月5日

端末の色やカーソル制御を行うには、curses を利用するのが一般的ですが、ライブラリを利用しなくとも、シェル経由である決まりのエスケープシーケンス文字列を端末に投げることにより、色やカーソル制御を行うことができます。

次のページに詳しく記載があります。

Bash Prompt HOWTO: ANSI エスケープシーケンス: 色とカーソル操作

  • 昨今の bash では\033\eに設定されているため、例えば\033[\0m\e[0mと記述できます。
  • また色指定の\e[31mなどを\[\]で囲む記述がありますが、囲まなくても大丈夫のようです。
    • 例) \[\033[41m\]\[\033[1;37m\] string \[\033[0m\]\e[41;1;37m string \e[0m
    • だいぶすっきり。

前景色・背景色の指定

xtermなど、一般的な端末でサポートされている ANSI カラーの 8 色です。太字設定をすると、それぞれの色が高輝度・低輝度な 2 種類の色になるため、事実上 16 色になります。

0 1 2 3 4 5 6 7

赤の前景なら\e[31m、青の背景なら\e[44という風に、それぞれ30番台と40番台の1の位が上の表となります。また、太字(強調文字・高輝度な文字)表現にするには、\e[32;1mのようにセミコロン区切りで属性を加えてやります。この1桁の属性値は順番は問わないようで\e[1;32mでも同じ意味になります。 属性値は 1.太字、4.下線、5.点滅、7.反転、8.非表示 らしいです。

色の見え具合は端末によって結構変動します。上の表は原色のビビットカラーで表現していますが、最近のオサレな端末なんかは、色がギラギラして見えづらいことに対する配慮のためだろうと思いますが、デフォルトでは抑えた色で表現するよう定義されています。端末によっては色のバインドを変更することも出来ますし、見え具合はあんまり保証されていません。

また、xterm の拡張256色サポートをしている端末も結構あります。こちらはRGB値を数値指定して端末上に表現しているものですから、見え具合の差異は少ないと思われます。 256色サポートについては、5年ほど前に2chの「コンソールゲーム」スレに書き込んでいたログに書いていた内容がまだ残っているようです。

vim で簡単ドット絵エディタ

前に作っていたコンソールベースのゲームは、0から7をひたすら書いたテキストファイルをグラフィックファイルとして用いています。 シンタックスハイライトに次のように定義してやると、vim が簡単ドット絵エディタになります。

let b:current_syntax = "amap"
syn match g0 /0/
syn match g1 /1/
syn match g2 /2/
syn match g3 /3/
syn match g4 /4/
syn match g5 /5/
syn match g6 /6/
syn match g7 /7/
hi g0 ctermfg=gray ctermbg=black
hi g1 ctermfg=gray ctermbg=red
hi g2 ctermfg=gray ctermbg=green
hi g3 ctermfg=gray ctermbg=yellow
hi g4 ctermfg=gray ctermbg=blue
hi g5 ctermfg=gray ctermbg=magenta
hi g6 ctermfg=gray ctermbg=cyan
hi g7 ctermfg=gray ctermbg=white

前景色を見やすいように gray に設定しているので、set t_Co=256を設定しておかないといけないかもしれません。 あとは対象のファイルを開いてset syntax=amapしてやるだけです。

00000400000
00004440000
00444444400
44407440744
44411111444
04444444440

こういうテキストファイルが

you know what

例のアレのように表現されます。

作図っていうと、ビジュアルモードの矩形選択や一括置換が力を発揮しますね。

端末に表示するスクリプト

最近、ruby が楽しくて仕方ありません。上記のファイルを端末に表示するスクリプトです。libcaca の img2text の劣化みたいですね。

#!/usr/bin/env ruby
# -*- mode:ruby; coding:utf-8 -*- 

class String
  def number?
    self =~ /\A-?\d+(.\d+)?\Z/
  end
end

def printimg(file)
  File.open(file) do |f|
    buf = f.read
    cur = nil
    buf.each_char do |c|
      if c.number?
        if cur != c
          print "\e[4" + c + ";1m "
          cur = c
        else
          print " "
        end
      else
        print "\n"
      end
    end
    print "\e[0m"
  end
end

ARGV.each do |file|
  printimg file
  puts "---"
end

このスクリプトに

66666666666666666666607777707770777077777707707777777077777777777777777077700777
66666666666666666666077777077707770777777707770770777077777777777777777707077777
66666666666666666666607777077077770777077707777070777007777777777777777770777777
66666666666666666666660077077077770777077077777707077070777707770777777777077777
66666666666666666666666607077077770777077000077777707077077000770777777777077777
66666666666666666666666666607077707777077077700777770770000707070777077777077700
66666666666666666666666666607077707770707077777077777707707707707070707777707077
66666666666666666666666666660077707770707077777777777777770707777070770777700707
66666666666666666666666666666077707700000000007777777777000000000000000777707707
66666666666666666666666666666077707770777711177777777777777777111177770777707707
66666666666666666666666666666077707707077711117777777777777777111117707077707707
66666666666666666666666666666077770707777777777777777777777777777777777077707707
67774747477747774746677747774707770707171777777777777777777777771717777077070707
67466747474747474746674746746007770707777777777770777777777777777777770777070770
67466777477747746746674746746007770707777777777777777777777777777777770777070770
67466747474747474746674746740707770770777777777777777777777777777777707770770770
67774747474747474777477746707700777077077777711111111111111111111111111707111117
66666666666666666666666666077070777701111111111111111111111111117770111111711111
67474747477747774746677740700711111111111111111111111111111111177777011111111711
67474747474667466746674660040701111111111111111111111111111111117770111111111171
67774747474747774746667406740701111111111111111111111111111111111111111111111110
67474747474747466746666746740700111111111111111111111111111111111117770111111000
67474777477747774777477746707770011111111111110000000000111111111177777011110001
66666666666666666666666666077777000111111110000000000000000001111117770111110001

という内容のテキストを食わせてやると

charlot

こんな感じになります。

さらに、拡張256色モードを駆使すると

xterm-256-map

こんな具合に!

enter image description here

こんな具合に!!!

…ゲームなんかで多様する茶色や肌色みたいな非原色が使えるのは嬉しいんですが、256種類も色があると

  • vim で描けない
  • 管理したり作ったりするのが大変
  • 色のバインドに迷う

と、色々弊害も出てきます。

今回はすべて背景色だけで描画していますが、前景色付きの文字を組み合わせて微妙な色を表現したり、さらにはUnicode文字の網掛けなどを駆使しつつ広大な縦横文字数を持つ端末を必須にするなどすれば、事実上、なんでも表現ができてしまいそうな気がします。

表現力にこだわりたいなら、SDL でも GL でもフレームバッファでも何でも使えばいいと思うので、色のつかいすぎは「端末だってここまでできるんだよ!」的なデモ用途にしか使う意味がないのかもしれません。で、そうなると、前述の libcaca などの高品位なデモを見ていればいいわけで・・・。

ゲーム用途には、大小アルファベットと数字で表現できる64色+RGBバインドくらいがちょうどいいのかなーと思うこの頃です。
ちなみに、モノクロでダンプできる rogue はいろんな意味で美しいです。

コメントを残す

メールアドレスが公開されることはありません。