文字と声と音と待ち時間の話

特に何もない、垂れ流すだけの雑文

私にとっては関連性を感じられる話が三つ続けて飛び込んできた
https://twitter.com/kemonar/status/973988187218501632
https://togetter.com/li/1209175
https://togetter.com/li/1209109#c4753347

これらに対するコメントとかは特にない.
雑多に書き流す

・言語野は一つの処理しか出来ない話
私はアニメをよく見る、ネット配信、前にも書いたが主にニコニコチャンネルで見る.
ニコ生のものも見る、一般ユーザーでも予約しておけば比較的自由に見られることが分かって以来よく見ている、なので一挙や配信してない作品でも振り返りとかやってると見る.
で、見れるもの何でも見るわけではないが、配信してさえいれば割とかなりの数見ている.
「切る」こともまああるが、矢張り一度見始めると続きが気になるもので、割とダラダラと見てしまうことが多い.
しかし、ニコニコで配信しているのはアニメ全体の中では一部とはいえそれでもかなりの数であり、
全部見ていると時間が幾らあっても足りない.
前置きが長くなったが、ならばどうしているのかというと、
私はじっくりと見たいお気に入りの作品数点を除いて、他のことをしながら横でアニメを流してなんとなく横目で見たり科白だけ聞いたりしている.
声を聞いているだけでも話の流れなどは割と把握出来る.
で、この「他のこと」というのが本題で、
分かり易いところだと、漫画を読みながらだとアニメの内容が全く頭に入らない、というより科白が聞こえていない.
体質的に集中して聞こえないということもあるにはあるのだが、それ以上に複数の言語による情報を同時に受け取ることが出来ない.
文字を読みながら別の声を聞くことが出来ない.
頑張れば、高速に意識を行き来させることで両方同時に認識することも不可能ではないのだが、辛い.
ネット上の文章、twitter上のちょっとした短文読んでるだけでも駄目、気がついたらアニメが置いてきぼりになっている.

余談だがプログラミングをしながらだと全く問題がない、このことから私はプログラミングに言語野は必要ない説を提唱している.

・頭の中で声が流れる
私は所謂「頭の中で音読するタイプ」です.
昔ブコメしたし
http://b.hatena.ne.jp/entry/blog.livedoor.jp/himasoku123/archives/51551349.html
比較的最近もその話があった
http://b.hatena.ne.jp/entry/mubou.seesaa.net/article/454156992.html
切り替えることは可能っちゃ可能なんですが、単なる速さとはあまり関係なく、ただ速く読むだけならやっぱり頭の中で声に変換している.
その場合、早口になっているというわけでもなく「時間が圧縮されている」という表現が適当に思う.(厨ニセンスに刺さるので気に入っている)

漫画がアニメ化されると、声に違和感がある、ということはままあるのですが、
必ずしも「声優を当てている」という感じで声を流しているわけでもない.
いや、「自分の中でのそのキャラクターの声」で再生されてはいるんだが、
経験的に少年マンガの方が違和感を覚えやすく、萌え系みたいなのではあまり気にならないことが多い.
ジャンプマンガのアニメ化では気にならなかったことがない.
昨今の声優の演技の方向がどうのこうのみたいな話題と関係あるかもしれない.
実は甲高いアニメ声はちょっと苦手な場合がある.
例えば好きなアニメの話だと、ファンタジスタドール妹ちゃん(鵜野みこ)の声はちょっと辛い時がある.

・エロゲの音声継続オプション
昔話題に出したことがあったなと思って見返してみたら、なんだかかなりクソゲーの話だった
https://p9silica.wordpress.com/2013/09/30/%E3%81%9F%E3%81%84%E3%81%9B%E3%81%A4%E3%81%AA%E3%81%8D%E3%81%BF%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AB%E3%80%81%E3%81%BC%E3%81%8F%E3%81%AB%E3%81%A7%E3%81%8D%E3%82%8B%E3%81%84%E3%81%A1%E3%81%B0%E3%82%93-2/
そこでも書いたが私はこの機能、重宝している.
一つの文字(言語)情報しか同時に処理出来ない話と矛盾してる様に思うかもしれないが、というか自分が今まず思ったが、
自分がどの様に読み進めているのかというのを丁寧に言語化する努力をしてみる.
まず情報の認識は文字ベースで行っている、テキスト表示は常に最速(瞬間表示)にし、文字を読む速さで物語を認識している.
そしてその声は脳内で再生されている、上記の通りだ.声のソースはその場にあるのだから、100%本人の声で全部再生される.
同時に音声も聞いてはいるが、読み終わりさえすれば次には行く.
音声を聞きながら別の文章を読んでいる形になるが、進む速さが違うだけであくまで同じ文章が対象であるので問題がないのだと思う.
そして実際のところ、音声の方は大して聞いてはおらず、声色(この単語であってる?)を確認しているだけ、という感じなのだと思う。脳内再生の補強として。
それだけなら、前の文章が終わって次へ行くのだから音声が途切れても問題ないと思うかもしれないが、流れている音声が途中で切れるのはそれはそれで違和感あるのだ.
地の文(大抵のゲームでは主人公科白もか)に対して脳内音声が流れないということはない、
そのまま次の文章に突入したら、次の文章が脳内再生されつつ、前の音声が耳から聞こえる、
という状態にはなるが、「前の文章」は既に脳内処理済みなので音声だけ後から受け入れるにも問題がないのだと思う.思う.
そんな説明しか出来ない.

・歌詞が聞き取れない
そもそも耳が悪いために、ボーカル曲聴いても日本語の歌詞が全く聞き取れない.
アニメやエロゲの音声は余程滑舌が悪くない限りは基本的に大丈夫だが.
歌の方も声や歌い方や内容にはよるんだが、基本的には聞こえない方向.
http://b.hatena.ne.jp/Silica/20130727#bookmark-155752498
つまりボーカルが奏でるメロディを聴いている.という様な表現になるのか.

余談だが、ニコニコで歌が流れると歌詞を書く人間がいる.
だが、そこで書かれる歌詞が大抵間違ってるのはどういうことか、誰もが聞き取れていないのではないか.
まあ訂正コメントが入るのだからそうでもないのだろうが、聞き取れる人間と聞き取れないがそれぞれいるのだとしても、
歌詞を聞き取れない人間ほど歌詞を書きたがるのは何故なのか.

中二病のテンプレの一つに洋楽を聴く、というものがあり、それに対する揶揄に「歌詞分からないのに何で聞いてるのwww」というのがあるが歌詞の内容がそんなに重要って思う.
私からすれば、音楽が分からないなんて…損してる!みたいな感想だが、
他人から見れば私も「歌詞が分からないなんて可哀想」となるのだろう.

・文字表示のウェイト
「死ね」
今twitterでホットなワードで呟いたら凍結されそうなので上の記事を見て言い換えを思いついた、「召されろ」

・待ち時間が苦手
なのである、そもそも.
https://twitter.com/search?f=tweets&vertical=default&q=from%3Apawwn%20%E3%82%A6%E3%82%A7%E3%82%A4%E3%83%88&src=typd
アニメを流し見する理由に時間がないということもあるが、
仮に時間が有り余っているのだとしても、30(24)分じっと見続けるというのはかなり辛い.
ファンタジスタドールが好きな理由に内容密度が高いからというのは決して無視出来ない割合で、ある.
わざわざタイトルを挙げるのはアレなんだが、美少女アニメばっかり見てる私なのにその理由でもって、
世間では人気あるが『のんのんびより』のアニメは結構苦手、間が長い.(原作は持っているYO!)
あの間があの作品の味ということも分かるし、自分も漫画読む時に常に最速で読むわけでもないのだが.
https://twitter.com/pawwn/status/326368183630065664
間があるとかどうとかじゃなく、単純に内容が薄いアニメも苦手だ勿論.
そしてショートアニメは大体好き.

dev.toが話題になった
https://dev.to/mizchi/-devto–b5
>ユーザーが混乱するほどに速い
の意味は分からなかった.
https://twitter.com/potato4d/status/930666766564544512
これとか完全に訓練され過ぎだろ…ってなる
https://twitter.com/nyaocat/status/931279576423993344
クリックという能動的アクションをもって遷移が起こるのに混乱も何もないだろって思う.

嘗てウェイトが必要かもしれないと思ったのは一度だけで、
以前作った動画も公開したダンジョンRPGで
http://www.nicovideo.jp/watch/sm16844256
移動→移動
と操作するつもりが、不意にエンカウントする為に
移動→エンカウント→戦闘メニュー操作
となってしまうことがあって、エンカウント開始時にはクッション挟んだ方がいいのかなって思った.
つまり、能動アクションに対してはウェイトは必要ないが、受け身に起こるアクションに対してはそれを認識する間があった方がいいのかなと思う.

・操作した通りに動いて欲しい、特にメニュー操作
ゲームの演出は大半我慢ならないのだが、
「メニュー開いて三つ目の項目を選択する」みたいな場合に、
素早く「START↓↓A」って操作すると、メニュー開くのに時間かかったりカーソル移動に演出が入ったりして思った通り操作出来ない、別の項目を選んでしまったりする.
http://b.hatena.ne.jp/Silica/20110516#bookmark-42566210
https://twitter.com/P9Silica/status/212885280351453185
セーブ時など、はい/いいえの選択ダイアログで初期いいえにカーソルがある場合に
「A↑A」という操作を良くするが、ダイアログを開くのに時間が掛かった結果上入力が受け付けられず、
ただダイアログを開いて閉じるだけになってストレスがマッハということがよくある.
確認ダイアログを流れ操作で飛ばそうとするのがよくないというのはあるが.

・操作不能時間が短いほど良い
https://twitter.com/P9Silica/status/684586393138417664
http://b.hatena.ne.jp/Silica/20170717#bookmark-341946725
ロックマンXの4だったかな、プレイしたことはないんだけど(3まではした)
TAS動画見ていると、ちょっと進んでは会話、ちょっと進んでは会話ってなって、なんだこのクソゲーは、ってなる.
これはTASだから強調されてる面もなくはないと思うけど、やっぱりアクションゲームで会話で足を止められるのは
(しかも自分から話し掛ける能動アクションによってではなく、強制的に割り込みされる)駄目な要素だと思います.
そういえばそんなゲームあったな、ダマスカスギヤhttps://p9silica.wordpress.com/2014/04/02/%e3%83%80%e3%83%9e%e3%82%b9%e3%82%ab%e3%82%b9%e3%82%ae%e3%83%a4-%e6%9d%b1%e4%ba%ac%e5%a7%8b%e6%88%a6-%e3%83%97%e3%83%ac%e3%82%a4%e3%81%97%e3%81%be%e3%81%97%e3%81%9f/
同じところの同じ系統のゲーム、『FANTASY HERO~unsigned legacy』もなんかセールの時に買って、Switchで出るの報を聞いてちょっとプレイしてみたんですが全く同じでしたね(更に他にも色々UIの問題やDLC商法等ある)

・加速チート(speed hack)
幸いにして、PC上で動くものはなんだか割と何とかなる.
FF13にもある程度使った.(私のPCの性能がそんなに高くないせいかあーんまりだけど)
開発者は鈍重演出でユーザーをイライラさせる自由がある、ユーザーは加速チートで演出をすっ飛ばす自由がある、それでいいじゃないか.

・Evoland
Evoland』というゲームがありまして、どういうゲームかは各所で紹介されてるのでそれを参照してもらうとして、
演出というのは本質的にストレスなのだということを身をもって教えてくれる神ゲーかと思ったんですが、
何かっていうと宝箱を開ける時に一々長い演出を見せられる様になって物凄くウザい.
途中、これも演出の進化によって画面切り替え時にローディング画面が入る様になるんですが、これは皮肉?でやってるのが分かるんですよ(実際後でカット出来る様になる)
2をやった結果、別に皮肉や嫌がらせではなく演出が本気で格好いいと思ってやってるらしくてぐにょーんってなりました.

書きたいことは書いた様な気がする、同時に新たに書きたいことも出てきたが、ここに書くべきでもないので終わり.

カテゴリー: ゲーム | コメントを残す

時計仕掛けのレイライン -朝霧に散る花- プレイしました

時計仕掛けのレイライン -朝霧に散る花-』プレイしました.

この手のゲームをずっと全くプレイしていなかった訳でもないのだが、
感想を書く程のアレがなかった.
これに書く程のアレがあるのかというと良く分からないが、
久々に書くには丁度いいタイトルだろう.

今度こそちゃんと完結しましたね.
正直あんまり事前情報をチェックしてなかったので、ハッキリとは分からないのですが、
1作目の時も2作目の時も、その点に関しての告知はなかったですよね?
最初から三部作だと分かった上で前編中編を買うのと、
プレイし終わった後で、まだ続きがあります、と言われるのとでは全然違うのだ.

そういう売り方が良いのか悪いのかについては、
個々のボリュームが見合っているのならいいんじゃないか、とは思うんですが、
この作品の様に、全部に決着が付いてないことが明らかに分かる形だったり、
あからさまな謎が残っていたりすると、
実際の容量と関わりなく不完全燃焼感というか、凄くモヤモヤしたものが残るので、
巧いこと、そこで区切ってもおかしくない、ポイントで切って欲しいですね.
そういえばバルスカゼロ2は買ってないです.

内容については…
主人公は完全に妹が作った魂のあるホムンクルスだと思っていたので、
その予想が裏切られただけでも良かったです.
ツッコミたいところは、妹が起きてる時でも全く鍵を使わせようとせずに、
使用回数が限られてることばかり強調してたことぐらいですかね.
まあでもこれは、一応、校内地理に疎いからと脳内補完可能なので、まあいいかな、と今思った.

お話そのものは嫌いではない故に、なんだかな…

カテゴリー: PCゲーム | コメントを残す

PCゲームが起動しない問題が起きていたが解決した話

エロゲでも時折起きていたんですが、
(その時の記事は 時計仕掛けのレイライン(2) とか ものべのHE とか)

先に結論を書くとハードディスクのせいなんですが、
別にぶっ壊れてたとかそんな単純な話じゃなくて、2Tの壁が関係ある様な気がするんだけど、
詳細は不明.

症状としては、
ゲームが起動しない、んだけど、
何のエラーも出ないで、ウインドウが出現すらしない.
タスクマネージャを眺めると、暫くプロセス名が表示された後、何事も無かったかの様に消える.
イベントログにはAPPCRASHとかが残ることもあるけど、何も残らないこともある.
PCを再起動すると、基本的には、再び普通にゲームが起動する.
そこでゲームを終了して再び起動しようとすると大抵は失敗する.
あるゲームが起動しない状態でも、他のゲームは起動することもある.
一度起動しなかったゲームも、その後長時間(24時間以上)PC付けっぱにした後、
再び思い出した様に起動してみると、普通に起動することもある.
起動したゲームのプレイ中に問題が起きることはない.
(唯一borderlands2だけは、マップ切替時にロードが終わらない問題が発生することがあったが、
同じ根の問題である疑いが強い.まだ現状で起きなくなったかは確認していない)

最近(2014夏のセール以来)steamに手を出しまして、
エロゲだけだと、そうそう見なかったのですが、それ以来頻度が上がりまして、
大分気に掛かる様になってきたのですよ.
何よりフレンドとマルチプレイするのに支障が出る.

で、ずっと、ビデオカードとかディスプレイドライバ関係のエラーだと思ってたんですよ.
起きるゲームと起きないゲームがあるし、
概ね3Dのゲームの方が起きて、2Dのゲームや古いゲームだと起きない.
意外なのはskyrimなんかでは起きなかったですね.
(あれが今一番プレイ時間長いのはそれが小さくない理由の一つです)
(ただまあskyrimは軽いゲームとして有名でもあるので、さもありなんという気も)
その為にドライバをクリーンインストールしたり、
ググって出てきた話に似てるなーと思って、
そこに書いてあることを試したり、ドライバをダウングレードしてみたり、
流石にOS再インストールする気にはならなかったのですが、
まあ出来る事は色々やってみたんですが、結局何も改善せず.

暫くは、その時起動したゲームを気ままにやる生活をしていたのですが、
ある時天啓を得て、
症状が起きていたゲームを別の論理ドライブ(物理的には同じ)にインストールし直してみたら、
普通に何度も連続して起動する様になった!

今のPC、プライマリなハードディスクが3Tで、
OSインストール時にはちゃんと認識しないので、
マザーボードのユーティリティーディスクに付いてたツールだったと記憶しているのですが、
そんなのを使って外側を別に認識させてそこをゲーム用のドライブとして利用しているのですが、
多分その辺が原因なんだと思います.
とはいえ、起動しない状態でsteamの「ゲームキャッシュの整合性を確認」しても、
特に何もないですし、普通にファイルにアクセスして何がどうということもない、
そもそも起きないゲームもあるので、単純なファイルアクセスがどうこうというわけではなく、
何かはあると思うのですが、その何かが何なのかは未だ不明.
32bitと64bitという話ではなかった筈、多分.

そんなこんなで、別のドライブにゲームをインストールする様にしたら解決しました.

カテゴリー: ゲーム, コンピューターとインターネット, PCゲーム | コメントを残す

演算子を動的追加可能なパーサ

https://twitter.com/yutopp/status/487860033485430784
私もそれやりたい.

私はついついパーサを何というか、ハードコーディングしてしまうのですが、
https://github.com/Silica/PSL/blob/master/PSL/parser.h
https://github.com/Silica/NES/blob/master/include/parser.h

例えば同じ様に四則演算を読むプログラムを書くとこんな感じ.
deleteするなという苦情は甘んじて受け入れますが、どうしようもないんです.

#include <string>

#include <stdio.h>

struct tree
{
	char op;
	union
	{
		int i;
		struct
		{
			tree *left;
			tree *right;
		};
	};
	tree(){op = 0;}
	~tree()
	{
		if (op)
		{
			delete left;
			delete right;
		}
	}
};

using std::string;

class parser
{
public:
	parser(const string &str) : s(str){}
	tree *parse()
	{
		return parse3();
	}
private:
	string s;
	void skip()
	{
		while (s[0] == ' ')
			s = s.substr(1);
	}
	void next()
	{
		s = s.substr(1);
	}
	tree *parse1()
	{
		skip();
		if (s[0] == '(')
		{
			next();
			tree *t = parse3();
			skip();
			if (s[0] != ')')
				printf("err\n");
			else
				next();
			return t;
		}
		if (s[0] < '0' && s[0] > '9')
		{
			printf("err\n");
			return NULL;
		}

		int i = 0;
		while (s[0] >= '0' && s[0] <= '9') 
		{
			i *= 10;
			i += s[0] - '0';
			next();
		}
		{
			tree *t = new tree();
			t->op = 0;
			t->i = i;
			return t;
		}
	}
	tree *parse2()
	{
		tree *l = parse1();
		while (s.length())
		{
			skip();
			if (s[0] == '*')
			{
				next();
				tree *o = new tree();
				o->op = '*';
				o->left = l;
				o->right = parse1();
				l = o;
			}
			else if (s[0] == '/')
			{
				next();
				tree *o = new tree();
				o->op = '/';
				o->left = l;
				o->right = parse1();
				l = o;
			}
			else
			{
				break;
			}
		}
		return l;
	}
	tree *parse3()
	{
		tree *l = parse2();
		while (s.length())
		{
			skip();
			if (s[0] == '+')
			{
				next();
				tree *o = new tree();
				o->op = '+';
				o->left = l;
				o->right = parse2();
				l = o;
			}
			else if (s[0] == '-')
			{
				next();
				tree *o = new tree();
				o->op = '-';
				o->left = l;
				o->right = parse2();
				l = o;
			}
			else
			{
				break;
			}
		}
		return l;
	}
};

int eval(tree &t)
{
	switch (t.op)
	{
	case 0:  return t.i;
	case '+':return eval(*t.left) + eval(*t.right);
	case '-':return eval(*t.left) - eval(*t.right);
	case '*':return eval(*t.left) * eval(*t.right);
	case '/':return eval(*t.left) / eval(*t.right);
	}
	return 0;
}

int main(int argc, char **argv)
{
	parser p(argv[1]);
	tree *t = p.parse();
	int i = eval(*t);
	printf("%s = %d\n", argv[1], i);

	delete t;
	return 0;
}

まあどう考えてもよくない書き方ではある.これぐらいシンプルだと際立つ.
もっと処理をまとめられるだろという感想しかない.分かってはいるんだが…
実行するとこんな感じ.
calc1
で、これの演算子はパラメータ化出来るよね.
それをすると、

#include <string>
#include <map>


#include <stdio.h>

struct tree
{
	char op;
	union
	{
		int i;
		struct
		{
			tree *left;
			tree *right;
		};
	};
	tree(){op = 0;}
	~tree()
	{
		if (op)
		{
			delete left;
			delete right;
		}
	}
};

using std::string;
using std::map;

class parser
{
public:
	parser(const string &str) : s(str)
	{
		operators['+'].priority = 2;
		operators['+'].next = 1;
		operators['-'].priority = 2;
		operators['-'].next = 1;
		operators['*'].priority = 1;
		operators['*'].next = 0;
		operators['/'].priority = 1;
		operators['/'].next = 0;
	}
	tree *parse()
	{
		return parse_exp(3);
	}
private:
	string s;
	struct ope
	{
		int priority;
		int next;
	};
	map<char, ope> operators;
	void skip()
	{
		while (s[0] == ' ')
			s = s.substr(1);
	}
	void next()
	{
		s = s.substr(1);
	}
	tree *parse_term()
	{
		skip();
		if (s[0] == '(')
		{
			next();
			tree *t = parse_exp(3);
			skip();
			if (s[0] != ')')
				printf("err\n");
			else
				next();
			return t;
		}
		if (s[0] < '0' && s[0] > '9')
		{
			printf("err\n");
			return NULL;
		}

		int i = 0;
		while (s[0] >= '0' && s[0] <= '9')
		{
			i *= 10;
			i += s[0] - '0';
			next();
		}
		{
			tree *t = new tree();
			t->op = 0;
			t->i = i;
			return t;
		}
	}
	tree *parse_exp(int priority)
	{
		if (priority == 0)
			return parse_term();
		tree *l = parse_exp(priority - 1);
		while (s.length())
		{
			skip();
			if (!operators.count(s[0]))
			{
				printf("unknown operator %c\n", s[0]);
			}
			else if (operators[s[0]].priority == priority)
			{
				tree *o = new tree();
				o->op = s[0];
				next();
				o->left = l;
				o->right = parse_exp(operators[o->op].next);
				l = o;
			}
			else
			{
				break;
			}
		}
		return l;
	}
};


typedef int (*binary_func)(int,int);
map<char, binary_func> evalop;

int eval(tree &t)
{
	if (t.op == 0)
		return t.i;

	if (!evalop.count(t.op))
	{
		printf("err\n");
		return 0;
	}
	return evalop[t.op](eval(*t.left), eval(*t.right));
}

int add(int a, int b){return a+b;}
int sub(int a, int b){return a-b;}
int mul(int a, int b){return a*b;}
int div(int a, int b){return a/b;}

int main(int argc, char **argv)
{
	parser p(argv[1]);

	tree *t = p.parse();

	evalop['+'] = add;
	evalop['-'] = sub;
	evalop['*'] = mul;
	evalop['/'] = div;

	int i = eval(*t);
	printf("%s = %d\n", argv[1], i);

	delete t;
	return 0;
}

これで同じ様に実行可能.
こうしておけばoperatorsやevalopに値を追加するだけで演算子を追加出来るよね.
これ1行の式前提だったから、このままそれを考えるのが面倒でこれ以上やらないけど、
パーサがなんか読んだらそれらに追加していけば、動的な演算子の追加は可能だよね、
という話.

ただこれは、優先順位と結合規則だけで処理出来る二項演算子に限っているから簡単だけど、
もっと複雑な構文を追加したい場合にパーサをどう書けばいいの?
っていうのは今ぱっと考えてみてもあんまりよく分からないですね.
同じ様に構文規則をパラメータ化しておけばいいのだろうけど.

カテゴリー: コンピューターとインターネット | コメントを残す

SDL2使ってのAndroid開発環境構築しました

実はケータイをスマホに変えました.

Javaに対する複雑な思い故に、仮想環境内でやろうと思ったんですが、
色々あって諦めてWindows上に構築することになりました.
結果的には大したことないのですが、
色々回り道したのでメモを書き残しておきます.

・PC側の設定

Android SDK ADT Bundleを落としてきます.
SDKとIDE(Eclipse)がセットになった奴です.
元々Eclipse使いだというのでない限りはこれを使うのがいいと思います.

Android NDKも落としてきます.

適当に解凍してADTの中のEclipseを起動.
予めJDKを入れておけとみんな言ってるのですが、
JREをここに入れておくだけでも最終的に実際にアプリを動かすとこまでいけました.
Javaを入れたくないからって個別にJREを用意しておくのは、
バージョン管理とか余計にアレじゃないか、という気もするのですが、
それについては後で考えることにします.

Window > Preferences > Android
多分元々大丈夫な気がするけど、SDK Locationを一応確認
Window > Preferences > Android > NDK
NDK Locationを設定(NDKを解凍した先)

Window > Android SDK Manager
で適当にアップデート

SDL2のandroidサンプルは自分で何かしら書かないといけないので別のサンプルを使用する.
SDL2でのandroid開発について解説(翻訳)してるサイトからです.
(色々参考にさせていただきました、ありがとうございます)

File > Import > Android > Existing Android Code Into Workspace
ディレクトリを選択(上のを解凍したもの)
Copy projects into workspaceのチェックは
入れればworkspace内にコピーして作業するし、
入れなければ、そのまま作業する?のかな?

チェックを入れたとして(私は入れた)
workspace/SDLActivity/jniに(workspaceは起動時に設定したディレクトリだ)
SDLへのシンボリックリンクがあるので、代わりに
SDL2のSource Codeをダウンロードしてきて解凍してディレクトリ名をSDLにしてそこに入れる.

実機を使わない場合は、
Window > Android Virtual Device Manager
からあれこれします.

・端末側の設定
野良アプリの許可
USBデバッグの許可
(PCに)ドライバをインストールする

野良アプリの許可はすぐ分かったというか、別件で既にやっていたのですが、
USBデバッグの許可は初期状態では隠れていました.
この辺は端末のサイト見たら情報ありました.ドライバも.

・試す
繋ぐ
Run
どこで走らせるか聞いてくるので選ぶ.
動く!

・追記
動かせてませんでした.
自分でndk-buildすると動かないのは知ってたので、
余計なことをせずにRunだけすれば、
eclipseが勝手にいい感じにやってくれたのだとばかり思っていたのですが、
上のサンプルは実はビルド済みのファイルも含まれていて、それを使っていただけでした.
動かない理由は、SDL自身のandroid用ソースは新しくなっているのに、
上のサンプルは古いandroid-projectを元にしているのが原因で、
SDL内のandroid-project/src/org/libsdl/app/SDLActivity.java
を、サンプル内の
src/org/libsdl/app/SDLActivity.java
に上書きすればOKです.
最初からSDL内のandroid-projectを使っていればよかった.
まあ横着せずに、簡単なサンプルぐらい自分で書けってことでしたね.

ndk-buildはworkspace内のプロジェクトトップで実行すればよいです.
ndk-buildに関して特に何かをする必要はなく、
フルパスでndk-buildを呼べば特に問題なく成功します.
eclipseから自動で実行するには、
Project > Properties > Builders > New > Program
適当な名前、私はndk-buildとしたが
Locationはndk-build.cmdのフルパス
Working Directoryは${workspace_loc}${project_path}
Refreshタブでチェックを入れて
Specific resourcesでボタンからjniディレクトリを選択する
この設定が正しいのかは私には分からないのだが、どうやらこれで成功はしている様だ.

カテゴリー: コンピューターとインターネット | コメントを残す

PSL 1.2.0 リリース

Github
Win用バイナリ
組み込みする場合はヘッダとして使うものでバイナリは必要ないし、
基本はバージョンとか気にせず常にgithubの最新版を使うことを推奨します.

変更
・バインドした関数にデフォルト引数を設定可能にした
・数値(intとdouble)変数を内外でシンプルに共有する仕組みを導入
・variableがスマートポインタの様に機能することでC++オブジェクトをシンプルに管理する仕組みを導入

詳細
・デフォルト引数
先日の記事を参照のこと.
その後クラスをバインドする際のメソッドについても対応しました.
addFunctionの第三引数として、バインド時に、
vm.addFunction(“funcname”, function, variable(10, 20, 30));
の様に指定することで、funcnameを呼び出す際に引数が足りないと、
与えておいた値で補ってくれる.
先の記事でも書いたが、引数を省略するのは右からだが、
ここで指定する際は左から指定しなければならない.どうしようもない.

・数値の共有
int i = 10;
variable vi(variable::IntPtr(), &i);
このviはただの数値型のスクリプト変数と全く同じに使えますが、
内部に値を自分で持つのではなく、iを参照する様になります.
これに対する操作はiに対する操作になります.
vi += 10;
printf(“%d\n”, i);
とかやれば20が表示されます.
iの寿命は自分で考慮すること.
またPSLはすぐにコピーを作るので、例えば、
variable x = vi;
とすればxはiとは全く関係無いただの数値型である.
欲しくなった機能を深く考えず付け足しただけで、
インターフェースとか全然考えてないのでまだあんまり使って欲しくない機能.
だけど折角作ったので公開はしておく.

・スマートポインタ的variable
variable v = variable::make_variable(new MyClass(1));
variable x = variable::make_variable<Base>(new Derived(2, 3));
みたいな感じ.
スクリプト変数の寿命が尽きれば、
newされたMyClassやDerivedのオブジェクトも勝手にdeleteしてくれる.
使う時は、
MyClass *mc = v;
Base *b = x;
または、
mc = v.toPointer<MyClass>();
mc = static_cast<MyClass*>(v);
とか.
make_variableはその為の内部クラスにポインタを入れたスクリプト変数、
を指すスクリプト参照を返します.
そのままだとvariableがコピーを作ったりした時に都合が悪いからである.
スクリプト参照は概ね参照先そのものと同じ様に扱えるので多分これがいい筈.
これを使えばクラスのバインディングをもう少しスマートに扱えると思うので、
後日ヘルプやサンプルも見直します.
variable::make_variableって明らかに長いんだけど、後で変えるかもしれない.
因みに、
make_variable(new Derived())とだけしたものをBase*で取り出すとどうなるかは保証しない.
(内部でvoid*を経由するのでそれがC++的にどうなのか私には分からない)

カテゴリー: コンピューターとインターネット | コメントを残す

組み込みスクリプトへの関数のバインディングとデフォルト引数

まずはコード.
途中経過から最終結論まで入っていてアレなんですが.

#include "PSL/PSL.h"
using namespace PSL;

int add_(int a, int b = 10)
{
	return a + b;
}

int add_3(int a, int b = 5, int c = 10)
{
	return a + b + c;
}

variable add_glue(variable &arg)
{
	int l = arg.length();
	int r;
	if (l == 1)
	{
		r = add_(arg[0].toInt());
	}
	else if (l >= 2)
	{
		r = add_(arg[0].toInt(), arg[1].toInt());
	}
	else
	{
		// なんか引数ないよ!みたいなエラーを出すことも出来る
		r = 0;
	}
	return variable(r);
}

variable add_glue2(variable &arg)
{
	int l = arg.length();
	int r = 0;
	if (l == 1)
	{
		r = add_(arg[0].toInt(), 10);
	}
	else if (l >= 2)
	{
		r = add_(arg[0].toInt(), arg[1].toInt());
	}
	// 省略

	return variable(r);
}

int main(void)
{
	using namespace std;

	PSLVM vm;
	vm.addFunction("add", add_);
	vm.addFunction("add3", add_3);
	variable add = vm.get("add");
	variable add3 = vm.get("add3");

	printf("%d\n", add(1, 2).toInt());	// 3
	printf("%d\n", add(1).toInt());		// 2
	printf("%d\n", add3(1, 2, 3).toInt());	// 6
	printf("%d\n", add3(1, 2).toInt());	// 3
	printf("%d\n", add3(1).toInt());	// 3
	// この様に元のデフォルト引数は考慮されない

	// 配列(厳密に言えば違う)の要素がない場合に次の様な処理になることが上の計算結果の理由
	variable x = 1;
	printf("%d,%d\n", x[0].toInt(), x[1].toInt());	// 1,1
	// 単なる値はどう配列アクセスしても元の値
	variable y(1, 2);
	printf("%d,%d,%d\n", y[0].toInt(), y[1].toInt(), y[2].toInt());	// 1,2,0
	// 存在しない配列要素はnull(==0)

	variable addx = add_glue;
	printf("%d\n", addx().toInt());		// 0
	printf("%d\n", addx(1).toInt());	// 11
	printf("%d\n", addx(1, 2).toInt());	// 3
	// 専用に調整したグルー関数を使えば当然想定した結果になる

	vm.addFunction("addy", add_3, variable(10, 20, 30));	// バインド時に引数のデフォルト値を指定する
	variable addy = vm.get("addy");
	printf("%d\n", addy().toInt());		// 60
	printf("%d\n", addy(1).toInt());	// 51
	printf("%d\n", addy(1, 2).toInt());	// 33
	printf("%d\n", addy(1, 2, 3).toInt());	// 6
	// 今回新たに追加した機能

	return 0;
}

add_とかadd_3が今回考える、デフォルト引数を持っていてスクリプトに組み込みたい関数です.
例えばint (*ope)(int,int);みたいな関数ポインタ経由でadd_を呼び出すとして、デフォルト引数を考慮することは出来ません.
add(1), add3(1, 2), add3(1)辺りの結果がその話.

それはそれとして、何故その結果が出るのかというのが次.
一応解説して、
一つの値に対して配列アクセスすると、添字に何を指定しても元の値が返ってきます.
配列に対して存在しない添字アクセスすると、nullが返ってきます.nullは数値変換すると0です.
add3(1, 2)は1 + 2 + 0で、add3(1)は1 + 1 + 1

渡された引数の数に応じて実際の関数にどこまで渡すかを調節する、
add_glueというグルー関数を考えます.
しかし、元の関数がどこまでデフォルト引数を持ってるか知る方法がないので、
これを自動でやろうと思ったら、引数の数の有効な範囲をバインド時に指定させるとかでしょうか.
addFunction<1, 3>(“hoge”, hoge);
みたいな.それは今後の課題として、
これを使えば問題なく想定通りの結果が返ってきます.

元の関数の定義は考えず、
引数の数を見て足りないところを予め与えられた値で補うadd_glue2を考えます.
ここでは直値で指定してますが、関数のバインドはそれ用のテンプレートクラスを作るので、
そこにデフォルト引数用配列を持たせればいいでしょう.

そういう考えで実際に機能を追加してみました.
addFunctionの第三引数(省略可能)でデフォルト引数用の配列を渡します.
実際の呼び出し時に引数が足りなければそこから値を取り出します.
呼び出し時に省略するのは後ろからなのに、
配列(じゃないけど)は0番目から順に指定しなければならないので、
前が必要なくともvariable(0, 0, 10, 20)みたいに適当に前を埋めなければならないのですが、
これはちょっとどうしようもないかな.

まあ元々はnullが出てきたり、一つの値が全体に振り分けられたりすることを考えれば、
ここでエラー用の値を指定しておけば引数のミスを検出しやすくなる…かも.

今のところただの関数でしかやってません.メンバ関数も同じ手順で可能なので、
明日以降やります.

カテゴリー: コンピューターとインターネット | 1件のコメント

SDL2触ってみた

Ubuntu上で動かしてみたサンプルがこちら.

・導入メモ
WindowsはVCでDLLを使うならかなり簡単.
SDL2-devel-2.0.?-VC.zipを落としてきてヘッダとlibをなんかするだけ.

Ubuntuは前はパッケージマネージャで入るバージョンが1.2だったから、
なんやかんやしてビルドした記憶があるんだが、
今は普通に2.0が入るので矢張り簡単.
sudo apt-get install libsdl2-dev
だったかなんか、そんな感じ.

・コンパイル
Ubuntu上だとinclude/SDL2/SDL.hになってたのでVCもそれに合わせることにした.
VCは、
cl /MD hoge.cpp /link /SUBSYSTEM:console
みたいな感じ.
Ubuntu上だと特に気を付けることもなく、
clang++ -std=c++11 hoge.cpp -lSDL2 -lSDL2main
みたいな感じ.
同じソースで両方動く.

使ったソースコードは、

#include "SDL2/SDL.h"

#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2main.lib")
#pragma comment(linker, "/subsystem:console")

#include <stdlib.h>
#include <time.h>

template<int X, int Y>class Field
{
public:
	void Init()
	{
		srand(time(NULL));
		for (int y = 0; y < Y; y++)
		{
			for (int x = 0; x < X; x++)
			{
				f[y][x] = (rand() & 1) ? true : false;
			}
		}
	}
	bool get(int x, int y) const
	{
		if (x < 0)	return false;
		if (x >= X)	return false;
		if (y < 0)	return false;
		if (y >= Y)	return false;
		return f[y][x];
	}
	Field<X, Y> Update() const
	{
		Field<X, Y> New;
		for (int y = 0; y < Y; y++)
		{
			for (int x = 0; x < X; x++)
			{
				int count = 0;
				if (get(x-1, y-1))	count++;
				if (get(x+0, y-1))	count++;
				if (get(x+1, y-1))	count++;
				if (get(x-1, y+0))	count++;
				if (get(x+1, y+0))	count++;
				if (get(x-1, y+1))	count++;
				if (get(x+0, y+1))	count++;
				if (get(x+1, y+1))	count++;

				if (get(x, y))
				{
					if (count == 2 || count == 3)
						New.f[y][x] = true;
					else
						New.f[y][x] = false;
				}
				else
				{
					if (count == 3)
						New.f[y][x] = true;
					else
						New.f[y][x] = false;
				}
			}
		}
		return New;
	}
	const static int max_x = X;
	const static int max_y = Y;
private:
	bool f[Y][X];
};

Uint32 my_callbackfunc(Uint32 interval, void *param)
{
	SDL_Event event;
	SDL_UserEvent userevent;

	userevent.type = SDL_USEREVENT;
	userevent.code = 1;

	event.type = SDL_USEREVENT;
	event.user = userevent;

	SDL_PushEvent(&event);
	return (interval);
}

template<int X, int Y>
void draw(const Field<X, Y> &f, SDL_Window *window)
{
	auto ws = SDL_GetWindowSurface(window);
	for (int x = 0; x < X; x++)
	{
		for (int y = 0; y < Y; y++)
		{
			SDL_Rect rect = {x * 16, y * 16, 16, 16};
			SDL_FillRect(ws, &rect, f.get(x, y) ? 0x00800080 : 0);
		}
	}
	SDL_UpdateWindowSurface(window);
}

#include <stdio.h>
int main(int argc, char *argv[])
{
	Field<640/16, 480/16> f;
	f.Init();

	if (int ret = SDL_Init(SDL_INIT_EVERYTHING))
	{
		printf("init:%d\n", ret);
		return 1;
	}
	auto window = SDL_CreateWindow("title", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_RESIZABLE);
	if (!window)
	{
		SDL_Quit();
		return 1;
	}

	SDL_AddTimer(250, my_callbackfunc, NULL);

	SDL_Event event;
	bool quit = false;
	while (SDL_WaitEvent(&event))
	{
		switch (event.type)
		{
		case SDL_KEYDOWN:
		case SDL_KEYUP:
			if (event.key.keysym.sym == SDLK_ESCAPE)
				quit = true;
			break;
		case SDL_QUIT:
			quit = true;
			break;
		case SDL_USEREVENT:
			if (event.user.code == 1)
			{
				f = f.Update();
				draw(f, window);
			}
			break;
		}
		if (quit)
			break;
	}

	SDL_Quit();

	return 0;
}

こんな感じ.
AddTimerしたものはRemoveTimerしないままでいいのかとか分からないこともあるので、
これが基本的なサンプルとして参考になるのかは微妙なところですが、
自分が見ていると、みんなwhile(true)でSDL_PollEvent(&event)とかやってる
(所謂CPU使用率が100になるパターン)ので、
まあ多少は参考になるかもしれないと、公開しておこう.

SDLは前はLGPLだったのでちょっと使い辛かったんですが、
SDL2になってzlib licenseになって凄くいい感じですね.

・追記
どんなAPIがあるかも全く把握出来ていなかったので分からなかったが、
Timerに限らずSQL_Quit以外の後処理を全くやっていないことに気付いた.
SDL_DestroyWindowとかSDL_FreeSurfaceとか.
しかし、SDL_GetWindowSurfaceで得たSDL_Surfaceを、
SDL_FreeSurfaceする必要があるのかどうかとかよく分からない.
Destroy時にFreeされるとは書いてあるが、
逆に先にFreeしてしまうことに問題があるのかどうかもよく分からない.

カテゴリー: コンピューターとインターネット | コメントを残す

ルータの話 その後

昨日、丁度タイミングがよかったのでもう、すぐに新しいルータ買ったんですよ.

で、家に帰ってきたあと、新しいルータを繋ごうと思って作業しながら、
一応もう一度元のルータに配線繋いでみたんですよ.
そしたらちゃんと繋がるではないか.

その後昨晩から今朝まで特に問題も起きていない.
いや、ニコ動で動画一つ切れて読み込み途中で失敗したか.
でもそれ自分の問題なのかどうかイマイチハッキリしないのよね、最近のニコ動は.

昨日一昨日繋がらなかったのは勘違いなどではないのは間違いないので、
何がどうなっていたのかだな.
たまたま繋がらなくなっていただけなのか、今は一時的に持ち直しているだけなのか.

カテゴリー: コンピューターとインターネット | コメントを残す

ルータだった

古いルータを改めて繋いで、きちんと確認してみようと思った.

矢張りネットには繋がらない.
ネットワーク関係の設定や表示を見ても、ルータに繋がってる気すらしないのだが、
記憶を頼りにブラウザにIPを入れると設定画面が見えた.
仮想環境との兼ね合いで、
ネットワーク関係の設定が私の様な素人にはよく分からないことになっているのがよくなかった.

ルータの設定画面から色々触っていたらネットにも繋がる様になった.
ということで、無線は繋がっていたことが目を曇らせたが、
ルータのせいだったということはハッキリした訳だが.

訳だが、壊れたのか、なんか設定がまずいのか.
設定を触ってはいないから、それはあんまりないと思うし、
設定画面は散々眺めたが、それらしいものもなかったのだが.

どうしたものか.

カテゴリー: コンピューターとインターネット | コメントを残す