FC2ブログ

RGSS2備忘録その7 臨機応変に行動できる自動戦闘のアクター

バグが見つかったので、こちらをご覧ください。

こんばんわ(´・ω・`)
そろそろQuest Maniaのクエストを作り始めようとしているすっぽんです。
今日は臨機応変に行動できる自動戦闘のアクターって言うのをやります。

こんな場面はあまりないと思いますが、パーティ全員が自動戦闘で、誰か一人だけが
戦闘不能の状態で、全員蘇生可能なスキルをもっているとします。
この状態で、戦闘を開始すると行動可能なアクターは、全員戦闘不能のアクターを
蘇生しようとします。本来なら、一人だけ蘇生させるスキルを使って、他のアクターは
攻撃に回ればいいのですが、自動戦闘だとこのようなことも起こり得ます。

そこで、今日はこのような自動戦闘アクターの無駄な行動を避けるスクリプトを
紹介したいと思います。
自動戦闘のアクターの行動は、ターンごとの戦闘開始前に決定されます。しかし、
この場合、それぞれのアクターは、他のアクターがどのような行動をするのかという
ことをぜんぜん考えてません。なので、戦闘中、行動できる順番が回ってきたら、
再度、行動内容の評価を行い、臨機応変に行動させるようにすれば、前述した
問題を回避することができます。

やりかたは以下のとおりです。
class Battle_Sceneのprocess_actionメソッドを次のように変えてください。
  #--------------------------------------------------------------------------
  # ● 戦闘行動の処理
  #--------------------------------------------------------------------------
  def process_action
    return if judge_win_loss
    return if $game_temp.next_scene != nil
    set_next_active_battler
    if @active_battler == nil
      turn_end
      return
    end
    return if @active_battler.dead?
    @message_window.clear
    wait(5)
    @active_battler.white_flash = true
    unless @active_battler.action.forcing
      @active_battler.action.prepare
    end
    if @active_battler.actor? #★
      @active_battler.make_action if @active_battler.auto_battle
    end #★
    if @active_battler.action.valid?
      execute_action
    end
    unless @active_battler.action.forcing
      @message_window.clear
      remove_states_auto
      display_current_state
    end
    @active_battler.white_flash = false
    @message_window.clear
  end

これで、自動戦闘のアクターは臨機応変に行動できるようになったと思います。
ていうか、こうするとプレイヤーが行動を決めるよりも有利に戦闘が展開します。( ^ω^)

今日はこのへんで
でわ(^▽^)ノシ
スポンサーサイト

テーマ : RPGツクール
ジャンル : ゲーム

RGSS2備忘録その6 目に優しい戦闘アニメーション

こんばんわー(^▽^)
最近、朝が寒すぎて目が覚めるSupponです。

えー、今日の本題に入る前に、やっぱり先日やった壁の上を歩行可能にする
スクリプトは完璧ではありませんでした。
小生スクリプトには、まだまだ不慣れで煩雑な構文になってしまったので、
あとで見直して、よりコンパクトにできたらまた、このブログで紹介したいと思います。
でも、そのまえに、他の人がもっといいスクリプトつくっちゃってるかもしれませんが。

では、きょうの本題に入ります。
今日は平日なので1分でできるやつを紹介します。( ^ω^)
みなさん、テストプレイしてるとなんだかすぐに目が疲れてきませんか?
俺だけですかね?
目が疲れた状態でプレイし続けると視力にも悪影響を及ぼす可能性が
あるかもしれませんよ。Σ(゚Д゚; エーッ!

で、目が疲れる原因、諸悪の根源はアニメーションのフラッシュです。
そこで今日は、アニメーションのフラッシュを無効にする目に優しいスクリプトを紹介します。

Sprite_Baseクラスのanimation_process_timing(timing)メソッドの
viewport.flash(timing.flash_color, timing.flash_duration * 4)という文を
コメントアウトしてください。
これでフラッシュはしなくなります。

これで、思う存分テストプレイしまくれます。(俺の場合)
これを発見する前までは、フルスクリーンでプレイすることもためらっていたSupponですが、
いまでは、バリバリフルスクリーンでやりまくっています。

今日はここまで、
みなさん、疲労を感じたら適度な休憩を取りましょう
でわ (^▽^)ノシ

テーマ : RPGツクール
ジャンル : ゲーム

RGSS2備忘録その5 壁の上を歩行可能にするスクリプト

こんばんわ( ^ω^)
平日は忙しくてブログが更新できませんでした。(´;ω;`)ウッ
今後は土日のみの更新になるとおもいます。
とはいえ、もうRGSSについて書くことがなくなってきましたがね。
書くことがなくなったらQuest Maniaの進捗状況でも書こうかと思います。

実は今日、以前紹介した壁の上を歩行可能にするスクリプトでは、
水平壁の端に歩行可能な上層レイヤーを置くと歩行可能になったり、
レイヤーがきちんと機能しないというご指摘を受けました。スミマセン(´;ω;`)ウッ
そこで、今日はその不具合を改善するスクリプトを紹介します。

まず、前回のスクリプトは削除していただいて、以下のスクリプトのコピーをお願いします。
class Game_Map
  #--------------------------------------------------------------------------
  # ● セットアップ
  #     map_id : マップ ID
  #--------------------------------------------------------------------------
  def setup(map_id)
    @map_id = map_id
    @map = load_data(sprintf("Data/Map%03d.rvdata", @map_id))
    @display_x = 0
    @display_y = 0
    @passages = $data_system.passages
    for m in 0..2
      for k in 0..7
        j = 5888 + 48 * k + 768 * m
        for i in 0..47
          if i < 16
            @passages[i + j] = 0x20
          elsif 16 <= i and  i <= 19
            @passages[i + j] = 0x21
          elsif 20 <= i and  i <= 23
            @passages[i + j] = 0x22
          elsif 24 <= i and  i <= 27
            @passages[i + j] = 0x24
          elsif 28 <= i and  i <= 31
            @passages[i + j] = 0x28
          elsif i == 32
            @passages[i + j] = 0x25
          elsif i == 33
            @passages[i + j] = 0x2a
          elsif i == 34 or i == 35
            @passages[i + j] = 0x23
          elsif i == 36 or i == 37
            @passages[i + j] = 0x26
          elsif i == 38 or i == 39
            @passages[i + j] = 0x2c
          elsif i == 40 or i == 41
            @passages[i + j] = 0x29
          elsif i == 42
            @passages[i + j] = 0x27
          elsif i == 43
            @passages[i + j] = 0x2b
          elsif i == 44
            @passages[i + j] = 0x2d
          elsif i == 45
            @passages[i + j] = 0x2e
          elsif i >= 46
            @passages[i + j] = 0x2f
          end
        end
      end
    end
    referesh_vehicles
    setup_events
    setup_scroll
    setup_parallax
    @need_refresh = false
  end
end

class Game_Character
  def map_passable?(x, y)
    return $game_map.passable2?(x, y, @x, @y)
  end
end

class Game_Player
  def map_passable?(x, y)
    case @vehicle_type
    when 0  # 小型船
      return $game_map.boat_passable?(x, y)
    when 1  # 大型船
      return $game_map.ship_passable?(x, y)
    when 2  # 飛行船
      return true
    else    # 徒歩
      return $game_map.passable2?(x, y, @x, @y)
    end
  end
end

class Game_Map
  def passable2?(x, y, self_x, self_y, flag = 0x01)
    wall = wall_passable?(x, y, self_x, self_y, flag)   
    for event in events_xy(x, y)            # 座標が一致するイベントを調べる
      next if event.tile_id == 0            # グラフィックがタイルではない
      next if event.priority_type > 0       # [通常キャラの下] ではない
      next if event.through                 # すり抜け状態
      pass = @passages[event.tile_id]       # 通行属性を取得
      next if pass & 0x10 == 0x10           # [☆] : 通行に影響しない
      return true if pass & flag == 0x00 and wall  # [○] : 通行可
      return false if pass & flag == flag   # [×] : 通行不可
    end
    for i in [2, 1, 0]                      # レイヤーの上から順に調べる
      tile_id = @map.data[x, y, i]          # タイル ID を取得
      return false if tile_id == nil        # タイル ID 取得失敗 : 通行不可
      pass = @passages[tile_id]
      next if pass & 0x10 == 0x10       # [☆] : 通行に影響しない
      pass = 0x00 if pass & 0x20 == 0x20
      return true if pass & flag == 0x00 and wall # [○] : 通行可
      return false if pass & flag == flag   # [×] : 通行不可
    end
    return false                            # 通行不可
  end
 
  def wall_passable?(x, y, self_x, self_y, flag = 0x01)
    return false if @map.data[x, y, 0] == nil
    pass = @passages[@map.data[x, y, 0]]         # 通行属性を取得
    self_pass = @passages[@map.data[self_x, self_y, 0]]
    if flag == 0x01
      if self_pass & 0x20 == 0x20
        if x > self_x #右を向いている
          return false if self_pass & 0x04 == 0x04
        elsif x < self_x #左を向いている
          return false if self_pass & 0x01 == 0x01
        elsif y > self_y #下を向いている
          return false if self_pass & 0x08 == 0x08
        elsif y < self_y #上を向いている
          return false if self_pass & 0x02 == 0x02
        end
      end
      if pass & 0x20 == 0x20 #★ 壁通路
        if x > self_x #右を向いている
          return false if pass & 0x01 == 0x01
        elsif x < self_x #左を向いている
          return false if pass & 0x04 == 0x04
        elsif y > self_y #下を向いている
          return false if pass & 0x02 == 0x02
        elsif y < self_y #上を向いている
          return false if pass & 0x08 == 0x08
        end
      end 
    end
    return true 
  end
end


これで、水平壁の上にレイヤーを敷いてもちゃんと機能するようになったと思います。
でも、このままだとプレイヤーの隣にいるイベントが、決定ボタンを押した場合や、
接したときにコマンドが起動するように設定してる場合、水平壁の端をまたいで
いたとしても、起動してしまいます。
この状況は避けたい場合は、以下のスクリプトもコピーしておいてください。
多少重くなるかも知れませんが(´・ω・`)

class Game_Player
  def check_event_trigger_there(triggers)
    return false if $game_map.interpreter.running?
    result = false
    front_x = $game_map.x_with_direction(@x, @direction)
    front_y = $game_map.y_with_direction(@y, @direction)
    if $game_map.passable2?(@x, @y, front_x, front_y) #★
      for event in $game_map.events_xy(front_x, front_y)
        if triggers.include?(event.trigger) and event.priority_type == 1
          event.start
          result = true
        end
      end
    end #★
    if result == false and $game_map.counter?(front_x, front_y)
      front_x = $game_map.x_with_direction(front_x, @direction)
      front_y = $game_map.y_with_direction(front_y, @direction)
      for event in $game_map.events_xy(front_x, front_y)
        if triggers.include?(event.trigger) and event.priority_type == 1
          event.start
          result = true
        end
      end
    end
    return result
  end
end

class Game_Character
  #--------------------------------------------------------------------------
  # ● 下に移動
  #     turn_ok : その場での向き変更を許可
  #--------------------------------------------------------------------------
  def move_down(turn_ok = true)
    if passable?(@x, @y+1)                  # 通行可能
      turn_down
      @y = $game_map.round_y(@y+1)
      @real_y = (@y-1)*256
      increase_steps
      @move_failed = false
    else                                    # 通行不可能
      turn_down if turn_ok
      if $game_map.passable2?(@x, @y+1, @x, @y) #★
        check_event_trigger_touch(@x, @y+1)   # 接触イベントの起動判定
      end #★
      @move_failed = true
    end
  end
  #--------------------------------------------------------------------------
  # ● 左に移動
  #     turn_ok : その場での向き変更を許可
  #--------------------------------------------------------------------------
  def move_left(turn_ok = true)
    if passable?(@x-1, @y)                  # 通行可能
      turn_left
      @x = $game_map.round_x(@x-1)
      @real_x = (@x+1)*256
      increase_steps
      @move_failed = false
    else                                    # 通行不可能
      turn_left if turn_ok
      if $game_map.passable2?(@x-1, @y, @x, @y) #★
        check_event_trigger_touch(@x-1, @y)   # 接触イベントの起動判定
      end #★
      @move_failed = true
    end
  end
  #--------------------------------------------------------------------------
  # ● 右に移動
  #     turn_ok : その場での向き変更を許可
  #--------------------------------------------------------------------------
  def move_right(turn_ok = true)
    if passable?(@x+1, @y)                  # 通行可能
      turn_right
      @x = $game_map.round_x(@x+1)
      @real_x = (@x-1)*256
      increase_steps
      @move_failed = false
    else                                    # 通行不可能
      turn_right if turn_ok
      if $game_map.passable2?(@x+1, @y, @x, @y) #★
        check_event_trigger_touch(@x+1, @y)   # 接触イベントの起動判定
      end #★
      @move_failed = true
    end
  end
  #--------------------------------------------------------------------------
  # ● 上に移動
  #     turn_ok : その場での向き変更を許可
  #--------------------------------------------------------------------------
  def move_up(turn_ok = true)
    if passable?(@x, @y-1)                  # 通行可能
      turn_up
      @y = $game_map.round_y(@y-1)
      @real_y = (@y+1)*256
      increase_steps
      @move_failed = false
    else                                    # 通行不可能
      turn_up if turn_ok
      if $game_map.passable2?(@x, @y-1, @x, @y) #★
        check_event_trigger_touch(@x, @y-1)   # 接触イベントの起動判定
      end #★
      @move_failed = true
    end
  end
end



これで問題はなくなったと思います。
もしまだ あったらすみません。(´・ω・`)

今日はもうひとつ、水平壁のマップの作り方を紹介します。
壁のタイルをマップ上に敷こうとすると、オートマップ機能で、
うまく設置できない場合があります。こういう場合は、Shiftキーをおしながら、
操作するとうまくいきます。
壁に階段やはしごを設置する場合、水平壁の端の部分は、
あらかじめ、ダミーを作っておき、それをShiftを押したままコピーし貼り付けます。
示した図のように矢印の方にコピーすればうまくいきます。
080112_01.jpg


まだ、あまりテストしてませんが、もしまだ不具合がありましたら
コメントのほうによろしくお願いいたします。
でわ( ^ω^)ノシ

テーマ : RPGツクール
ジャンル : ゲーム

RPGツクールVX RGSS2備忘録その4 キャラクターの回転

正月休みも残り1日となりました(´;ω;`)ウッ
今回の正月休みは、ずうっとRGSS三昧だったので、
熱中してたぶん、過ぎるのが早く感じます。

で、今日はスクリプトをつかってキャラクターを回転させるって言うのをやります。
回転といっても、向きを上下左右にするの意味ではなく、視覚的に角度を指定して
キャラクターを回転させます。
080105_01.jpg

RPGツクールVXには、もともとキャラクターを回転させるっていうイベントコマンドがないので
人が倒れたりしているところなどを見た目では表現できませんでした。
そこで、今回そういうのを可能にするスクリプトを作ってみました。

class Game_Character
  attr_accessor :angle #角度
  def initialize
    @angle = 0
    @id = 0
    @x = 0
    @y = 0
   
    ‥‥省略‥‥
   
    @move_failed = false                  # 移動失敗フラグ
  end
end


まずは、Game_Characterクラスからです。
こちらは簡単、ただ、@angleというインスタンス変数を付け加えるだけです。

つぎに、Sprite_Characterクラスです。

class Sprite_Character
  def initialize(viewport, character = nil)
    super(viewport)
    @character = character
    @balloon_duration = 0
    @adjust = 0
    update
  end
  def update
    super
    update_bitmap
    self.visible = (not @character.transparent)
    update_src_rect
    self.x = @character.screen_x
    #★
    if self.angle != @character.angle
      self.angle = @character.angle
      if self.angle != 0
        self.oy = @adjust = self.height / 2
      else
        self.oy = @ch
        @adjust = 0
      end
    end
    #★
    self.y = @character.screen_y - @adjust
    self.z = @character.screen_z
    self.opacity = @character.opacity
    self.blend_type = @character.blend_type
    self.bush_depth = @character.bush_depth
    update_balloon
    if @character.animation_id != 0
      animation = $data_animations[@character.animation_id]
      start_animation(animation)
      @character.animation_id = 0
    end
    if @character.balloon_id != 0
      @balloon_id = @character.balloon_id
      start_balloon
      @character.balloon_id = 0
    end
  end
end


こちらは、意外や意外、あっさりできるかと思いましたが、そこそこ苦戦しました。
まず、キャラクターの原点はx方向で見れば真ん中ですが、y方向でみると
下端になっています。なので、これをself.oy = @adjust = self.height / 2
で中央にもってこなければなりません。
最初はこれだけでできるかとおもったら、少し、下のほうにずれて表示されます。
これは、原点が本来の足元に来るようにスプライトが表示される仕組みになってるので、
さらに、スプライトの高さの半分分、上の方向にずらさなければなりません。
@adjustというインスタンス変数を準備しておき、スプライトの角度が変わったときだけ
self.y = @character.screen_y - @adjustで上にずらしてやる
という風にしてあげました。

最後に、イベントコマンドをつかって実際に角度指定する場合は、
スクリプトで、90°回転させたいなら、$game_map.events[@event_id].angle = 90
というように、また、自分を回転させる場合は
$game_player.angle = 90というふうに、します。
080105_02.jpg

乱数をつかってあそんでみました( ^ω^)

みなさん、でわまたです( ^ω^)ノシ

テーマ : RPGツクール
ジャンル : ゲーム

RPGツクールVX RGSS2備忘録その3 パーティ定員を増やす。つづき

こんばんわみなさん( ^ω^)
正月だからといって食べ過ぎてはいけませんよm9( ・`ω・´)

Quest Maniaの製作に向けて、スクリプト周りだけみれば、半分くらいはおわったとこかな
という感じです。正月休み明けぐらいには、ゲームのコンテンツに手をつけれればなと
思ってます。( ^ω^)

今日は昨日に引き続き、パーティ人数の上限を増やすっていうのをやります。
昨日も書きましたが、ただ単に、パーティ人数を自分で設定するだけなら、
Game_PartyクラスのMAX_MEMBERSという定数に数字を代入するだけで
できます。
問題は、メニュー画面や、戦闘画面でのステータスウインドウがきちんと表示されない
ということでした。今日はステータスウインドウがきちんと表示されるスクリプトを
紹介したいと思います。

とはいっても、今回紹介するのは1画面で、パーティ全員分のステータスを把握
できるような高度なものではありません。
ウインドウの表示内容物がスクロールするようにする単純なスクリプトです。

まず、メニュー画面のステータスウィンドウはこんな感じです。

class Window_MenuStatus < Window_Selectable
  WLHS = 96
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #     x : ウィンドウの X 座標
  #     y : ウィンドウの Y 座標
  #--------------------------------------------------------------------------
  def initialize(x, y)
    super(x, y, 384, 32 + WLHS * $game_party.members.size)
    refresh
    self.active = false
    self.index = -1
    self.height = 416
  end
  #--------------------------------------------------------------------------
  # ● ウィンドウ内容の作成
  #--------------------------------------------------------------------------
  def create_contents
    self.contents.dispose
    self.contents = Bitmap.new(width - 32, [height - 32, row_max * WLHS].max)
  end
  #--------------------------------------------------------------------------
  # ● 先頭の行の取得
  #--------------------------------------------------------------------------
  def top_row
    return self.oy / WLHS
  end
  #--------------------------------------------------------------------------
  # ● 先頭の行の設定
  #     row : 先頭に表示する行
  #--------------------------------------------------------------------------
  def top_row=(row)
    row = 0 if row < 0
    row = row_max - 1 if row > row_max - 1
    self.oy = row * WLHS
  end
  #--------------------------------------------------------------------------
  # ● 1 ページに表示できる行数の取得
  #--------------------------------------------------------------------------
  def page_row_max
    return (self.height - 32) / WLHS
  end
  #--------------------------------------------------------------------------
  # ● 項目を描画する矩形の取得
  #     index : 項目番号
  #--------------------------------------------------------------------------
  def item_rect(index)
    rect = Rect.new(0, 0, 0, 0)
    rect.width = (contents.width + @spacing) / @column_max - @spacing
    rect.height = WLHS
    rect.x = index % @column_max * (rect.width + @spacing)
    rect.y = index / @column_max * WLHS
    return rect
  end
  #--------------------------------------------------------------------------
  # ● カーソルの更新
  #--------------------------------------------------------------------------
  def update_cursor
    if @index < 0               # カーソルなし
      self.cursor_rect.empty
    elsif @index < @item_max    # 通常
      self.cursor_rect.set(0, @index * 96, contents.width, 96)
      row = @index / @column_max    # 現在の行を取得
      if row < top_row              # 表示されている先頭の行より前の場合
        self.top_row = row          # 現在の行が先頭になるようにスクロール
      end
      if row > bottom_row           # 表示されている末尾の行より後ろの場合
        self.bottom_row = row       # 現在の行が末尾になるようにスクロール
      end
      rect = item_rect(@index)      # 選択されている項目の矩形を取得
      rect.y -= self.oy             # 矩形をスクロール位置に合わせる
      self.cursor_rect = rect       # カーソルの矩形を更新
    elsif @index >= 100         # 自分
      self.cursor_rect.set(0, (@index - 100) * 96, contents.width, 96)
    else                        # 全体
      self.cursor_rect.set(0, 0, contents.width, @item_max * 96)
    end
  end
end


今回扱うWindow_MenuStatusは、Window_Selectableを継承してる厄介なクラスです。
なんで、厄介かというと俺自身がよくわからないからです。(´;ω;`)ウッ
単に個人的なもんだいです。でも、とりあえず動いたので紹介したいと思います。

まずWLHS=96という定数を設定しておきます。これは一人分の高さです。
そして、initializeでウインドウそのものは、一度人数分だけ、縦長の高さに設定しておき、
あとで、1画面分の高さ幅に設定します。こうすると、うまく、全員のステータスが
描画されます。

次に、Window_Selectableクラスのメソッドで、WLHという一行分の高さを表す定数が
含まれるメソッドのWLHをすべて、WLHSに変えます。こうすることによって、
カーソルがきちんとした大きさと位置に動くようになります。

最後にupdate_cursorで、カーソルが画面の外に出たときに、ウインドウの表示の内容が
動くように設定します。これは、もともとWindow_Selectableクラスのupdate_cursor
をただ、間に挿入しただけのものです。

次に戦闘画面でのステータスウインドウについてです。
class Window_BattleStatus < Window_Selectable
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #--------------------------------------------------------------------------
  def initialize
    super(0, 0, 416, 32 + $game_party.members.size * WLH)
    refresh
    self.active = false
    self.height = 128
  end
end
こっちはこれだけでちゃんと動きます。
やってることは、さっきのメニュー画面のinitializeメソッドでやった内容と同じものです。

このスクリプトで、メニュー画面中で、アイテムを使ったり、スキルを使ったりと
基本的なことは、問題なくできました。ただ、イベントコマンド(インタプリタ)は、まだ
ぜんぜん試してないので、ちゃんと動くかどうかわかりません。
もし何か不具合があったら、またブログでお知らせしようかなと思います。

でわ今日はこの辺で(^▽^)ノシ

テーマ : RPGツクール
ジャンル : ゲーム

RPGツクールVX RGSS2備忘録その3 パーティ定員を増やす。

あけまして おめでとうございまーす。\(^o^)/
今年こそはきちんとしたゲームを完成させたいと思います。( ・`ω・´)


今日はパーティ定員を増やすっていうのをやります。
通常RPGツクールのパーティ人数は4人と決まっていました。(たぶん俺が知る限りでは)
今回はそれをスクリプトをいじって、もっと増やしたいと思います。

やり方は超簡単です。
Game_PartyクラスのMAX_MEMBERS = 4ていうところの数字を好きな数字にすれば
すきなだけ大人数のパーティが組めるようになります。(゚∀゚)

ただーし!( ・`ω・´)
それだけだと、メニューのウインドウに表示しきれないので、
ステータスとか把握しづらいです。(´・ω・`)

なので、今度、きちんと表示されるスクリプトができたら、紹介したいと思います。
今日はこの辺で、
みなさん、今年もよろしくお願いいたしまーす(^▽^)ノシ

テーマ : RPGツクール
ジャンル : ゲーム

プロフィール

Suppon

Author:Suppon
すっぽんでーす。(^▽^)
カルドラ鋭意制作中( ・`ω・´)
Twitter => suppon01
メールはこちらへ
suppon2008@gmail.com

Twitter...

Twitter Reload

最近のコメント
最近のトラックバック
月別アーカイブ
カテゴリー
ブログ内検索
RSSフィード
リンク
ブロとも申請フォーム

この人とブロともになる