Born Neet
ActionScript再挑戦!Flexで人工無脳作ってみた
- 2009-03-05 (Thu)
- Flash・Flex・AS
2009/06/20 20:10
誤字脱字修正。
2009/03/06 22:45
htmlのソースとプロジェクトファイル一式を追加しました。
たぶん、今までで一番長文です。
1年以上前に書いた、
・ Hello, Flash!:ActionScriptを初めて使ってみたらお絵かきが簡単にできてびっくりした - Born Neet
は今だに僕のブログで一番の人気記事です。
(HolyGrailさんのおかげでしょうが…)
にもかかわらず、それから一年以上ASの話題を扱わないという体たらく。
(地味に勉強してたりはしたんですが、記事にするまでにはいたりませんでした。)
というわけで改めて学びなおすべく、新しい言語の学習にはもってこい(?)の人工無脳を作ってみました。
以下に手順をメモしますので興味があればどうぞ。
ソースのライセンスも(使用ライブラリに合わせて)修正BSDにしとくのでご自由に。
前やった時は、FlexのFの字もわかってなかったのでたいした説明はできませんでしたが、
今回は本読んだり、(だいぶ前ですが)セミナーに行ったりもしてたのでいくらかはマシだと思います。
Flexとは
Flexは、Flashの技術を利用したリッチインターネットアプリケーション(RIA)です。
Flexならデザインセンスのカケラもない僕でも、プログラミングするだけでFlash Player上で動くアプリケーションを作ることができるのです。
と、僕は理解してます。
まぁ細かい定義なんかはどうでもよくて、FlashPlayerがあればどこでも同じように動く、
つまり、「Firefoxで開発→IEで確認→動かない→直す→Firefoxで変に」みたいなことをしなくて済むのです。
ということで僕は勝手にFlexはRIAの本命なんじゃないかと思ってます。
準備
まずは環境を整えます。
やったのは半年以上前なので抜けてるかもしれません。
開発には、FlashDevelopを使います。
補完がないときついので。(テキストエディタでJavaを開発するぐらいのしんどさです)
というわけで、以下のサイトあたりを参考にFlex SDKとFlashDevelopを(ないならJavaも)インストール。
(自分が参考にしたサイトは忘れちゃったので適当に検索しました。)
・ Adobe Flex SDK超入門 (treasuring misc.)
・ フリーのFlash統合開発環境 FlashDevelop (+flex 3 SDK)を入れてみました:■ 音楽方丈記 ■
他にも情報は山ほどあるのでここは省略。
Flexプログラミング入門
Flashはどうやって作ってるのかさっぱりわかりませんが、
FlexはWebプログラマにとって非常にわかりやすい構造です。
(カッコ内参照)
作成するファイルは以下の3つ。
・ mxml(htmlの役目)
・ ActionScript(JavaScriptの役目)
・ CSS(そのまま)
HTMLベースのアプリと同様に、
mxmlで構造、ASでイベント等を設定、CSSで装飾します。
さらにASとJSは型とclassがあること意外ほとんど同じなので、
mxmlだけ覚えればもう何でも作れちゃうのです!
(実際は以前の僕の記事のようにmxmlなしでも作れますが…)
FlashDevelopのプロジェクトの作り方
さて、そろそろ飽きてきましたが。。。
FlashDevelopは高機能なので初めて使う時若干とまどいます。
特に新規プロジェクト作成の選択肢が多すぎる!
ってことで僕の作成手順を書いときます。
1. AS3のEmptyProject(なんか2個あるけど上の方)を選択
・ Nameは適当(一応ラクダ文字が規則みたい)
・ packageは空で
これで余計なものが何もない空のプロジェクトが作られます。
2. Projectの箱を右クリックしてmxmlを追加
さらに
・ 追加したmxmlを右クリックしてAlwaysCompileにチェック
・ プロジェクトのプロパティ(右クリック or メニューから)で
「No output~」のチェックを外し、ファイル名を適当に入力
FlashDevelopがちゃんとインストールされていれば、
これでコンパイルでき、Flashでおなじみのswfファイルが作成されるはずです。
(参考サイトにもありますが、External Playerの設定大事)
あとはASなりCSSなりサブのmxmlなりをガシガシ追加していくだけです。
SWFObject
もう駄目だ…長文すぎる…。
というわけで、ソース以外はこれで最後です。
Flashを公開する時はHTMLに埋め込むことになります。
しかしこれが、objectタグやらembedタグやら実にややこしい。
そこで、swfobjectです。
このライブラリを使えばid指定とjs1行で済みます。
詳しくは、
・ 八角研究所 : FlashをHTMLに貼るライブラリ swfobject 2 を使う
・ SWFObject v2.0 ドキュメント日本語訳 : Media Technology Labs (MTL) : メディアテクノロジーラボ ブログ
・ nondelion.com - 続・swfobject で flash を Window 内に全画面表示
等をどうぞ。
Flexな人工無脳
ようやく本題です。
もう説明する気力は残ってないのでコメントを参照してください。
僕の試行錯誤の結果です…。
動くサンプルはここに置いときます→Flexで人工無能 - tnantoka.net
(はじめは空っぽですが、会話を覚えていきます。
またstudyタブからテキストをaddすれば賢くなるかもしれません。)
プロジェクトファイル一式も置いとくのでどうぞ。
Main.mxml
画面構造
<?xml version="1.0" encoding="utf-8"?>
<!--
Applicationにwidth,heightを設定すれば、-config.xmlをいじらなくても大きさ指定できる
creationCompleteはwindow.onload的存在(as内からは設定できないので属性で)
-->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
<!-- as,css外部ファイル化 -->
<mx:Script source="init.as"/>
<mx:Style source="style.css"/>
<!-- 大枠 -->
<mx:Panel width="100%" height="100%" title="Flexで人工無脳" horizontalAlign="center">
<!--
タブ切り替え
creationPolicy="all"にしないと初期化時にイベント設定できない
-->
<mx:ViewStack id="viewStack" width="100%" height="100%" creationPolicy="all">
<!-- 会話画面 -->
<mx:VBox label="talk" horizontalAlign="center">
<mx:HBox width="70%">
<mx:TextInput width="90%" id="msgInput" />
<mx:Button label="send" id="sendButton" />
</mx:HBox>
<mx:TextArea width="70%" height="85%" id="historyArea"></mx:TextArea>
</mx:VBox>
<!-- 学習画面 -->
<mx:VBox label="study" horizontalAlign="center" styleName="study">
<mx:TextArea width="70%" height="87%" id="sourceArea"></mx:TextArea>
<mx:HBox width="70%">
<mx:Button label="init" id="initButton" />
<mx:Spacer width="100%"/>
<mx:Button label="add" id="addButton" />
</mx:HBox>
</mx:VBox>
</mx:ViewStack>
<!-- タブメニュー(各要素のlabelがタブに) -->
<mx:LinkBar dataProvider="{viewStack}"/>
</mx:Panel>
</mx:Application>
init.as
イベントを設定したり、Munouクラスを使ったり。
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import mx.events.FlexEvent;
import mx.rpc.http.HTTPService;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.controls.Alert;
// グローバル使わないために関数内で処理
private function init():void {
var munou:Munou;
// テキストを読み込んで学習
// XMLHttpRequest同様に使える
var http:HTTPService = new HTTPService();
http.url = 'test.txt';
http.resultFormat = 'text'; // デフォルトはXML
// 成功
http.addEventListener(ResultEvent.RESULT, function(event:ResultEvent):void {
// munou = new Munou(event.result as String);
munou = new Munou(''); // 今回は白紙で
});
// 失敗
http.addEventListener(FaultEvent.FAULT, function(event:FaultEvent):void {
Alert.show(event.fault.faultString);
});
http.send();
// sendクリックで話しかける
sendButton.addEventListener(MouseEvent.CLICK, send);
// エンター押下にも同じイベント設定
msgInput.addEventListener(FlexEvent.ENTER, send);
// 初期化
initButton.addEventListener(MouseEvent.CLICK, function(event:Event):void {
//
munou.init(sourceArea.text);
sourceArea.text = '';
});
// 覚えさせる
addButton.addEventListener(MouseEvent.CLICK, function(event:Event):void {
if (sourceArea.text) {
munou.learn(sourceArea.text);
sourceArea.text = '';
}
});
// 話しかける
function send(event:Event):void {
var msg:String = msgInput.text;
// ちょっとだけhtmlが使える
historyArea.htmlText += '<p>あなたの発言:<br/>\t' + msg + '</p>';
historyArea.htmlText += '<p><font color="#0000ff">munouの発言:<br/>\t' + munou.response(msg) + '</font></p>';
msgInput.text = '';
// スクロールバーを最終行に設定
// (遅延実行?しないと更新前の位置になる)
callLater(function():void {
historyArea.verticalScrollPosition = historyArea.maxVerticalScrollPosition;
});
}
// かぎカッコ内のテキストを抽出(不使用)
function getTalk(text:String):String {
var talk:Array = text.match(/「[^」]+?」/g);
for(var i:int = 0; i < talk.length; i++) {
talk[i] = talk[i].replace(/[「\s 」]+/g, '');
}
return talk.join('\n');
}
} // init
Munou.as
人工無脳(簡易形態素解析+マルコフ連鎖)で会話するclassです。
/**
* 人工無脳(簡易形態素解析+マルコフ連鎖)で会話するClass
*/
package {
// import org.coderepos.text.TinySegmenter; // CodeRepos
import uwi.tinysegmenter.TinySegmenter; // SparkProject
// mxml名と被るとダメ
public class Munou {
// 文頭・文末識別子
private const START:String = '__START__';
private const END:String = '__END__';
// 初期化テキストを一応保存
private var text:String;
// 形態素解析マシン
private var segmenter:TinySegmenter;
// 学習した結果
private var dic:Object;
// コンストラクタ
public function Munou(text:String) {
this.text = text;
segmenter = new TinySegmenter();
init(text);
}
// 初期化
public function init(text:String):void {
dic = {};
learn(text);
}
// 学習
public function learn(text:String):void {
if (text) {
// スペース・タブは1字に圧縮
// 改行で区切る(スペース・タブは残したいので\sは使わず)
// そのほかの記号は無視
var a:Array =
text.replace(/([ \t])+/g, '$1')
.split(/[\r\n]+/);
// Vistaならtraceログは以下に
// C:\Users\<user>\AppData\Roaming\Macromedia\Flash Player\Logs
// trace(a.join(','));
for (var i:int = 0; i < a.length; i++) {
var b:Array = morph(a[i]);
trace(b.join('|'));
b.unshift(START);
b.push(END);
for (var j:int = 0; j < b.length - 1; j++) {
var s:String = b[j];
if (dic[s]) dic[s].push(b[j + 1])
else dic[s] = [b[j + 1]];
}
}
}
}
// 形態素解析
private function morph(text:String):Array {
return segmenter.segment(text);
}
// マルコフ連鎖で文章作成
private function markov(text:String):String {
if (!dic[START]) return '';
var s:String = dic[START][Math.floor(Math.random() * dic[START].length)];
var r:String = '';
while (s != END) {
r += s;
// 文末っぽい記号が出ても終了
if (s.match(/[。!!??]/)) break;
s = dic[s][Math.floor(Math.random() * dic[s].length)];
}
return r;
}
// 応答
public function response(msg:String):String {
var func:Function;
learn(msg);
// 今回はマルコフのみで
// switch(Math.floor(Math.random() * 10)) {
// 1/10の確立でオウム返し
// case 0:
// func = echo;
// break;
// 1/10の確立で定型応答
// case 1:
// func = random;
// break;
// その他はマルコフで
// default:
func = markov;
// }
return func(msg);
}
// オウム返し(不使用)
private function echo(msg:String):String {
return msg;
}
// ランダム応答(不使用)
private function random(msg:String):String {
var a:Array = [
'',
];
return a[Math.floor(Math.random() * a.length)];
}
// 初期化テキスト表示(不使用)
public function getText():String {
return text;
}
} // class
} // package
style.css
見た目を調整。
htmlほど自由には設定できません。
@charset "UTF-8";
Application {
background-color: #fff;
/* width: 300px; 効かない */
}
Panel {
/* width: 100%; %指定はエラー */
/* padding: 10px; 効かない */
padding-top: 20px;
padding-bottom: 20px;
}
HBox {
/* text-align: center; TextInputの中身がセンタリングされちゃう */
padding-top: 10px;
padding-bottom: 10px;
}
.study {
padding-top: 10px;
}
index.html
swfを全画面表示
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Flexで人工無脳 - tnantoka.net</title>
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<link rel="stylesheet" type="text/css" href="style.css" media="all" />
<script type="text/javascript" src="swfobject.js"></script>
<script type="text/javascript">
swfobject.embedSWF('Munou.swf', 'swfArea', '100%', '100%', '9.0.0');
</script>
<style type="text/css">
/* for Firefox */
html, body {
height: 100%;
}
html {
overflow: hidden;
}
/* common */
body {
margin: 0;
}
</style>
</head>
<body>
<div id="swfArea" style="width: 100%; height: 100%;">
</div>
</body>
</html>
読んだ本
だいぶ前に読んだ時はイマイチと思ったんだけど、
(AIRプログラミング入門と内容を使い回しすぎだし)
今回作るにあたってリファレンスとして結構役に立った。
初Flexのお供に是非。
ホントはFlex3.0SDKで学ぶActionScript3.0入門が欲しかったんだけど、
どこにもないので(発売後1年足らずなのに…)しかたなく、同じくFlex SDK(Builderではなく)で作るこの本を購入。
(安さにつられたわけじゃ…)
プログラミング初心者用の本のため、他言語の経験者にはあまり向かない。
僕はE4Xを触ったことがなかったので多少はためになりました。
(本は対象読者を見てから買いましょう、ですね!)
あと、ところどころに、ん?と思う説明が。。。
関数名だけで利用される関数をクロージャともいいます(p.168)
…いいません!(笑)
とかね。
その他参考
・ TextArea自動スクロール
FirefoxのTextAreaと同じくtextを追加するとスクロールバーが先頭に戻っちゃう対策。
・ [Air/Flex]ViewStackの落とし穴 - 秘密結社ぎゅう☆ぎゅう倶楽部
スムーズな画面遷移と引き換えにイベント設定ができなくなるとか…。
・ たまご日記(別館) [flex] テキストの改行が余分に付く
改行の扱いにわりと苦労させられました。
・ /air/TLife/src/uwi/tinysegmenter/TinySegmenter.as - Spark project
・ /lang/actionscript/as3tinysegmenter/src/org/coderepos/text/TinySegmenter.as -- CodeRepos::Share -- Trac
TinySegmenterのAS移植版。
僕はなんとなくSparkProjectの方を使ってます。
GoogleのTwitter初発言をJavaScriptで読めるように
- 2009-03-01 (Sun)
- JavaScript
遅い(※)。しかもしょうもない。
が、自分が読むために作っちゃったので公開しとく。
※ googleがじゃなく僕がブログのネタにするのが…ね、一応。
2進表記を文字に直すのは、
String.fromCharCode(parseInt('01100110', 2)); // f
でOK。
というわけで、Googleの発言を読めるようにしましょう。
できました。
答えは…
最近何かと話題のGoogle日本のトップ画面からいつのまにか消え去ったあの機能です。
あぁあのシンプルな画面がなつかしい。
一応ソース
<textarea id="decodeGoogle" cols="30" rows="3"></textarea><br />
<input type="button" value="変換" onclick="(function(){
var chars = document.getElementById('decodeGoogle').value.split(/\s/);
for(var i = 0; i < chars.length; i++) {
if (!isNaN(chars[i])) chars[i] = String.fromCharCode(parseInt(chars[i], 2));
else chars[i] = chars[i] + ' ';
}
document.getElementById('decodeGoogle').value = chars.join('');
})()" />
以上です。
WEB+DB PRESSを1000円引きで定期購読(ブロガー限定!?)
- 2009-02-23 (Mon)
- 読んだ本
![]() |
WEB+DB PRESS 出版社:技術評論社 発売日:隔月(毎偶月)24日 サイズ:B5 最新刊の情報はこちら |
ついに購読しちゃいました。
まぁ見かけるたびに買ってる気がするし、
隔月って忘れがちだからちょうどいいんだけど…
月の書籍用予算(10000円と設定)が吹っ飛んだorz
あ、釣りっぽいタイトルの件ですが、ウソじゃないですよ。
ブロガーじゃなくWebサイト持ってる人全般OKですが。
というわけで以下にその方法を。
利用するのは
Fujisan.co.jpのアフィリエイト登録者全員に1000円引きギフト券プレゼント
なキャンペーン。
はい、もうおわかりですね。
というわけで、通常9,324円のWEB+DB PRESSが8,324円で買えました。
1冊あたりに換算すると1,554円→1,387.333...円ということでわりと安くなります。
以上、ブログ・Webサイトを持ってる人は是非。
良ければこの記事のリンクから飛んで下さい^^
LighediTorをSourceForge.jpで公開しました
- 2009-02-21 (Sat)
- JavaScript
ここ最近いじり続けてたエディタですが、SourceForge.jpでホスティングすることにしました。
名前はLighediTor。スペルミス注意です。
SF.jpにしたのは、比較的自由に使えるサーバスペースが手に入るから。
先日の障害の際、xrea一極集中はやばいと感じたので調度良かったのです。
以下が主なページです。
プロジェクト概要
プロジェクトページ(サンプル)
ソース(Subversion)
というわけで改めてよろしくお願いします。
当面の課題は補完候補を充実させることと、
Ctrl+Enterでの閉じタグ補完をうまく動くようにすることです。
※ IE6でとてつもなく重いのはとりあえず見なかったことにします。。。
皆さんもよければコミッタになってください!
SF.jpの使い方(SVN/シェルサーバ/FTP)
わりとつまづいたのでメモ。
TortpiseSVNで接続
基本的に以下の記事を参考にやればOK。
cl.pocari.org - TortoiseSVN で公開鍵を使う方法
ただ、
2. 公開鍵をサーバに登録
Public key for pasting into OpenSSH authorized_keys file の部分をコピーして $HOME/.ssh/authorized_keys に貼り付けます.
の部分は、
ユーザ設定 » アカウント基本情報設定 » [鍵の編集]から貼り付ければOK。
あと、パスフレーズは空白でOK。
未だにディレクトリとかコピーしながら開発してるので、これを気に活用していこうと思います。
せっかく環境が用意されてるようなので、Gitも使ってみたいところ。
Tera Termでシェルサーバに接続
上でputtygenをもう1回使います。
1. Conversions » Import keyからさっき作った秘密鍵をインポート。
2. Conversions » Export OpenSSH keyで変換。(identityっていう名前にしとくと楽)
※ 鍵を作る時にOpenSSH形式でExportもしておけば、importを省ける。
3. Teratermでshell.sourceforge.jpにSSHでログイン。
4. RSA/DSA鍵を使うを選択し、identityを読み込み。
(ユーザ/パスは、SF.jpアカウントのもの。)
5. あとは普通にシェルを使える。
参考。
SF.jpのsvnリポジトリへのアクセス設定(亀用) - つくば日記 (仮)
WinSCPでSFTP接続
これは特につまづくところはない。
ユーザ名/パスはSF.jpアカウント。
秘密鍵は、.ppkの方を指定。
プロトコルはSFTP。
あとは普通にFTPできる。
以上、SF.jpはホスティング環境としてかなりいい感じ。
(アドセンスも簡単に使えるしね。)
ソースの公開とかにはgoogle codeの方が向いてそうだけど。
JavaScriptで非WYGIWYGな軽量HTMLエディタを作ろう(最終回)
- 2009-02-21 (Sat)
- JavaScript
What You Code Is What You Get.
実は地味に進めてました。
めんどくさいのでまとめて投稿、すいません。。。
[実装2-1 入力補完候補表示]
手始めにカーソル位置の文字を取得して、補完候補を選ぶとこまでやりました。
結果がこちら→Editor2.1
<aと入力すれば、<a>、<adress>等と表示されます。
※ 候補が貧弱なのはキーワードを少ししか登録していないからです。
お世話になったサイトをあげておきます。
textareaの選択範囲を取得し、前後に文字列を挿入する - Archiva
前回以上にこのエントリに頼りっぱなしでした。
上のエントリを理解するために見たMSDN達。
duplicate
moveToElementText
setEndPoint
[実装2-2 カーソル座標にBOX表示]
2-1の候補をカーソル位置に表示しなくちゃだめなので、
まずはただのdivを表示してみることにしました。
結果→Editor2.2
textarea内で入力すれば、そのすぐ右下にBOXが移動します。
参考サイトはこちら。
テキストエリア内のキャレット座標を取得する - Webと文字
getBoundingClientRect()を使って要素の絶対座標を求める - 素人がプログラミングを勉強するブログ
[実装3 入力補完実装]
パーツは揃ったのでいよいよ実装。
ついでにjQueryプラグイン化。
必要なDOM要素は動的に作成するようにし、
複数のtextareaに対応しました。
※ あわせてツールバーを整理。
選択範囲変換、置換機能も実装しました。
Editor3
参考。
jQuery のプラグインを作成する : ブログの新着記事を表示:Goodpic
[実装4 リファクタリング]
ソースコードがあまりにもひどくなっていたので、書き直し。
が、大して変わらず。
他のjQueryプラグインを参考に何とかしようと思います。
※ 何がいいかなぁ…。
uiはちょっとでかすぎるし。
ここまでの成果がこれ↓
Editor4
Editor4-a
(4-aはボタンでエディタ化するサンプル。
上のtextareaの入力内容はすぐ下のdivに反映。
下のtextareaはツールバーなしバージョン。)
[まとめ]
補完候補が少なさを筆頭に不具合はまだまだありますが、ある程度できてきました。
今は、微調整を繰り返している状況です。(Editor5で公開)
そろそろ管理も煩雑になってきたので、どこかにhostingしようと思います。
というわけで連載風ブログでの公開は今回で最後です。
まさか2回で終わるとは思いませんでしたが…;
以上、今回はこの辺で。
- Search
-
Loading
- Feeds
- Links
- スポンサードリンク