スクリプト/Rmakeで自由なゲームをつくろう / 第03回 キャラクターをキー入力で操作する
最終投稿者: aoihikawa
更新:2011/08/11 23:05:58
Rmakeで自由なゲームをつくろう
第03回 キャラクターをキー入力で操作する
こんにちは。
フリーデザイナープログラマー(自称)の
簸川 葵(ひかわ あおい)と申します。
第02回 ではキャラクターの表示までのお話でした。
第03回はそれを踏まえて、画面に表示したキャラクターを
キー入力で操作するところまで、やっていきますね。
03-01 前回までのおさらいと、スクリプトの整理
さて、前回の最後に完成したスクリプトですが、
#メニュー項目等の表示OFF setMenuItemVisible(getMenuBackLog(), false) setMenuItemVisible(getMenuSave(), false) setMenuItemVisible(getMenuLoad(), false) setHelpVisible(false) #キャンバスの初期化 setCanvasVisible(false) deleteAllSprite() drawCanvas() #キャラクター画像の設定 img_char_no = 76793 img_char = createSprite(img_char_no) get_x = 0 get_y = 0 get_w = 64 get_h = 64 set_x = 0 set_y = 0 set_w = 64 set_h = 64 set_z = 7 pos_x = 368 pos_y = 500 setSpriteRect(img_char, get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h) setSpriteZOrder(img_char, set_z) setSpritePosition(img_char, pos_x, pos_y) #背景画像の設定 img_bg_no = 76800 img_bg = createSprite(img_bg_no) get_x = 0 get_y = 0 get_w = 800 get_h = 600 set_x = 0 set_y = 0 set_w = 800 set_h = 600 set_z = 1 pos_x = 0 pos_y = 0 setSpriteRect(img_bg, get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h) setSpriteZOrder(img_bg, set_z) setSpritePosition(img_bg, pos_x, pos_y) #画面の更新 drawCanvas() setCanvasVisible(true)
と、とても縦に長いですね。
キャラクターと背景の設定も似たようなところが多くありますし、
今後、敵キャラクターなどの追加でさらに長くなると
つくりかけの場所を探すのも大変です。
そこで、まずはこのスクリプトをすっきりと整理しましょう。
#メニュー項目等の表示OFF setMenuItemVisible(getMenuBackLog(), false) setMenuItemVisible(getMenuSave(), false) setMenuItemVisible(getMenuLoad(), false) setHelpVisible(false) #キャンバスの初期化 setCanvasVisible(false) deleteAllSprite() drawCanvas() #_/_/_/_/_/_/_/_/_/_/_/_/ 変更分 _/_/_/_/_/_/_/_/_/_/_/_/ #画像を設定する関数 def setCreateSprite(img_name, img_no, get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h, set_z, pos_x, pos_y) setVariable(img_name, createSprite(img_no)) setSpriteRect(getVariable(img_name), get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h) setSpriteZOrder(getVariable(img_name), set_z) setSpritePosition(getVariable(img_name), pos_x, pos_y) end #キャラクター画像の設定 img_no = 76793 img_char_name = "img_char" get_x = 0; get_y = 0; get_w = 64; get_h = 64 set_x = 0; set_y = 0; set_w = 64; set_h = 64 set_z = 7 pos_char_x = 368; pos_char_y = 500 setCreateSprite(img_char_name, img_no, get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h, set_z, pos_char_x, pos_char_y) #背景画像の設定 img_no = 76800 img_bg_name = "img_bg" get_x = 0; get_y = 0; get_w = 800; get_h = 600 set_x = 0; set_y = 0; set_w = 800; set_h = 600 set_z = 1 pos_bg_x = 0; pos_bg_y = 0 setCreateSprite(img_bg_name, img_no, get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h, set_z, pos_bg_x, pos_bg_y) #_/_/_/_/_/_/_/_/_/_/_/_/ここまで_/_/_/_/_/_/_/_/_/_/_/_/ #画面の更新 drawCanvas() setCanvasVisible(true)
いかがでしょうか。
かなりすっきりとしましたが、動作する内容は前回と全く同じです。
それでは、どのように変更したのか、1つずつ説明していきます。
1つめテクニックは「,」と「;」です。
文の最後が関数の途中の「,」だったとき、
スクリプトでは改行された次の行も同じ行として判断されます。
#次の2つは同一 setCreateSprite(img_name, img_no, get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h, set_z, pos_char_x, pos_char_y) #-------------------------------------------------- setCreateSprite(img_name, img_no, get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h, set_z, pos_char_x, pos_char_y)
また、文の途中に「;」があったとき、
スクリプトではそこで次の行として判断されます。
#次の2つは同一 get_x = 0 get_y = 0 get_w = 800 get_h = 600 set_x = 0 set_y = 0 set_w = 800 set_h = 600 #-------------------------------------------------- get_x = 0; get_y = 0; get_w = 800; get_h = 600 set_x = 0; set_y = 0; set_w = 800; set_h = 600
引数が多くて長すぎる関数は、見た目を短く、
似たような値の代入だけという短い変数は、見た目を纏めてセットに
することができるのです。
さて、2つめのテクニックは「自作の関数」をつくることです。
似ているところを1つの関数として纏めてしまうわけです。
def文で、setCreateSpriteという名前の関数を作っています。
#画像を設定する関数 def setCreateSprite(img_name, img_no, get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h, set_z, pos_x, pos_y) setVariable(img_name, createSprite(img_no)) setSpriteRect(getVariable(img_name), get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h) setSpriteZOrder(getVariable(img_name), set_z) setSpritePosition(getVariable(img_name), pos_x, pos_y) end
この関数の中で、前回のものとは異なっているところとして、
setVariableとgetVariableという関数が追加されています。
実は、変数は通常の「x = 1」と書かれているだけの場合、
その変数がつくられた関数や、チャプターの中だけでしか使うことが出来ません。
関数や、チャプターの終了時に消えてしまうのです。
これを「ローカル変数」と呼びます。
これでは、折角関数でスクリプトデータを作っても、関数の外では利用できないのです。
そこで、先ほどの2つの関数の登場です。
「setVariable("suuji", 1)」は変数への値の代入である「suuji = 1」、
「getVariable("suuji")」は変数からの値の取得である「suuji」と同じ意味で
「通常変数」を生成してくれます。
この「通常変数」は「ローカル変数」と異なり、同じゲーム内であれば
関数や、チャプターの外でも消えずに残ります。
ただし、通常変数とローカル変数で同じ名前を設定していても
別の変数として扱われることに注意してください。
つまり、この関数の中身は、というと、
「setVariable(img_name, createSprite(img_no))」は「img_name = createSprite(img_no)」
と、変数の形が異なるだけの同じ意味なので、
createSprite、setSpriteRect、setSpriteZOrder、setSpritePositionという
スプライトの設定に必要な関数をセットで実行してくれます。
設定をするときに必要となる値を、関数の引数として受け取ることによって、
異なる画像のや値の場合でも、この関数で実行できるようになっています。
03-02 ゲームのメインループをつくろう
第01回で少しお話に出てきましたが、
ゲームとは、「キーの入力→さまざまな判定→描画→はじめに戻る」、
の繰り返しので出来ています。
この固定された大きな循環を「メインループ」と呼びます。
先ほどの途中からスクリプトの続きを描きますね。
#ここから↑は省略 #画面の更新 drawCanvas() setCanvasVisible(true) #_/_/_/_/_/_/_/_/_/_/_/_/ 追加分 _/_/_/_/_/_/_/_/_/_/_/_/ startInput() #入力受付の開始 #メインループの開始 mainloop = true while mainloop #キー入力の判定 while hasInput() takeInput() #----- キー入力の判定処理を入れる場所 ----- end #----- ゲームのメイン処理を入れる場所 ----- #(ゲームの終了時はmainloopをfalseに) #----- 画面の更新 ----- drawCanvas() waitTime(30) end endInput() #入力受付の終了 goEnding() #ゲームのクリア #_/_/_/_/_/_/_/_/_/_/_/_/ここまで_/_/_/_/_/_/_/_/_/_/_/_/
これがメインループの枠組みです。
それでは、新しく登場した関数と命令文を上から順に見ていきましょう。
startInputはキーやマウスの入力の取得を開始させる関数です。
最後の辺りにあるendInputとセットになっており、
こちらはキーやマウスの入力の取得を終了する関数です。
この2つの関数で囲まれている間、スクリプトが押されたキーを覚えてくれます。
命令文のwhile文が登場しました。
よく見ると、条件文のところが変数だけになっています。
これは、この変数「mainloop」自体が、
スイッチと同様な true か false になっているためです。
つまり、変数がtrueになっている間、ずっとこの中を繰り返します。
再びwhile文です。
今度は条件式に関数が入っています。
これはこの関数が、 true か false を返す関数のため、使用できる省略方法です。
#次の2つは同一 InputFlg = hasInput() while InputFlg takeInput() end #-------------------------------------------------- while hasInput() takeInput() end
hasInputは覚えているキーやマウスの入力があったかどうか調べる関数です。
そして、takeInputは覚えているキーやマウスの入力を取り出す関数です。
つまり、この2つの関数のセットとwhile文で、
覚えているキーやマウスの入力を、覚えている分だけ取り出すという動作になります。
waitTimeは引数のミリ秒(1/1000秒)だけ、何もせずに待つ関数です。
今回は「30」が設定されているため、1回あたりのループを、
約0.03秒毎に実行してください。という意味になります。
「約」とつけたのは、この待っている関数以外のところで、
他の関数などを実行している時間があるためです。
最後に、goEnding。これは、エンディング画面に移動する関数です。
現在のところ、メインループから抜けることがないため、
エンディング画面に移動することはないのですが、形式的なもので先に書いておきました。
03-03 キー入力を受けとろう
先ほどのメインループに、キーが押され時にON、離された時にOFFとなる
仕組みを入れていきます。
ONとOFFをスクリプトで扱うときは、trueとfalseでしたね。
まずは、使用するスイッチの変数を準備します。
#ここから↑は省略 #_/_/_/_/_/_/_/_/_/_/_/_/ 追加分 _/_/_/_/_/_/_/_/_/_/_/_/ #キー入力フラグの設定 key_flg_left = false key_flg_right = false key_flg_up = false key_flg_down = false key_flg_z = false #_/_/_/_/_/_/_/_/_/_/_/_/ここまで_/_/_/_/_/_/_/_/_/_/_/_/ #画面の更新 drawCanvas() setCanvasVisible(true) startInput() #入力受付の開始 #メインループの開始 mainloop = true while mainloop #キー入力の判定 while hasInput() takeInput() #----- キー入力の判定処理を入れる場所 ----- end #----- ゲームのメイン処理を入れる場所 ----- #(ゲームの終了時はmainloopをfalseに) #----- 画面の更新 ----- drawCanvas() waitTime(30) end endInput() #入力受付の終了 goEnding() #ゲームのクリア
カーソルの4つのキーと、Zキーの全部で5つの変数を準備しました。
次は、実際にこの変数のスイッチを、
キー入力によって入れ替えるスクリプトを書きます。
#ここから↑は省略 #キー入力フラグの設定 key_flg_left = false key_flg_right = false key_flg_up = false key_flg_down = false key_flg_z = false #画面の更新 drawCanvas() setCanvasVisible(true) startInput() #入力受付の開始 #メインループの開始 mainloop = true while mainloop #キー入力の判定 while hasInput() takeInput() #----- キー入力の判定処理を入れる場所 ----- #_/_/_/_/_/_/_/_/_/_/_/_/ 追加分 _/_/_/_/_/_/_/_/_/_/_/_/ if isKeyDown("LEFT") key_flg_left = true elsif isKeyUp("LEFT") key_flg_left = false elsif isKeyDown("RIGHT") key_flg_right = true elsif isKeyUp("RIGHT") key_flg_right = false elsif isKeyDown("UP") key_flg_up = true elsif isKeyUp("UP") key_flg_up = false elsif isKeyDown("DOWN") key_flg_down = true elsif isKeyUp("DOWN") key_flg_down = false elsif isKeyDown("Z") key_flg_z = true elsif isKeyUp("Z") key_flg_z = false end #_/_/_/_/_/_/_/_/_/_/_/_/ここまで_/_/_/_/_/_/_/_/_/_/_/_/ end #----- ゲームのメイン処理を入れる場所 ----- #(ゲームの終了時はmainloopをfalseに) #----- 画面の更新 ----- drawCanvas() waitTime(30) end endInput() #入力受付の終了 goEnding() #ゲームのクリア
isKeyDownはキーが押されたときかどうかを判断する関数、
isKeyUpはキーが離されたときかどうかを判断する関数です。
これを命令文のif文で分岐することによって、
どの変数をどう変更するか動作を分岐させています。
03-04 キャラクターを動かそう
いよいよキャラクターを動かします。
キャラクターが動く、というのはスプライトの表示座標が動くことです。
横方向はx座標、縦方向はy座標となっており、
x座標は右に行くほど数値が大きくなり、y座標は下に行くほど数値が大きくなります。
それでは、これをスクリプトで書いてみましょう。
まずは定数の準備から。
#ここから↑は省略 #キー入力フラグの設定 key_flg_left = false key_flg_right = false key_flg_up = false key_flg_down = false key_flg_z = false #_/_/_/_/_/_/_/_/_/_/_/_/ 追加分 _/_/_/_/_/_/_/_/_/_/_/_/ #定数の設定 char_speed = 10 char_w_max = 800 - 64 char_h_max = 600 - 64 #_/_/_/_/_/_/_/_/_/_/_/_/ここまで_/_/_/_/_/_/_/_/_/_/_/_/ #画面の更新 drawCanvas() setCanvasVisible(true) startInput() #入力受付の開始 #メインループの開始 mainloop = true while mainloop #キー入力の判定 while hasInput() takeInput() #----- キー入力の判定処理を入れる場所 ----- if isKeyDown("LEFT") key_flg_left = true elsif isKeyUp("LEFT") key_flg_left = false elsif isKeyDown("RIGHT") key_flg_right = true elsif isKeyUp("RIGHT") key_flg_right = false elsif isKeyDown("UP") key_flg_up = true elsif isKeyUp("UP") key_flg_up = false elsif isKeyDown("DOWN") key_flg_down = true elsif isKeyUp("DOWN") key_flg_down = false elsif isKeyDown("Z") key_flg_z = true elsif isKeyUp("Z") key_flg_z = false end end #----- ゲームのメイン処理を入れる場所 ----- #(ゲームの終了時はmainloopをfalseに) #----- 画面の更新 ----- drawCanvas() waitTime(30) end endInput() #入力受付の終了 goEnding() #ゲームのクリア
定数という言葉が出てきましたが、定数とは、
ゲームが終了するまで、最初に入れた値が変化することがない変数のことです。
使い方は変数と変わりません。
char_speedは1回辺りの移動量、char_w_maxは横方向の最大値、char_h_maxは縦方向の最大値が
入れてあります。
各最大値については、キャラクターの座標の基準点が左上なので、
キャラクターの高さと横幅である「64」という数値を、全体のサイズから引いています。
次に、キャラクターを移動させるスクリプトを書きます。
#ここから↑は省略 #キー入力フラグの設定 key_flg_left = false key_flg_right = false key_flg_up = false key_flg_down = false key_flg_z = false #定数の設定 char_speed = 10 char_w_max = 800 - 64 char_h_max = 600 - 64 #画面の更新 drawCanvas() setCanvasVisible(true) startInput() #入力受付の開始 #メインループの開始 mainloop = true while mainloop #キー入力の判定 while hasInput() takeInput() #----- キー入力の判定処理を入れる場所 ----- if isKeyDown("LEFT") key_flg_left = true elsif isKeyUp("LEFT") key_flg_left = false elsif isKeyDown("RIGHT") key_flg_right = true elsif isKeyUp("RIGHT") key_flg_right = false elsif isKeyDown("UP") key_flg_up = true elsif isKeyUp("UP") key_flg_up = false elsif isKeyDown("DOWN") key_flg_down = true elsif isKeyUp("DOWN") key_flg_down = false elsif isKeyDown("Z") key_flg_z = true elsif isKeyUp("Z") key_flg_z = false end end #----- ゲームのメイン処理を入れる場所 ----- #(ゲームの終了時はmainloopをfalseに) #_/_/_/_/_/_/_/_/_/_/_/_/ 追加分 _/_/_/_/_/_/_/_/_/_/_/_/ #----- キャラクターの移動 ----- if key_flg_left #左 pos_char_x = pos_char_x - char_speed if pos_char_x < 0 pos_char_x = 0 end setSpritePosition(getVariable(img_char_name), pos_char_x, pos_char_y) elsif key_flg_right #右 pos_char_x = pos_char_x + char_speed if pos_char_x > char_w_max pos_char_x = char_w_max end setSpritePosition(getVariable(img_char_name), pos_char_x, pos_char_y) end if key_flg_up #上 pos_char_y = pos_char_y - char_speed if pos_char_y < 0 pos_char_y = 0 end setSpritePosition(getVariable(img_char_name), pos_char_x, pos_char_y) elsif key_flg_down #下 pos_char_y = pos_char_y + char_speed if pos_char_y > char_h_max pos_char_y = char_h_max end setSpritePosition(getVariable(img_char_name), pos_char_x, pos_char_y) end #_/_/_/_/_/_/_/_/_/_/_/_/ここまで_/_/_/_/_/_/_/_/_/_/_/_/ #----- 画面の更新 ----- drawCanvas() waitTime(30) end endInput() #入力受付の終了 goEnding() #ゲームのクリア
pos_char_x、pos_char_yはキャラクターの画像を設定したときに、
表示する位置の値を入れていた変数ですね。
この変数の値を、char_speedの値だけ加算、減算したあと
再度pos_char_x、pos_char_yに戻しています。
つまり、1回の入力の判定ごとにpos_char_x、pos_char_yがchar_speed分
移動していくようになります。
if文の中にある、もうひとつのif文は、
座標の最小値である「0」、先ほど定数として準備しておいた最大値の座標を
キャラクターが超えてしまったとき、最小値または最大値に戻すための分岐です。
こうすることで、画面外までキャラクターが飛び出してしまうのを防いでいます。
setSpritePositionは第02回に登場した、
キャンバスに表示するときの位置を設定する関数でしたね。
スプライトデータは今回一番最初に作成した関数の中ですが、
ローカル変数から通常変数に変更したため、このようにメインループからでも
getVariable(img_char_name)で利用することが出来るようになっています。
これで、一通り完成です。
保存、終了をして、テストプレーを行い、
キー入力によってキャラクターが動くようになったかどうか、確認してみましょう。
03-05 キャラクターにアニメーションをつけよう
これまでの「まとめ」と「復習」も兼ねて、
キャラクターの動きに合わせて、アニメーションをつけてみましょう。
まずは、キャラクターの表示を切り替える関数の準備から。
スクリプトの一番上に戻ります。
#メニュー項目等の表示OFF setMenuItemVisible(getMenuBackLog(), false) setMenuItemVisible(getMenuSave(), false) setMenuItemVisible(getMenuLoad(), false) setHelpVisible(false) #キャンバスの初期化 setCanvasVisible(false) deleteAllSprite() drawCanvas() #画像を設定する関数 def setCreateSprite(img_name, img_no, get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h, set_z, pos_x, pos_y) setVariable(img_name, createSprite(img_no)) setSpriteRect(getVariable(img_name), get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h) setSpriteZOrder(getVariable(img_name), set_z) setSpritePosition(getVariable(img_name), pos_x, pos_y) end #_/_/_/_/_/_/_/_/_/_/_/_/ 追加分 _/_/_/_/_/_/_/_/_/_/_/_/ #キャラクターの表示内容を変更する関数 def setCharSpritePattern(no) img_char_name = "img_char" get_x = 0; get_y = 0; get_w = 64; get_h = 64 set_x = 0; set_y = 0; set_w = 64; set_h = 64 if no == 1 get_x = 0; get_y = 0 elsif no == 2 get_x = 64; get_y = 0 elsif no == 3 get_x = 128; get_y = 0 elsif no == 4 get_x = 0; get_y = 64 elsif no == 5 get_x = 64; get_y = 64 elsif no == 6 get_x = 128; get_y = 64 else get_w = 0; get_h = 0 set_w = 0; set_h = 0 end setSpriteRect(getVariable(img_char_name), get_x, get_y, get_w, get_h, set_x, set_y, set_w, set_h) end #_/_/_/_/_/_/_/_/_/_/_/_/ここまで_/_/_/_/_/_/_/_/_/_/_/_/ #ここから↓は省略
引数の値によって、setSpriteRectの切り出し場所を変更する関数です。
1~6の番号は、次の場所を切り出すように設定されています。
1~6以外の番号を引数として渡された場合、横幅と高さが「0」、
つまり何も表示しない、という実行内容になっています。
次に使用する定数と変数を準備します。
スクリプトの中央辺りです。
#ここから↑は省略 #定数の設定 char_speed = 10 char_w_max = 800 - 64 char_h_max = 600 - 64 #_/_/_/_/_/_/_/_/_/_/_/_/ 追加分 _/_/_/_/_/_/_/_/_/_/_/_/ char_anime_change = 3 #変数の設定 char_anime_count = 0 #_/_/_/_/_/_/_/_/_/_/_/_/ここまで_/_/_/_/_/_/_/_/_/_/_/_/ #画面の更新 drawCanvas() setCanvasVisible(true) #ここから↓は省略
アニメーションをするためのカウンターとなる変数と、
アニメーションを切り替えるポイントとなる値の定数を
設定しています。
最後に、アニメーションカウンターの使用と、
キー入力とアニメーションカウンターの状態から
先ほどの関数を使用して、表示を切り替えます。
スクリプトの一番下へ。
#ここから↑は省略 if key_flg_up #上 pos_char_y = pos_char_y - char_speed if pos_char_y < 0 pos_char_y = 0 end setSpritePosition(getVariable(img_char_name), pos_char_x, pos_char_y) elsif key_flg_down #下 pos_char_y = pos_char_y + char_speed if pos_char_y > char_h_max pos_char_y = char_h_max end setSpritePosition(getVariable(img_char_name), pos_char_x, pos_char_y) end #_/_/_/_/_/_/_/_/_/_/_/_/ 追加分 _/_/_/_/_/_/_/_/_/_/_/_/ #----- キャラクターのアニメーション ----- #アニメーション用カウンター char_anime_count = char_anime_count + 1 if char_anime_count == (char_anime_change * 2) char_anime_count = 0 end #キャラクターの表示を切替 if key_flg_left if char_anime_count < char_anime_change setCharSpritePattern(2) else setCharSpritePattern(5) end elsif key_flg_right if char_anime_count < char_anime_change setCharSpritePattern(3) else setCharSpritePattern(6) end else if char_anime_count < char_anime_change setCharSpritePattern(1) else setCharSpritePattern(4) end end #_/_/_/_/_/_/_/_/_/_/_/_/ここまで_/_/_/_/_/_/_/_/_/_/_/_/ #----- 画面の更新 ----- drawCanvas() waitTime(30) end endInput() #入力受付の終了 goEnding() #ゲームのクリア
アニメーションカウンターのカウント実行と、
切り替えポイントの2倍の値に達したとき、
アニメーションカウンターをリセットする処理が前半部分。
後半部分では、キー入力状態の変数とアニメーションカウンターを
if文で分岐させることで、表示したい画像の番号を
キャラクターの表示内容を変更する関数にセットしています。
スクリプト全体(再び、少し整理しています)
これで、一通り完成です。
保存、終了をして、テストプレーを行い、
キャラクターの動きに合わせて、アニメーションされるように
なったかどうか、確認してみましょう。
03-06 おわりに
いかがでしたでしょうか。
動きとアニメーションがついて、
より、ゲーム画面らしくなってきました。
さて、次回は
「敵キャラクターを登場させる」を実践してみましょう。
第01回 スクリプトって何?
第02回 画面にキャラクターを描画する
第03回 キャラクターをキー入力で操作する
第04回 敵キャラクターを登場させる
第05回 弾を発射させる
第06回 より面白くするために
この記事についてご質問等がありましたら
こちらのブログ記事のコメントへご投稿、
よろしくお願いいたします。
コメントする
コメントするには、ログインする必要があります。
コメント一覧
コメントはありません。