Successfully reported this slideshow.
Your SlideShare is downloading. ×

node+socket.io+enchant.jsでチャットゲーを作る

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 48 Ad

More Related Content

Slideshows for you (20)

Viewers also liked (17)

Advertisement

Similar to node+socket.io+enchant.jsでチャットゲーを作る (20)

More from Kiyoshi SATOH (18)

Advertisement

Recently uploaded (20)

node+socket.io+enchant.jsでチャットゲーを作る

  1. 1. node+websocket+enchant.js で 2時間でピグみたいなチャットゲーを作る! 2012/2/11 長野ソフトウェア技術者グループ NSEG 第 24 回 勉強会 有限会社ジーワークス 佐藤 潔
  2. 2. 今日やることの概要 まずは実際につないで遊んでみてください http://hakuba.jp:20105/
  3. 3. 書いたコードはこれだけ クライアント→ サーバ↓
  4. 4. Node ● サーバサイド javascript ● イベントドリブン ● シングルスレッド ● C10K(1 万クライアント ) 問題に対応 ● javascript は遅そう? → javascript は今や遅くない
  5. 5. websocket ● はやりの HTML5 な技術 ● http 上で VPN 張るイメージ ● socket.io というフレームワークが超優れもの → websocket 非対応のクライアント上でも同 じ API で通信できる
  6. 6. enchant.js ● javascript のゲームフレームワーク ● 日本の会社(ユビキタスエンターテイメント)製 ● ベーマガ世代な人には特にぐっとくるポリシー
  7. 7. この構成にしたわけ ● 今回は C10K とかは全然関係ない ● node+socket.io ですごく簡単にリアルタイム 通信アプリが書けるから ● enchant.js の RPG っぽいサンプルを流用す ることで遊べるものが簡単に作れる
  8. 8. 環境構築 学習用サンプルソースの URL https://github.com/stealthinu/enchat/zipball/master Git のリポジトリは git@github.com:stealthinu/enchat.git
  9. 9. Linux/MacOS の方は自前のノートへ インストール パッケージを利用する場合 Installing Node.js via package manager https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager Debian / Ubuntu / Cent / OS X などメジャーな のはだいたいある
  10. 10. 手で入れる場合 自分が Ubuntu-10.04-LTS での例 「 libssl-dev 」と npm のインストールで必要な「 curl 」を入れとく > sudo aptitude install libssl-dev curl node.js 本体のインストール > wget http://nodejs.org/dist/v0.6.10/node-v0.6.10.tar.gz > tar -zxf node-v0.4.8.tar.gz > cd node-v0.4.8.tar.gz > ./configure > make > sudo make install
  11. 11. パッケージマネージャ npm のインストール 自分は root になってないとうまく入らなかった。しかし sudo でやんの は推奨ではないらしい。 が他の方法もなんかどうしたもんか、という感じだったのでこれでいく。 > sudo -s # curl http://npmjs.org/install.sh | sh npm の使い方はこんな感じ > npm install express > npm install socket.io
  12. 12. nvm も使ったほうがよい node はアップデートが非常に早くどんどん進化 していってるため、新しいバージョンでは動かな いということもちょくちょく起こる。 そこで nvm という、バージョンを切り替えて使う ためのものツールも利用すると良い。
  13. 13. Windows の方 僕のノートにアカウントと Cloud9IDE を準備してあります Cloud9IDE とは → node で動く javascript で書かれた統合開発環境  ブラウザ上でばしばし動く   Cloud9 という同じ名前で PaaS もあるよ   http://c9.io/
  14. 14. 使い方 twitter のアカウント名とパスワードでログイン ~/cloud9/config.js の port を確認 ユーザ認証ないので今回はポートで分けるため注意! Cloud9 IDE を起動 > cd ~/cloud9 > bin/cloud9.sh -l 192.168.x.x -w workspace ブラウザでアクセス http://192.168.x.x:xxx
  15. 15. node でチャットを作る node で簡単なチャットを作ることを目標に進めます ● まず適当に node とかフォルダ作る ● サンプルファイルを落として参照ようとして展開する ● 出来上がりサンプルは ex? という名前で作ってあります ● 同じファイルをどんどん追記修正していく方針でいきます ● git とかの利用は各自適当に
  16. 16. コンソールに "Hello World!" を表示 コンソールに表示するにはほんとに 1 行だけ。 これくらいなら include とか require とか全く不要 --- ex1.js console.log("Hello World!"); --- enchat.js というファイルをエディタで作ります Cloud9 IDE の人は File->new->JavaScrip file->"enchat.js"
  17. 17. 実行するには > node enchat.js Cloud9 IDE では、上のボタン「 run 」押すと configuration のウィンドウが開く。 Name:chat JavaScript file:enchat.js となってりゃ OK なのでこれで「 Run 」押す。 下の「 Output 」に Hello World! が表示される!
  18. 18. http サーバを作って "Hello Wold!" 次は http サーバを作ってブラウザ上に Hello World します。 基本的には http のライブラリを呼んで設定をしてやるだけ。 --- ex2.js var sys = require( 'sys' ); var HTTP = require( 'http' ); var server = http.createServer( function http_createServer( request, response ) { response.writeHead( 200, { 'content-type': 'text/plain' } ); response.write( "Hello World!" ); response.end(); } ).listen(20005); console.log("Server started."); --- http://192.168.x.x:xxxx を開いてみます。
  19. 19. express を使ってもっとお手軽に express というフレームワークを使うともっと簡単に。 まず npm という node.js のパッケージマネージャを使って「 express 」 をインストール。 > npm install express これで express がインストールされます。 --- ex3.js var app = require( 'express' ).createServer(); app.get( '/', function( req, res ) { res.send( 'Hello World!' ); }); app.listen(20005); console.log("Server started."); --- こりゃ簡潔!
  20. 20. express で Web サーバを作ってみる express を使うとものすごく簡単に Web サーバを作ることが出来ます。 実は express が全部やってくれるので、公開するフォルダをどこにする かを設定するだけ。 --- ex4.js var express = require( 'express' ); var app = express.createServer(); app.configure( function() { app.use( express.static( __dirname + '/public' ) ); }); app.listen( 300x ); console.log( "Server started." ); --- ほぼなんもしてないですね。 "public" というフォルダを作り、その下に適当に "index.html" とかを 作ってアクセスしてみましょう。
  21. 21. websocket でメッセージを送って表示 ここまではつまらなかったですがやっと本題の websocket に入ります。 node では簡単に websocket 等を使ったリアルタイム通信を行うため の socket.io というフレームワークが利用できます。 これを使ってクライアントにメッセージを送ってみます。 まずは WebSocket を使うためのパッケージ「 socket.io 」をインストー ルします。 > npm install socket.io
  22. 22. websocket でメッセージを送って表示 (2) 今回はサーバ側とクライアント側どちらも、また最初に開く HTML も必要になります。 html とクライアント用 js は public 以下に index.html と chat.js などの名前で作成。 --- ex5.html message: <div id="message"></div> <script type="text/javascript" src="ex5c.js"></script> --- ex5c.js socket.on("connect", function() { socket.emit("message", "Hello server!"); }); socket.on("message", function(text) { $("#message").html(text); }); --- ex5.js io.sockets.on("connection", function(socket) { console.log("connect new client."); socket.emit("message", "Hello client!"); // 繋がったらとりあえず送る // こちらがメッセージを受けた時の処理 socket.on("message", function(text) { console.log("message:" + text); }); }); ---
  23. 23. websocket でメッセージを送って表示 (3) サーバ側のソースで io.sockets.on("connection", function(socket) { … の中にソケットに接続があったときにどういう動きをするか記述します。 そして同じような記述で socket.on("message" function(text) { … の中には「 message 」というタグで通信を受けた時の動きを記述します。
  24. 24. websocket でメッセージを送って表示 (4) クライアントも似ていますが、接続されることが無いため socket.on("connect", function() { … に接続が出来た時の記述を socket.on("message" function(text) { … にサーバと同じく「 message 」というタグでメッセージを受けた時の動きを記述します。 socket.emit("message", text) で「 message 」というタグを付けて text の内容を送信してします。 $("#message").html(text); でクライアントでメッセージを受け取ったら jquery を利用して中身を書き換えています。 ブラウザに「 Hello client! 」が表示されサーバコンソールに「 Hello server! 」が表示さ れます。 また、複数の接続も可能なことも確認してみてください。
  25. 25. broadcast を使って全員と通信 このままだとサーバとクライアントが 1 体 1 でしか通信できないので複 数の接続への送信を行います。 そのためには broadcast を使います。 --- ex6.html message: <div id="message"></div> <input id="message_text" type="text"> <input id="message_button" type="button" value="send"> <script type="text/javascript" src="ex6c.js"></script> --- ex6c.js socket.on("message", function(text) { $("#message").html(text); }); $("#message_button").click(function() { var message = $("#message_text").val(); socket.emit("message", message); });
  26. 26. broadcast を使って全員と通信 (2) --- ex6.js io.sockets.on("connection", function(socket) { console.log("connect new client."); socket.emit("message", "Hello client!"); // 繋がったらとりあえず送る socket.broadcast.emit("message", "new client login!"); // 全員に知らせる // こちらがメッセージを受けた時の処理 socket.on("message", function(text) { console.log("message:" + text); socket.broadcast.emit("message", text); }); // 切断した時の処理 socket.on("disconnect", function() { console.log("disconnect."); socket.broadcast.emit("message", "client logout."); }); }); --- broadcast はたぶんすぐわかるでしょう。ついでに切断した時の処理と $("#message_button").click(function() { … で jquery を使って送信ボタンを押したときに emit 掛けるように追加しています。 ☆ 出来た人は、自分の発言も表示されるように修正してみましょう。
  27. 27. ログイン時に名前を送って表示 なんてことないようですが、ここでクロージャの概念が出てきます。 あと、メッセージについてるタグ毎にイベントを変更出来ることを確認し て下さい。 --- ex7c.js name = prompt("Input name:"); socket.on("connect", function() { socket.emit("name", name); }); socket.on("name", function(text) { $("#message").html($("#message").html() + "<br>" + "login:" + text); });
  28. 28. ログイン時に名前を送って表示 (2) --- ex7.js // 名前を送ってきた時の処理 var login_name = "unknown"; socket.on("name", function(text) { console.log("name: " + text); login_name = text; socket.broadcast.emit("name", login_name); }); // こちらがメッセージを受けた時の処理 socket.on("message", function(text) { console.log("message:" + text); socket.broadcast.emit("message", login_name + ":" + text); }); --- 「 name 」のタグが付いたときは、クライアントの名前 login_name を変更します。 function の 中 の 変 数 な の で す が 、 connect 毎 に こ の 関 数 は 作 ら れ て 、 中 の login_name は個別に保持されます。 つまりクロージャとして使われています。 ☆ 自分が発言したものも表示されて色分けてわかるようにしたり、切断時に誰が切断し たか表示してみましょう。
  29. 29. enchant.js の使い方 次は enchant.js の RPG デモを改造して簡単に使 い方を覚えます。 ● ローカルで開いてデモを動かす ● 名前やフキダシの表示 ● 他のキャラクタの表示 ● 他のキャラクタをクリックを起点に移動
  30. 30. まずデモを動かしてみる enchant.js のアーカイブを enchantjs とかに展開してローカルのファイルの enchantjs/examples/rpg/index.html を開いてみます。 キーパッドを押すと動き回れることを確認して下さい。 ゲームメイン部分ソースは enchantjs/examples/rpg/game.js で、データ部分が大きいですが、それを除けば 80 行ほどとコンパクトです。 このデモのソース自体の解説についてはこちらが詳しいのでおすすめ enchant.js のサンプルコードを解読する( RPG 編その 1 ) | IDEA*IDEA http://www.ideaxidea.com/archives/2011/04/enchant_rgb_undocumented.html
  31. 31. 例によって Hello World から --- ex8.js var label = new Label( "Hello World!" ); label.color = "black"; label.x = 7 * 16 - 8; label.y = 10 * 16; --- stage.addChild(label); --- まず文字列のオブジェクトを作って色や表示位置を指定します。 作ったオブジェクトを表示用のレイヤーに追加します。
  32. 32. 名前とフキダシの表示 addChild した順番に重ね合わせされるため、レイヤーを分ける必要があるの で、 chara_group と message_group 作ります。 CSS で見栄えを修正するためラベルに class の属性を追加してやります。 index.html に CSS 追記し角丸半透明でフキダシが表示されるようにします。 --- ex9.html .message{ text-align:center; border-radius:10px; -webkit-border-radius:10px; -moz-border-radius:10px; filter: alpha(opacity=75); -moz-opacity:0.75; opacity:0.75; } .login_name{ text-align:center; }
  33. 33. 名前とフキダシの表示 (2) --- ex9.js var chara_group = new Group(); var message_group = new Group(); --- // 名前の表示 player.login_name = new Label( " えぬせぐ " ); player.login_name._element.setAttribute( 'class', 'login_name' ); …略 // チャット内容の表示 player.message = new Label( "Hello World!" ); player.message._element.setAttribute( 'class', 'message' ); …略 // キャラクタ表示レイヤーとメッセージ表示レイヤーに追加 chara_group.addChild(player); chara_group.addChild(player.login_name); message_group.addChild(player.message); --- stage.addChild(chara_group); stage.addChild(foregroundMap); stage.addChild(message_group); ---
  34. 34. 名前とフキダシの表示 (3) 動かしてみると、名前とフキダシがそのまま残ってしまいます。 なので一緒に動くように修正します。 this.moveBy(this.vx, this.vy); のところで移動をしているので、その下で名前とフキダシの位置も移動 してやります。 --- ex10.js this.login_name.x = this.x - 35; this.login_name.y = this.y + 32; this.message.x = this.x - 30; this.message.y = this.y - 16; ---
  35. 35. 他キャラクタの表示 他の人を表示するため、 player の表示部分をそのまま使い、オブジェク トを作ってレイヤーに追加してやります。 イメージを変更するために、 image2.draw の引数を変更して、表示に 使うキャラクタを変えています。 --- ex11.js var other_player = new Sprite(32, 32); other_player.x = 7 * 16 - 8; other_player.y = 11 * 16; var image2 = new Surface(96, 128); image2.draw(game.assets['chara0.gif'], 100, 0, 96, 128, 0, 0, 96, 128); other_player.image = image2; // 名前の表示 other_player.login_name = new Label( " はくば " ); …略 // キャラクタ表示レイヤーとメッセージ表示レイヤーに追加 chara_group.addChild(other_player); chara_group.addChild(other_player.login_name); message_group.addChild(other_player.message); ---
  36. 36. 他キャラクタの移動 (クリックでどっかへ移動) これだけではなにも動きが無くて動かそうとした時ちゃんと動くか不安です。 このキャラクタがクリックされたら移動するようにしてみます。 EventListener に 'touchend' (クリックが離されたら)で動作を追加します。 --- ex12.js // キャラクタが押されたら右へ移動していく other_player.addEventListener( 'touchend', function() { this.x += 4; this.login_name.x = this.x - 35; this.message.x = this.x - 30; }); --- キャラをクリックするたびに右へ移動していきます。
  37. 37. メッセージの入力と変更 チャットさせるためには文字入力をさせなければいけません。 enchant.js で文字入力させる場合、 prompt を使わないと無理だそうで す。 なので、フキダシをクリックされたら prompt を出すようにしてみます。 ついでにフキダシの内容も変更してみましょう。 --- ex13.js // メッセージの入力とフキダシ内容変更 player.message.addEventListener('touchend',function(e) { var message = prompt( 'input message:', 'hi!' ); if ( message != '' ) { player.message.text = message; } }); --- フキダシを押されたら prompt を表示して、中身が空でなければフキダシ の中身を変更します。
  38. 38. 通信部分と enchant の合体 これまで作った node のチャット部分と enchant.js の部分とを合体していきます。 まず public に置いて素の enchant デモ動くこと確認します。 enchant.js と plugin/ui.enchant.js と images にある 4 つの画像ファイル apad.png pad.png chara0.gif map1.gif を public にコピーします。 今まで enchant.js で編集していた index.html と game.js を public のフォルダ に入れます。 index.html で enchant.js 等の読み込み先を変更します。 --- ex14.html <script type="text/javascript" src="enchant.js"></script> <script type="text/javascript" src="ui.enchant.js"></script> ---
  39. 39. node の通信部分を enchant.js に組み込む html に socket.io を読み込むための行を追加します。 クライアントの先頭でチャットを作った時の接続部分を組み込みます。 --- ex15.html <script type="text/javascript" src="/socket.io/socket.io.js"></script> --- ex15c.js name = prompt("Input name:"); var port = 20005; var socket = io.connect("/", { port: port }); socket.on("connect", function() { socket.emit("name", name); }); --- // 名前の表示 player.login_name = new Label( name ); --- サーバのコンソールに名前が送られてきているのを確認して下さい。
  40. 40. メッセージをサーバに送ることと 他キャラクタへのフキダシ表示テスト フキダシで入力する処理のところに socket.emit も追加します。 また、ソケットに message が来たら other_player のフキダシを修正 するようにします。 --- ex16c.js // メッセージの入力とフキダシ内容変更 player.message.addEventListener('touchend',function(e) { var message = prompt( 'input message:', 'hi!' ); if ( message != '' ) { player.message.text = message; socket.emit("message", message); } }); --- // サーバからメッセージが来たら他のキャラクタのフキダシに表示 socket.on("message", function(text) { other_player.message.text = text; }); --- サーバが送ってきたメッセージ「 Hello Client! 」が表示されて、こちらか ら送ったメッセージがコンソールに表示されてることを確認して下さい。
  41. 41. name が来たら新たなキャラクターを表示 このままだと、他のユーザキャラクタが 1 体しか表示されないため、名 前が送られてきたら新たなユーザがログインしたと認識して、新たな キャラクターを表示するようにします。 そこで、今まで他のユーザを表示していたところを、 name が来たら動く クロージャを作ってくくってやります。 --- ex17c.js // 他のユーザのログイン socket.on("name", function(text) { var login_name = text; …略 // 名前の表示 other_player.login_name = new Label( login_name ); …略 }); --- 複数の接続を行うと、それ毎にキャラクターが新しくその名前で表示さ れているのがわかります。 また、メッセージを書くと変更されることも確認して下さい。
  42. 42. ユーザ毎の処理を行えるようにする 今は誰からのメッセージかを考慮せずにそのまま表示するので、すべてのクラ イアントのフキダシがかわってしまいます。そこで、誰からのメッセージなのかを タグに一緒に埋め込んで送って判別出来るようにします。 今まではタグを単に「 message 」としていましたが「 message: 【ログイン 名】」とするようにします。 --- ex18.js // こちらがメッセージを受けた時の処理 socket.on("message", function(text) { console.log("message:" + login_name + " " + text); socket.broadcast.emit("message:" + login_name, text); }); --- ex18c.js // サーバからこのユーザのメッセージが来たらフキダシに表示 socket.on("message:" + login_name, function(text) { other_player.message.text = text; }); --- ユーザ毎に違うメッセージが表示されていることを確認して下さい。 キャラをクリックして少しずらしてやると見やすいです。
  43. 43. position を送って移動 移動できるように修正し、名前や位置などを player にまとめます。 --- ex19.js var player = { login_name : "", x : 0, y : 0, message : "…" }; その他 login_name など関連を player.login_name に修正 --- // こちらがメッセージを受けた時の処理 socket.on("message", function(text) { console.log("message:" + player.login_name + " " + text); player.message = text; socket.broadcast.emit("message:" + player.login_name, text); }); --- // 移動処理 socket.on("position", function(pos) { // console.log("position:" + player.login_name + " " + text); player.x = pos.x; player.y = pos.y; socket.broadcast.emit("position:" + player.login_name, pos); });
  44. 44. position を送って移動 (2) --- ex19c.js socket.emit("position", { x : this.x, y : this.y }); --- // サーバからこのユーザの移動が来たら移動させる socket.on("position:" + login_name, function(pos) { other_player.x = pos.x; other_player.y = pos.y; other_player.message.x = other_player.x - 30; other_player.message.y = other_player.y - 16; other_player.login_name.x = other_player.x - 35; other_player.login_name.y = other_player.y + 32; }); ※ この部分はもう削除しとく // キャラクタが押されたら右へ移動していく --- ☆ 余裕のある人は、他キャラクターの向き変更もしてみましょう。   direction 送って frame を direction にあわせて変更します。
  45. 45. ログアウト時の処理 接続が切れてもキャラクタが残るので、ログアウト時に削除しましょう。 --- ex20.js // 切断した時の処理 socket.on("disconnect", function() { console.log("disconnect:" + player.login_name); socket.broadcast.emit("disconnect:" + player.login_name); }); --- ex20c.js // 切断が送られてきたら表示とオブジェクトの消去 socket.on("disconnect:" + login_name, function() { // レイヤーから削除 chara_group.removeChild(other_player); chara_group.removeChild(other_player.login_name); message_group.removeChild(other_player.message); delete other_player; }); --- removeChild を使ってレイヤーからの削除を行います。 ☆ ログイン時に名前以外に位置やメッセージ情報も送ってみよう。
  46. 46. 新たにログインしたユーザに 現時点でログインしてるユーザ情報を送る 後からログインしたユーザは、その前にログインしていたユーザを見るこ とが出来ません。 そのため、ログインした時に、今入っているユーザの情報を全て送ってや る必要があります。 自分はここでちょっとつまづいてしまったので、せっかくなので皆さん考 えてみてください。 ex21 がログイン時にユーザ情報を送るバージョンのサンプルです。 サンプルで動かしてる完成バージョンが下記ファイルです。 example/enchat.js example/public/index.html example/public/game.js
  47. 47. 早く出来た人・後の時間用ネタ XSS 対策   node-validate でサーバ側  フキダシ壊れるからクライアント側でもタグ落とす login_name 重複の対応 名前とフキダシの表示リファクタリング   Chara クラスに機能追加 Chara.prototype = new Sprite(); ログイン時に名前だけじゃなく場所とフキダシも送る  ちゃんとやるには deferred 使う ( DB から読み出す場合とかも) ゲーム化(ゾンビ感染ゲーム、雪合戦、 NPC を捕まえる、など)
  48. 48. Special Thanks ● @shi3z_bot enchant.js でメッセージ入力するにはどうしたらよいか困って いた時に的確なアドバイスをいただきました。 enchant.js というすばらしいフレームワークを提供いただいて いること自体にも感謝です。 ● @hkoba ログイン時に全ユーザ情報を送る処理について、クロージャ自 体をハッシュに突っ込もうとして出来なくて困ってた時にアドバ イスいただき、助かりました。 ● @KojiSaito ハンズオンで早く終わった人用になにか追加の課題を用意し ておいたら、というアドバイスいただき、使わせていただきまし た。

×