ソシャゲのシナリオをテキストデータに変換する(フレーム抽出編)

ある日元気にソシャゲのオタクがはまったばっかりの時に作るメモを書いていました。

blog_img1.jpg
段々苛立ってきた。令和にもなってテキストを探すためになんでこんなことをやっているんだ?ロード時間、必死に記憶をたどってあちこちの章をうろうろする時間、話の冒頭だけチェックして→やっぱ違ったかも→いややっぱこの話の最後の方だわってやる時間。あ~全部無駄。
紙の本ってすごいな。テキストを大雑把に検索する方法って未だに紙の本が一番早い。シナリオ本が欲しい。次点でシナリオ検索ボックス。両方あるともっといい。ソーシャルゲームとしてのシナリオへの誠意……(「誠意」という言葉を私は世間で言う「愛情」のように容易に使いがちで宜しくないと思いますがここでいうシナリオへの誠意とはゲーム内でシナリオという要素が結局顧客の愛着、課金につながっていることを自覚した開発コストをかけろという要求を示しています)。しかし私は極めて理性的な人間なのでわかります。シナリオはゲームを起動させるためのインセンティブを担っているわけですよね。それからゲームのブランドとしてシナリオ本を出すためのコストも考慮しなければいけない。俺達は直打ちのtxtファイルでいいと言っても公式がそんなもの出すわけにはいかないんだ(でも突然WAVファイルを配布するゲームならワンチャンあるかもな)。
世は大自己責任時代。ソシャゲのユーザーも楽しみ方は自らで見いださなければならないとされる。
うーん、じゃあ自分でやるか。
オートモードで録画するとシナリオが動画として保存できるから、ここからテキストが抽出できればそれでいいだろう。本当は録画さえ面倒くさいがそこはもうどうしようもないので妥協しましょう。しかしシナリオのオート機能って到底人が読めない速さで流れていくけど、なんのための機能なんだろうね。もしかして公式としてテキスト抽出を推奨しているのかな?(自己の正当化)
#動画の切り出し
import cv2
import matplotlib.pyplot as plt
import numpy as np
import time
import winsound
def mov_info(cap):
    print(“フレームレート(枚数/second)”, cap.get(cv2.CAP_PROP_FPS))
    print(“フレーム枚数(枚数/second)”, cap.get(cv2.CAP_PROP_FRAME_COUNT))
    print(“秒数”, cap.get(cv2.CAP_PROP_FRAME_COUNT)/cap.get(cv2.CAP_PROP_FPS))
(出力)
フレームレート(枚数/second) 29.74832623603263
フレーム枚数(枚数/second) 12819.0
秒数 430.915
まずはopencvで動画の情報を調べてみます。動画というのはパラパラ漫画です。このデータは430秒の動画が12819枚の画像で構成されているという訳だ。これを全部保存、閲覧するのは無駄なので、この中から必要な画像を抜き出していきます。
# movieのキャプチャを配列に変換
def mov_cap(capture, triming, grayscale, frame_sp):
    frame_list = []
    frame_del_list = []
    frame_prev = []
    framenum_prev = 0
    counter = 0
    frame_ls = list(np.arange(0, cap.get(cv2.CAP_PROP_FRAME_COUNT), frame_sp)) + [cap.get(cv2.CAP_PROP_FRAME_COUNT)]
    while True:
        ret, frame = capture.read()
        if not ret:
            break
        frame = cv2.flip(frame, -1)
        #グレースケール
        if grayscale == True:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            
        #トリミング
        if triming == True:
            frame = frame[y1:y2, x1:x2]
            
        frame_list.append(frame)
        #1個目でないなら差を計算してframe_del_listに追加
        if frame_prev!=[]:
            frameDelta = cv2.absdiff(frame_prev, frame)
            frameDeltaSum = frameDelta
            frame_del_list.append(frameDeltaSum)
        frame_prev = frame
        framenum_prev = cap.get(cv2.CAP_PROP_POS_FRAMES)
        counter += 1
        if counter >= len(frame_ls):
            break
        cap.set(cv2.CAP_PROP_POS_FRAMES, frame_ls[counter])
                
    return frame_list, frame_del_list
できた。
動画から切り出された画像は 縦ピクセル数×横ピクセル数×3(RとGとB)の数字の行列で表現されます(正確には配列という)。どんな神絵師の絵も配列だと思うと楽しいね。「人間は突き詰めればタンパク質の塊(もっと分解すればアミノ酸)」みたいなもん。
配列の操作は簡単です。まずはテキストボックスの座標を調べてトリミング。その後、RとGとBの値の平均を取ってグレースケールに変換。こうすると配列の長さと次元が減って軽くなる。そのあと、前フレームの画像の配列と今フレームの画像の配列を引き算して結果を保存。これで値が大きく動いた時に、テキストが切り替わったと判断できるというわけ。テキストボックスをトリミングしたのが効いてきて、動画の途中でソロモンが跳ねたりしてもテキストの動きだけ判断できるんですね。
差分をヒストグラムと時系列の折れ線グラフで図示してみます。
blog_img2.jpg
いいね。
ヒストグラムが0に寄っていることは動画がほとんど動いていないことを示しています。頻度ががくっと落ちているところを「動いた」の検出閾値とすればいい、と。
折れ線グラフは定期的に動画が動いていることを示しています。これを見ると約xxフレームでテキストが切り替わるようなので、全てのフレーム読み込む必要はなく、yyくらいの雑さでも問題ないことがわかります(パラメーターを書くとなんとなくよくなさそうなのでやってみたい人は自分で調べてみてね)。
#フレーム抽出用
start = time.time()
frame_list, frame_del_list = mov_cap(cap, triming, grayscale, frame_sp)
end = time.time()
t = time.time() – start
winsound.Beep(523, 1500)
print(t)
上で書いた関数をこんな感じで動かしてみる。2分くらいかかった。
Rを使っていた頃は時間がかかる処理が終わったあと電子レンジの音を鳴らしていたけどpythonになってエラーみたいな音しか鳴らせなくなっちゃった。通知音なんてなんでもいいと思ってたけどないと寂しいな。
#配列2種から大きく変化している画像のみを抽出
def frame_select(frame_list, frame_del_list, threshold):
    frame_sel = [i for (i, j) in enumerate(frame_del_list) if np.sum(j) > threshold]
    frame_sel_list = [j for i, j in enumerate(frame_list) if i in frame_sel]
    return frame_sel_list
画像をダウンロードして見てみますか。
blog_img3.jpg
うんうん。
いいですね。一章で大体60MB、784枚か。画面がフェードアウトするところなんかでやや無駄な画像を保存している様子。これは閾値を上げても解決しなさそうなので許容としましょう。
これで終わりでもいいような気もするけど、検索の利便性のためにテキストファイルへの変換をやってみましょうかね。
次回に続く。
参考文献

性根の曲がった男のこと

人類……キングレオ4話……。
もうキングレオのこと何もわからねぇよ……。「このミステリは名探偵と助手の関係をお楽しみ頂くため邪魔にならない程度の差し障りのない推理をお楽しみいただく作品です」じゃなくなったのか……?思えば冒険を単行本で読んで衝撃を受けてから5年……。覇権になって欲しいぜ!とかやんや言い続けてきたけど……。そして私が応援していたのとは一切関係なく続編が決まってそれは今だって嬉しいと思っているが……。世界にキングレオを勧めた責任を感じており……。勿論何を読み何を感じているかは読んだ君達が決めることで作者が、ましてや推薦者が非難される謂れは一切ないのだが……。しかしそれはそれとして推薦という行為もまた私自身の信頼を賭けた価値観の表明であるわけだから……。要するに何が言いたいかというと私が責任を感じることに君達は一切関係がなく私はただ責任を感じていく……そういうことだよ……。でも冒険を読んだ時点でこんなことになるなんていくら私でも予想できないよ……許して欲しい……。
うゆう、こたつの似合う男。
獅子丸は人生について考え始めないで……。なんか死にそうに見えて不安だから……。
>「余計な馬鹿を晒すな。黙ってろ」
本日の罵倒かわいい獅子丸。軽犯罪を思い浮かべるうゆうの憎めねえ小物感もかわいい。うゆうはいいヤツなんだよ。
獅子丸、身内(広義の)年上には割と礼儀正しいところがあり、かわいい。
ウワーーーーッ存在はずっと唆されていた城坂家兄!!!そう、城坂論語、あんな一人っ子みたいな顔しておいて弟なんですよね……。
論語君に小学生と中学生時代があったということ、もっと言うと血縁が存在するということが城坂論語の人間感を示すもので新鮮だ。
兄の名前春秋なのか。もしかして……別冊文藝春秋!? (No.)
陽虎といい、微妙に対にはならないのが論語君にベター・ハーフはいないことを痛感させられる。
クリアしたゲームのデータを消さない獅子丸。身内と認めた人間の好感は絶対に覆らない獅子丸……。
大河は怒ったり人間をブロック塀に叩きつけたり忙しいのに獅子丸のほうが静かなのもうダメだ。
獅子丸がふわりと微笑む(ふわりと微笑む!?いい加減にしてくれ)の、美人薄命というか残された時間が短い感あり、やめて欲しい。死ぬな。それは論語君の特許ですよ。
一年の失踪期間を加味すると獅子丸は26、27かそれより上のはずなのですが(https://twitter.com/vanmadoy/status/1058537006668505088)、ふと獅子丸は幼い頃に得たたった一人の理解者を敵に回してこれから50年程続く人生をずっと一人ぽっちで生きていくのかよと思ったらめちゃくちゃしんどくなってしまった。あの時論語を追いかけてなかったらこうなっていなかったのか?「名探偵だって自分のことを解って欲しいんだよ」って大河が言った……。はぁ……。
そういえばこの感想を書くために毎度冒険と回想と今回は河原町とさらに原典まで並べて勝手にダメージを受けているんですけど(原作のホームズとワトソンは仲良くお出かけしていて羨ましいよ)、なにげに大河の家庭を持ちたいという欲求みたいなこところは回想が単行本化する際に加筆修正されてたんだな。まぁそもそも原典から言ってワトソンは家庭持ちだが……。
さりげなく柏手を打つな。そういう単語じゃないだろ。
>「いい加減、不愉快なんだよ」
わーん……ついに怒った……。
本日は情緒がウェットなので、最早ここまで来ると「離婚調停」という言葉でコメディタッチにされると逆にそれは状況を軽薄に見ることにつながっているのではないか?という気がしてきた。
我々が推しカプの離婚調停が見たーい!と主張する時にそれが意味するのは可愛さ余って憎さ百倍ドタバタラブコメディ、もしくは切ないすれ違いと予定調和の様相であってこういうリアルな「無関心」じゃないだろ。ここにあるのはただただ人間が人間を諦めた結果そこにある離別ですよ……。感情がねぇという状況を見せられても困るんだって……。
>「そこそこ大河のセンスだな」→減点だ
はいかわいい………………。獅子丸が一人ぼっちでかわいいことだけが世界の真実……。
よしのよしのさんだーっ!なんか嬉しい……。吉野さんが論語君のこと我が子のように思っていて河原町で真っ先に食事の心配していたのが好きなんだよ。論語君がな、大事にされると嬉しい。
論語君が兄を馬鹿にしないの解釈合ってて超最高。論語君にとって馬鹿にするのは極上の愛情表現なので(みかがさんの話をしておる)。
兄の宿題を手伝う城坂論語何?!??!?人間じゃん……。
「バイスプレジデント」「アドバイザー」のルビうぜ~!いいなこれ。そのままカタカナで書くのではなくあえてルビ振ってにしているところでこう……学生起業家感が出ているな(オブラート表現)。
踊る人魚が出てくることで世界がつながっている感覚がありこういうのに無条件に好きだ。そう……冒険と……帰還は同一世界線……。同一シリーズ……。うっ……。
テーブル折丸、元気がよくて嬉しいですね。レオにこれができるか!?どうよ大河!?行為もできないよ……。どうよ……。
「キングレオ性」に続き「論語性」まで出た……。なんなのその変な〇〇性……。「論語」が既存の固有名詞だからますます変な感じに……。
>『お前の声を聞き間違える筈がないだろう』
きっっっしょくわるっ!ドン引きですよ!意図して表現されたものだろうから、もう、すごいよ!!!読者も大好きな冒険屈指の名シーンをAI加工すな!
大河やっぱりデレ仕様になるようにパラメーター調整しとらん?気持ち悪いぞマジで。本人の前でやらないで
>「悔しいが獅子丸しかいないんだ」
獅子丸……ちょろ丸……大体お前ドラマの俳優なんかできないでしょ……。
結局回想で飛び降りなかったとしても待っていたのはこういう待遇な訳で、どちらにしても獅子丸の幸せなんてなかったのかもしれん。どうして……。
今回ミステリでよかったな……。論語のノートが兄の手で売っ払われていたのも納得だし……。
最終話に向けてなんか盛り上がっとる!!!探偵と助手の物語は完全においていかれたのに!主題どこいったの!だから言ったじゃん!論語君出てくると全部を乗っ取るって!!!
でも次回論語君(論語君ではないです)の語りがいきなり聞けるのは正直嬉しい……。論語君の退場に未練はないので自分でもこの感情は意外だが……。これが大河の気持ちだったってことか……?大河には獅子丸と離別した一年がある訳で……やはりそこを何も聞けていないのに……獅子丸の肩ばかり持つのはよくないかもしれん……嘘……やっぱり獅子丸の肩持つ……。
論語君のAIが誰の意志なのか考えてたんですけどもともとはサントアリオが慈恩のために開発していて、回想の最後で「死を観測されない限り、僕は生き続けますよ」とか言っているので、論語君が準備したものの可能性があるな……。
獅子丸、大河、あと落花に関してはマジで続刊があるんじゃないかな~と思っている……。思っているけど、それはそれで帰還でされたことは忘れん……。それに無関心からの続刊があったとしてそれを信じられるのか……?読者は獅子丸ではないのですが……。キングレオが10巻続くシリーズならそれもわかるけどリミットはあと2巻である訳で……。
もうキングレオのこと何もわからねぇよ……。

春に買って嬉しかったもの

最近買って嬉しかったもの(買ってよかったものは君が君の金で買ってから君の価値観で決めるんだ)の話をします。
・乾燥機付き洗濯機
https://jp.sharp/sentaku/products/ess7d.html
これまで住んできた家が共用洗濯機つきの家だったので初めての購入だったが、奮発した。
洗濯という家事は様々な観点から本当に最悪だったから。
1. そもそもゴミが取れない
洗濯機が古かったとか洗濯物を入れすぎとかそういう事情もありそうだが、フリース生地の服が洗濯機内でゴミを吸着しまくり、洗濯が終わった後にそれをコロコロで取るという作業が発生して最悪。フリース以外もはたくとホコリ出るし。なんでよ。洗っているにも関わらずゴミがついているので、油汚れ等についても本当に落ちているか疑念が生じてくるのが最悪。
ニューで高等な洗濯機になってゴミが取れるようになった。洗濯パートと乾燥パートで2箇所ゴミ受けがあるのだが、洗う度にそこのホコリを掃除するのが楽しい。取れておるわい、と思うので。
2. 干す際の整形は無価値
これは私が言うまでもなく全人類が気がついていることと思うが干して畳むという作業は完全に無駄。
着る(服の形)→洗濯(無秩序な形)→干す(服の形)→畳む(収納に相応しい形)→着る(服の形)のうち、干している部分の整形作業は何の意味もない。耐えられんよこんなことは。
このうちどこかを省略するとしたら論理的に考えて選択肢は「服の形のままクローゼットに収納」か「干す作業をスキップする」しかない訳だから。でも論理的に考えると最も相応しいあり方は洗濯機がでかい部屋みたいになって「干した形のまま洗濯から収納まで一つの部屋で終わる」ことだ。そういう技術が開発された暁にはいち早く言及した貢献を評価して私が利用できるようにして欲しい。
3. 干すのが面倒
外は暑いし寒い。雨の様子とか気にしなきゃいけないのも嫌。そういうのは山にいる時だけで十分。
私は服に何も、全く、一切の興味がないのだが、興味がないからこそ服に関心を払いたくない。触っていたくもない。だからお金を払って手間をスキップする。よかったよかった。
・食洗機
https://panasonic.jp/dish/p-db/NP-TCM4.html
ドラム式洗濯機に続き所謂現代の三種の神器。これが欲しくて引っ越しの時に広い台所を条件にしたくらいだ。食器を洗うという作業はマジで最悪だから。
そもそも飯を作るという偉業を達成しているのに何故その上で更に働かなくてはならない?飯を作る作業はいいよ。創作だし。生産的だし。片付けは嫌でしょ。労働だよ。なんで飯を作るという褒められるべき作業をしたというのにその結果労働が発生するのか。作業が発生するタイミングも嫌。食事した後に働きたくない。
その癖洗わないと飯を作る作業が滞る。食器は無限ではないので。逆に言うと食洗機を導入して飯を作るハードルが下がった。最高。あとでスーパー行こ。たらこパスタ作りたい。
あとこれが全部と言えば全部だが、手が荒れる。皮膚弱いのに。洗濯物も手が荒れるから触りたくなかったところがある。これで残す濡れ家事は風呂掃除だけだ。あれも擦らずに済むスプレーが最近はあるらしい……。
食洗機、聞いてはいたがやや癖がある。どうしても場所を取るし、このサイズだと調理器具が入らなかったりする。あと食器の詰め方に工夫がいるとか(絶対に洗えてないだろって箇所が洗えていたりして謎)。でも、もうそういう癖も含めて君と付き合っていくって決めたよ……一生……。嘘じゃない、見てくれ、君のためにハンドルが取れる鍋だって買ったんだ……。
・コーヒーメーカー
https://www.delonghi.co.jp/products/detail/id/445
コーヒーを抽出する作業は前2つに比べるとそこまで最悪ではない。でもコーヒーを抽出している時間は台所に立ってティファールを持って完全に「コーヒーを抽出する」という作業にかかりきりになる。何様なんだ。たかが飲み物の分際で強気に出たな。毎日飲まれるからってつけあがるな。
今は粉を用意しているけど豆からでもいいなと思っている。ミルで惹く作業はつけあがってもよい。楽しそうだから。
コーヒーの味って正直そこまでわからないけどアロマ機能で淹れるとおいしい。赤いし小さくてかわいい。満足。
・サボテン
なんかやたら花がつく。見てると元気になる。

読書メーターからブクログに読了本のデータを移行する

5年位前から読書メーターで読みたい本と読み終わった本と感想の管理をしていたのだが、だんだん非公開にできないのが不満になってきた。これは読書メーターが読書好きが集まるSNS(率直にいってXXXXXだ)を標榜しており5年前の私がそれに気が付かなかったから仕方がないことなのだが、読む本もそこに書く感想も割と赤裸々なものがあり、一切非公開にできないのは、ちょっと。 思想の自由を守れ。

耐えきれなくなったので1年前にブクログに移行した。その際、読みたい本一覧についてはどうしようもないので選別も兼ねて手動でデータを移行した。これは読書メーターがXXXXXなサービスでインポートサービスがあるにも関わらずエクスポートサービスがないから。ちなみにブクログはどちらもある。
150冊くらいデータがある読了本(シリーズは最初の一冊しか登録してなかったり職の本を端折ったりしているから実際は多分5年でもうちょい読んでますよ:読書する人間的自意識主張)に関しては、流石に手作業で移行するのは面倒でそのまま置いておいた。スクレイピングとやらができれば抽出できると知ったけど、Rのその辺りはよくわからなかったので諦めた。1年前の私に解説すると、スクレイピングとはそのパソコンに命令して君の代わりにネット上の情報収集をやらせることでありこれをすると例えば昔書いたスコアアタックモデルのデータ入力パートが省略できるようになったりするよ。
そういう状況だったのだが春に転職してあれがそっち系になったので、pythonの練習をかねてコードを書いた。これでXXXXXサービスとはおさらば。ヨッシャッ!
まず読書メーターのどのページから情報を拾ってくるか決める。一年程前に書かれた記事によると、当時は読んだ本まとめみたいなコードが生成できたらしいのだが、読書メーターの仕様変更で一切できなくなっていた。すごいな。こんなに早く一方的に情報が無価値になることがあるのか。
ログインしたあとの読書管理ページのリストから拾うことにした。これは若干失敗で、読了本とそれ以外(未読本とか)のページの書式が違ったので、読了本にしか使えないコードになっちゃった。まぁいっか……。
#ログイン
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from lxml import html
import time
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Chrome(options=chrome_options)
driver.get(“https://bookmeter.com/login”)
# ID/PASSを入力
id = driver.find_element_by_id(“session_email_address”)
id.send_keys(“YYYYY”)
password = driver.find_element_by_id(“session_password”)
password.send_keys(“ZZZZZ”)
time.sleep(1)
# ログインボタンをクリック
login_button = driver.find_element_by_name(“button”)
login_button.click()
できた~。うれし~。
ブクログにインポートする時に必要なデータを調べる。ISBN(13桁)が読書メーター側のリストに入ってなかったので面倒だなと思ったけど、これは必ずしも必要ないことがわかった。最低限必要なのはASIN(カンフージェネレーション)だけみたいだ。あとはレビューと読了日付を引っ張ってきたいな。
#読了日付リスト
date_list = []
for i in range(1,9):
    url = “https://bookmeter.com/users/WWWWW/books/read?display_type=list&page={}”.format(i)
    driver.get(url)
    date = driver.find_elements_by_class_name(“detail__date”)
    for i in date:
        date_list.append(i.text)
    time.sleep(1)
読了日付はユニークなクラス名があるので難しくなかった。
#ASIN関数
import re
def extract_asin(string):
    asin = re.search(pattern=r”(?<=asin\”:\”).+?(?=\”)”, string=string)
    return asin.group()
#review関数
def extract_rev(string):
    review = re.search(pattern=r”(?<=\”review\”:{\”text\”:\”).+?(?=\”)”,
                     string=string)
    if(review is None):
        return “”
    else:
        return review.group()
#ASINとレビューのリスト
asin_list = []
rev_list = []
for i in range(1,9):
    url = “https://bookmeter.com/users/WWWWW/books/read?display_type=list&page={}”.format(i)
    driver.get(url)
    #モーダルウィンドウ
    content = driver.find_element_by_class_name(“detail__edit”)
    content2 = content.find_element_by_xpath(“div”)
    string = content2.get_attribute(“data-modal”)
    content_list = driver.find_elements_by_class_name(“detail__edit”)
    for i in range(len(content_list)):
        content2 = content_list[i].find_element_by_xpath(“div”)
        string = string = content2.get_attribute(“data-modal”)
        asin_list.append(extract_asin(string))
        rev_list.append(extract_rev(string))
    time.sleep(1)
こっちが面倒だった……。モーダルウィンドウとかいうページ上で立ち上がるウィンドウがあるのだが、その要素の指定の仕方がわからなかった。クラスで名指ししようとしてもエラーが出るし……。結局モーダルウィンドウの手前のdivを指定してから、階層を一つ下げてパスで指定した。なんでこれでうまくいくのか一切わからん。詳しい人がいたら教えて下さい。
モーダルウィンドウの中身の取り出し方も最初わからなかった。多分こっちはget_attributeであっていると思う。出てくる文章は200文字くらいのタグ等含む文字列なのであとは正規表現で取り出し。正規表現もよくわからね~。宇宙人の言語か?見た目が記号の乱舞になるので本当に宇宙人の言語みたいに見える。正規表現って100年くらい歴史があるらしいけど、作った人絶対面白がってやったでしょ。
import pandas as pd
import csv
date_list2 = list(map(lambda x: str(pd.to_datetime(x, format=”%Y/%m/%d”)), date_list))
df = pd.DataFrame({“serviceID”: “1”,
                   “itemID”:asin_list, 
                   “ISBN”:””,
                   “category”:”-“,
                   “eval”:””,
                   “statement”:”読み終わった”,
                   “review”:rev_list, 
                   “tag”:”読メ”,
                   “memo”:””,
                   “regist_date”:date_list2,
                   “finish_date”:date_list2})
df.to_csv(“out.txt”, encoding=”cp932″, sep=”,”, index=False,
          header=False, quoting=csv.QUOTE_ALL)
あとはブクログの形式にあわせてデータフレームで加工。encordingはさくっといったけどquotingとダブルクオーテーション挿入の仕様がいまいちわからずしばし苦戦。
できた~!丸一日かかった~!面白かったからまぁいいか~。

ハイ・ライズ読み終わった……。うお~……半分読んだところで心が折れそうだったので安堵……。
読んだ本の記録は基本的にクローズドな場所でやっているけど、そうするとあまり響かなかった本に対して感想文を書くのをさぼるという問題がある。響かなかった本こそ記録しておく意義があるので。
読もうと思ったきっかけは2回あるはずだが(異なる場所で2回出会った本は読むことにしている)、片方は間違いなくバーナード嬢、もう片方は完全に忘れた。バーナード嬢でもどういう文脈で紹介されていたか忘れたので見返したら、ファイト・クラブと共に紹介されていて納得した。何故ならファイト・クラブは去年挫折したので。読書に割く時間が減る一方なので、途中で挫折してしまうとかけた労力が無駄になってしまってなかなか辛い。
ド嬢、巻末に参考文献付けてくれるの本当に助かる。わかってる。痒い所に手が届く。
私は「世界の真理に近付こうとする行い」それこそがサイエンスだと定義している。サイエンス・フィクションもサイエンスを掲げるからには、世界が主役でありそこに登場する人間は従なのだと思っている。だから登場人物がどんなに魅力的でなかろうがそれは減点要素にはならないのだが、それにしてもこう記号的人物が視点の叙述を延々と続けられると単純に頭に負荷がかかるな……。後半になるにつれて会話文を意図的に削って人物への共感を防いでいるのかもしれないが、殺人だのセックスだの集会だのを全部叙述で済ませられるのがなかなか……。何を読んでいるのかわからなくなってしまう……。この前、安部公房の他人の顔を読んだがあれは内省的になるあまり全然会話文のない小説だった。世界に目を向けても一人の人間の中身に目を向けても会話文が減ることになるというのは面白いな。
ファイト・クラブでも思ったが、暴力と荒廃みたいな描写にあまりぐっとこないみたいだ。やたらと長いし……。同様に撤退してしまった小説に時計じかけのオレンジがある。
でも世界の方の描写はロイヤルの高層マンション・カモメ・犬により動物園の比喩がかなり美しくてよかった。一冊を通して美しい風景が一瞬垣間見えたらそれだけで十分なのかもしれないな。ただ、テクノロジー三部作と言われる面は正直よくわからなくて、まぁこれは現代に置いて高層マンションはテクノロジーの象徴にはならないという時代性があるのでしょうがないかなと思う。そう考えるとSFって儚いな。宇宙が舞台のSFもそのうちこんな風に言われるようになっちゃうんだろう。
解説が読んでいて楽しかった。やっぱり文芸批評を勉強したいけどそのためには原典の読破が必要だしどこから入ったものか。