せらぴんブログ

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

積んでるゲーム紹介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:暗号化なんて概念はなかった

積んでるゲーム紹介5:ゴジラ -GODZILLA-

www.adventar.org

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

本日紹介するのはPS3の「ゴジラ -GODZILLA-」。

こいつはなんていうかその……。
爽快感があるかと言われるとそんなこともなく
重厚感があるかと聞かれるとそういうわけでもなく。
身も蓋もない言い方をすれば、ゴジラが街を闊歩して目標の建造物を破壊するだけ。
ゴジラを操って街を壊す」ところがウリなんだろうけど……、個人的にはそこに楽しみを見出だせなかった。

ゲームとして捉えても、序盤は簡単すぎるし
中盤スーパーXとスーパーX2が出てくると途端に難しくなるし。

ゴジラのファンなら絶対買い!!」みたいなノリに煽られて買ったはいいものの……。
そういう安易な動機で買うべきゲームじゃなかったと思います。

積んでるゲーム紹介4:影牢 ダークサイドプリンセス

www.adventar.org

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

今回紹介するのはPS3ソフト「影牢 ダークサイドプリンセス」です。
こいつは刻命館シリーズの1作なのですが、
私と刻命館シリーズとの出会いは「蒼魔灯」のときでした。

蒼魔灯は物語全体に漂う悲壮感と、
舞台となるお城の中世を彷彿とさせる誂えが非常に気に入っています。
ストーリー後半の地獄騎士が強いのなんのっての……。
友人から教えてもらった「ベアトラップ→バズソーLv3」必殺コンボがなければクリアできなかっただろうなぁ……。

前々から存在は知ってたし、いつかプレイしたいなぁとは思ってたんで、PS3買ってから割と早い段階で買ったんですよね。
一番期待していたベースのところ、
トラップを仕掛けてコンボを決めて侵略者をぶち殺す部分はちゃんと楽しみとして存在してると思います。
また、トラップ無効の侵略者も多く、安定したコンボが存在しないので
逆に頭を働かせる余地があるとも思います。
でも残念な点も3点ほどあります。

第一に、アーマーブレイクの条件が厳しい。
弱点となるトラップを当てなければならないのですが
どれが弱点であるかは特に明示されておらず、
事前の攻略情報なしだとまず狙ってできるものではない。
アーマーブレイク付けるなら、もっとポンポン女の子を剥かせるようにしてもよかったのでは。

第二に、捕虜が苦労が大きい割にリターンが少ない。
個人的にこれが一番辛かったかもしれない。
せっかく女の子を捉えて鳥かごに突っ込んで置けるんだから、
もうちょっと3Dで下着姿を360度上下左右から眺めるとかさぁ!!
そういう意味ではホントにやりこみ要素以上の何者でもないです

第三に、これは単に好みの問題だけど、ステージがダサい。
俺は第一ステージみたいな荒れ果てた古城が好きなんだ……。
廃工場はスチームパンク的にまだ許容できるけど、廃遊園地ってなんなんだ……。
しかもお化け屋敷暗すぎて見えねぇ。


蒼魔灯の段階で「女の子を痛め抜いて悲鳴を挙げさせまくった上で断末魔を聞く」という
リョナ向けのコンセプトは完成されていたんだから、
要素を追加するならもっと媚びてほしかった。それこそ閃乱カグラレベルで*1

*1:閃乱カグラやったことないけど

積んでるゲーム紹介3:ユバの徽

www.adventar.org

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

今回紹介する「ユバの徽」はソシャゲです!!
ほら今あなた、
「ソシャゲにはクリアなんて概念ないんだから、”積んでる”って言わないだろ!?」と
思いませんでした?

逆に考えるんだ、
すべてのソシャゲはやがて積まれる運命にある」と!!

自分の過去を振り返った時
複数のソシャゲ(スマホゲー)を並行してやった記憶がほぼありません。

「熱しやすく冷めやすい」と言えばそれまでなんでしょうが、
もしかすると「ソシャゲのリソース回復待ちの間に別のソシャゲをやり始める」という
負のスパイラルを恐れた結果なのかもしれません。

閑話休題

ユバの徽は「艦これ」同様にソーシャル要素が少なく、
ソシャゲというよりはFree-to-Playのオンラインゲームに分類されるでしょう。
このゲームの特徴は、悪趣味なまでに徹底された死と生贄に根ざしたフレーバーです。

  • 戦士のレベルを上げるには、他の戦士を生贄にしなければならない
  • 戦士には保有上限があるので、どんどん生贄にしないと次がつかえる
  • 祈り人(俺屍でいう神様役)にも保有上限があるので、空きがなくなったら生贄になる
  • 祈り人のエピソード解放条件の1つに「進化させてから生贄にする」がある
  • 1人ガチャをすると、2人のうち片方を選び、選ばれなかった方は見殺し扱いになる
  • 敵を倒すとドクロがもらえる。ドクロを祭壇にささげることで祭壇レベルが上がる(艦これで言う司令部レベル)

もともと私はエログロリョナも好きだったので
そんな思い切ったゲームが出たのか! よっしゃやったろ!!」と喜び勇んで始めました。
これに関しては1~2ヶ月くらいどっぷりハマってましたし、多少ですが課金もしました。
戦士と祈り人が契り(意味深)*1をして新たな戦士を産むシステムなんですが、
この遺伝を模したシステムを使って、いろんな遺伝子の組み合わせを実現するのが純粋に楽しいんですね*2

ゲームバランスも序盤あたりは存外によく、
難点を挙げるとしたら、祈り人を進化させるために必要なリリースが存外に多いかな?という点くらい。


まぁでも結局は、最初のイベントが終わり、島ステージの攻略の兆しが見えない段階になって自然にフェードアウトしていきました。
戦士も6種類までは出たけど、最後の水-水系の戦士だけは出なかったなぁ……。


ちなみに、このゲームが縁で俺屍に興味を持ち、
PSゲームアーカイブスで購入に至ったのはまた別のお話。
俺屍も実は積んでるんだけどな!!

*1:ちなみに契りに性別は関係ないらしく(?!)、ホモもレズも可能

*2:父母ともに「地水火天」の4種類の遺伝子を持っていて、その遺伝子の組み合わせによって生まれる戦士が7種類に分類される。このうち天が優性遺伝