せらぴんブログ

サークル「せらぴん」のうのはな透です。やっぱり眼鏡っ娘が好き!!

積んでるゲーム紹介10:魔壊神トリリオン

www.adventar.org

積んでるゲーム Advent Calendar 10日目です。

折角なのでVita枠その2、本日紹介するのはPSVitaソフト「魔壊神トリリオン」。

このソフトの存在を知るきっかけになったのは、
死亡シーン集動画漁りをしていた時に見つけたトリリオンプレイ動画でした。
このトリリオンとか言うゲーム、聞けばかなりのマゾゲーなようで、
7人の美少女魔王を1人ずつラスボスと戦わせて供物に焼べるゲーム(意訳)とのこと。
「手塩にかけて親愛度まで上げた美少女を死地に送り出すのはどんな気持ちなんだろう」と
気になって気になって仕方なかったのでPSVitaが手に入ったらぜひ買おうと思っていたんですよね。

で、昨日の記事にあるようにPSVitaを手に入れる機会があったので、購入に至ったわけです。

オープニングを見たところで、綿密にドナドナする順を検討し、プレイ開始。
最初の一人はマモン。サボってなければ門番としてラスボスにヌッコロされるはずだったし、順当だよね(
最初は木人にも勝てなかったマモンだが、修行を繰り返してメキメキ強くなる。
トリリオン1戦目は要領を間違えほとんどダメージを与えられないうちに撤退。
さらに修行を繰り返して2戦目はそれなりにダメージが入る。
そして最後の3戦目。最終防衛ラインまで攻撃を繰り返すもののラスボスは健在。
「まだマモン戦えるけど、ライン越えたらどうなるんだろ……?」と思ったけど、
ラスボスの絶対殺すビームを食らってあえなく撃沈。

望むべくして得た絶望感と、これがあと5人分*1味わえるのかという期待感。
このときは思いましたね。「このゲーム、買って正解だった」と。

ただ2人目の白羽の矢をペルペルに立ててから徐々に失速。
このゲーム、個々の魔王に対してやることあんまり変わんないですよね
そこに若干のマンネリ感があるのは否めず。

でも負けない! いつかCVアサミンゴスレヴィアと一緒にトリリオンを打ち倒すまでは!!
――と思っていたけど今HP確認したら1周目は3人目レヴィアたん固定らしい。つらい!!ちくしょう!!!!!

やっぱトリリオンってマゾゲーだよね

*1:最後の1人には流石に勝ってもらわないと…

積んでるゲーム紹介9:絶対迎撃ウォーズ

www.adventar.org

積んでるゲーム Advent Calendar 9日目です。

今日紹介するのは「絶対迎撃ウォーズ」。
もともと「あ~~~~~なんか面白いゲームないの~~~~?」と
PS3Playstation Storeで体験版を漁っていた時に発見したのがこいつの体験版でした。

ゲームとしては「アクション性を伴ったタワーディフェンス」とでも言いましょうか。
砲台の搭載された3連リングをガチャガチャ回して、360度全方角から出てくるモンスターをやっつけていくゲームです。
ゲーム性も気に入りましたが、何より気に入ったのがキャラの一人モナリーちゃん
露出高めのおっぱい眼鏡で、トドメにCV山本彩乃とか破壊力高すぎでしょ!!!

他にも魅力的な眼鏡っ娘が2人もいる*1ということで、
「これは買いたいな!」
駅メモの10連に突っ込むくらいならこっち買うか!!」と
Amazonでポチったんですよ。

で、届いたんですよ。

PSVita版が。

私Vita持ってないんだけど!!


マルチプラットフォームの罠でした……。
もちろんよく確認せずに買った私が悪いのですが、これは辛い。
このままでは本当の意味で積んでしまうところでしたが、
運良くVitaの共同購入話が持ち上がったので、それに乗っかって無事プレイすることが出来ました。

それでも積んでしまうんだよねぇ……。
2体目のボス戦中だったかな、
激戦の結果、タワーのHPギリギリの状態の中、ボスを撃破した。
撃破したと思ったんですよ。
ボスの中から子供がわんさか出てきて対処に遅れてタワーが決壊……。
ラス殺しってあぁいうのを言うんだろうね……。

でもそろそろモナリーちゃんのおっぱい眺めたいから再開したいな(

*1:関係ないけど、キーヨ役の遠藤ゆりかさんは今期だと怪獣娘でウィンダムやってますね

積んでるゲーム紹介8:ゲームボーイウォーズアドバンス1+2

www.adventar.org

積んでるゲーム Advent Calendar 8日目です。

今日紹介するのは「ゲームボーイウォーズアドバンス1+2」。
ファミコンウォーズから連綿と続くウォーシミュのシリーズ作です。
私はGBAは持っていませんが、今ならWiiバーチャルコンソールでプレイできるぞ!!

そう、バーチャルコンソール……。
今回積んでる理由の遠因はそのWiiがある場所なんです。
自宅じゃないんです。

こいつがインストールされたWiiUがあるのは、職場の休憩室なんだ。

社員の福利厚生のために置かれたWiiUの中にインストールされているわけですが、
ただでさえ社員同士でWiiUの奪い合いが発生する*1上に、
業務が終わった*2開放感から休憩室に寄らずに買える確率が99%以上。

同様の理由でスプラトゥーンも最近やってないなぁ。
スプラトゥーンもあれはあれで終わりがないゲームだから“積む”とは……と思ったけどヒーローモード積んでた(

*1:言うほど発生はしてない

*2:さすがに業務時間中には遊べない

積んでるゲーム紹介7:アストロノーカ

www.adventar.org

積んでるゲーム Advent Calendar 7日目(だったはずの記事)です。

今回紹介するのは「アストロノーカ」。
知る人ぞ知る宇宙で野菜育てるSLGです。

詰んだ理由は単純で、バブーに羽が生えたから

いやね、最初は「羽が生えたくらいでそんな詰むわけないでしょ」って
タカをくくってたわけです。
いやね、ホントね、全然太刀打ちできなくなった。
そうなると毎夜毎夜手塩にかけた野菜たちが傷物にされてしまうのを
指をくわえて見てるしかなくなるんですよね。

羽が生えた時の恐ろしさは一回痛い目を見ないとわからないですね……。

というわけで詰んだので積みました、お後がよろしいようで。

Enumerableを信じよ

www.adventar.org

Ruby Game Developing Advent Calendar 2016 8日目の記事です。

昨日はaoitakuさんの「委譲とコンポジション」でした。
Forwardableは実際便利で「パズルっぽいゲーム」でも使ってます。
でもChipmunkは使ってない……あのゲーム物理演算が必要な域に達してないから(震え声)

さて、「Enumerableを信じよ」とは言いましたが、
実際RubyでプログラミングしてればEnumerableは使えて当たり前なんで
何を今更?????と思われてもしょうがないんですが。
一昨日の記事で一部書き忘れたことがあったので、初心者向けも兼ねて書く。

Enumerableとは?

module Enumerable (Ruby 2.3.0)

ArrayとかHashとかRangeとかSetとかの大本。

Enumerableの思想とは?

旧版は、ブロックや自機がどこにいるかという座標情報をfield二次元配列の順序という形で持っている。
新版は、座標情報をブロックや自機の基底クラスPuzzleObject内にPosオブジェクトとして持っている。

どちらがいいかと聞かれたら、そりゃ後者じゃないのかなって。

こういう違いが出るのは、
そもそもRubyのEnumerableは「配列順序を気にしたくないから」なんだろうなぁって思うんです。
もちろんArrayクラスにはeach_indexなりfind_indexなり要素の位置を意識したメソッドはいくつかあります。
ですが、Arrayを使う箇所って、大半は単なるEnumerableの実装として使ってる節があります。

  • Arrayの中には異なるクラスのオブジェクトを突っ込んでよい
  • ArrayはEnumerableを継承しているので、大半の集合操作(map, selectなど)を実行できる

Enumrableの実装の中で「順序を気にしたくない」という欲求を純粋に体現しているのがSetクラスなんですが、
Setクラスは同時に「重複も許さない」んで、使える場面は必ずしも広くない。
というのと、[a, b, c]と書くのと、Set[a, b, c]って書くのだと、ちょっとだけ前者のほうがラク。

このあたりの話をすると、SQLを思い出してしまう。
SQLはorder byを使うまではSetで、order byを使い出すとArrayになるという特性を持つ(持ってない)。


最終的に何が言いたいかって言うと、
Set使えるところはSet使うといいよってところと、
Arrayでも配列順を気にしないようにリファクタリングできるならした方がいいんじゃないかな、ってことかな!


明日はt_tutiyaさんの「rubyゲーム開発にテスティングフレームワークを導入する(1)」です。
rspecとか使うのかな。お楽しみに!

積んでるゲーム紹介6:ロックマンDASH2

www.adventar.org

積んでるゲーム Advent Calendar 6日目です。

今日紹介する「ロックマンDASH2」は割といわくつきの逸品です。
なにせ3回もニューゲームしてそのうち2回も同じダンジョンで詰まったゲームだからです。

最初にやったのは中学生の頃、友人から借りてプレイしました。
でもあるダンジョンで行き詰まって、どうにもいかないうちに期限が来て返してしまったのです。

「あの頃クリアできなかったDASH2を是が非でもやりたい……!!」
そう思って、大学生時代の時、たまたま中古ショップで見つけたPS版DASH2を即買い。
しかし昔やってた頃よりアクションが難しくなっており*1
前回詰んだ箇所まで行かずに積みゲーの墓場行き……。

ぬあぁ~~~~、もう一度やりたい! 今度こそクリアしてやる!!!
と社会人になってからフラストレーションが溜まりはじめたが、肝心のPSがない!!
仕方ないのでPSP版を購入してPSPでプレイ
最初に詰まったダンジョンまでは行き着いたものの、やっぱりどこへ行ったらいいかわからないうちに、PSP自体を紛失……。

と、3戦3敗という燦々たる成績を残しているわけであります。
アクションゲー下手すぎでしょ……。

「じゃあどこの遺跡で詰まったの?」と思うところでしょう。
それはね、

ニーノ島の遺跡だよ!!

あの水中ダンジョンマジでわからんかった……。

*1:人それを「アクションゲーの腕が落ちた」と言う

DxRuby::Spriteの思想を信じよ

www.adventar.org

Ruby Game Developing Advent Calendar 2016 6日目の記事です。

昨日はvivit_jcさんの「DXRubyで作ったシューティングゲームを交えて高校生向けプログラミング講座をしてきた話」でした。
実際あのプログラムを一晩で書いたって聞かされたときは「は?なんだよ天才かよ」って思いました。さすがvivitさんやね!
あと1~2日目の記事を見て「司エンジンむっちゃ面白そうやん……」ってなったので、来年あたり使ってみたい。

本日は拙作「パズルっぽいゲーム」を制作した時の、DxRubyのSpriteの設計に大変助けられたよ、というお話をします。

7年越しの落ちゲー2度目の挑戦

実は「落ちゲー*1を作ろう!」と思ったのは今回が初めてではありません。
思い返せば7年ほど前。自分のPCとVisualStudio、そしてDXライブラリを手に入れて無敵感に舞い上がった私がまず作ろうと思ったのが
「ポーカーゲーム*2」と「落ちゲー」でした。

そして旧版と「パズルっぽいゲーム」を比較した時、より短期間でよりグレードの高いものになっています。
一体どうしてでしょう?

Spriteの仕様にゲームを“合わせる”

それは私のプログラミング能力が向上したから!!
――で済ませてもいいのですが、実際はそうでもないです。

今回「パズルっぽいゲーム」の制作では、(結果論ではありますが)DxRuby::Spriteの以下の3つの特徴にトコトン乗っかる形で設計していきました。

  • Spriteは継承してナンボ
  • 更新したいものはすべてSprite.update()に突っ込む
  • 描写したいものはすべてSprite.draw()に突っ込む

これが功を奏したのでは、と思うんですよね。
しかしこれ、言葉にすればチョー簡単ですよね?

実際に見てみましょう。下記がPuzzleField.rb(一部抜粋)。ゲームの肝となるクラスです。

# パズルフィールドクラス
class PuzzleField
  extend Forwardable
  def_delegators :@fight, :attack

  # パズル中のメインループ処理
  def process
    # アニメーション実行中なら消去/移動は行わない
    unless animating?
      processes = [Proc.new{ fall }, Proc.new{ erase }, Proc.new{ raising }, Proc.new{ move_player }]
      processes.each do |pr|
        # trueが返ってきた(何らかの処理が実行された)ら、そこで打ち止め
        break if pr.call
      end
    end
    update
  end
  
  # 描写のみを行い、内部の更新を一切行わない
  # ポーズ時などに使用
  def draw
    Sprite.draw([puzzle_objects, @field_images, @chain_counter, @score, @level, @damage_counter, @back_image])
  end
  
  # 各種更新処理&描写処理
  def update
    Sprite.update([puzzle_objects, @sounds.values, @erase_sounds, @score, @level, @damage_counter, @cube_factory])
    Sprite.clean(@cubes)
    draw
  end
  
  # キューブとプレイヤーの合算
  def puzzle_objects
    [@cubes, @player].flatten
  end

注目すべきはupdateメソッドとdrawメソッド。
ここに突っ込んでるのはすべてSpriteを継承した各種画像オブジェクトです。
updateで座標やらを更新して、drawで書き出す。もうチョー簡単! めんどくさいこと一切なし!
めんどくさいことはprocessメソッドの中にある fall(), erase(), raising(), move_player() の四天王が一括して管理してます。

旧版ではどうやってた?

ちなみに旧版でどうやってたかというと、例えば描写が以下の通り*3

void cField::view(){
  ClearDrawScreen();
  int color;
  for( int i=1; i<FIELD_WIDTH-1; i++ ){
    for( int j=1; j<FIELD_HEIGHT; j++ ){
      switch( field[i][j] ){
        case FLARE:
          color = GetColor( 255, 80, 80 );
          break;
        case AQUA:
          color = GetColor( 40, 200, 255 );
          break;
        case GAIA:
          color = GetColor( 40, 160, 40 );
          break;
        case GUST:
          color = GetColor( 220, 220, 220 );
          break;
        case STELLAR:
          color = GetColor( 255, 250, 100 );
          break;
        case METAL:
          color = GetColor( 40, 40, 60 );
          break;
        case NEU:
          color = GetColor( 40, 0, 40 );
          break;
        default:
          color = GetColor( 0, 0, 0 );
      }
      DrawString( i*20,j*20,"●",color);
    }
  }
  DrawString( cursor.x*20,cursor.y*20,"★",GetColor( 255, 255, 255 ));
  DrawLine( 15, 276, 160, 276, GetColor( 255, 255, 255 ) );
  DrawString( 300, 20, "ESCで終了・SHIFTで段上げ", 0xffffff );
  ScreenFlip();
}

わぁすごい! 神クラスによる一括描写だ!!
ちなみに新版ではupdateまでですべての作業を終わらせて、drawはSpriteのメソッドを呼んでいるだけなので、ある意味0行です。

座標情報はオブジェクトの中に持った方がいいんじゃない?

あとrubyに関係ない設計の話ですが、旧版と新版で大きく違うなぁと思う場所がもう一点。

旧版は、ブロックや自機がどこにいるかという座標情報をfield二次元配列の順序という形で持っている。
新版は、座標情報をブロックや自機の基底クラスPuzzleObject内にPosオブジェクトとして持っている。

どちらがいいかと聞かれたら、そりゃ後者じゃないのかなって。

「配列の位置関係」という形で座標を持つと、ブロックが移動するたび配列の要素の交換が発生します。
この手の落ちゲーは何につけてもブロックや自機が動くし落下するし競り上がるしで、交換の手間が馬鹿になりません。

それより何より、添字として持つことで「Posをオブジェクトとして扱えない」という欠点もあります*4

Posに雑にメソッドを突っ込んでいくのはいいぞー、楽しいぞー。

# 座標を表すクラス
class Pos
  attr_accessor :x, :y
  
  def ==(o)
    @x == o.x && @y == o.y
  end
  
  def eql?(o)
    self == o
  end
  
  def +(o)
    Pos.new(@x+o.x, @y+o.y)
  end
  
  # 座標が指定する範囲内にあれば真を返す
  def between?(x_range, y_range)
    x_range.include?(@x) && y_range.include?(@y)
  end
  
  # 座標を指定する範囲内に収める
  def clamp(x_range, y_range)
    @x = @x.clamp(x_range)
    @y = @y.clamp(y_range)
  end
  
  # チェックすべき範囲のPos群を得る
  # ただしcheckedに含まれるものは除く
  def check_positions(checked = Set.new)
    result = Set.new(Pos.directions.map{|p| self + p})
    result.select! {|pos| pos.between?(0..FIELD_WIDTH-1, 0..FIELD_HEIGHT-1)}
    result - checked
  end
  
  # 1歩分進むための配列を返す
  def self.directions
    [Pos.new(0,1), Pos.new(-1,0), Pos.new(0,-1), Pos.new(1,0)]
  end
end

その他こまごまとしたこと

Sprite継承推奨法則を応用して、
Ayameを継承したMusicクラスを作り、
さらにMusicが同時に鳴らないようにJukeboxクラスを作って制御してたりします。

とまぁ、こんな感じです。
「パズルっぽいゲーム」はゲームをDLすれば雑にコードが確認できるから*5
皆もDLして教師にするなり反面教師にするなりしてくれよな!!


明日は……あれ?! 明日誰もいないぞ!?
まぁでもきっと誰かが颯爽と現れて良記事書いてくれるでしょう。
そう期待してますんで! 頼むよ! お楽しみに!!

*1:「パズルっぽいゲーム」が所謂“落ちゲー”に分類されるかは正直微妙。

*2:つまりうのはなポーカーも6年越し2度目の挑戦だったわけだ!!

*3:色の名前が厨臭いの許して……ホント勘弁して……。

*4:RubyならPosオブジェクトをキーとしたハッシュにするって手もあるけど、流石にそれをするならオブジェクトの内に持とうよ……と思う。

*5:暗号化なんて概念はなかった