mieki256's diary



2017/12/01(金) [n年前の日記]

#1 [raspberrypi][love2d] 端末へのキー入力を無視してくれる shut-term-keys をRaspberry Pi Zero Wにインストールしてみたり

Raspberry Pi Zero W上で、OpenGL ESを有効にしたSDL2を使うと、キー入力をした際、入力したキーが端末上にも打ち込まれてしまって非常に危ない。うっかり「rm *[Enter]」なんて打ち込んだ日にはどうなることやら。 *1

そのあたりのキー入力を無視する状態にできる、shut-term-keys というプログラムがあるらしいので、試しに導入してみたり。ちなみに、Raspberry Pi1上で gosu をインストール手順について説明してる、 _Getting Started on Raspbian (Raspberry Pi) - gosu/gosu Wiki で紹介されてた。

_inoremap/shut-term-keys: Workaround for keystrokes leaking into terminal on Raspbian

作業環境は Raspberry Pi Zero W + raspbian jessie。 *2

git でクローンして make してビルド。
git clone https://github.com/inoremap/shut-term-keys.git
cd shut-term-keys
make

shut-term-keys というプログラムが生成された。

使い易い場所にコピーしておく。今回は ~/bin を作って、そこに入れておくことにした。
mkdir ~/bin
cp shut-term-keys ~/bin

~/.bashrc を編集して、~/bin にパスを通す。
export PATH="$PATH:/home/USERNAME/bin"

反映させる。
source ~/.bashrc

これで、shut-term-keys というコマンド(?)が使えるようになったはず。

例えば love2d で使ってみる。
shut-term-keys love hoge
キー入力が端末に打ち込まれなくなった。ありがたや。
*1: OpenGL を有効にしたSDL2を使う場合は、こういった問題は起きない。あくまで、OpenGL ES を有効にしたSDL2を使ってる場合の話。
*2: Raspberry Pi Zero W + raspbian stretch では、音声を鳴らせる SDL2 がビルドできないので、仕方なく raspbian jessie を使ってる。

2017/12/02() [n年前の日記]

#1 [cg_tools] ボス敵の画像を作成中

ボス敵が欲しいなと思えてきたのでボス敵の画像を作成中。

Kritaを使って下絵を描いた。640x480のゲーム画面だから、倍の1280x960で新規画像を作成。グレースケールに見える絵を描いてから、フィルタレイヤーを載せて、影、ハイライト等の色を変更するフィルタを利用。縮小してpngでエクスポート。EDGE2で読み込んでドット修正。

最初からドットを打って作ったソレとは違う印象の画像になってしまった。うーん。でもまあ、いいか…。

#2 [anime] 「3月のライオン」が録画できてなかった

NHK総合で放送されてる「3月のライオン」が録画できてなかったことに気づいてなんだかもやもや。あのアニメの放送時間ってガンガンずらされてしまう上に、何故か東芝製HDDレコーダは件の番組の放送時間変更に追従できなくて、たまに録画失敗するという。1期の放送中も同じことが…。自分だけかと思ったら東芝製品ユーザが何故か結構録画失敗していて、NHKは一体何をやらかしたんだと…。いや、どう考えても東芝製品がダメなんだろうけど。

もういいや。こんなアニメ見ねえよ。というか見れねえよ。視聴中止するしかないよ。NHKのこんな妙ちくりんな放送スケジュールには追従できませんがな。

と若干自棄になったけど、そのうち再放送するだろうから、その際に視聴することにしよう…。初回放送時の視聴は半分諦めモードで。録画できてたら「うわあ、珍しいなあー、ラッキーだわー」なノリで…。ということで、再放送してくれることに望みをかける方向で。

にしても、2期になったらかなりキツイ内容になってきたなと…。この歳になって、アニメの中の登場人物をぶん殴りたい気分になったのは久々というか。やはり小中学校のあらゆる場所に監視カメラは必要だな…と思ったけど対応する側がアレでは効果が無いのか。

2017/12/03() [n年前の日記]

#1 [love2d] ボス敵作成中

love2d でボス敵作成中。

せっかくステージの両端に壁があるのだから、ボスの動きや攻撃に利用したい…ところだけど、どうしたもんか。壁に足でぶら下がってる、とか。撃った弾が壁で反射、とか。ボスが壁にぶつかって反射、とか。壁をゴリゴリ削ってアタリを持った破片が飛び散る、とか。でも、キツイ攻撃になりそうな気もする。

2017/12/04(月) [n年前の日記]

#1 [nitijyou] 部屋の電灯を弄った

部屋の電灯のスイッチが切り替わらなくなったので、天井から外して分解して少し弄った。ちなみに、紐がぶら下がってるタイプ。未だに蛍光灯。

脚立を持ってきて外そうとしたけど足場が確保しづらくて苦労した、けど、なんとか外せた。とりあえず、スイッチ部分にKUREの接点復活スプレーを吹いてみた。一応動くようにはなったけど…どうかな…。

ちなみに以前も同じ症状になったことがあって。その時の自分はどうやら頭がどうかしてたようで、天井にぶら下がってる状態、つまりは通電してる状態で接点復活スプレーをかけてしまって、火がボワッとついて紐が燃えて布団の上に火のついた紐が落っこちてきて…。ホント、あの時は何を考えていたのだか…。常識で考えれば火事になると分かるだろうに…。

2017/12/05(火) [n年前の日記]

#1 [dtm] AudacityでVSTプラグインが使えなくて悩んだり

ゲーム用のSE(効果音)を作ろうと、Windows10 x64上で _Audacity 2.2.0 を起動したのだけど、VSTプラグインが使えなくて悩んだり。

巷の解説ページによると…。
  1. Audacityインストール場所\Plug-Ins\ 以下に、VSTプラグインの .dll をコピー。
  2. Audacity起動後、エフェクト → プラグインの追加/削除を選んで各プラグインの有効無効を切り替え。
これで使えるようになる、はず、らしいのだけど…。有効にしてOKを押してみても反映されない。また無効に戻ってしまう。

アレコレ試してみたけれど、結局、以下の作業をしたら、ようやく反映された。
  1. Audacity 2.2.0 をコントロールパネル経由でアンインストール。
  2. 不具合修正されてることを期待して、Audacity 2.2.1rc2 をDL。
  3. 今まで C:\Prog\Audacity2.0\ にインストールしてたけど、デフォルトのインストール場所の C:\Program Files (x86)\Audacity\ に、Audacity をインストール。もしかすると Audacity は各フォルダの場所を決め打ちにして作ってあるのではないかと思えてきたので。
  4. LADSPA_plugins-win-0.4.15.exe をインストール。C:\Program Files (x86)\Audacity\Plug-Ins\ 以下にインストールした。
  5. Lame_v3.99.3_for_Windows.exe をインストール。C:\Program Files (x86)\Lame For Audacity\ にインストールした。
  6. ffmpeg-win-2.2.2.exe をインストール。C:\Program Files (x86)\FFmpeg for Audacity\ にインストールした。
  7. Audacity は C:\Program Files (x86)\Steinberg\Vstplugins\ を自動で読みにいくらしいので、Audacity で使いたいVSTの .dll を件の場所にコピー。
  8. 設定ファイルが入ってるのであろう、C:\Users\ユーザ名\AppData\Roaming\audacityフォルダを全削除。
この状態で Audacity を起動してプラグインの追加/削除を選んでみたら、VSTプラグインが「NEW」表示になって、有効にすることができた。

Audacity 2.2.1 rc2 は以下から辿って入手。

_Audacity 2.2.1 Release Candidate 2 | Audacity

LADSPA plug-ins、LAME MP3 encoder、FFmpeg import/export library は、以下から辿って入手。

_Windows | Audacity

Audacity が C:\Program Files (x86)\Steinberg\Vstplugins\ を自動で読みに行くという話は、以下で見かけた。

_KeroVeeのインストール | g200kg Music & Software

VSTプラグインの場所はユーザ側で指定できたら助かったのだけどな…。C:\Program Files (x86)\Steinberg\Vstplugins\ は他のDAWソフトも見に行くので、Audacity だけで使いたいVSTまで入れるのも…。いやまあ、それについては Audacity\Plug-Insフォルダに入れれば済むはず、なのだろうか。しかし何故自分の環境では反映されないのか…。

2017/12/06(水) [n年前の日記]

#1 [love2d] キャラ画像を作成し直し

love2dを使ってシューティングゲームっぽいものを書いてるのだけど、どうも自機や雑魚敵の画像が大き過ぎるように思えてきて。640x480の画面で、64x64ドットのキャラ画像を出していたけど、アクションゲームならともかく、スクロール系のシューティングゲームでは大き過ぎるようだなと…。

考えてみたら、「既存のその手のゲームの各キャラは、1280x720の画面ならどの程度の大きさになるか?」と検討して、64x64ドットというサイズを求めた気がする。しかし今回640x480の画面なのだから、もっと小さくしないとダメなんだよな…。

とりあえず48x48ドットで描き直し。EDGE2で縮小してからドット修正。

しかし今度はちと小さ過ぎる気がしてきた。うーん。

既存タイトルのキャプチャ画像を探してドットを測ってみたら…。アレ? 320x224の画面で雑魚敵は16x16ドットだな…。すると640x480の画面なら32x32ドット程度になるはず…。しかし実際にそのサイズにしてみるとますます小さ過ぎる印象に。横スクロールと縦スクロールでは、サイズに対する印象も違ってくるのかな…。どうもよく分からん…。

2017/12/07(木) [n年前の日記]

#1 [love2d] love2dのParticleSystemについて調べてたり

love2dのwikiを眺めていたら、ParticleSystem(パーティクルシステム)なる機能があることに気がついて。爆発エフェクトを作る際に便利かもしれないと思えてきたので、使い方について調べていたり。

_love.graphics.newParticleSystem (日本語) - LOVE
_ParticleSystem (日本語) - LOVE

紹介されてるサンプルを動かしてみたら、「もわ〜ん」な感じのパーティクルが出てきて、「これじゃ爆発には使えないのでは…」と思ったけれど。他のメソッドを呼んで色々設定してたら、もうちょっと勢いのある動きもつけられそうだと分かってきた。

とりあえず現時点で分かったことは…。

パーティクルエディタがあるらしい。 :

おそらくは love2d で実装されたパーティクルエディタがいつかあるらしい。パラメータを変えて動きを確認できる。

_Super Particle Editting and Rendering Machine - LOVE
_Super Particle Editting and Rendering Machine - Page 2 - LOVE

love2d 0.10.xで動く版は2ページ目の最後のあたりで公開されてる。ただ、setLinearDamping() (= 速度を減衰させる指定)はサポートしてない模様。

_emitor - Particle system editor - LOVE

love2d 0.10.2 でも動いたが、setSpread() (= 飛んでいく範囲を角度で指定)が無いので、全方向に飛んでいくようなパーティクルの動きは作りづらい。まあ、初速度が0の状態から加速していく動きなら作れるけど…。

_APE (Another Particle Editor) for LOVE2D - LOVE
_Monthly Update: April | Aeon of Sands

ape_v3.love が、love2d 0.10.2 でも動作した。setSpread()、setLinearDamping() もサポートしてる模様。爆発エフェクトに使えそうな動きも指定できた。

2017/12/08(金) [n年前の日記]

#1 [love2d] love2dでパーティクルを出すサンプル

love2dでパーティクルを出すサンプルソースをアップしてみたり。

ParticleSystemの使い方については、 _昨日の日記 を参照のこと。

こんな感じの画面に。

particle_test01_ss.gif

画像とソース。 :

License : CC0 / Public Domain ってことで。画像は以下。

_box01_64x64.png
_star02_48x48.png
_ball01_64x64.png

ソースは以下。

_conf.lua
function love.conf(t)
  t.window.title = "ParticleSystem Test 01"
  -- t.window.vsync = true
  -- t.window.width = 1280
  -- t.window.height = 720
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- ParticleSystem test 01

function love.load()
  -- set filter
  love.graphics.setDefaultFilter("nearest", "nearest")

  scr_w = 640
  scr_h = 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  -- load image
  -- img = love.graphics.newImage("star02_48x48.png")
  img = love.graphics.newImage("box01_64x64.png")
  -- img = love.graphics.newImage("ball01_64x64.png")

  -- init particle
  psystem = love.graphics.newParticleSystem(img, 128)

  psystem:setParticleLifetime(0.5, 1.0)
  psystem:setColors(255, 255, 255, 255, 255, 255, 255, 0)

  -- psystem:setRelativeRotation(true)
  -- psystem:setRelativeRotation(false)

  local spd = 600
  psystem:setSpeed(spd, spd * 1.2)
  psystem:setLinearDamping(2.0, 4.0)
  -- psystem:setLinearAcceleration(-spd, -spd, spd, spd)
  -- psystem:setRadialAcceleration(-spd * 1.5, -spd * 1.0)
  -- psystem:setTangentialAcceleration(-900, 900)

  psystem:setSpread(math.rad(360))
  -- psystem:setSpread(math.rad(90))

  psystem:setRotation(-math.rad(45), math.rad(45))
  -- psystem:setRotation(math.rad(0), math.rad(360))
  -- psystem:setSpin(-math.rad(360 * 2), math.rad(360 * 2))

  psystem:setSizes(1.0, 0.1)
  psystem:setSizeVariation(0.3)

  -- psystem:setEmissionRate(60)

  timer = 0
end

function love.update(dt)
  timer = timer + dt
  local t = 2.0
  if timer >= t then
    timer = timer - t
    psystem:emit(64)
  end

  psystem:update(dt)
end

function love.draw()
  -- set canvas
  love.graphics.setCanvas(canvas)

  love.graphics.setColor(0, 32, 64)
  love.graphics.rectangle("fill", 0, 0, scr_w, scr_h)
  -- love.graphics.draw(bgimg, 0, 0)

  love.graphics.setColor(255, 255, 255)
  -- love.graphics.setBlendMode("add")
  love.graphics.draw(psystem, scr_w / 2, scr_h / 2)
  love.graphics.setBlendMode("alpha")

  -- unset canvas
  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ofsx = (wdw_w - (scr_w * scr_scale)) / 2
  scr_ofsy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ofsx, scr_ofsy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  end
end

コメントアウト(行頭に「--」がついてる箇所)を外してみたり、与えている数値を書き換えてみれば、動きが変わったりするので、使い方もなんとなく分かるかなと…。

#2 [anime] いぬやしき、面白いな

ノイタミナ枠で放送されてる「いぬやしき」というアニメを一応視聴しているのだけど。展開はアレだけど設定は面白いなと毎回感心していたり。

外見で展開が違ってくること。 :

例えば、あのキャラの外見が、いかにもな感じの異星人の殺戮マシンだの、あるいはゴジラだの、ウルトラマンだの、そういう見た目であったなら、日本人は「あ、こりゃ歯が立ちませんわ」的にじっとして嵐が過ぎ去るのを待つだろうなと。台風が過ぎるのを待つというか、地震の揺れが収まるのを待つというか…。天災への対応に近いソレになるのが自然、かなと。

でも、件の作品においては、ソレは人間にしか見えないし、会話もできるかのように振舞うわけで。なので「これは自分達でもどうにかできるのではないか」と勘違いして…。今のところ、周囲が延々と勘違いし続けているその様子を丹念に描くことで作品になっている、ような気もするわけで、そこが面白いというか興味深いなと。内面・内包する設定は既存作品と同じでも、外見が違うだけでこうも展開が変わってきてしまうのだ、みたいな。

まあ、「この人、一応人語を喋ってはいるけれど、おそらく会話が成立しないのではあるまいか」などと不安になる場面が現実世界にもチラホラあるわけで。そこで行使される力の大小は別にして、状況自体は実は結構よくある風景、だったりするあたりもホラーというか。

漫画というメディアの影響力。 :

ジャンプだかワンピースだかを絶賛してるキャラが、そういうアレコレをしてしまう設定も、なんだか興味深い。

少年漫画の作者さんの中には、「少年漫画とはこうあるべき」「これこれこういうことを伝えなければ」的主張をする人も稀に居るし、例えばNHKのクローズアップ現代でも「ワンピースを読んで私は大事なことを学びました」などと言い出す人が紹介されたりもしたわけだけど。

おそらくこの漫画の作者さんは、そういった真面目ぶった(?)姿勢に対して「んなわけねえじゃん」「アホか」などと思ってそうだなと。漫画の読者なんて頭が悪いから漫画の影響力なんてそこまでないよ、頓珍漢な受け止め方をされるのが関の山だよ、メッセージなんか含めてみたってどうせこれっぽっちも伝わらないよ、等々を…。

というか漫画の影響力がそこまであるなら、この漫画を読んだことでそういう事件を起こす人が出てこないと筋が通らないような気もする。ややこしいな。

ヒーローが持つ「ノブレス・オブリージュ」に近い意識。 :

なんとなくだけど、見ているとサム・ライミ版スパイダーマンを思い出したりもして。当初主人公は手に入れた能力を私利私欲でアレしてしまうけど、ある事件をキッカケに…みたいな。「大いなる力には大いなる責任が伴う」、だっけか…。アメコミなら、そういう展開があり得る。

しかしこっちの作品は、アメコミのような展開にはならない。それは何故か。

日本人が書いているから、かもしれない。日本人には、いや、韓国人や中国人も含めてアジア人には、「ノブレス・オブリージュ」に近い意識が浸透してないから…かもしれず。「力を持っている者はいくらでも好き勝手していいのだ」「下々の人権などいくら踏みにじっても構わんのだ」「責任? 知るかそんなもん」みたいな意識がアジア人にはあるよなと。だから、漫画作品内の展開すらそうなってしまうのだ…てのは考え過ぎだろうか。いやまあ、この作品の場合、別にそんなアレコレは考えてなくて、「展開として面白いのはどれか」で話を進めたらこうなっただけだろうとは思うのだけど。しかしこの展開に「No」を突き付けない読者達、という状況が日本的と言えなくもないのか。ややこしいな。

そういえば、二人の主人公の容姿がアメコミとは逆転してるあたりも興味深い。フツー、若いイケメンがヒーロー側で、歳をとったブサメンは悪役(ヴィラン?)側だよな…。そこを反転してるあたりもなんだか日本的というか。いや、日本の各作品も大体はそうか。単にこの作品が天邪鬼なだけだな…。

まあ、スマホやTVをBTTF冒頭のアレっぽく使ってソレする場面も含めて、設定面が面白いなと思いながら視聴しています。てな感じの感想をなんとなくメモ。

#3 [nitijyou] 日記をアップロード

気が付いたら、2017/11/06に日記をアップしてからその後アップロードしてなかった…。のでアップロード。

2017/12/09() [n年前の日記]

#1 [love2d][lua] love2dを使って縦スクロールシューティングゲームっぽいサンプルを書いてみたり

love2dの使い方を色々調べてたけど、それっぽい形にまとまってきたのでアップロード。





以下の環境で動作確認したけれど、どちらもそこそこの速度で動いてくれた。

ソース。 :

ソースは以下。

_mieki256/love2d_stg_sample02: love2d sample. top view scroll shooting game.

main.lua や画像ファイル(.png)、サウンドファイル(.ogg)は License : CC0 / Public Domain にしておくので自由に使ってください。

ただし、main.lua から呼び出して使ってる/同梱してある _stiライブラリ は、他の人が作ったライブラリでライセンスも違うので、そこは注意を。自分(mieki256)が書いた main.lua は好きにしていいよ、ということで。

実行の仕方。 :

実行の仕方は、 _love2d_stg_sample02.love をダウンロードして、love2d がインストールされてる環境で、
love love2d_stg_sample02.love
と打てば実行できる…はず。たぶん。

git がインストールしてある環境なら、github からソースや画像をまるっと git clone して動かしてもOK。
git clone https://github.com/mieki256/love2d_stg_sample02.git
love love2d_stg_sample02

あるいは、Windows、かつ、love2d 未インストール環境なら、 _love2d_stg_sample02_pack.zip をダウンロード・解凍して、love2d_stg_sample02.exe を実行しても動かせる…はず。たぶん。

操作方法。 :

  • カーソルキー or WASD : 自機移動
  • Zキー : 弾の発射方向の変更/固定の切り替え
  • F11キー : フルスクリーンモードの切り替え
  • ESCキー : アプリの終了

ちなみに、ボスを倒すと最初に戻ります。

love2dについて補足。 :

動作には love2d が必要。

_LOVE - Free 2D Game Engine

love2d(LOVE)というのは、Luaというプログラミング言語から、SDL2という画像描画ライブラリを制御して、2Dゲームを作ることができる、ライブラリだかフレームワークだかそんな感じのもの。Windows、Mac、Linux、Android上で動かせるらしい。

ちなみに、正式名称は「LOVE」なのだけど、あまりにも一般的な単語過ぎるので、「love2d」と呼んで扱う場合が多いという…。なんでこんなググりにくい名前つけたんや。

Raspberry Pi Zero W で love2d を動かす場合の注意点。 :

Windows上で love2d を動かす場合は、 _love2d の公式サイト からインストーラをダウンロードして実行するだけで、love2d の動作に必要なファイルが一通り入ってくれるのだけど…。

Raspberry Pi Zero W で love2d を動かすのは、ちょっと面倒臭くて。

まず、Raspberry Pi Zero W + raspbian stretch は、love2d でサウンドを鳴らすと love2d が落ちるので使えない。 _raspbian jessie ならサウンドを鳴らしても落ちないので、現状では「Raspberry Pi Zero W 上でlove2dを使ってみたい」→「raspbian jessie を使う」しかなさそうで。

更に、raspbian の公式リポジトリに入ってる SDL2 は、「OpenGL有効 / OpenGL ES無効」版なので、「OpenGL無効 / OpenGL ES有効」版を自分でビルドしないといけない。1時間半ぐらいかかります。

そしてlove2dも、公式リポジトリに入ってる版はバージョンが 0.9.x と古いので、現行版 0.10.2 のソースをダウンロードしてビルドしないといけない。これも1時間半ぐらいかかります。

てな感じで、結構面倒臭い…。

ビルド手順については以下にメモしてあるので参照してもらえればと。

_Raspberry Pi Zero W + raspbian jessie でlove2dを使えるようにする手順をメモ

もっとも、このあたり、おそらくは Raspberry Pi1 / Zero W だけの問題で。

Pi1 / Zero W ではなく、例えば Raspberry Pi3 + raspbian stretch を使えば、サウンドがちゃんと鳴るし、GL driver を有効にすれば公式リポジトリ版の SDL2 も動くので、Pi3 を使ってる人は、現行版の love2d をビルドするだけで済むのではないか…と。

SDL2 にしても love2d にしても、raspbian用のdebパッケージを作って配布したら楽になりそうだけど。作り方なんて分からん…。

余談。 :

初めて Lua を触ってみたけど、イイ感じですな…。「えっ」と思う言語仕様はほとんど無いし。クラスの作り方がちょっとアレだけど、慣れればそれほどでも。

#2 [love2d] love2dを使ったソースをまとめる方法についてメモ

love2dを使ってゲームっぽいものを作ったとして、それを1つのファイルにまとめたり、exe化できたらヨサゲだなと。てなわけでググってみたら、そもそも公式wikiに方法が書いてあった。ありがたや。

_Game Distribution (日本語) - LOVE

とりあえず、.loveファイルを作れば、1つのファイルを配布するだけで済むから楽になるようで。

.loveファイルの作り方は、main.lua や conf.lua、使用画像や使用サウンドファイルを1つのzipにまとめて、.zip を .love にリネームするだけ、らしい。

zipにする際、フォルダ構成に気をつけろ、と書いてあるな…。以下ではダメで…。
hoge/main.lua
hoge/conf.lua
hoge/images/fuga.png
以下の状態でzipにしろ、と…。
main.lua
conf.lua
images/fuga.png

.loveファイルができたら、love.exe の尻に .loveファイルをくっつけることで exe化できるらしい。
copy /b love.exe+hoge.love hoge.exe
ただ、.dll等も動作には必要になるので同梱すること、とも書いてある。

#3 [love2d] love2dのParticleSystemの位置の指定について

love2dのパーティクル機能(ParticleSystem)で、発生位置を指定する方法について調べてたのだけど、与えるべき値がちと変わってるようなので一応メモ。

位置の指定は、 _ParticleSystem:moveTo(x, y) でできるけど、どうも画面の真ん中が (0,0) になってる模様。

つまり…。例えば640x480の画面であれば…。
以下のような指定になるのかなと。
moveTo( x - (画面横幅 / 2), y - (画面縦幅 / 2) )

2017/12/10() [n年前の日記]

#1 [love2d] love2dのShaderについて試していたり

love2dにはShader機能があるらしい。興味が湧いたので使い方を調べているところ。

この場合のShaderというのは…。コアが少ないCPUではなく、コアがたくさん入ってるGPU側で処理をすることで、リアルタイム・高速に画像の色や形を変えたり、あるいは元画像が無くても計算式でリアルタイムに画像を生成して描画できる機能、という説明で合っているのかな。どうなんだろう。

参考ページ。 :

以下のページが参考になった。ありがたや。特に一番最初のページが分かりやすい。提示されてるサンプルソースを眺めてるだけでもぼんやり分かる、かなと。

_A Beginner's Guide to Shaders | LOVE - Community Blogs
_Tutorial:Introduction to Shaders (日本語) - LOVE
_love.graphics.newShader (日本語) - LOVE
_Shader (日本語) - LOVE
_Shader:send (日本語) - LOVE
_GLSLについてのメモ - Qiita
_GLSL (OpenGL ES2.0)リファレンス.md

分かった範囲でメモ。 :

  • Shaderの処理は、GLSLという言語で書く。
  • Shaderは、love.load() の中で、love.graphics.newShader() を使って作成する。
  • love.graphics.newShader("shader.fs") という形で、別ファイルにGLSLを書いておいて、そのファイルを読み込んで作成できる。
  • あるいは、love.graphics.newShader() に、GLSL を文字列としてずらずら書いて渡して作ることもできる。
  • GLSLをずらずら書いて渡す際は、[[ 〜 ]] で囲んで、複数行文字列として書くのがフツーっぽい。

使うときは、love.graphics.setShader(Shaderが入った変数) で使いたいShaderに切り替えて、描画が終わったら love.graphics.setShader() でデフォルトのShaderに戻す。
function love.draw()
  love.graphics.setShader(myshader)  -- 使うShaderに切り替え

  -- 何かしら描画

  love.graphics.setShader()          -- デフォルトのShaderに戻す
end

ShaderのGLSLは以下のような感じで書くけれど…。
function love.load()
  myShader = love.graphics.newShader[[
    vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
      vec4 pixel = Texel(texture, texture_coords );
      return pixel * color;
    }
  ]]
end
以下の4つの変数が渡された状態で処理をするようで。
  • vec4 color : love.graphics.setColor(r, g, b, a) で指定された値が入っている。color.r、color.g、color.b、color.a で値を読み取れる。値の範囲は、0.0〜1.0 に変換されている。
  • Image texture : 画像データを渡してるらしい。
  • vec2 texture_coords : テクスチャのuv値が渡される。texture_coords.x、texture_coords.y の形で利用できる。値の範囲は、0.0〜1.0。
  • vec2 screen_coords : スクリーン座標が入ってる。screen_coords.x、screen_coords.y の形で利用できる。それぞれドット単位で値が入る。例えば640x480の画面なら、0〜639、0〜479 の値が入ってくるし、800x600の画面なら、0〜799、0〜599の値が入ってくる。

vec4 pixel = Texel(texture, texture_coords) で、現在のスクリーン座標に描画されるはずのピクセル情報が得られる。pixel.r、pixel.g、pixel.b、pixel.a の形で利用できる。それぞれ 0.0〜1.0の範囲で値が入ってる。

Shaderに何か値を渡したいときは、 _Shader:send() を使う。あらかじめShader側に、値を受け取るための変数を列挙しておくこと。ちなみに、Shader側で、処理に使わない変数が書かれてるとエラーになる。

GLSL と、love2d の Shader で使うキーワードは一部違う、らしい。以下の置き換えが必要、かもしれない。
  • GLSL で float → love2d では number と書く
  • GLSL で uniform → love2d では extern (uniform のままでも一応動く?)
  • GLSL で sampler2D → love2d では Image
  • GLSL で texture2D(tex, uv) → love2d では Texel(tex, uv)

ラスタースクロールのテスト。 :

ラスタースクロールっぽい処理を書いてみたり。こんな感じに。

shader_test03_ss.gif


使用画像は以下。

_bg_1024x1024.png

ソースは以下。

_conf.lua
function love.conf(t)
  t.window.title = "Shader test 03"
  t.window.vsync = true
  t.window.resizable = true
  t.window.width = 640
  t.window.height = 480
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end
_main.lua
-- Shader test 03
-- raster scroll modoki

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  -- load image
  img = love.graphics.newImage("bg_1024x1024.png")

  -- make shader
  myshader = love.graphics.newShader(
    [[
      extern number ang;
      extern vec2 scrSize;
      extern vec2 r;
      extern vec2 curve;
      extern vec2 basePos;
      vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
        number factor_x = radians(r.x * (screen_coords.x / scrSize.x));
        number factor_y = radians(r.y * (screen_coords.y / scrSize.y));
        vec2 uv = texture_coords;
        uv.x = mod(-basePos.x + uv.x - curve.x * sin(ang + factor_y), 1.0);
        uv.y = mod(-basePos.y + uv.y - curve.y * sin(ang + factor_x), 1.0);
        vec4 pixel = Texel(texture, uv);
        return pixel * color;
      }
    ]]
  )

  myshader:send("scrSize", {scr_w, scr_h})
  myshader:send("r", {360.0 * 1.5, 360.0 * 1.5})
  myshader:send("curve", {0.02, 0.02})
  myshader:send("basePos", {0.0, 0.0})
  angle = 0
  bx = 0
  by = 0
end

function love.update(dt)
  bx = (bx + (64 / scr_w) * dt) % 1.0
  by = (by + (64 / scr_h) * dt) % 1.0
  myshader:send("basePos", {bx, by})

  angle = (angle + 180 * dt) % 360.0
  myshader:send("ang", math.rad(angle))

  px = (scr_w - img:getWidth()) / 2
  py = (scr_h - img:getHeight()) / 2
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(0, 0, 0, 255)

  love.graphics.setShader(myshader)

  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(img, px, py)

  love.graphics.setShader()

  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ofsx = (wdw_w - (scr_w * scr_scale)) / 2
  scr_ofsy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ofsx, scr_ofsy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  end
end

2017/12/11(月) [n年前の日記]

#1 [nitijyou] 自宅サーバ機が不調

朝、親父さんから「自宅サーバに繋がらないぞ」と報告が。自室に戻って確認してみたら、電源LEDは点いてるものの、pingも通らないし、直接接続してるディスプレイにも画面が映ってなくて。

電源ボタンを押してもシャットダウン処理が始まる気配は無く、電源ボタン長押しで強制電源オフをしてから電源を入れても画面は出てこない。M/Bか電源が壊れたのかな…。

足元から引っ張り出してケースを開けて、HDDやケースファンを外して電源投入。しかし画面が出てこない。ボタン型電池を外して電圧を確認してみたり *1 、別電源を発掘して繋いでみた。画面が出た。ということは、M/Bは壊れてないな…。電源がダメなんだな…。

と思ったのに、今まで使ってた電源を繋ぎ直してみたら、画面が出た。なんでや。お前が壊れてたんとちゃうんか。

もしかして、部屋の温度が下がり過ぎると電源がちゃんと動かなくなるのだろうか。だとすれば、電源を交換したほうがいいのだろうけど。

しかし、利用してるケース、 _Antec NSK 1480 と一緒についてきた電源だから、交換可能な電源は売ってないわけで。大きさとしては TFX電源の部類だろうけど、ケースのファンの穴に合うようにデザインされてるので、代替品が無い…。日記を検索したら _2014/03/24 にサイズ等をメモしてあった。なかなか厳しい。

ちなみに、使ってる M/B は、 _VIA EPIA LN10000EG だったはず。オンボードのCPUは、VIA C7 1.0GHz…。もしかして Raspberry Pi3 のほうが性能が高かったりしたらどうしよう。

ファンも交換。 :

ケースを開けたついでに、前々から時々異音を出してた8cmケースファンも交換。今回は _Ainex CFY-80S と交換してみた。パッケージには、1200rpm、14.61CFM、11.9dB(A) と書いてある。

*1: ちゃんと3V程度はあった。電池が切れてるわけではないらしい。

#2 [windows] メインPCのWindows10を、Windows 10 Fall Creators Update にアップグレード

Windows10が、「更新プログラムが適用できるよ」と言い出したので、試しにアップグレード。バージョンが、1703 から 1709 になった。OSビルドは16299.98。

今のところ致命的な不具合は出ていない…けれど、そのうち何か不具合に気づく展開になるのだろうな。

2017/12/12(火) [n年前の日記]

#1 [love2d] love2dのShaderを使ってパレット書き換えっぽいことをする

love2dのShaderを使えば特定の色だけをぽわーんぽわーんと点滅させたりできるのではないか、つまりは大昔の2Dゲーム画面でよく見かけたパレット書き換えっぽいソレがビミョーに再現できるのではないかと思えてきたので、そのあたりを実験してみたり。環境は Windows10 x64 + love2d 0.10.2。

こんな感じに。

shader_test04_palettechange_ss.gif

画像とソース。 :

使用画像は以下。

_colorbar_circle01.png

ソースは以下。

_conf.lua
/function love.conf(t)
  t.window.title = "Shader test 04 palette change modoki"
  t.window.vsync = true
  t.window.resizable = true
  t.window.width = 640
  t.window.height = 480
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- Shader test 04
-- palette change modoki

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  -- load image
  img = love.graphics.newImage("colorbar_circle01.png")

  -- make shader
  myshader = love.graphics.newShader(
    [[
      extern number factor;
      extern vec3 checkcolor;
      extern vec3 replacecolor;

      vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
        vec4 pixel = Texel(texture, texture_coords);
        if (pixel.r == checkcolor.r && pixel.g == checkcolor.g && pixel.b == checkcolor.b) {
          pixel.r = pixel.r * (1.0 - factor) + replacecolor.r * factor;
          pixel.g = pixel.g * (1.0 - factor) + replacecolor.g * factor;
          pixel.b = pixel.b * (1.0 - factor) + replacecolor.b * factor;
        }
        return pixel * color;
      }
    ]]
  )

  myshader:send("checkcolor", {0.0, 1.0, 0.0})  -- R,G,B
  myshader:send("replacecolor", {0.0, 0.0, 0.0})  -- R,G,B
  angle = 0
end

function love.update(dt)
  angle = (angle + 90 * dt) % 360.0
  local v = 1.0 - math.abs(math.sin(math.rad(angle)))
  myshader:send("factor", v)  -- set 0.0 - 1.0

  px = (scr_w - img:getWidth()) / 2
  py = (scr_h - img:getHeight()) / 2
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(0, 0, 0, 255)

  love.graphics.setShader(myshader)
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(img, px, py)
  love.graphics.setShader()

  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ofsx = (wdw_w - (scr_w * scr_scale)) / 2
  scr_ofsy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ofsx, scr_ofsy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  end
end

特定色が見つかったら、その時だけピクセルのRGB値を変更、てな処理をしてる。

ただ、こういうGLSLの書き方はあまりよくないらしい。GLSL関連の説明ページを見ると、「条件分岐(if文)はコストがかかる」と言われてたりするわけで。とは言え、if文を使わずにこういう処理を書けるのか、そこらへんよく分からんわけで。この場合、違う書き方はできるのかな…?

hsv変換をして色を変えてみる。 :

上記のソースを書いて動作確認しているうちに、もしかして、RGBをHSV(色相、彩度、明度)に変換して色々処理ができるのでは、と思えてきたわけで。

しかし、GLSLでRGB to HSV や HSV to RGB ってどうやるのかな…。と思ってググってみたら、以下のページで「このやり方が速いよ!」てな書き方が公開されてた。

_Blog: Fast branchless RGB to HSV conversion in GLSL - Lol Engine

また、HSV to RGB なら、love2dの公式wikiにも説明があった。

_HSV color (日本語) - LOVE

ありがたい。試しに使わせてもらったり。

結果はこんな感じに。

shader_test05_hsv_ss.gif


使用画像は以下。

_hsvbar.png

ソースは以下。

_conf.lua
function love.conf(t)
  t.window.title = "Shader test 05 hsv"
  t.window.vsync = true
  t.window.resizable = true
  t.window.width = 640
  t.window.height = 480
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- Shader test 05 hsv
--
-- reference
-- Blog: Fast branchless RGB to HSV conversion in GLSL - Lol Engine
-- http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  -- load image
  img = love.graphics.newImage("hsvbar.png")

  -- make shader
  myshader = love.graphics.newShader(
    [[
      extern number factor;

      vec3 rgb2hsv(vec3 c) {
        vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
        vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);
        vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);
        float d = q.x - min(q.w, q.y);
        float e = 1.0e-10;
        return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
      }

      vec3 hsv2rgb(vec3 c) {
          vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
          vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
          return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
      }

      vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
        vec4 pixel = Texel(texture, texture_coords);
        vec3 hsv = rgb2hsv(pixel.rgb);
        hsv.x = mod(hsv.x + factor, 1.0);
        vec3 rgb = hsv2rgb(hsv);
        pixel.r = rgb.r;
        pixel.g = rgb.g;
        pixel.b = rgb.b;
        return pixel * color;
      }
    ]]
  )
  factor = 0.0
  angle = 0
end

function love.update(dt)
  factor = factor + 0.25 * dt
  myshader:send("factor", factor)  -- set 0.0 - 1.0

  px = (scr_w - img:getWidth()) / 2
  py = (scr_h - img:getHeight()) / 2
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(0, 0, 0, 255)

  love.graphics.setShader(myshader)
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(img, px, py)
  love.graphics.setShader()

  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ofsx = (wdw_w - (scr_w * scr_scale)) / 2
  scr_ofsy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ofsx, scr_ofsy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  end
end

RGB値からHSVを求めて、H(色相)を少し変化させて、またRGB値に戻して描画する、みたいな。

#2 [nitijyou] 雪がそこそこ積もった

昨晩から結構雪が降ったようで。犬の散歩も一苦労。

2017/12/13(水) [n年前の日記]

#1 [love2d] love2dのShaderで実験中

love2dのShaderを使って色々実験中。

2017/12/14(木) [n年前の日記]

#1 [love2d] love2dでShaderを使ってラスタースクロールその2

随分昔に、 _DXRubyでラスタースクロール処理 を試したのだけど、それを love2d + Shader機能でもできるか実験。

こんな感じに。環境は、Windows10 x64 + love2d 0.10.2。

shader_test07_raster_ss.gif

64ドット分スクロールしたらまた0に戻す、みたいなことをしているけど、元画像の作り方次第で誤魔化せる、みたいな。

ソースは以下。 :

画像は、 _DXRubyでラスタースクロール処理 で使った _bg_ras_c.png を使用。

ソースは以下。

_conf.lua
function love.conf(t)
  t.window.title = "Shader test 07 raster"
  t.window.vsync = true
  t.window.resizable = true
  t.window.width = 640
  t.window.height = 480
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- Shader test 07 raster

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)
  img = love.graphics.newImage("bg_ras_c.png")

  -- make shader
  local shadercode = [[
    extern vec2 imgsize;
    extern number factor;

    vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) {
      texture_coords.x = mod(texture_coords.x + ((factor + (factor * texture_coords.y)) / imgsize.x), 1.0);
      vec4 texcolor = Texel(texture, texture_coords);
      return texcolor * color;
    }
  ]]
  myshader = love.graphics.newShader(shadercode)
  myshader:send("imgsize", {img:getWidth(), img:getHeight()})
  myshader:send("factor", 0.0)

  v = 0.0
  px = (scr_w - img:getWidth()) / 2
  py = scr_h - img:getHeight()
end

function love.update(dt)
  v = v + 128 * dt
  myshader:send("factor", v % 64.0)
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(0, 0, 0, 255)

  love.graphics.setColor(255, 255, 255)
  love.graphics.setShader(myshader)
  love.graphics.draw(img, px, py)
  love.graphics.setShader()

  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ox = (wdw_w - (scr_w * scr_scale)) / 2
  scr_oy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ox, scr_oy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  end
end

#2 [love2d] love2dのShaderでなんだかよく分からないエフェクトになったり

love2dの、 _公式Wikiサンプル を動かしていたらなんだかよく分からないエフェクトになってきたのでアップロードしてみるテスト。

こんな感じに。

shader_test06_sincos_ss.gif

自分は頭が悪いので、どうしてこういう効果になるのかは分かってないのだけど、これはこれでなんだか面白いなと。

画像とソース。 :

使用画像。

_hsvbar.png

_conf.lua
function love.conf(t)
  t.window.title = "Shader test 06 sin cos"
  t.window.vsync = true
  t.window.resizable = true
  t.window.width = 640
  t.window.height = 480
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- Shader test 06 sin cos
--
-- love.graphics.newShader - LOVE
-- https://love2d.org/wiki/love.graphics.newShader

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)
  img = love.graphics.newImage("hsvbar.png")

  -- make shader
  myshader = love.graphics.newShader("shader.fs")
  myshader:send("factor", 0.0)

  angle = 0.0
  px = (scr_w - img:getWidth()) / 2
  py = (scr_h - img:getHeight()) / 2
end

function love.update(dt)
  angle = (angle + 30 * dt) % 360.0;
  myshader:send("factor", 1.0 * (1.0 - math.sin(math.rad(angle))))
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(0, 0, 0, 255)

  love.graphics.setColor(255, 255, 255)
  love.graphics.setShader(myshader)
  love.graphics.draw(img, px, py)
  love.graphics.setShader()

  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ox = (wdw_w - (scr_w * scr_scale)) / 2
  scr_oy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ox, scr_oy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  end
end

_shader.fs
extern number factor;
varying vec4 vpos;

#ifdef VERTEX
vec4 position( mat4 transform_projection, vec4 vertex_position ) {
  vpos = vertex_position;
  return transform_projection * vertex_position;
}
#endif

#ifdef PIXEL
vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) {
  texture_coords += vec2(factor * cos(0.1 * vpos.x), factor * sin(0.075 * vpos.y));
  vec4 texcolor = Texel(texture, texture_coords);
  return texcolor * color;
}
#endif

今回は Shader部分を別ファイルに分けてみたり。

2017/12/15(金) [n年前の日記]

#1 [windows] あふのタイムスタンプがよく分からず

とある事情で、複数のファイルに対して微妙にずれたタイムスタンプを設定したくて、 _BulkFileChanger_Flexible Renamer を使ってタイムスタンプを設定しなおしてみたのだけど。常用してる、 _あふ という二画面ファイラーで確認したら、タイムスタンプが設定値と一致しなくて悩んだり。エクスプローラで確認すると設定値通りに見えるのだけどなあ…。

あふ上でタイムスタンプがずれて表示される、その法則が分からない。タイムスタンプを見てコピーや移動をする際の関係で、2秒以内なら同じタイムスタンプとして扱う、てな設定が関係あるのかと思ったけれど、分単位で時間をずらして設定しても同じ時間として表示される時もあるし。色々試したところ、6分以上時間をずらして設定しないと、異なるタイムスタンプとして表示されないっぽい。なんでだろ…。

BulkFileChangerを使ったタイムスタンプの変更の仕方。 :

一応メモ。

_BulkFileChanger のダウンロードと使い方 - k本的に無料ソフト・フリーソフト で解説されてるけど…。今回は以下のように設定。
  • エクスプローラから、変更したいファイル群をD&D(ドラッグアンドドロップ)。
  • 「作成日時」にチェックを入れて、値を指定。
  • 「シーケンスモードで日時変更」にチェックを入れて。
  • 「加算」にチェックを入れてずらす値を指定。
  • 「選択した日時をコピー」にチェックを入れて、コピー元は「作成日時」を選択。「更新日時」「アクセス日時」にコピーするようにチェックを入れる。
  • 「変更」ボタンを押して確定。

Flexible Renamerを使ったタイムスタンプの変更の仕方。 :

  • 左側のフォルダリストで、変更したいファイル群が入ってるフォルダを選択。
  • 「属性」のタブを選択。
  • 「タイムスタンプ:」で「連続生成」を選択。
  • 開始時刻等を指定。
  • 適用先として、「作成日時」「更新日時」「アクセス日時」があるので必要な個所にチェックを入れる。
  • 「属性」は、今回は「アーカイブ」だけチェックを入れて、他はOFFにした。
  • 「設定開始」をクリックすると、現在選択されているファイルのみに反映される。

#2 [movie] 「スター・ウォーズ/フォースの覚醒」を視聴

TV放映されてたので見てみたり。ちなみに初見。

昔のSTAR WARSらしさをどこか再現しつつも今風な作り込みをしてある映像に感心。よくまあここまで…。にしても、CG技術の発達、ノウハウの蓄積って凄いなあ…。

偉い人達はかつての大ヒットシリーズをもう一度と望んでるだろうし、スタッフもかつての名作に追いつきたいと願ってるだろうし、つまりは ダースベイダーに憧れつつも自分がそこまで辿り着けるかちょっと自信が無くて「力を貸してくれ…」とつぶやいてる彼は、この映画の置かれてる状況そのものを象徴する存在と言えるのかもしれないなと思ったりもして。まあ、狙ってそういうキャラにしているのではないかとも思うけど。

2017/12/16() [n年前の日記]

#1 [windows] Windows 10 Fall Creators Updateにアップグレードしたらちと不具合が

数日前に Windows 10 Fall Creators Update にアップグレードしたわけだけど、ちと不具合に気づいたり。

C:\Program Files\ や C:\Program Files (x86)\ 内のシンボリックリンクがリンク先を見つけられない状態になっていて、アプリが開けなくなっていた。とりあえず、リンクを張り直したけど…。他のフォルダのシンボリックリンクは大丈夫そうなのだけど。

保存場所を変更していた、「ダウンロード」「ピクチャ」フォルダもなんだか妙な感じに。プロパティを見ると、マイドキュメント相当を全部対象にしてしまう。場所の設定は変わってないように見えるのだけどな…。

2017/12/17() [n年前の日記]

#1 [love2d] love2dのShaderを使ってまだ実験中

DXRuby の Shader を使って実験したソレを love2d でもできそうか実験中。

画面サイズより大きいテクスチャを渡さないと、画面を覆うような描画ができないあたりが、何かこう…。いやまあ、おそらく love.graphics.newCanvas で Canvas を作って、そこに小さいサイズのテクスチャを何度か描画すれば、その Canvas を大きいテクスチャとして使えるのではないかと予想はしてるのだけど。とりあえず今回はサンプル的なソレということで、そこまではしないつもりだけど。

雲模様っぽい画像をGIMPで作ろうとして悩んでしまった。以前はどうやって作ったのだっけ…。その時々のノリで画像作成をしてるから…やり方を忘れた…。フィルター → 下塗り → _ソリッドノイズ で雲模様っぽいもやもやした模様は作れるけど、そこから加工していっても何かイメージと違う。ひょっとして前回は、CC0の雲の写真から加工していったのかな…。どうだったかな…。

2017/12/18(月) [n年前の日記]

#1 [love2d] love2dのShaderを使って某STGのアレ

昔、 _DXRubyを使って床ラスタースクロール っぽいことを _以前試した わけだけど。コレを love2d の Shader を使ってもできないかと実験。環境は Windows10 x64 + love2d 0.10.2。

こんな感じに。

shader_test08_raster2_ss_01.gif

一応それっぽくできた、のかな。どうなんだ。

画像とソース。 :

使った画像は以下。

_cloud_1024x1024.png

ソースは以下。

_conf.lua
function love.conf(t)
  t.window.title = "Shader test 08 raster 2"
  t.window.vsync = true
  t.window.resizable = true
  t.window.width = 640
  t.window.height = 480
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- Shader test 08 raster 2

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  img = love.graphics.newImage("cloud_1024x1024.png")
  -- img = love.graphics.newImage("scifi_bg_640x640.png")
  -- img = love.graphics.newImage("scifi_bg_640x640_blur.png")

  -- make shader
  local shadercode = [[
    extern number screen_distance;
    extern number floor_distance;
    extern number scroll_x;
    extern number scroll_y;
    extern number scroll_z;
    extern number upper_z_dir;

    const number z_limit = 6.0;

    vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) {
      float sx = screen_coords.x / love_ScreenSize.x - 0.5;
      float sy = screen_coords.y / love_ScreenSize.y - 0.5 - scroll_y;
      // float sx = texture_coords.x - 0.5;
      // float sy = texture_coords.y - 0.5 - scroll_y;

      float zv = (sy < 0.0)? upper_z_dir : 1.0;
      sy = abs(sy);
      // sy = max(sy, 0.0);

      float pz = (screen_distance * floor_distance) / sy;
      float px = sx * pz / screen_distance;
      texture_coords.x = mod(px + scroll_x, 1.0);
      texture_coords.y = mod(pz + scroll_z * zv, 1.0);
      vec4 texcolor = Texel(texture, texture_coords);
      texcolor.a *= (1.0 - step(z_limit, pz));
      return texcolor * color;
    }
  ]]
  myshader = love.graphics.newShader(shadercode)
  myshader:send("screen_distance", 0.7)
  myshader:send("floor_distance", 0.3)
  myshader:send("scroll_x", 0.0)
  myshader:send("scroll_y", 0.0)
  myshader:send("scroll_z", 0.0)
  myshader:send("upper_z_dir", 1.0)  -- upper half of the screen in z direction. 1.0 or -1.0

  v = 0.0
  px = (scr_w - img:getWidth()) / 2
  py = (scr_h - img:getHeight()) / 2
end

function love.update(dt)
  v = v + dt
  myshader:send("scroll_x", v * 1.2)
  myshader:send("scroll_z", v * -0.4)
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(0, 0, 0, 255)

  love.graphics.setColor(255, 255, 255)
  love.graphics.setShader(myshader)
  love.graphics.draw(img, px, py)
  love.graphics.setShader()

  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ox = (wdw_w - (scr_w * scr_scale)) / 2
  scr_oy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ox, scr_oy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  elseif key == "f11" then
    -- toggle fullscreen
    if love.window.getFullscreen() then
      love.window.setFullscreen(false)
    else
      love.window.setFullscreen(true)
    end
  end
end

画像もソースも、License: CC0 / Public Domain ってことで。

実行の仕方は、画像ファイル、conf.lua、main.lua を1つのフォルダに入れて、「love フォルダ名」でOK。

問題点。 :

上記のような、 _ぼんやりした感じの雲模様テクスチャ を表示する分にはイイ感じに見えるのだけど。テクスチャによっては酷い結果になることに気付いてしまった。

例えば、 _境界がハッキリしたメカっぽいテクスチャ などを表示すると、画質がガビガビになってしまう。

shader_test08_raster2_ss_02.gif

このGIFアニメのフレームレートは低いから、これだとガビガビに見えないかもしれないけれど、60FPSで動いてる様子を眺めてみれば「なんじゃこりゃああ!!」って感じの酷い見た目になる。

何故かというと、Z軸方向の値に応じてテクスチャからピクセルを拾ってくる際、元のテクスチャから飛び飛びでピクセルを拾ってきてしまうので、線だの何だのがちゃんと繋がってない状態で描画されてガビガビになるという。

解決策・回避策としては…。例えば、Z軸方向 ―― テクスチャで言えばy方向にのみ、 _事前にぼかしをかけておく という手があるかなと。 *1 各ピクセルが滑らかに変化していれば、飛び飛びでピクセル値を拾ってきても結果はそれほど破綻しないはず、みたいな。

shader_test08_raster2_ss_03.gif

こうすれば、多少は見た目がマシになる。もちろん、ぼかしをかけてあるから全体的にぼやけた印象になるけれど、それでもガビガビよりはマシだろうと…。

もしかすると、love2d の Shader側・GLSL側でテクスチャにぼかしをかけつつ利用、てな処理だってできるかもしれない。面倒臭いから今回はそこまでやってないけど…。

テクスチャ画像読み込みの直後に img:setFilter("linear", "linear") を呼んで、テクスチャに使うフィルタを設定したら改善するかと思ったけれど、今回の例ではあまり効果は感じられなかった。

_FilterMode (日本語) - LOVE
_Texture:setFilter (日本語) - LOVE

*1: 今回は GIMP の、フィルター → ぼかし → _タイル化可能ぼかし を使って、垂直方向のみにチェックを入れてぼかしてみた。

2017/12/19(火) [n年前の日記]

#1 [love2d] love2dのShaderで円柱っぽいBG描画を試したり

昔、 _DXRubyのShader機能で円柱だかパイプだかっぽいBG描画を試した ことがあったのだけど。それを love2d でもやれないものかなと思えてきたので試したり。環境は Windows10 x64 + love2d 0.10.2。

こんな感じに。

shader_test09_pipe_ss01.gif

shader_test09_pipe_ss02.gif

何かビミョーにおかしいような気もするけど…。まあ、それっぽくなってるから、いいか…。でも、考え方、あるいは計算式が間違ってる可能性も否定できず。

画像とソース。 :

画像は以下。

_grid_bg_640x640.png
_scifi_bg_640x640.png

ソースは以下。

_conf.lua
function love.conf(t)
  t.window.title = "Shader test 09 pipe"
  t.window.vsync = true
  t.window.resizable = true
  t.window.width = 640
  t.window.height = 480
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- Shader test 09 pipe

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  img = love.graphics.newImage("grid_bg_640x640.png")
  -- img = love.graphics.newImage("scifi_bg_640x640.png")
  img:setFilter("linear", "linear")

  -- make shader
  local shadercode = [[
    extern number scr_dist;
    extern number r;
    extern number angle_max;
    extern number start_x;
    extern number start_y;

    vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) {
      float sx = texture_coords.x - 0.5;
      float sy = texture_coords.y - 0.5;
      float pz = r * cos(atan(sy, scr_dist));
      float u = sx * pz / scr_dist;
      float v = atan(sy, scr_dist) / angle_max;
      texture_coords.x = mod(u + 0.5 + start_x, 1.0);
      texture_coords.y = mod(v + 0.5 + start_y, 1.0);
      vec4 texcolor = Texel(texture, texture_coords);
      return texcolor * color;
    }
  ]]
  myshader = love.graphics.newShader(shadercode)
  local scr_dist = 0.6
  myshader:send("scr_dist", scr_dist)
  myshader:send("r", scr_dist + 0.3)
  myshader:send("start_x", 0.0)
  myshader:send("start_y", 0.0)
  myshader:send("angle_max", math.atan2(0.5, scr_dist))

  v = 0.0
  px = (scr_w - img:getWidth()) / 2
  py = (scr_h - img:getHeight()) / 2
end

function love.update(dt)
  v = v + dt
  myshader:send("start_x", v * 0.2)
  myshader:send("start_y", v * 0.4)
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(0, 0, 0, 255)

  love.graphics.setColor(255, 255, 255)
  love.graphics.setShader(myshader)
  love.graphics.draw(img, px, py)
  love.graphics.setShader()

  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ox = (wdw_w - (scr_w * scr_scale)) / 2
  scr_oy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ox, scr_oy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  elseif key == "f11" then
    -- toggle fullscreen
    if love.window.getFullscreen() then
      love.window.setFullscreen(false)
    else
      love.window.setFullscreen(true)
    end
  end
end

画像もソースも、License : CC0 / Public Domain ってことで。

Shader部分の仕組みというか考え方は、 _DXRuby版 と同じなのでそちらを参照してもらえればと。

余談。 :

HLSL にはatan2() があるけれど GLSL には atan2() が無くてどうしようと思ったら、GLSL の場合、atan() が atan2() 相当らしくて。 _GLSLをHLSLに書き換える - Qiita が参考になりました。ありがたや。ちなみに、件のページには atan(x,y) と書いてあるけど、たぶん atan(y,x) ではあるまいか。

今回、利用するテクスチャに linearフィルタを設定したら、見た目がちょっとマシになったように感じたり。処理内容によっては、その手のフィルタ設定が効果を発揮する場合もあるようだなと。

#2 [love2d] love2dのShaderでスカイドームっぽい描画を試したり

love2dのShaderで円柱だかパイプだかっぽい描画を試してるうちに、もうちょっとアレすれば某STGのスカイドームっぽい背景描画もできるんじゃないかと思えてきたので試したり。…スカイドームという呼び方で合ってるのかな。違うのかな。分からんけど。とりあえず、環境は Windows10 x64 + love2d 0.10.2。

こんな感じに。



ちょっと何をやってるか分かりづらいかな…。要は、1枚のテクスチャ画像を、以下のように歪めて描画する処理をしてるのだけど。

shader_test10_sphere_ss02.png

テクスチャをそのまま描画すると、2D絵を表示してますねー、てな印象しか受けないけれど。こういうことをすれば1枚絵でもビミョーに空間を感じられなくもない見た目にできる。かもしれない。みたいな。

ただ、考え方や計算式がこれで合ってるかどうかは自信無し…。この見た目で合ってるのかな…。どうなんだ…。

画像とソース。 :

使用画像は以下。

_star_bg_1024x1024.png

ソースは以下。

_conf.lua
function love.conf(t)
  t.window.title = "Shader test 10 sphere"
  t.window.vsync = true
  t.window.resizable = true
  t.window.width = 640
  t.window.height = 480
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- Shader test 10 sphere

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  img = love.graphics.newImage("star_bg_1024x1024.png")
  -- img = love.graphics.newImage("uvcheckermap01-1024.png")
  img:setFilter("linear", "linear")

  -- make shader
  local shadercode = [[
    extern number scr_dist;
    extern number r;
    extern number scale;
    extern number start_x;
    extern number start_y;

    vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) {
      float sx = (screen_coords.x - (love_ScreenSize.x / 2)) / love_ScreenSize.x;
      float sy = (screen_coords.y - (love_ScreenSize.y / 2)) / love_ScreenSize.x;
      // float sx = texture_coords.x - 0.5;
      // float sy = texture_coords.y - 0.5;
      float ang_v = atan(sy, scr_dist);
      float ang_u = atan(sx, scr_dist);
      float pz = r * cos(ang_v);
      float qz = pz * cos(ang_u);
      float u = sx * qz / scr_dist;
      float v = sy * qz / scr_dist;
      texture_coords.x = mod((u + start_x) * scale + 0.5, 1.0);
      texture_coords.y = mod((v + start_y) * scale + 0.5, 1.0);
      vec4 texcolor = Texel(texture, texture_coords);
      return texcolor * color;
    }
  ]]
  myshader = love.graphics.newShader(shadercode)
  local angle_of_view = 60
  local scr_dist = math.cos(math.rad(angle_of_view/2))
  myshader:send("scr_dist", scr_dist)
  myshader:send("r", scr_dist + 0.1)
  myshader:send("scale", 0.4)
  myshader:send("start_x", 0.0)
  myshader:send("start_y", 0.0)

  ang_a = 0.0
  ang_b = 0.0
  px = (scr_w - img:getWidth()) / 2
  py = (scr_h - img:getHeight()) / 2
end

function love.update(dt)
  ang_a = ang_a + 10 * dt
  ang_b = ang_b + 16 * dt
  myshader:send("start_x", (1.0 - math.sin(math.rad(ang_a))))
  myshader:send("start_y", (1.0 - math.sin(math.rad(ang_b))))
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(0, 0, 0, 255)

  love.graphics.setColor(255, 255, 255)
  love.graphics.setShader(myshader)
  love.graphics.draw(img, px, py)
  love.graphics.setShader()

  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ox = (wdw_w - (scr_w * scr_scale)) / 2
  scr_oy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ox, scr_oy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  elseif key == "f11" then
    -- toggle fullscreen
    if love.window.getFullscreen() then
      love.window.setFullscreen(false)
    else
      love.window.setFullscreen(true)
    end
  end
end

ソースも画像も、License : CC0 / Public Domain ってことで。

処理の考え方。 :

考え方としては、テクスチャが貼ってある球の中から外を覗いている、てな状況と仮定して。

視線の向きについて、縦方向(垂直方向)の角度が a、横方向(水平方向)の角度が b、として…。

まず、横方向(水平方向)の角度が0度の状態で考えてみる。横方向から見た時の図は以下になる。

love2d_shader_sphere_about2.png
  • sz : 視点(0,0,0) から画面までの距離。
  • sy : 画面を通る直線のy値。
  • P : 球と交差する点。
  • pz : 球と交差する点のz座標。
  • a : 垂直方向の角度。
  • r : 球の半径。
a = atan(sy, sz)
pz = r * cos(a)

上から見た図で考える。球と交差する点 P は、半径 pz の円の上にあるはずだから…。

love2d_shader_sphere_about3.png
b = atan(sx, z)
qz = pz * cos(b)
これで、球と交差する点 P のz値、qz が得られた。

z値 pz と、画面上のx,y座標 sx, sy と、画面までの距離 sz が分かっているので、読み取るべきテクスチャ座標を求めることができる。
u = sx * qz / sz
v = sy * qz / sz

てな感じで求めたのだけど。球と直線が交差した点のz値を取得するあたりで、もっと簡単な方法がありそうな気もする…。

UVChecker-map画像について。 :

UVChecker-map画像は、以下で公開されてる画像を使わせてもらいました。ありがたや。この手の実験をする時はマジ助かるです。Public Domain 扱いになってる点もありがたい。

_Arahnoid/UVChecker-map: A collection of free images what can be used during unwrapping of 3D models

2017/12/20(水) [n年前の日記]

#1 [love2d] 直線と球の交点の求め方を調べてたり

昨日、直線と球の交点をatan()やcos()で求めてたけど。もっと簡単(?)に求める方法があるはずだよなとググって調べてみたり。以下のページが参考になった。

_球面と直線の交点 -点P(Px,Py,Pz)から方向ベクトル(x,y,z)にのびた直線- 数学 | 教えて!goo

直線の原点が P(Px, Py, Pz) で、方向ベクトルが (x, y, z) の場合、直線は (X,Y,Z) = (Px, Py, Pz) + t * (x, y, z) と書ける…けど、今回は原点を (0, 0, 0) としてるので、
X = t * x
Y = t * y
Z = t * z
と書けるなと。

そして球は、X^2 + Y^2 + Z^2 = r^2 だから…。X,Y,Zに直線のソレを代入して…。
X^2 + Y^2 + Z^2 = r^2
(t * x) * (t * x) + (t * y) * (t * y) + (t * z) * (t * z) = r * r
t^2 * (x^2 + y^2 + z^2) = r^2
t^2 = (r^2) / (x^2 + y^2 + z^2)
t = sqrt( (r^2) / (x^2 + y^2 + z^2) )
例えば、直線が x = 0, y = 0 の時、z = 1.0 になるだろうから…。
t = sqrt( (r^2) / (0^2 + 0^2 + 1.0^2) )
  = sqrt( (r^2) / 1.0 )
  = sqrt( r^2 )
  = r
t = r にしちゃっていい、のかなと。ホントかな。怪しいな。

ということは、x, y が決まっている時、z値は…。
(t * x)^2 + (t * y)^2 + (t * z)^2 = r^2
(t * z)^2 = r^2 - (t * x)^2 - (t * y)^2

t = r

(r * z)^2 = r^2 - (r * x)^2 - (r * y)^2
r^2 * z^2 = r^2 * (1.0 - x^2 - y^2)
z^2 = (1.0 - x^2 - y^2)
z = sqrt(1.0 - x^2 - y^2)
アレ? 球の半径 r が式の中から無くなってしまった…。これで合ってるのかな…? どうなのよ。

love2d の Shader で動作確認。 :

love2d の Shader を使って、どんな結果になるか確認。

shader_test11_sphere2_ss01.png

歪み方は以前のやり方と似たような感じになった。ただ、球の半径 r を変更してもテクスチャの拡大縮小はされなくなった。式の中に r が入ってないから当たり前だろうけど。

どこかで何かを間違えてる気がしないでもないけど、それっぽく表示されたから、まあ、いいか…。

ソースは以下。

_conf.lua
_main.lua

変更したところだけメモ。
function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  img = love.graphics.newImage("star_bg_1024x1024.png")
  -- img = love.graphics.newImage("uvcheckermap01-1024.png")
  img:setFilter("linear", "linear")

  -- make shader
  local shadercode = [[
    extern number scr_dist;
    extern number scale;
    extern number start_x;
    extern number start_y;
    // extern number r;

    vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) {
      float sx = (screen_coords.x - (love_ScreenSize.x / 2)) / love_ScreenSize.x;
      float sy = (screen_coords.y - (love_ScreenSize.y / 2)) / love_ScreenSize.x;
      vec3 ray = normalize(vec3(sx, sy, scr_dist));
      // float qz = sqrt( r * r * (1.0 - ray.x * ray.x - ray.y * ray.y) / (r * r));
      float qz = sqrt( 1.0 - ray.x * ray.x - ray.y * ray.y);
      float u = sx * qz / scr_dist;
      float v = sy * qz / scr_dist;
      texture_coords.x = mod((u + start_x) * scale + 0.5, 1.0);
      texture_coords.y = mod((v + start_y) * scale + 0.5, 1.0);
      vec4 texcolor = Texel(texture, texture_coords);
      return texcolor * color;
    }
  ]]

  myshader = love.graphics.newShader(shadercode)
  local angle_of_view = 60
  local scr_dist = math.cos(math.rad(angle_of_view/2))
  myshader:send("scr_dist", scr_dist)
  -- myshader:send("r", scr_dist + 0.1)
  myshader:send("scale", 0.4)
  myshader:send("start_x", 0.0)
  myshader:send("start_y", 0.0)

  ang_a = 0.0
  ang_b = 0.0
  px = (scr_w - img:getWidth()) / 2
  py = (scr_h - img:getHeight()) / 2
end

2017/12/21(木) [n年前の日記]

#1 [love2d] love2dのShaderで惑星をグルグル回す感じのソレを試したり

球を相手にアレコレ考えているうちに、某STGのどピンクな背景上でグルグル回ってたアレを love2d の Shader でもできるのではないかと思えてきたので試してみたり。

とりあえず、こんな感じに。

shader_test12_planet_ss01.gif

更に、向きを90度変えて(xとyを入れ替えて)、テクスチャを変えて、ついでに雲のテクスチャも別途回してみたらこんな感じに。



その手の地図(メルカトル図法?)を扱う時と同様に、南極・北極のあたりが怪しいことになってるけど…。でもまあ、例えば2Dゲームの背景として表示して雰囲気作りに使う分にはこれでもイケそうな。

画像とソース。 :

使った画像は以下。

_grid_bg_480x480.png
_planet_tex_480x480.png
_planet_cloud_only_496x496.png

ソースは以下。

_conf.lua
function love.conf(t)
  t.window.title = "Shader test 12 planet"
  t.window.vsync = true
  t.window.resizable = true
  t.window.width = 640
  t.window.height = 480
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- Shader test 12 planet

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  img = love.graphics.newImage("grid_bg_480x480.png")
  img:setFilter("linear", "linear")

  -- make shader
  local shadercode = [[
    extern number start_x;
    extern number start_y;
    const float PI = 3.14159265;

    vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ) {
      // float sx = (screen_coords.x - (love_ScreenSize.x / 2)) / love_ScreenSize.y;
      // float sy = (screen_coords.y - (love_ScreenSize.y / 2)) / love_ScreenSize.y;
      float tx = texture_coords.x - 0.5;
      float ty = texture_coords.y - 0.5;
      float a = asin(ty * 2);
      float v = a / PI;
      float r = cos(a);
      float b = asin(tx * 2 / r);
      float u = b / PI;
      texture_coords.x = mod((u + start_x) + 0.5, 1.0);
      texture_coords.y = mod((v + start_y) + 0.5, 1.0);
      vec4 texcolor = Texel(texture, texture_coords);
      texcolor.a = (tx * 2 > r)? 0.0 : texcolor.a;
      texcolor.a = (tx * 2 < -r)? 0.0 : texcolor.a;
      return texcolor * color;
    }
  ]]

  myshader = love.graphics.newShader(shadercode)
  myshader:send("start_x", 0.0)
  myshader:send("start_y", 0.0)

  ang = 0.0
  px = (scr_w - img:getWidth()) / 2
  py = (scr_h - img:getHeight()) / 2
end

function love.update(dt)
  ang = ang + 0.1 * dt
  myshader:send("start_x", ang)
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(24, 64, 176, 255)

  love.graphics.setColor(255, 255, 255)
  love.graphics.setShader(myshader)
  love.graphics.draw(img, px, py)
  love.graphics.setShader()

  love.graphics.setCanvas()

  -- draw canvas to window
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ox = (wdw_w - (scr_w * scr_scale)) / 2
  scr_oy = (wdw_h - (scr_h * scr_scale)) / 2
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ox, scr_oy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  elseif key == "f11" then
    -- toggle fullscreen
    if love.window.getFullscreen() then
      love.window.setFullscreen(false)
    else
      love.window.setFullscreen(true)
    end
  end
end

向きを変えて、雲レイヤー(?)も追加した版のソースは以下。

_conf.lua
_main.lua

画像もソースも、License : CC0 / Public Domain ってことで。

考え方。 :

処理の考え方をメモ。

shader_planet_about01.png

そこに球があるとして、y方向の角度 a、x方向の角度 b を、テクスチャの v,u 値として利用することにする。

Shader の texture_coords.x、texture_coords.y には、0.0〜1.0 の値が入ってくるけど、それを -0.5 〜 +0.5 にした tx,ty があるとして。

y方向の角度 a は、
a = asin(ty * 2)
で求められる。

また、ty の高さには、上から見て cos(a) を半径とする円がそこにあるはずで。なので、x方向の角度 b は、
b = asin(tx * 2 / r)
で求められる…かなと。

asin() が返してくる値はラジアン単位だから…。π(PI)で割ってやれば、-0.5 〜 0.5 の値になってくれるので、テクスチャの u,v値として使い易いはず。180度 = πラジアン、360度 = 2πラジアンだから。
u = b / PI
v = a / PI

このままだと、球の外側に相当する部分も描画されてしまうので…。tx と r = cos(a) を利用して、球の外側を処理してるなら描画する点のアルファ値を0に、球の内側を処理してるならアルファ値を本来のアルファ値にすることで、球だけを描画してるように見せかける。
texcolor.a = (tx * 2 > r)? 0.0 : texcolor.a;
texcolor.a = (tx * 2 < -r)? 0.0 : texcolor.a;

こんな感じで合ってる…のかな。どうなんだ。自信無し。

元ネタは全然違うっぽい。 :

ここまでやってから _元ネタ を確認してみたら、見た目からして違う処理をしていた…。さて、これはどういう処理をすればいいんだろう? もうちょっと考えてみないと…。

待てよ。もしかしてコレ、テクスチャのスクロールする方向を横じゃなくて縦にすればいいのかな。



どうだろう。似てるような、ビミョーに何か違うような…。

使ったテクスチャは以下。

_planet_tex_480x480_3.png

このテクスチャ画像は、 _File:Full moon.jpeg - Wikimedia Commons で公開されてる画像を加工して作ったのだけど、Wikipediaのソレは Public Domain となってるように見えるから、この画像も Public Domain ってことで。

#2 [nitijyou] 灯油ファンヒーターが壊れた

部屋で使ってた corona製灯油ファンヒーターが壊れてしまった。電源を入れても、しばらくするとLED表示部分に「E1」と表示されて動かない。

ググってみたら、「E1」表示は電気系統の故障という意味の表示らしいけど、実際には燃焼室(?)でススが溜まってショートしてる場合が多いそうで、分解して該当パーツを清掃したら復活した事例も少なくないらしい。もちろん分解してそんなことしたら保証は無くなるけど。ちなみに、セーブモードで使い続けるとススが溜まりやすいそうで。自分、ずっとセーブモードで使い続けてた…。失敗した。更に、DAINICHI製は違う構造になっていて、似たような表示になったら部品交換するしかないから自分で直せない、という話も見かけた。

親父さん達に相談したところ、少し前に追加購入したファンヒーターをとりあえず使ってしのげないか、という話に。しばらく前に、1階洋間で使っていたファンヒーターも壊れてしまって、買い替えてみたものの、何故かうっかり火力が弱い製品を買ってしまったそうで。追加でもう1つファンヒーターを買った、という状況なので、1階には2つファンヒーターがあるわけで。

とりあえず、1階から借りてきたファンヒーターの型番をメモ。 _CORONA FH-G3217Y 。木造9畳までを想定した製品。今まで使ってた製品は10畳までを想定したスペックだったから、そんなに違いはないだろう…。消費電力は、点火時は650W食うけど、燃焼中は20W程度と取扱説明書に書いてあった。今まで使ってた製品と比べて本体は随分と小さい。それでいてタンクは大きいし容量も結構多い。随分と進歩してるのだなあと。いや、今まで使ってた製品が「コレいつの製品なんだよ…」と不安になるぐらい昔の製品だったからアレなのだけど。この手の製品って、近年はそれほど変わってないよな…。

2017/12/22(金) [n年前の日記]

#1 [nitijyou] ファンヒーターを親父さんが修理した

先日、CORONA製ファンヒーターが「E1」表示になって壊れてしまったのだけど。親父さんが分解して修理した、とメモ。親父さん、スゴイ。さすが。自分は、親父さんがファンヒータの前面パネルを開いてみた段階で、「うむ。中身がさっぱり分からん。これは分解できない。無理」と諦めモードになってしまったのだけど。親父さんは「作る時に組み立てているんだから分解だってできるはず」とその後もチャレンジして燃焼室まで辿り着けたという。まあ、再度組み立てたら何故かネジが2本余ると言うお約束な展開もあったけど…。 *1

Web上で見かける故障・復活事例と同様、燃焼室はススだらけだった。そこを掃除したらたしかに復活した。CORONA製で良かった…。DAINICHI製なら部品交換になるからこうはいかなかった…。

ただ、ファンヒーターは1991年製。そんなに古い製品を使い続けて大丈夫なのか、という不安が…。もっとも、先日購入したファンヒーターをメインに使って、補助的に使うことになるだろうから、おそらく大丈夫…だといいな…。
*1: 右側パネルを内部で固定するネジをつけ忘れていた、というオチだった。

#2 [anime] 「Re:ゼロから始める異世界生活」を視聴中

BS11で、一晩につき2話〜5話ずつ一挙放送してるので視聴中。昨晩で、11話が終わったあたり、だろうか。

今まで「Re:ゼロが○○とコラボ!」的なCM映像をBSチャンネル上でも散々見せられてたけど、本編はBSで一度も放送されてなかったので、CMを見るたびにもやもやしてたわけで。例えば「けものフレンズ」は「けものは居ても、のけものは居ない」と歌ってるらしいけど、BS民はのけもの扱いさ…。「悪いなあ、のび太。このアニメ東京人じゃないと見れないんだよ。」状態さ…。などともやもやしてたけど、今回の放送でちょっとスッキリ。ありがたや。まあ、相変わらず「けもフレ」のほうは田舎者がのけもの扱いなのだけど。 *1

それはさておき、これはたしかに面白い作品だなと。そりゃ話題になるわ…。みたいな。

ゲームを原作としたアニメはえてしてどれもこれもアレなことになってしまうものだけど。ゲームそのものを原作とするのではなく、ゲームに特有な要素を観察分析して見つけ出し、それらをしっかり咀嚼して取り込めば、ちゃんと面白い小説・漫画・アニメが作れるのだなと…。それは逆に、ゲーム原作アニメがアレな出来になるということは、ゲームには映像作品として成立することを阻害してしまう何かが多く含まれてしまっている、いわば薬より毒が多いということでもあるのだろうと。さて、その毒とは何なのだろう。そこに気づければゲーム原作アニメの作り方も大きく変えていけるはず…。てなあたりをなんだかつい考えてしまうぐらいに、この作品は面白いなと。

そういえば、似たような設定で、ハリウッドで実写化されたラノベがあったな…。「All You Need Is Kill」だったっけ…。ググってみたら、あっちは2004年、こっちは2012年の作品なのか…。ひょっとするとこの手の設定は、もはやジャンルになってたりするのだろうか。

本編中で「泣いた赤鬼」の話が出てきて、それがキャラクターのアレコレと重なるあたりに、なんだか感心したり。おそらくエピソードを作ってから件の童話を重ねた、という感じかなと想像するのだけど、逆に、件の童話をアレンジして、こういうキャラ設定・エピソードに繋げることも可能だったりするのだろうかと。よく知られている話もアレンジ次第でまた違う印象を受ける話にできるはず、みたいな。

何にせよ、この作品、面白い。と思ったことをメモ。
*1: あんな状態になったから、アレはもう、全国放送は無いだろうな…。自分達で勢いのあるタイトルの息の根を止めていくとかチョー余裕ある会社だよな…。どうか「Re:ゼロ」はそうなりませんように。

2017/12/23() [n年前の日記]

#1 [love2d] love2d + stiライブラリでTiledで作ったタイルマップのオブジェクトレイヤー情報を取得

レトロ風の2Dゲームを作る際、えてして背景(BG、Background)は、タイル状の画像を並べたマップ(タイルマップ)で表現したりするわけだけど。

そういったタイルマップの作成ツールとして、 _Tiledマップエディタ があり。例えば love2d も、 _sti (Simple Tiled Implementation) というライブラリを使わせてもらうことで、Tiled で作ったタイルマップを簡単に表示できたりするわけで。

で。単に表示するだけなら話はこれで終わりなのだけど。せっかくだから、Tiled で作ったタイルマップに敵発生用の情報も含めておいて、それを読み取って使いたいものだよなと。でも、どうすればそんなことができるのだろう。

Tiled は、色々な種類のレイヤーを作ることができるけど…。「この位置に、この種類の敵を置きたい」なんて時は、オブジェクトレイヤー(Object Layer)を使えば目的を果たすことができるかもしれないなと。love2d + sti で、オブジェクトレイヤーの情報を読み取ることができれば…。

てなわけで、そのあたりを調べて実験したり。

こんな感じになった。「a」「b」と表示されてる白い四角があるけれど、これがオブジェクトレイヤーの情報を読み取って、その位置に表示してみたソレ。

tilemap_test04_getobject_ss01.png

Tiledで作業する際の前提条件。 :

  • 敵発生位置は、オブジェクトレイヤー(Object layer)を作って指定する。
  • オブジェクトレイヤーには、複数のオブジェクトを置くことができる。
  • 今回は、32x32ドットの四角形(rectangle)を置いて、位置を指定した。
  • 敵種類の判別は、各オブジェクトの名前を利用する。「a」「b」等を指定した。
Tiled の操作方法については、別記事でまとめるつもり。

画像とソース。 :

使った画像・タイルマップデータは以下。

_enemy_mark.png
_tile01_b.png
_mecha_bg2_map_with_enemy.lua
_mecha_bg2_map_with_enemy.tmx

ソースは以下。 _conf.lua
function love.conf(t)
  t.window.title = "Tilemap Test 04 get object"
  t.window.width = 1280
  t.window.height = 720
  t.window.vsync = true
  t.console = true
  -- t.window.fullscreen = true
  -- t.window.fullscreentype = "exclusive"
end

_main.lua
-- tilemap test 04 get object
--
-- use sti library
-- https://github.com/karai17/Simple-Tiled-Implementation

local sti = require "sti"

function love.load()
  love.graphics.setDefaultFilter("nearest", "nearest")
  scr_w, scr_h = 640, 480
  canvas = love.graphics.newCanvas(scr_w, scr_h)

  -- load image
  mark_img = love.graphics.newImage("enemy_mark.png")
  mark_quads = {}
  for i, k in ipairs({"a", "b", "c", "d"}) do
    local x, y = (i - 1) * 32, 0
    local w, h = 32, 32
    mark_quads[k] = love.graphics.newQuad(x, y, w, h, mark_img:getDimensions())
  end

  -- load tilemap
  map = sti("mecha_bg2_map_with_enemy.lua")

  map.getGidByPixel = function(self, x, y, layer_name)
    local tilex, tiley = self:convertPixelToTile(math.floor(x), math.floor(y))
    tilex = math.floor(tilex)
    tiley = math.floor(tiley)
    local layer = map.layers[layer_name]
    local tilew, tileh = layer.width, layer.height
    if tilex < 0 or tilex >= tilew or tiley < 0 or tiley >= tileh then return -2 end
    local tile = layer.data[tiley + 1][tilex + 1]
    if tile == nil then return -1 end
    return tile.gid
  end

  -- get map objects
  enemy_objs = {}
  for k, obj in pairs(map.objects) do
    local x, y, w, h = obj.x, obj.y, obj.width, obj.height
    x = x + w / 2
    y = y + h / 2
    table.insert(enemy_objs, {kind=obj.name, x=x, y=y})
  end

  -- sort objects by y
  table.sort(enemy_objs, function(a, b) return (a.y > b.y) end)

  map.layers["enemy_tbl"].visible = false

  tx_start = 32
  ty_start = 480 * 7
  tx = tx_start
  ty = ty_start
  gid = 0

  -- framerate steady
  min_dt = 1 / 60
  next_time = love.timer.getTime()
end

function love.update(dt)
  next_time = next_time + min_dt
  wdw_w, wdw_h = love.graphics.getDimensions()
  scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h))
  scr_ofsx = (wdw_w - (scr_w * scr_scale)) / 2
  scr_ofsy = (wdw_h - (scr_h * scr_scale)) / 2

  map:update(dt)

  -- keyboard check
  local speed = 160 * dt
  local kd = love.keyboard.isDown
  if kd("left") or kd("a") then tx = tx - speed end
  if kd("right") or kd("d") then tx = tx + speed end
  if kd("up") or kd("w") then ty = ty - speed end
  if kd("down") or kd("s") then ty = ty + speed end

  map.layers["enemy_tbl"].x = -tx
  map.layers["enemy_tbl"].y = -ty
  map.layers["bg_a"].x = -tx
  map.layers["bg_a"].y = -ty
  map.layers["bg_b"].x = -tx / 2
  map.layers["bg_b"].y = -ty / 2
  map.layers["bg_c"].x = -tx / 4
  map.layers["bg_c"].y = -ty / 4

  -- get mouse position
  local mx, my = love.mouse.getPosition()
  mx = (mx - scr_ofsx) / scr_scale
  my = (my - scr_ofsy) / scr_scale

  -- get tile gid
  mx = mx + tx
  my = my + ty
  gid = map:getGidByPixel(mx, my, "bg_a")
end

function love.draw()
  love.graphics.setCanvas(canvas)
  love.graphics.clear(0, 0, 0, 255)

  -- draw tilemap BG
  love.graphics.setColor(255, 255, 255, 255)
  map:draw()

  -- draw objects
  love.graphics.setColor(255, 255, 255, 255)
  for i, obj in ipairs(enemy_objs) do
    local x = obj.x - tx
    local y = obj.y - ty
    love.graphics.draw(mark_img, mark_quads[obj.kind], x, y, 0, 1, 1, 16, 16)
  end

  love.graphics.setCanvas()

  -- draw canvas to window
  love.graphics.setColor(255, 255, 255)
  love.graphics.draw(canvas, scr_ofsx, scr_ofsy, 0, scr_scale, scr_scale)

  love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10)
  love.graphics.print("tx = "..tostring(tx), 10, 40)
  love.graphics.print("ty = "..tostring(ty), 10, 60)
  love.graphics.print("R : reset", 10, 100)
  love.graphics.print("ESC : exit", 10, 120)
  love.graphics.print("objs = "..tostring(#enemy_objs), 10, 140)
  love.graphics.print("gid = "..tostring(gid), 10, 160)

  if love.system.getOS() == "Windows" then
    -- wait
    local cur_time = love.timer.getTime()
    if next_time <= cur_time then
      next_time = cur_time
    else
      love.timer.sleep(next_time - cur_time)
    end
  end
end

function love.keypressed(key, isrepeat)
  if key == "escape" then
    -- ESC to exit
    love.event.quit()
  elseif key == "r" then
    -- reset scroll position
    tx = tx_start
    ty = ty_start
  end
end

動作には、 _stiライブラリ が必要。 _karai17/Simple-Tiled-Implementation: Tiled library for LOVE の右上の「Clone or download」から、zip をダウンロードして解凍してもいいし、git がインストールしてある環境なら、
git clone https://github.com/karai17/Simple-Tiled-Implementation.git
と打って入手してもいい。main.lua と同じ階層に、stiフォルダが置いてあるようなファイル・フォルダ配置にして使う。

画像、タイルマップデータ(.tmx)、main.lua は License: CC0 / Public Domain ってことで。

stiライブラリは MIT/X11 Open Source License なので注意。

少し解説。 :

stiライブラリは、最低限の使い方であれば、以下で済む。
local sti = require "sti"

function love.load()
  map = sti("hoge_tilemap.lua")
end

function love.update(dt)
  map:update(dt)
end

function love.draw()
  love.graphics.clear(0, 0, 0, 255)

  love.graphics.setColor(255, 255, 255, 255)
  map:draw()
end
  • 最初に、local sti = require "sti" と書くことで「stiライブラリを使うよ」と指定して。
  • 初期化処理を行う love.load() の中で、Tiled からエクスポートした .lua を読み込んで。
  • 更新処理を行う love.update(dt) の中で、map:update(dt) を呼んで。
  • 描画処理を行う love.draw() の中で、map:draw() を呼んで描画する。

オブジェクトレイヤーに乗ってるオブジェクトの情報を取得するなら、map.objects にアクセスすればいい。
  enemy_objs = {}
  for k, obj in pairs(map.objects) do
    local x, y, w, h = obj.x, obj.y, obj.width, obj.height
    x = x + w / 2
    y = y + h / 2
    table.insert(enemy_objs, {kind=obj.name, x=x, y=y})
  end
各オブジェクトは、以下の情報を持ってる。
{
  id = 16,              -- ID
  name = "a",           -- 名前
  type = "",            -- 種類
  shape = "rectangle",  -- 形状
  x = 96,               -- x
  y = 3360,             -- y
  width = 32,           -- 幅
  height = 32,          -- 高さ
  rotation = 0,         -- 角度
  visible = true,       -- 表示/非表示
  properties = {}       -- カスタムプロパティ
},

#2 [love2d][cg_tools] Tiledマップエディタでオブジェクトレイヤーを使う際の操作手順をメモ

Tiledマップエディタでオブジェクトレイヤーを使う際の操作手順を一応メモ。

文章で説明すると分かりづらいだろうから、一応動画でキャプチャしてみたり。



  1. レイヤーウインドウの左下のボタンを押すとレイヤーが追加できる。その中から「Object Layer」を選んで追加する。
  2. オブジェクトウインドウを表示すれば、オブジェクトレイヤーに登録されてるオブジェクトの一覧が確認できる。
  3. 上のツールバーから、「四角形を追加」を選ぶ。
  4. メインウインドウ内で四角を描くようにドラッグすると、四角形(rectangle)が追加できる。
  5. プロパティウインドウを表示して、幅、高さ、名前を変更。
  6. 上のツールバーから、「オブジェクトを選択」を選ぶ。
  7. オブジェクトをクリックして選択。ドラッグ操作でオブジェクトを移動できる。
  8. Ctrlキーを押しながらドラッグすると、タイル単位でオブジェクトを移動できる。
  9. Ctrl + C、Cirl + V で、オブジェクトのコピーと貼り付けができる。

キャプチャし忘れた操作についてもメモ。 Ctrl + ドラッグを使って、タイル単位でざっくりと移動してから、カーソルキーを使って1ドット単位で位置を調整すれば、比較的きっちりと位置指定ができるはず。もちろん、プロパティウインドウで直接座標値を打ち込んでもOK。

タイルマップデータを love2d で使う方法。 :

タイルマップデータを love2d で使いたいなら、Lua形式でエクスポートすればいい。ファイル → 名前を付けてエクスポート → Lua形式を選んでエクスポート。中身は Lua のテーブルになってるので使い易いはず。

2017/12/24() [n年前の日記]

#1 [love2d] ドット絵作成中

BGスクロールに合わせて指定の位置に敵を出したい。只の四角等を描画しても気分が出ないのでドット絵を作成中。

2017/12/25(月) [n年前の日記]

#1 [love2d] love2d + stiライブラリでTiledデータのオブジェクト情報を取得その2

love2d と stiライブラリを使って、Tiled で作ったタイルマップデータからオブジェクトレイヤーの情報を取得して、敵を発生させる処理ができそうか試したり。

例えば、Tiled上で四角形のオブジェクトを以下のように配置すれば…。

tilemap_test05_getobject2_ss02.png

こんな感じで、スクロールに合わせて敵が画面に出てくる、みたいな。

画像とソース。 :

使用画像とタイルマップデータは以下。License: CC0 / Public Domain ってことで。

_largecanon.png
_beam01_64x64.png
_block01.png
_tile01_b.png
_mecha_bg2_map_with_enemy.lua
_mecha_bg2_map_with_enemy.tmx

ソースは以下。

_conf.lua
_main.lua

動作には _stiライブラリ が必要。

少し説明。 :

Tiled 上で敵の配置をする際、オブジェクトにカスタムプロパティを追加して速度も指定していたり。

love2d側でカスタムプロパティがどう見えているかは…。 _mecha_bg2_map_with_enemy.lua を開いて最後のあたりを見れば、フツーに Lua のテーブルになってるので分かりやすいかなと。以下のような感じだけど。
  {
    id = 26,
    name = "b",
    type = "180",
    shape = "rectangle",
    x = 384,
    y = 2336,
    width = 64,
    height = 64,
    rotation = 0,
    visible = true,
    properties = {
      ["speed"] = 128
    }
  },
ちなみに上記の例では、type には角度を入れてる。

rotation で角度を指定すれば良かったのでは、と思ったけど、x, y の位置を中心にして回転してしまうから、ちょっと都合が悪いのだな…。中心位置を別途指定できれば助かりそうだけど、それはそれで情報が増えちゃうからよろしくないであろう予感も。

今回、砲台からビームも撃たせてみたけれど。love2d は描画の優先順位を自由に指定できない・描画した順に上書きされていくので、ちょっと悩んだり。砲台の奥に描画できたら見た目はマシになるのだけど…。結局、発生位置を調整して誤魔化した。こんな時、DXRubyの、z値(描画優先順位)を指定可能な Sprite、てな仕様の便利さを再認識するというか。

2017/12/26(火) [n年前の日記]

#1 [anime] 名探偵ホームズ「貨車が消えた!?教授の大魔術」を視聴

宮崎駿監督担当回の放送は終わってしまったけれど、その後もBS11で放送されてる「名探偵ホームズ」を視聴していたり。

正直なところ、宮崎駿監督担当回以外は見る価値は無いかなと結構失礼なことを思っていたけれど、「貨車が〜」の回を見て、他の回もフツーに見どころがあるなーと印象が変わったというか。伊藤恒久脚本のせいなのか、ホームズがちゃんと名探偵をしていて驚いたりとか。奥田誠治コンテも、宮崎駿コンテのように1カット1カットが絵になってるわけではないものの、映像として飽きが来ないようにカメラ位置を工夫しているように感じたりとか。 *1 北原健雄+古田詔治作監回は、ルパン三世シリーズを支えてきた方々だけあって、ルパンっぽいハチャメチャな動きが各所に盛り込まれていて唸ったりとか。

まあ、他の回を見ると、箸にも棒にも掛からない回もちょこちょこあるのだけど、「これは…!」と思ってしまう回もフツーにあるのだなと。

特に今回、作画の方向性が個人的にはかなり好みで。あらゆる方向に伸びたり縮んだりする、教授やワトソンの顔がイイ…。何かにつけて動きがアグレッシブ&フリーダム。手描きアニメはこうでないと…。しかし今時は、こういう作画ってすぐに作画崩壊と言われちゃうのがツライ。皆そんなに整ったお人形さんが好きなら、今後は全部3DCGで作る以外ないやん、てな気分になってくる…。いや、自分はCG大好き人間なのでそれはそれでアリだけど。しかし「こんなのCGじゃ無理だわ、さすが手描きだわ」てなアニメも見たいわけで。だけど、そういう作画は一般的な視聴者から叩かれてしまうという。なんだかな。

志村〜うしろうしろ〜。 :

それはともかく、Aパートの列車のシーンで、「志村〜うしろうしろ〜」が入っていて、ああいうのはやはり鉄板だよなー、どんどん使ったほうがいいよなー、と。

_天丼 や、 _昔話の3の法則 もそうだけど、そういうアレコレは体系としてまとめられていないのだろうか。脚本家さん個人の中には溜まってそうだけど、今時の若い演出家さんのほとんどは知識として持ってなさそうな印象も。

いや、知ってても・持っていても、使える企画が無いだけか…。深夜アニメ作品群を思い返すと…使える場面が無いよな。

*1: ほとんどのカットが単なる説明のための記号的カットに過ぎない早川啓二コンテあたりと比べると、映像作品として成立させるためのプラスαを奥田コンテには感じるというか…。もっとも、宮崎駿コンテはさらにその上を行くわけだけど。映像として成立するのはもちろんのこと、どのカットを切り出してもそこだけで絵になるほどのレイアウト…。恐ろしい。

2017/12/27(水) [n年前の日記]

#1 [love2d] 先日作ったSTGモドキサンプルに敵セットテーブルを追加

BGスクロールに合わせて敵を発生する処理をせっかく試したので、先日書いたSTGモドキサンプルに処理をコピペしているところ。

今まではランダムに雑魚敵を発生させていたけど、マップに合わせて敵の配置ができるようになると、結構ゲームっぽくなってくるなと…。動作確認を目的としたテストプレイをしていても、敵を避けられなかったり、全部の敵を倒せなかったりで、ビミョーに遊んじゃってる気分になってくる…。

#2 [anime][neta] 全裸でバトルしちゃダメなんだろうか

「戦刻ナイトブラッド」という、おそらくは女性向けアニメの最終回を見ていたら、作画がどんどん怪しくなってきて、しかし飛んだり跳ねたりのバトルシーンを描いていて、なんだかなと思ってしまったり。女性向けアニメなんだから動くことよりお人形さんの奇麗な御尊顔のほうが優先されるのではないのかな…。こんなバトルシーン、誰が喜ぶのだろう…。

そもそも件のアニメはキャラデザの線が妙に多かったわけで。和服+鎧+装飾品だらけだし。しかも服にテクスチャを貼り込むのがデフォルト。そんな面倒臭いキャラデザを動かすなよ…。無謀だよ…。どう考えても動かすことを諦めてる系のキャラデザじゃんよ…。それを前提にしたコンテを描けよ…。あんなのを手描きで動かすとかどんな地獄だよ…。後工程のことを考えろよ…。お前だけコンテ作業が終わればそれでいいのかよ…。「ダイナミックコード」を少しは見習いなさいよ…。や、全部見習わなくていいから。少しでいいから見習おう。基本姿勢だけでも見習おう。みたいな。

てなことを思ってるうちに、せめて服や鎧が無くて全裸だったらもうちょっとなんとかなったのかなと思えてきたわけで。

秀吉 「ぐうっ!」
   秀吉、敵から攻撃を受けて、服と鎧が吹き飛び、全裸状態に。
武将A 「大丈夫か!? 秀吉!」
秀吉 「なあに、このくらい平気さ…。むしろ、おかげでちょっとは体が軽くなったかな?」
ヒロイン 「秀吉くん。せめて鎧だけでも…。えっ? 何コレ?」
     ヒロイン、地面に落ちた鎧を持とうとするが重くて持てない。
ヒロイン 「まさか、今までこんな重いものをつけて戦ってたの?」
狸 「そうです! 秀吉様は、常にこの鎧をつけて鍛えていたのです!」
秀吉 「フッ…だからここからは、完全に俺のターンだああッ!!」
   秀吉、全裸のまま高速に動きまくって敵を切りつける。
   秀吉と一緒になって、画面の中で荒ぶる謎の白い光。
ヒロイン 「スゴイ…まるで光の速さで動いてるみたい…!」
武将A 「ああ…眩しいぜ、秀吉!」
武将B 「この輝き、さすがと言わざるを得ない…」
信長 「ええい、アヤツだけには任せてはおけぬ。私自らが出る!」
   信長も服と鎧をキャストオフ&クロックアップして全裸参戦。
武将A 「なんと神々しい…。まるで光の饗宴…」
武将B 「二人が共に戦う、このような光景がまた見れるとは…眼福! 眼福!」

とかダメですか。ダメか。視聴者的には「全裸キター!」「これで勝った!」ってなりそうですけど。ならないか。

「太陽の牙ダグラム」に出てきた _パジャマソルティック (?)みたいなもんで、設定としては一応アリにならないか…。しかも、女性視聴者と作画スタッフへのダブルサービス。一石二鳥。何より、クライマックスになるとイケメンキャラの肉体美が披露されるというのはストーリー展開と映像面がシンクロして妙な高揚感に繋がるはず。マクロスでアイドルが歌い始めると「勝った!」と思えてくる、あの感覚を得られるのではあるまいか。

なんちてぽっくん。

まあ、そんなことより女性向けアニメはさっさと3DCGに移行してほしいなと…。もはや手描きで作るもんじゃないよなあ、線が多過ぎる、と思いながら眺めてました。これがまだ WIT STUDIO作品のように最後までちゃんと作画できてるなら何も言わないけど…予想通りヘロヘロじゃないスか…。だったらせめて全裸に、いや、全裸じゃなくていいんだけど何かしらの工夫をね…。バトルシーンになると変身して線が少なくなったりとか。「マジンボーン」のように変身後は3DCGになるとか。刀を一振りするだけで後は謎エフェクトや謎パーティクルが敵を倒してくれるとか。色々と手はあるでしょうよ、と。

そういえば「UQ HOLDER!」って、バトルシーンになると何故か美少女キャラ達が全裸にされてたっけ。まさか、そういう理由で…? 赤松先生+アニメスタッフ…なんという策士! 見直した! でもホントか? 実は邪なアレしかないんじゃないか!? どうなんだ!?

出崎演出を参考にできないものか。 :

その手のシーンを見ると、出崎演出を使えないものかなと思うのだけど、昨今ほとんど見かけることがなくて残念に思っていたり。いやまあ、出崎監督しかできない技と捉えられてるのかもしれんけど。止め絵とPANを駆使するあたりはダイナミックコードと同じと言えなくもないけど、しかし出崎演出作品を見ていてもダイナミックコードを見ているような感覚には全くならないわけで。つまりああいった手法は今でも利用できるんじゃないのかなと…。

もっとも、アレは止め絵を描く人が上手くないといけないから、そんなアニメーターさんが居ることを期待できない状況では難しいのかもしれないし。筆でタッチを入れたりもするから、デジタル制作時代にはやりたくてもやりづらいところがあったりするのだろうか。分業化が進み過ぎて、そういう画面が作れないとか…?

でも、もったいないなあ。せっかくああいった技を先人が生み出してくれたのに、全然継承されてない・活用されてない・発展させる人も居ないってのが…。しかも、もっとスゴイことができてるならともかく、現状ではダイナミックコードみたいなのが出てくるわけで…。 *1

「出崎演出は俺がマスターしてやる。そして発展させちゃる!」てな演出家さんが出てこないかな。

いや、既に居るのかもしれないか。さりげなく使いこなしているから見てる側が気付いてない、そんな可能性だってあるのかもしれない。実に自然に見えるように上手に作られた映像は、技を駆使してることに気づかれない。みたいな。

*1: と言っても、ダイナミックコードは、放送に間に合っただけでも褒め称えるべき状況だった可能性が高そうだなと勝手な邪推をしていたりもするのだけど。

2017/12/28(木) [n年前の日記]

#1 [love2d] love2dのSTGモドキサンプルにShaderの処理を入れてみたり

love2dのSTGモドキサンプルに、Shader機能を使って、背景の赤い部分が点滅するような処理を入れてみたのだけど。なんだか見た目、ちっとも目立たないというか、点滅してることに気づかないというか…。多少はリッチさが増すかなと想像してたけど、そんなことはなかった。無くてもいいぐらいの処理だな…。

パレット内容がシフトして置き換わるような処理ができれば、もうちょっと違ってくるのだろうけど。例えば昔の2Dゲームでは、パレット書き換えで _滝が流れる様子 を表現してたりしたわけで。そういう見せ方ができれば、もうちょっと…。いや、どういうGLSLを書けばそんなことできるのかちょっと思いつかないけど。

赤い点がほわんほわんと点滅するぐらいでは目立たないのだよな。 _このゲームの背景でもほわんほわんと点滅してる けど、コレ、気づかないよなあ…。

2017/12/29(金) [n年前の日記]

#1 [cg_tools] darktableを少しだけ試用

darktable という、フリーで使える写真編集ソフト(現像ソフト?)があるらしいと知り。今まではLinux版やMac版しかなかったらしいけど、Windows版が出たらしいので、darktable 2.4 を Windows10 x64上でインストールしてみたり。

_darktable
_写真RAW現像/編集管理ソフトウェア「Darktable 2.4」リリース、Windowsへの移植が完了 | OSDN Magazine
_第474回 UbuntuとdarktableではじめるRAW現像入門:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社

公式ブログでは「Windows版はまだバグがたくさんあるはずだからガンガン報告してくれ」と書いてあった。

_公式サイト の install をクリックするとOSのマーク画像があるので、Windowsのそれをクリック。darktable-2.4.0-win64.exe がダウンロードできる。

インストールはウイザード形式で進む。今回は、C:\Prog\darktable にインストールしてみた。

インストール時にいくつかの環境変数が追加・設定されるらしい。しかし、MAGIC_HOME という環境変数のパスがおかしかった。
C:\Prog\darktable\lib\GraphicsMagick-1.3.25\modules-Q8\coders
になってたけど、
C:\Prog\darktable\lib\GraphicsMagick-1.3.27\modules-Q8\coders
が正しいのだと思う。古い GraphicsMagick のバージョンを指定したまま、インストーラを作っちゃったのだろうな…。

他に追加された環境変数は以下。
CAMLIBS=C:\Prog\darktable\lib\libgphoto2\2.5.16
IOLIBS=C:\Prog\darktable\lib\libgphoto2_port\0.12.0

少し使用してみたけど、本当に写真画像編集ソフトだった。写真画像に対してコントラストを弄ったりホワイトバランスを弄ったり等、必要になりそうな機能は一通り実装されてる印象だけど、お絵かき機能の類は持ってないように見えた。本来ならRAW画像を開いてアレコレするソフトなのだろう…。

画像管理ソフトとして使えたりしないかと期待してたけど、現状ではなんだか動作がもっさりなのであまりメリットはナサゲ。

#2 [anime] 「アニメガタリズ」最終回を視聴

凄かった…。ここまでやるか…。これはスゴイアニメだ…。まさかこんな展開を用意していたとは…。

なんとなくアキバレンジャーを思い出したりもして。アレもビックリしたけど、これもビックリ。いやはや…よくぞここまで…。

2017/12/30() [n年前の日記]

#1 [anime] 「ガールズ&パンツァー劇場版」を視聴

TV放送された版を録画したまま見てなかったのだけどようやく視聴。

面白かった。戦車については全く知らないし、各学園の元ネタもさっぱり分からんし、どうも細かいところでカットされてたような気もしたけれど、それでも見ていてフツーに面白かった。TVシリーズを視聴していた人ならこのシーンで盛り上がるだろうなあ、とか、大画面でこのシーンを見たらスゴイ迫力だろうなあ、とか、そのあたりを容易に想像できるぐらいによくできたアニメ作品のように感じたり。

何度も劇場に足を運んだ人が居るらしいけど、この内容ならそれも当然と思えてくるほどの出来。これは何度も見たくなるわな…。素晴らしい。

それにしても、よくまあこんなシチュエーションを思いつくなあ…。また、映画の長さに対して脚本の量も少なかったりしないかと想像したりもして。ほとんどが戦車の追いかけっこ、台詞無しのシーンばかりだし。コンテを描くのも大変だったのでは…。

#2 [movie] 「ペーパー・ムーン」を視聴

録画したまま見れてなかった、映画「ペーパー・ムーン」をようやく視聴。録画日を確認したら 2016/03/04だった…。ずっとHDDレコーダに入れっぱなしだった…。

映画のタイトルはあちこちでよく目にしていたけど、視聴したことは無かったわけで。なるほど、こういう話だったのか…。昔だったから作れたけど、今じゃこんな話作れない予感。というか子供の喫煙シーンだけでもアウトだよな…。

ずっと見てるうちに、なんだか見覚えがあるようなないような、妙な感覚に。不安になってきて日記を検索してみたら…。アレ? 自分コレ _見たことある じゃん! マズい。完全に忘れていた…。しかも似たような感想書いてるし…。もうダメだ。自分の記憶力はもうダメだ。

#3 [movie] 「キャプテン・アメリカ/ウィンター・ソルジャー」を視聴

録画したまま見れてなかったけどようやく視聴。

見始めてから気付いたけど、コレって続編映画だったのか…。設定が分かるかなと不安になったけど、まあ大体なんとなく分かった。CGとアクションがスゴイことになってるから、そこだけでも楽しめる印象。

#4 [ubuntu] キューブ機にXubuntu 16.04 LTSをインストール

自宅サーバ機の電源がどうも怪しくなってきている気がするので、代替用として随分昔に購入したまま埃を被っていたキューブケースのPCを少しセットアップしておこうかと。とりあえず Xubuntu 16.04 LTS をインストールして、そこから作業をしていこうかな、と。

キューブ機のスペックは…。 だったような気がする。

_Xubuntu から、isoをダウンロード。Atom N270 は32bit版しか動かなかったはずなので、32bit版のiso、xubuntu-16.04.3-desktop-i386.iso を選んだ。 _Intel Atom - Wikipedia によると、デスクトップ用 Atom の 230 や 330 なら64bitに対応してるっぽいのだけど、モバイル用の N270 は64bit非対応らしい。

_UNetbootin (unetbootin-windows-657.exe)を使って、USBメモリに、USBブートできる形でisoを書き込み。USBメモリをキューブ機に差して起動。BIOS設定で、USBメモリから起動するようになってたようで、そのまま Xubuntu が起動した。

ディスクを削除する形でインストール。インストール自体は、体感で30分〜1時間ぐらいかかったものの、すんなりできた。

しかし、再起動後、ネットワークに接続できなくて悩んだり。ググってるうちに思い出してきた。昔の Intel Atom + Intelチップセットを使ってる環境で Ubuntu を入れると、r8168 ではなく r8169 が間違って使われてしまう…のではなかったか。ドライバをインストールし直さないと。

2017/12/31() [n年前の日記]

#1 [ubuntu] キューブ機を少しセットアップ

Intel Atom N270 が載ってるキューブ機を少しだけセットアップ。

NICドライバを入れ替え。 :

_2014/04/02の日記 に手順をメモしてあったので、同じような感じで作業。

#2 [nitijyou] 部屋を掃除

弟が帰省するので、午前中に部屋を掃除。

#3 [nitijyou] 弟が帰省

夕方頃に到着。

色々なPCパーツを持ってきてくれた。とりあえず親父さんが使ってた MITSUBISHI製24インチ液晶ディスプレイを、弟が持ってきてくれた DELL製25インチ液晶ディスプレイと交換。DisplayPortとやらで接続するらしいけど…。幸い、親父さんPCのM/BにはDisplayPortがついていた。助かった。今回弟が持ってきた液晶ディスプレイは、2560x1440の解像度だそうで、親父さんがウチで一番スペックの高いディスプレイを使っていることに。

以上、31 日分です。

過去ログ表示

Prev - 2017/12 - Next
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project