More Related Content
Similar to node+socket.io+enchant.jsでチャットゲーを作る (20)
More from Kiyoshi SATOH (18)
node+socket.io+enchant.jsでチャットゲーを作る
- 4. Node
● サーバサイド javascript
● イベントドリブン
● シングルスレッド
● C10K(1 万クライアント ) 問題に対応
● javascript は遅そう?
→ javascript は今や遅くない
- 5. websocket
● はやりの HTML5 な技術
● http 上で VPN 張るイメージ
● socket.io というフレームワークが超優れもの
→ websocket 非対応のクライアント上でも同
じ API で通信できる
- 6. enchant.js
● javascript のゲームフレームワーク
● 日本の会社(ユビキタスエンターテイメント)製
● ベーマガ世代な人には特にぐっとくるポリシー
- 7. この構成にしたわけ
● 今回は C10K とかは全然関係ない
● node+socket.io ですごく簡単にリアルタイム
通信アプリが書けるから
● enchant.js の RPG っぽいサンプルを流用す
ることで遊べるものが簡単に作れる
- 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. 手で入れる場合
自分が 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. パッケージマネージャ npm のインストール
自分は root になってないとうまく入らなかった。しかし sudo でやんの
は推奨ではないらしい。
が他の方法もなんかどうしたもんか、という感じだったのでこれでいく。
> sudo -s
# curl http://npmjs.org/install.sh | sh
npm の使い方はこんな感じ
> npm install express
> npm install socket.io
- 16. コンソールに "Hello World!" を表示
コンソールに表示するにはほんとに 1 行だけ。
これくらいなら include とか require とか全く不要
--- ex1.js
console.log("Hello World!");
---
enchat.js というファイルをエディタで作ります
Cloud9 IDE の人は
File->new->JavaScrip file->"enchat.js"
- 17. 実行するには
> node enchat.js
Cloud9 IDE では、上のボタン「 run 」押すと configuration
のウィンドウが開く。
Name:chat JavaScript file:enchat.js
となってりゃ OK なのでこれで「 Run 」押す。
下の「 Output 」に
Hello World!
が表示される!
- 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. 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. 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" とかを
作ってアクセスしてみましょう。
- 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);
});
});
---
- 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. 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. 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 掛けるように追加しています。
☆ 出来た人は、自分の発言も表示されるように修正してみましょう。
- 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 は個別に保持されます。
つまりクロージャとして使われています。
☆ 自分が発言したものも表示されて色分けてわかるようにしたり、切断時に誰が切断し
たか表示してみましょう。
- 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. 例によって 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. 名前とフキダシの表示
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. 名前とフキダシの表示 (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);
---
- 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. 他キャラクタの移動
(クリックでどっかへ移動)
これだけではなにも動きが無くて動かそうとした時ちゃんと動くか不安です。
このキャラクタがクリックされたら移動するようにしてみます。
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. メッセージの入力と変更
チャットさせるためには文字入力をさせなければいけません。
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. 通信部分と 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. 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. メッセージをサーバに送ることと
他キャラクタへのフキダシ表示テスト
フキダシで入力する処理のところに 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. name が来たら新たなキャラクターを表示
このままだと、他のユーザキャラクタが 1 体しか表示されないため、名
前が送られてきたら新たなユーザがログインしたと認識して、新たな
キャラクターを表示するようにします。
そこで、今まで他のユーザを表示していたところを、 name が来たら動く
クロージャを作ってくくってやります。
--- ex17c.js
// 他のユーザのログイン
socket.on("name", function(text) {
var login_name = text;
…略
// 名前の表示
other_player.login_name = new Label( login_name );
…略
});
---
複数の接続を行うと、それ毎にキャラクターが新しくその名前で表示さ
れているのがわかります。
また、メッセージを書くと変更されることも確認して下さい。
- 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. 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. ログアウト時の処理
接続が切れてもキャラクタが残るので、ログアウト時に削除しましょう。
--- 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 を使ってレイヤーからの削除を行います。
☆ ログイン時に名前以外に位置やメッセージ情報も送ってみよう。
- 47. 早く出来た人・後の時間用ネタ
XSS 対策
node-validate でサーバ側
フキダシ壊れるからクライアント側でもタグ落とす
login_name 重複の対応
名前とフキダシの表示リファクタリング
Chara クラスに機能追加 Chara.prototype = new Sprite();
ログイン時に名前だけじゃなく場所とフキダシも送る
ちゃんとやるには deferred 使う
( DB から読み出す場合とかも)
ゲーム化(ゾンビ感染ゲーム、雪合戦、 NPC を捕まえる、など)
- 48. Special Thanks
● @shi3z_bot
enchant.js でメッセージ入力するにはどうしたらよいか困って
いた時に的確なアドバイスをいただきました。
enchant.js というすばらしいフレームワークを提供いただいて
いること自体にも感謝です。
● @hkoba
ログイン時に全ユーザ情報を送る処理について、クロージャ自
体をハッシュに突っ込もうとして出来なくて困ってた時にアドバ
イスいただき、助かりました。
● @KojiSaito
ハンズオンで早く終わった人用になにか追加の課題を用意し
ておいたら、というアドバイスいただき、使わせていただきまし
た。