あるプログラミング実況YouTuberのかたがパックマンを作っていたので、つい勢いで「あなたがC言語 コンソールでつくるなら私はJavaScriptでつくる」と宣言してしまいました。
宣言から3日も経過してしまったのでとりあえず公開することにしました。
⇒ 動作確認はこちらからどうぞ。
現状の問題点。
モンスターの追跡アルゴリズムがいい加減すぎる。乱数で適当に進路変更させているだけ。
パックマンに撃退されたモンスターは目玉だけになって巣に戻るが、その部分が実装できていない。
パックマンがモンスターを食べた場合、獲得点数が200点、400点、800点、1600点と表示されるが、その部分ができていない。
他にも欲を言い出すとフルーツを出すとかいろいろあるのですが、今後改良していきます。
これ以上、なにも書かないのではコンテンツ不足なので、迷路の部分だけ書きます。
これはパックマン実況プレイの動画をキャプチャしたものです。
これをもとに迷路を再現します。餌が置かれている間隔を1とし、これを適当な定数をかけてある程度の大きさで描画できるようにします。背景を黒で塗りつぶし、最初に太くて青い線を引き、そのあと少し細くて黒い線を引けば、輪郭部分だけが青く残ります。
イメージ的には青いテープを貼り付けて、そのあとそのうえに黒いテープを貼るという感じですが、角の部分が欠けてしまわないように少し外側にずらします。
どこからどこまで直線を描画するかですが、これはキャプチャした画像から調べます(けっこう大変かも)。
とりあえず今回つかうグローバル変数です。Main関数が呼ばれたらプログラミングスタートです。
1 2 3 4 5 6 7 8 9 10 |
let can: HTMLCanvasElement = null; let con: CanvasRenderingContext2D = null; // 迷路を表示する場所 const offsetX = 40; const offsetY = 60; const magnification = 18.0; Main(); |
Main関数のなかでCanvasやContextを取得して0.01秒ごとに再描画をさせます。
1 2 3 4 5 6 7 8 9 10 |
function Main() { can = <HTMLCanvasElement>document.getElementById('can'); con = can.getContext("2d"); can.width = offsetX * 2 + magnification * 25; // 餌は横は25、縦は28配置されている can.height = offsetY * 2 + magnification * 28; can.style.border = "1px solid #111"; setInterval(Draw, 10); // 0.01秒ごとに再描画をする } |
今回は迷路部分だけです。
1 2 3 |
function Draw() { DrawMazes(); // 他にも描画するものがあるが、いまは通路だけ表示する } |
迷路を描画するためのDrawMazes関数を示します。
青で太い線を描画して、そのあと黒で一回り細い線を描画することで、青い部分を壁のように見せかけます。線の太さはDrawAisle関数とDrawWall関数にあるように35ピクセルと30ピクセルです。DrawWalls関数とDrawAisles関数のなかで適切な座標を指定してDrawWall関数とDrawAisle関数を呼び出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
function DrawMazes() { con.fillStyle = "black"; con.fillRect(0, 0, can.width, can.height); DrawWalls(); DrawAisles(); } function DrawAisle(x1, y1, x2, y2) { con.strokeStyle = "black"; con.lineWidth = 30; // 横に直線を引くときは幅を長めに、縦に描画するときは高さを長めにする if (y1 == y2) { x1 -= 0.8; x2 += 0.8; } if (x1 == x2) { y1 -= 0.8; y2 += 0.8; } con.beginPath(); con.moveTo(x1 * magnification + offsetX, y1 * magnification + offsetY); con.lineTo(x2 * magnification + offsetX, y2 * magnification + offsetY); con.stroke(); } function DrawWall(x1, y1, x2, y2) { con.strokeStyle = "blue"; con.lineWidth = 35; // 横に直線を引くときは幅を長めに、縦に描画するときは高さを長めにする if (y1 == y2) { x1 -= 0.8; x2 += 0.8; } if (x1 == x2) { y1 -= 0.8; y2 += 0.8; } con.beginPath(); con.moveTo(x1 * magnification + offsetX, y1 * magnification + offsetY); con.lineTo(x2 * magnification + offsetX, y2 * magnification + offsetY); con.stroke(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
function DrawAisles() { DrawAisle(0, 0, 11, 0); DrawAisle(0, 4, 25, 4); DrawAisle(0, 7, 5, 7); DrawAisle(11, 0, 11, 4); DrawAisle(0, 0, 0, 7); DrawAisle(5, 0, 5, 25); DrawAisle(0, 7, 5, 7); DrawAisle(0, 13, 8, 13); DrawAisle(14, 0, 25, 0); DrawAisle(14, 4, 25, 4); DrawAisle(20, 7, 25, 7); DrawAisle(25, 0, 25, 7); DrawAisle(20, 0, 20, 25); DrawAisle(14, 0, 14, 4); DrawAisle(8, 4, 8, 7); DrawAisle(17, 4, 17, 7); DrawAisle(8, 7, 11, 7); DrawAisle(14, 7, 17, 7); DrawAisle(8, 7, 11, 7); DrawAisle(14, 7, 17, 7); DrawAisle(11, 7, 11, 10); DrawAisle(14, 7, 14, 10); DrawAisle(8, 10, 17, 10); DrawAisle(8, 16, 17, 16); DrawAisle(8, 10, 8, 19); DrawAisle(17, 10, 17, 19); DrawAisle(17, 13, 25, 13); DrawAisle(0, 19, 11, 19); DrawAisle(14, 19, 25, 19); DrawAisle(0, 19, 0, 22); DrawAisle(11, 19, 11, 22); DrawAisle(14, 19, 14, 22); DrawAisle(25, 19, 25, 22); DrawAisle(5, 22, 20, 22); DrawAisle(0, 22, 2, 22); DrawAisle(23, 22, 25, 22); DrawAisle(2, 22, 2, 25); DrawAisle(8, 22, 8, 25); DrawAisle(17, 22, 17, 25); DrawAisle(23, 22, 23, 25); DrawAisle(0, 25, 5, 25); DrawAisle(8, 25, 11, 25); DrawAisle(14, 25, 17, 25); DrawAisle(20, 25, 25, 25); DrawAisle(0, 25, 0, 28); DrawAisle(11, 25, 11, 28); DrawAisle(14, 25, 14, 28); DrawAisle(25, 25, 25, 28); DrawAisle(0, 28, 25, 28); } function DrawWalls() { DrawWall(0, 0, 11, 0); DrawWall(0, 4, 25, 4); DrawWall(0, 7, 5, 7); DrawWall(11, 0, 11, 4); DrawWall(0, 0, 0, 7); DrawWall(5, 0, 5, 25); DrawWall(0, 7, 5, 7); DrawWall(0, 13, 8, 13); DrawWall(14, 0, 25, 0); DrawWall(20, 7, 25, 7); DrawWall(25, 0, 25, 7); DrawWall(20, 0, 20, 25); DrawWall(14, 0, 14, 4); DrawWall(8, 4, 8, 7); DrawWall(17, 4, 17, 7); DrawWall(8, 7, 11, 7); DrawWall(14, 7, 17, 7); DrawWall(8, 7, 11, 7); DrawWall(14, 7, 17, 7); DrawWall(11, 7, 11, 10); DrawWall(14, 7, 14, 10); DrawWall(8, 10, 17, 10); DrawWall(8, 16, 17, 16); DrawWall(8, 10, 8, 19); DrawWall(17, 10, 17, 19); DrawWall(17, 13, 25, 13); DrawWall(0, 19, 11, 19); DrawWall(14, 19, 25, 19); DrawWall(0, 19, 0, 22); DrawWall(11, 19, 11, 22); DrawWall(14, 19, 14, 22); DrawWall(25, 19, 25, 22); DrawWall(5, 22, 20, 22); DrawWall(0, 22, 2, 22); DrawWall(23, 22, 25, 22); DrawWall(2, 22, 2, 25); DrawWall(8, 22, 8, 25); DrawWall(17, 22, 17, 25); DrawWall(23, 22, 23, 25); DrawWall(0, 25, 5, 25); DrawWall(8, 25, 11, 25); DrawWall(14, 25, 17, 25); DrawWall(20, 25, 25, 25); DrawWall(0, 25, 0, 28); DrawWall(11, 25, 11, 28); DrawWall(14, 25, 14, 28); DrawWall(25, 25, 25, 28); DrawWall(0, 28, 25, 28); } |
これで迷路部分の描画にかんしては完成です。
http://anonimo0611.web.fc2.com/PACMAN/
同じく館長に感化されてはや10ヶ月
パックマンしか作れない私がつくり続けてるパックマン
それまでAC版も移植版も遊んだことはなく
知名度の高さからキャラの存在だけ知ってる状態
ES5までの知識しかなくゲームなど作る気はなかった
ふるい仕様では不便かつ非効率なのでES6を学習
CSSとDOM操作のほうが簡単なのでcanvasは不使用
先日、お問い合わせを送ったのですが届いてますか…
あのにもさん、コメントありがとうございます。
リンク先みました。めっちゃ完成度高いですやん。
あと先日のお問い合わせも確認しました。
たしかにhttps://lets-csharp.com/monster-go-home/のやりかたはよくないですね。
ちょっと時間をみつけて書き直そうかと・・・
第一の難関は迷路の描画
Canvas APIで忠実に再現するのは
私には難しくてムリなので1枚絵の画像(PNG)で描きました
第二の難関は移動速度 60fpsで原作を再現するばあい
8×8のタイルで実装してビットシフトを使った
固定小数点での細かい速度調整が必要らしい
不勉強なのでやりかたがよくわからず挫折しました
CSSのアニメと移動はGPUが良きに計らって
レイヤー化するので再描画も更新の同期も不要で
座標を変えるだけで簡単に移動可能です
キャラごとにミリ秒単位で個別に動かせます
第三の難関は滑らかなスケーリング
PCブラウザが対象だからウィンドウサイズに追随したい
CSSの画像スケーリングは綺麗だけどCanvasの拡縮は汚い
様々なサイズの画像を用意しなさいってことらしい
SVGやCanvas APIでスプライトを描くのは難しくてムリ
知見のあるウェブ技術の応用でゲームを作ったので
まったくつぶしが効きません Chromeが優秀なのです