[Javascript] パックマンをHTMLで作ってみるブログ#9 エサを食べるモード

2023年7月1日

Javascript

eyecatch パックマンゲームの黄色いドットって、あれ食べてるからエサなんですかね? とりあえず、wikipediaにエサとパワーエサという単語で書かれていたので、エサと呼ぶことにします。 参考 : wikipedia「パックマン」

今回の目的

- エサと自キャラが重なったら、エサを非表示にする。

対象ファイル一覧

  1. js/feed.js
  2. js/frame.js
  3. js/pacman.js

ソースコード

[新規追加] js/feed.js

import { Frame } from './frame.js' import { Pacman } from './pacman.js' export class Feed{ static move_map(){ const num = Frame.get_pos2num(Pacman.coodinates) const item = Frame.frame_datas[num] switch(item){ case 'P1': this.eat_normal_dot(num) break case 'P2': this.eat_big_dot(num) break } } static eat_normal_dot(num){ Frame.frame_datas[num] = 'S5' const elm = Frame.get_elm(num) if(!elm){return} elm.setAttribute('class','S5') } static eat_big_dot(num){ Frame.frame_datas[num] = 'S5' const elm = Frame.get_elm(num) if(!elm){return} elm.setAttribute('class','S5') } }

[一部更新] js/frame.js

下記のコードの赤字の箇所を追記してください。 ※2箇所ありますよ。 export class Frame{ constructor(){ return new Promise(resolve => { this.resolve = resolve Frame.stage_datas = this.stage_datas = [] this.load_asset() }) } static get root(){ return document.querySelector(`.frame-area`) } get block_size(){ return Frame.block_size } static get block_size(){ const s5 = document.querySelector('.S5') return s5.offsetWidth } get cols_count(){ return ~~(Frame.root.offsetWidth / Frame.block_size) } static get_elm(num){ return Frame.root.querySelector(`[data-num='${num}']`) } load_asset(){ const xhr = new XMLHttpRequest() xhr.open('get' , `assets/frame.json` , true) xhr.setRequestHeader('Content-Type', 'text/html'); xhr.onreadystatechange = ((e) => { if(xhr.readyState !== XMLHttpRequest.DONE){return} if(xhr.status === 404){return} if (xhr.status === 200) { Frame.frame_datas =this.frame_datas = JSON.parse(e.target.response) this.view() this.set_collision() this.finish() } }).bind(this) xhr.send() } view(){ for(let i=0; i<this.frame_datas.length; i++){ const p = document.createElement('p') p.className = this.frame_datas[i] Frame.root.appendChild(p) p.setAttribute('data-num' , i) } } finish(){ if(this.resolve){ this.resolve(this) } } static put(elm , coodinates){ if(!elm){return} const pos = this.calc_coodinates2position(coodinates) this.pos(elm , pos) elm.setAttribute('data-x' , coodinates.x) elm.setAttribute('data-y' , coodinates.y) } static calc_coodinates2position(coodinates){ const size = Frame.block_size return { x : (coodinates.x) * size, y : (coodinates.y) * size, } } static pos(elm , pos){ elm.style.setProperty('left' , `${pos.x}px` , '') elm.style.setProperty('top' , `${pos.y}px` , '') } // 壁座標に1を設置 set_collision(){ const cols_count = this.cols_count const maps = [] let row_count = 0 for(const frame_data of this.frame_datas){ maps[row_count] = maps[row_count] || [] // 移動できる if(frame_data.match(/^P/i) || frame_data.toUpperCase() === 'S5'){ maps[row_count].push(0) } // 壁 else{ maps[row_count].push(1) } if(maps[row_count].length === cols_count){ row_count++ } } Frame.map = this.map = maps } static is_collision(map){ return Frame.map[map.y][map.x] } static get_pos2num(pos){ return pos.y * Frame.map[0].length + pos.x } static get_num2pos(num){ return { x : num % Frame.map[0].length, y : ~~(num / Frame.map[0].length), } } static is_warp(map){ const num = Frame.get_pos2num(map) return Frame.frame_datas[num] === 'W1' ? true : false } static get_another_warp_pos(map){ const warp_index_arr = Frame.filterIndex(Frame.frame_datas , 'W1') const current_index = Frame.get_pos2num(map) const another_num = warp_index_arr.find(e => e !== current_index) return Frame.get_num2pos(another_num) } static filterIndex(datas,target){ const res_arr = [] for(let i=0; i<datas.length; i++){ if(datas[i] === target){ res_arr.push(i) } } return res_arr } }

[一部追加] js/pacman.js

下記の赤字の箇所を追加してください。 feed.jsのモジュール処理を、自機のモーション切り替えのタイミングで行っています。 ※これも2箇所あります。 import { Frame } from './frame.js' import { Control } from './control.js' import { Feed } from './feed.js' export class Pacman{ // 初期表示座標処理 constructor(){ Pacman.anim_speed = 300 Pacman.coodinates = this.start_coodinates Frame.put(this.elm, Pacman.coodinates) this.elm.style.setProperty('--anim-speed' , `${Pacman.anim_speed}ms` , '') } get start_coodinates(){ return { x : 14, y : 23, } } get elm(){ return Pacman.elm } static get elm(){ return document.querySelector('.pacman') } static move(direction){ if(Pacman.direction){ return } Pacman.direction = direction this.elm.setAttribute('data-anim' , "true") this.moving() } static moving(){ let next_pos = Pacman.next_pos(Pacman.direction) //warp if(Frame.is_warp(next_pos)){ Pacman.coodinates = Frame.get_another_warp_pos(next_pos) next_pos = Pacman.next_pos(Pacman.direction) } if(Frame.is_collision(next_pos)){ this.elm.setAttribute('data-anim' , "") delete Pacman.direction return } this.elm.setAttribute('data-direction' , Pacman.direction) this.elm.animate( [ { left : `${Pacman.coodinates.x * Frame.block_size}px`, top : `${Pacman.coodinates.y * Frame.block_size}px`, }, { left : `${next_pos.x * Frame.block_size}px`, top : `${next_pos.y * Frame.block_size}px`, } ], { duration: Pacman.anim_speed } ) Promise.all(this.elm.getAnimations().map(e => e.finished)).then(()=>{ Pacman.moved(next_pos) }) } static moved(next_pos){ Pacman.coodinates = next_pos Frame.put(this.elm, Pacman.coodinates) Feed.move_map() if(Control.direction && Control.direction !== Pacman.direction){ const temp_pos = Pacman.next_pos(Control.direction) if(!Frame.is_collision(temp_pos)){ Pacman.direction = Control.direction } } Pacman.moving() } static next_pos(name){ const next_pos = { x : Pacman.coodinates.x, y : Pacman.coodinates.y, } switch(name){ case 'left': next_pos.x -= 1 break case 'right': next_pos.x += 1 break case 'up': next_pos.y -= 1 break case 'down': next_pos.y += 1 break default: return } return next_pos } }

画面キャプチャ

解説

今回は, feed.jsを追加して、frame.jsとpacman.jsに組み込みました。 ステージ記号のP1(エサ)とP2(パワーエサ)を自キャラが動く座標から判別して、同じ座標になった場合に、S5(空欄)に書き換えています。 P1とP2でそれぞれ同じ処理が書かれた関数に分けているのは、今後2つの関数で違った処理をするかもしれないな〜と思っているだけで、今の時点では、同じ関数を処理してもいいかもしれません。 でもまあ、とりあえず、エサをパクパク食べるゲームの醍醐味ができて、より完成に近づきました。

知財

パックマンは、バンダイナムコ社の登録商標です。 PAC-MAN™ & ©1980 BANDAI NAMCO Entertainment Inc.

このブログを検索

ごあいさつ

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