ゲームライブラリ構築までの道「ブロック崩し編」#4 : ゲームスタートとゲームオーバー

2020年7月7日

Javascript テクノロジー プログラミング 特集

以前に作ったプログラムを見直すとどうしても書き直したくなってしまう、ユゲタです。 リファクタリングは重要な作業ですが、その時々で、コーディングルールが変わってきてしまい、どうしても、最新の自分のマイ・フェイバリットコーディングルールに思考が寄ってしまうので、書き直しをしたくなるんですね。 その中で書き直さなくても納得の行く書き方ができているのは、自分のプログラミングの型が決まっているものは、書き直しをしなくてもコードに安定感を感じるため、プログラミングにも武道の型のようなものが存在するということを、改めて理解することができました。 そして、壁打ちテニスも、操作性がつくことによって、ゲームっぽくなってきたので、今回は、ゲーム開始と終了の処理を追加してみたいと思います。

本日のIT謎掛け

「コーディング・ルール」と、かけまして・・・ 「駐車テクニック」と、ときます。 そのココロは・・・ スペースにこだわる人が、多いでしょう。

ソースコード

$$gamekit_draw = (function(w,d,n){ // default-set var __options = { flg_mouse : null, mouse_over : { color_bg : "#7BA6A1", }, mouse_down : { color_bg : "#C5D9D2", }, mouse_up : { color_bg : "#7BA6A1", }, color_bg : "#58838C", color_txt : "#F2E6A7", color_stroke : "#3F6473", width_stroke : 0, align : "center", // [ left , center , right ] valign : "middle", // [ top , middle , bottom ] width : 0, // % or px *数値のみの場合はpx height : 0, // % or px *数値のみの場合はpx margin : 0, // 全ての方向に対して一律指定 : %指定の場合はwidthに対しての% or px *数値のみの場合はpx padding : "10px", // 全ての方向に対して一律指定 : %指定の場合はwidthに対しての% or px *数値のみの場合はpx radius : 0, textAlign : "center", textFont : "sans-serif", textColor : "white", textBase : "top", textWeight: "bold", textSize : "16px", x : 0, // 自動設定 y : 0, // 自動設定 tx : 0, // 自動設定 ty : 0, // 自動設定 click : function(e , options){}, $:0 }; // ----- // Object make var MAIN = function(canvas_selector){ canvas_selector = canvas_selector || "canvas"; var canvas = d.querySelector(canvas_selector); if(canvas){ this.canvas = canvas; return 1; } else{ this.canvas = null; return 0; } }; // ----- // Dialog // options : __options is default // texts : array strings of text-line MAIN.prototype.dialog = function(options){ if(!this.canvas){return;} var canvas = this.canvas; options = new $$gamekit_common().setOption(__options , options); options = this.dialogOptions_adjust(options); var ctx = canvas.getContext("2d"); this.viewBase(ctx , options); this.viewMessage(ctx , options); }; MAIN.prototype.dialogOptions_adjust = function(options){ if(!this.canvas){return;} var canvas = this.canvas; // サイズ処理 options.width = this.px_per2num(options.width , canvas.offsetWidth); options.height = this.px_per2num(options.height , canvas.offsetHeight); options.margin = this.px_per2num(options.margin , canvas.offsetWidth); options.padding = this.px_per2num(options.padding , canvas.offsetWidth); options.textSize = Number(String(options.textSize).replace("px","")); options.radius = this.px_per2num(options.radius , canvas.offsetWidth); options.width_stroke = Number(String(options.width_stroke).replace("px","")); if(options.x === 0){ switch(options.align){ case "left": options.x = options.margin; break; case "right": options.x = canvas.offsetWidth - (options.width + options.margin); break; case "center": default: options.x = (canvas.offsetWidth / 2) - (options.width / 2); break; } } else{ options.x = this.px_per2num(options.x , canvas.offsetWidth); } if(options.y === 0){ switch(options.valign){ case "top": optinos.y = options.margin; break; case "bottom": options.y = canvas.offsetHeight - (options.height + options.margin); break; case "middle": default: options.y = (canvas.offsetHeight / 2) - (options.height / 2); break; } } else{ options.y = this.px_per2num(options.y , canvas.offsetHeight); } // text情報 options.ty = options.y + options.padding; switch(options.textAlign){ case "left": options.tx = options.x + options.padding; break; case "right": options.tx = canvas.offsetWidth - options.x - options.padding; break; case "center": default: options.tx = options.x + (options.width / 2); break; } return options; }; // @px,@%を数値に変換する MAIN.prototype.px_per2num = function(data , max){ if(String(data).match(/^([0-9]+?)%$/)){ var num = Number(RegExp.$1); num = num <= 100 ? num : 100; return parseInt(max * (num / 100)); } else if(String(data).match(/^([0-9]+?)px$/)){ return Number(String(data).replace("px","")); } else{ return Number(data); } }; MAIN.prototype.viewBase = function(ctx , options){ ctx.fillStyle = options.color_bg; ctx.strokeStyle = options.color_stroke; ctx.lineWidth = options.width_stroke * 2; if(options.radius === 0){ ctx.strokeRect(options.x , options.y , options.width , options.height); ctx.fillRect(options.x , options.y , options.width , options.height); } else{ var w = options.width; var h = options.height; var x = options.x; var y = options.y; var r = options.radius; ctx.beginPath(); ctx.moveTo(x,y + r); ctx.arc(x+r , y+h-r , r , Math.PI , Math.PI*0.5 , true); ctx.arc(x+w-r , y+h-r , r , Math.PI*0.5,0 , 1); ctx.arc(x+w-r , y+r , r , 0 , Math.PI*1.5 , 1); ctx.arc(x+r , y+r , r , Math.PI*1.5 , Math.PI , 1); ctx.closePath(); ctx.stroke(); ctx.fill(); } }; MAIN.prototype.viewMessage = function(ctx , options){ // 文字 ctx.fillStyle = options.color_txt; ctx.font = options.textWeight +" "+ options.textSize +"px "+ options.textFont; ctx.textAlign = options.textAlign; ctx.textBaseline = options.textBase; options.texts = typeof options.texts === "string" ? [options.texts] : options.texts; for(var i=0; i<options.texts.length; i++){ ctx.fillText(options.texts[i], options.tx, options.ty + (i * options.textSize + 4) , options.width); } }; // ----- // button MAIN.prototype.button = function(options){ options = new $$gamekit_common().setOption(__options , options); options = this.dialogOptions_adjust(options); options.mouse_over = new $$gamekit_common().setOption(options , options.mouse_over); options.mouse_down = new $$gamekit_common().setOption(options , options.mouse_down); options.mouse_up = new $$gamekit_common().setOption(options , options.mouse_up); this.button_draw(options); this.setButtonEvent(options); }; MAIN.prototype.button_draw = function(options){ if(!this.canvas){return;} var ctx = this.canvas.getContext("2d"); options = options.flg_mouse === null ? options : options["mouse_"+options.flg_mouse]; this.viewBase(ctx , options); this.viewMessage(ctx , options); }; MAIN.prototype.setButtonEvent = function(options){ if(!this.canvas){return;} new $$gamekit_common().event(this.canvas , "mousemove" , (function(options,e){this.event_button_mouse(e,options,"over")}).bind(this , options)); new $$gamekit_common().event(this.canvas , "mousedown" , (function(options,e){this.event_button_mouse(e,options,"down")}).bind(this , options)); new $$gamekit_common().event(this.canvas , "mouseup" , (function(options,e){this.event_button_mouse(e,options,"up")}).bind(this , options)); new $$gamekit_common().event(this.canvas , "click" , (function(options,e){this.event_button_click(e,options)}).bind(this , options)); }; MAIN.prototype.event_button_mouse = function(e,options , flg){ if(flg === "over" && options.flg_mouse === "down"){return;} if(flg === "over" && options.flg_mouse === "up"){return;} var canvas = e.currentTarget; var x = e.clientX - canvas.getBoundingClientRect().left; var y = e.clientY - canvas.getBoundingClientRect().top; // buttonエリア内 if(options.x <= x && x <= options.x + options.width && options.y <= y && y <= options.y + options.height){ options.flg_mouse = flg; this.button_draw(options); } else if(options.flg_mouse !== null){ options.flg_mouse = null; this.button_draw(options); } if(options.flg_mouse === "down" || options.flg_mouse === "up"){ // options.flg_mouse = null; setTimeout((function(options){options.flg_mouse = null;}).bind(this , options) , 100); } }; MAIN.prototype.event_button_click = function(e,options){ var canvas = e.currentTarget; var x = e.clientX - canvas.getBoundingClientRect().left; var y = e.clientY - canvas.getBoundingClientRect().top; // buttonエリア内 if(options.x <= x && x <= options.x + options.width && options.y <= y && y <= options.y + options.height && options.click){ options.click(e , {x:x,y:y}); } }; // ----- // Image var __options_image = { src : null, x : 0, y : 0, width : 0, height : 0 }; MAIN.prototype.view = function(options){ if(!this.canvas){return;} if(!options){return;} options = new $$gamekit_common().setOption(__options_image , options); options = this.dialogOptions_adjust_image(options); var img = new Image(); img.src = options.src; img.onload = (function(e){ var img = e.target; var ctx = this.canvas.getContext("2d"); ctx.drawImage(img , options.x, options.y ,options.w , options.h); }).bind(this); }; MAIN.prototype.dialogOptions_adjust_image = function(options){ if(!this.canvas){return;} var canvas = this.canvas; // サイズ処理 options.width = this.px_per2num(options.width , canvas.offsetWidth); options.height = this.px_per2num(options.height , canvas.offsetHeight); options.x = this.px_per2num(options.x , canvas.offsetWidth); options.y = this.px_per2num(options.y , canvas.offsetWidth); return options; }; console.log("- lib draw : loaded"); return MAIN; })(window,document,navigator);

本日のまとめ

プログラムを起動した際に、ゲームタイトルダイアログを表示し、画面のどこかをクリックすると、ゲームスタートする仕様にしました。 あと、ボールが、画面下に落ちた場合に、ゲームオーバーダイアログを表示ます。 ゲームオーバー・ダイアログは、画面クリックをすると、何度でもゲーム再開できるようになっているので、無限に遊ぶことができます。 それから、カーソルを使ってラケットを移動するのが、非常にトロかったので、ロジックを作り直してスムーズにしました。 ゲームとしては、単純ですが、これで完成ですが、このあとブロック崩し要素を追加したいので、次回は、リファクタリングを行ってコードをスッキリさせてみますね。 お楽しみに。 全体のソースは、githubにpushしているので、そちらから取得してください。 https://github.com/yugeta/game_block

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。