前回作った以下のゲームの解説です。

素材画像とソースコード一式は、こちら。ゲームのプレイはこちら
今回、4択式のゲームという事で、ボタンを4つ下方に配置しています。

enchant.jsを使ったボタンに関しては、ui.enchant.jsを読み込んでButtonクラスを使う、という手もありますが、自分の望むようなイメージのボタンを作ることが出来ません。
ここは一つ、面倒くさいのですが、ボタンを押したときと離したとき2枚の画像を作ってその上に文字を配置するという方法でオリジナルなボタンを作ろうと考えました。(そんなの普通じゃん、とか言わないで下さい。こういう考えに現代人の多くは至らないんです。なぜなら出来合いのものを使うことに慣れてしまっているからです)

わたしはデザインセンスがないので、この程度ですが、画像制作が上手な人はもっとかっこよくなります。
ボタンは、押したときと離したとき(何もしない状態)の2つの状態がある、という事になります。
物事を分解して考えることは仕組みを考えることにもつながって面白いことです。(考えている時がそのそも楽しいからです。その考えがつまりはプログラムなんです。ちょっとでもそんなことを考えたことがある人は、プログラマだとわたしは思います!)
日本白地図のプログラムで解説する部分としては、このボタン部分かなあと思います。
あとは、ボタンを押したら何をするか、です。
ボタンを押した後は、
- 問題に正解か不正解かを判定し
- 正解の時は、○の画像、不正解の時は、×の画像を表示し
- ときどき、「Great!」や「ファンタスティック!」などの画像を表示し
- 次の問題に移る
ということを順に実行していきます。
3.の部分がわたしなりのこだわりです。何か独自性を出したいですよね、自分で作る以上は。(まあ、たいしたこだわりでもありませんが、アホっぽくなったと思います)
ところで、「Great!」や「ファンタスティック!」等の画像ですが、太めの線で縁取りされた文字はポップな印象ですよね。
ぜひ作ってみたいものです。

「無料ロゴ作成.com」等のロゴ作成サイトを利用すればセンスのないあなたも安心です。背景が透明になるPNG形式で保存すればゲームに利用しやすいです。
そんなこんなで作った4択式のゲーム用ボタン部分のプログラムは、以下のとおりです。
/*
* ラベルボタンクラス(ラベルクラスとスプライトクラスを結合してボタンに見せるクラス)
*/
var LabelButton = Class.create(Group, {
initialize: function(x, y, text){
Group.call(this, BUTTON_WIDTH, BUTTON_HEIGHT);
this.moveTo(x, y);
// ボタンの画像(スプライトクラスを使用)
this.button = new Sprite(BUTTON_WIDTH, BUTTON_HEIGHT);
this.button.image = game.assets[IMG_BUTTON];
this.addChild(this.button);
// ボタンのラベル(ラベルクラスを使用)
this.label = new Label(text);
this.label.font = BUTTON_FONT;
this.label.text = text;
this.label.width = this.width;
this.label.height = this.height;
console.log("text.length = " + text.length);
this.label.x = this.centerX(text);
this.label.y = 19;
this.addChild(this.label);
this.show();
},
centerX: function(text){
return(this.button.width/2 - (text.length * 16)/2); // 文字を中央に配置
},
setText: function(text){
this.label.x = this.centerX(text);
this.label.text = text;
},
show: function(){
this.button.visible = true;
this.label.visible = true;
},
hide: function(){
this.button.visible = false;
this.label.visible = false;
},
push: function(){
this.button.image = game.assets[IMG_BUTTONPUSH];
this.label.color = "#fff";
},
release: function(){
this.button.image = game.assets[IMG_BUTTON];
this.label.color = "#000";
},
ontouchstart: function(){
this.push();
},
ontouchend: function(){ // 押し終えた時、解答チェックする
if(game.mode == GAME){
game.mode = EFFECT;
console.log("Mode: " + game.mode)
this.release();
// 正誤チェック
var marubatu = null;
if(this.label.text == prefs[mondai[no]]){ // 正解
marubatu = new Marubatu(0, 0, IMG_MARUBATU, 0); // ○を表示
game.score++;
console.log("Score: " + game.score);
scoreText.setText("Score: " + game.score);
// 盛り上げるためのエフェクト
if(game.score % 3 == 0){ // Great!
scGame.addChild(new Effect(SCREEN_WIDTH/2-80, SCREEN_HEIGHT/2-100, 160, 60, IMG_GREAT, TYPE_USUAL));
}
if(game.score % 5 == 0){ // ファンタスティック!
scGame.addChild(new Effect(rnd(0, SCREEN_WIDTH-358), rnd(0, SCREEN_HEIGHT/4), 358, 180, IMG_FANTASTIC, TYPE_USUAL));
}
if(game.score == 24){ // あと半分!
scGame.addChild(new Effect(SCREEN_WIDTH/2 - 301/2, SCREEN_HEIGHT/4, 301, 82, IMG_CENTER, TYPE_USUAL));
}
else if(game.score == PREFS-3){ // あと3つ!
scGame.addChild(new Effect(SCREEN_WIDTH/2 - 301/2, SCREEN_HEIGHT/4, 301, 79, IMG_LAST3, TYPE_USUAL));
}
else if(game.score == PREFS-1){ // あと1!!
scGame.addChild(new Effect(SCREEN_WIDTH/2 - 321/2, SCREEN_HEIGHT/4, 321, 101, IMG_LAST1, TYPE_USUAL));
}
}
else{ // 不正解
marubatu = new Marubatu(0, 0, IMG_MARUBATU, 1); // ×を表示
}
scGame.addChild(marubatu);
}
},
});
クラス全体は、Groupクラスで定義しています。
その中で、ボタンの背景画像用のSpriteクラスとボタンの文字表示用のLabelクラスを定義しています。Groupクラスの中で複数の別クラスを定義すると合体したものになる仕組みです。
押したとき(ontouchstart)で、ボタンの画像を押したとき用の画像に変えて、離したとき(ontouchend)で元のボタン画像に戻して押したように見せています。(押したように見えればそれがボタンです!コンピュータゲームなんてみんな惑わしているだけです)
盛り上げる為のエフェクトという部分が無駄に記述が多いですが、盛り上げるため(笑)です。
実際のエフェクトは、プログラムにあるようにEffectクラスで行っています。
/*
* エフェクトクラス
*/
var Effect = Class.create(Sprite, {
initialize: function(x, y, width, height, image, type){
Sprite.call(this, width, height);
this.image = game.assets[image];
this.opacity = 1.0;
this.vy = -rnd(5, 15);
this.moveTo(x, y);
this.startFrame = game.frame;
if(type == TYPE_USUAL){
this.update = this.move1;
}
else if(type == TYPE_LAST){
this.update = this.move2;
}
},
move1: function(){
// ちょっと上がって下に落ちていく感じ
this.vy += 0.8;
this.y += this.vy;
this.opacity -= 0.02;
if(this.opacity < 0 || this.y > SCREEN_HEIGHT){
this.parentNode.removeChild(this); // シーンから消す
}
},
move2: function(){
// ぐるぐる回る
this.rotate(45);
if(this.rotation > 360) this.rotation = 0;
// 1.5秒後に停止して消える
if((game.frame-this.startFrame)/game.fps > 1.5){
this.rotation = 0;
this.opacity -= 0.01;
if(this.opacity < 0){
this.parentNode.removeChild(this); // シーンから消す
}
}
},
onenterframe: function(){
this.update();
},
});
move1とmove2の2つの動きを用意しましたが、通常の「Great!」や「ファンタスティック!」などはmove1です。ちょっと上にいってから下に落ちていく感じにしました。move2は47都道府県全部クリアしたときに出てくる「完全制覇」の文字の動きです。
Effectクラスは、生成時にエフェクトのxy座標、画像の幅と高さ、画像ファイル、タイプ(move1かmove2)を指定するようになっています。
new Effect(100, 100, 160, 60, IMG_GREAT, TYPE_USUAL)
のように指定すれば、そこにエフェクト画像が出現します。onenterframeごとにopacity(透明度)を減らして、見えなくなったらシーンから消すようにしているので、管理が楽です。newで生成すればあとは勝手に消えてなくなるからです。ゲームでちょっとだけ何か出現させたいときの処理として妥当です。
最初のバージョンでは、ボタンの文字が中央に配置されずにいたのですが、次の式で中央揃えが出来るようになりました。
文字を中央にするx座標 =
ボタンの幅 / 2 – ( 表示する文字の長さ * 1文字のピクセルサイズ ) / 2 )
実際のコードは、こんな感じです。
this.button.width/2 – (text.length * 16)/2)
式なんて書くまでもないだろう、と考える人もいるかと思いますが、わたしは馬鹿なので書かないと理解できません。(もっとも、この式を得るまでにボタンと文字の絵を描いて試行錯誤していますが...)
ワードの機能で指定した文字を中央ぞろえにする「三」のようなボタンアイコンがあります。
あのボタンの機能がまさにこの式です。ワードを作った人は天才です。
わたしは小学校の算数で割り算や引き算を習っていたので、なんとかこの式を自分で導く事ができました。
自分で考えなくてもネットで調べればすぐに対処法は見つかるかもしれません。
ただ、ネットだけに頼っていては、いつまでたっても自分の能力や経験値は上がりません。
RPGで言うとラスボスにいつまでたっても立ち向かえない状態です。
それがいやなら若いうちの勉強はしておくべきです。どこで役に立つか分かりませんから。
話が少しそれてしまいました。
でも、わたしはいつも「もっと明晰な頭脳だったら、もっと凄いものが作れるのになあ」と思います。

コメント