創作內容

4 GP

[RMXP RGSS] 簡易的橫向動態戰鬥製作教學 part2 (190811更新)

作者:未来ずら│2019-07-27 15:57:27│巴幣:1,204│人氣:499
經過前一篇的教學現在戰鬥已經能夠橫向化了
而這篇是把動態做出來


※19/08/11:修正戰鬥中用事件指定戰鬥動畫時會出錯的問題



公開變數
公開Scene_Battle的兩個變數用於動作判斷
class Scene_Battle
  #--------------------------------------------------------------------------
  # ● 公開變數
  #--------------------------------------------------------------------------
  attr_reader :phase                 # 戰鬥phase
  attr_reader :active_battler     # 目前行動者
end



更改戰鬥圖的讀取路徑
還記得上一篇一開始發的素材嗎,
現在要把實際用在遊戲中的戰鬥圖改成讀取Graphics/Battlers2/內與資料庫同名的圖片
這樣在編輯時也比較方便

為此先給RPG::Cache追加新方法
module RPG
  module Cache
    def self.battler2(filename, hue)
      self.load_bitmap("Graphics/Battlers2/", filename, hue)
    end
  end
end

接下來找到Sprite_Battlerupdate
self.bitmap = RPG::Cache.battler(@battler_name, @battler_hue)
改成
self.bitmap = RPG::Cache.battler2(@battler_name, @battler_hue)



註解掉一些不需要的功能
像是這一整段:
    if @battler.is_a?(Game_Actor) and @battler_visible
      # 不是主狀態的時候稍稍降低點透明度
      if $game_temp.battle_main_phase
        self.opacity += 3 if self.opacity < 255
      else
        self.opacity -= 3 if self.opacity > 207
      end
    end
內建狀態仔細觀察會發現進入行動階段時我方非行動角色會稍微半透明,
但橫向戰鬥有這功能我覺得反而很怪,所以註解

角色出現:
    # 如果是戰鬥不能或者是隱藏狀態就把透明度設定為 0     
    if @battler.hidden # or @battler.dead?
        self.opacity = 0
     end
註解「or @battler.dead?」
沒註解的話,我方角色在陣亡時進入戰鬥會看不見(直到復活)

陣亡:
      if @battler.damage == nil and @battler.dead?
        if @battler.is_a?(Game_Enemy)
          $game_system.se_play($data_system.enemy_collapse_se)
          collapse  # 移到這裡
        else
          $game_system.se_play($data_system.actor_collapse_se)
        end
        # collapse  # 註解這個
        @battler_visible = false
      end
collapse是陣亡效果(變紅然後消失)
這修改會變成只有敵方陣亡才會消失,因為待會要讓角色出現陣亡動作
當然如果想兩邊都有陣亡動作,大可完全移除collapse的調用



動作分割
接下來定義分割的方式:
    # 檔案名稱和色相與目前情況有差異的情況下
    if @battler.battler_name != @battler_name or
       @battler.battler_hue != @battler_hue
      # 獲取、設定點陣圖
      @battler_name = @battler.battler_name
      @battler_hue = @battler.battler_hue
      self.bitmap = RPG::Cache.battler2(@battler_name, @battler_hue)
      # @width = bitmap.width
      # @height = bitmap.height
      setup_motion  # 追加一個新方法
      self.ox = @width / 2
      self.oy = @height
      # 如果是戰鬥不能或者是隱藏狀態就把透明度設定為 0
      if @battler.hidden # or @battler.dead?
        self.opacity = 0
      end
    end
這裡看到了調用了一個內建沒有的方法,要對其進行定義
在這個方法藥用來宣告一些motion(動作)和分割圖片要用到的變數:
  #--------------------------------------------------------------------------
  # ● 設置Motion變數
  #--------------------------------------------------------------------------
  def setup_motion
    @motion_id = 0            # 動作ID (判斷目前是站立、施法、受傷等)
    @motion_loop = true    # motion要不要循環播放
    @now_frame = 0          # 目前畫格,也就是顯示該motion哪一張圖
    @frame_count = 0       # 目前畫格的時間計數
    @frame_wait = 5         # 該畫格的等待時間
    @damage_count = 0    # 受傷動作的持續時間
    @frame_max = 4         # 每個motion最大frame數
    @motion_max = 8       # 最大motion數
    @width = bitmap.width / @frame_max
    @height = bitmap.height / @motion_max
  end
上一篇提供的分割圖素材不難看出可以裁成4x8格,
而最後兩行的意思即是把角色戰鬥圖的寬/4,得到每格的寬,並存入@width
同理,把每格的高存入@height

至於宣告motion的部分請看註解


如果想要讓某些角色的分割數量不同,那可以用條件分岐做成這樣的結構:
  #--------------------------------------------------------------------------
  # ● 設置Motion變數
  #--------------------------------------------------------------------------
  def setup_motion
    @motion_id = 0            # 動作ID (判斷目前是站立、施法、受傷等)
    @motion_loop = true    # motion要不要循環播放
    @now_frame = 0          # 目前畫格,也就是顯示該motion哪一張圖
    @frame_count = 0       # 目前畫格的時間計數
    @frame_wait = 5         # 該畫格的等待時間
    @damage_count = 0    # 受傷動作的持續時間
    @frame_max = 4         # 每個motion最大frame數
    @motion_max = 8       # 最大motion數
    # ========== 我方角色
    if @battler.is_a?(Game_Actor)
       case @battler.id
       when 100  # 100號角色
         @frame_max = 6
         @motion_max = 10
       else
          #
       end
    # ========== 敵方角色
    else
       #
    end
    @width = bitmap.width / @frame_max
    @height = bitmap.height / @motion_max
  end
這樣一來我方100號角色就可以用6x10格的戰鬥圖了
這個結構之後motion更新判斷也可以使用

也可以用目前戰鬥圖名稱來判斷:
  #--------------------------------------------------------------------------
  # ● 設置Motion變數
  #--------------------------------------------------------------------------
  def setup_motion
    @motion_id = 0            
    @motion_loop = true    
    @now_frame = 0          
    @frame_count = 0       
    @frame_wait = 5         
    @damage_count = 0    
    @frame_max = 4         
    @motion_max = 8       
    
    case @battler_name
    # 如果戰鬥圖名稱是 003-Fighter03
    when "003-Fighter03"
       @frame_max = 6
       @motion_max = 12
    else
      #
    end
    @width = bitmap.width / @frame_max
    @height = bitmap.height / @motion_max
  end




取得了分割後的寬、高後,再來定義以下方法:
  #--------------------------------------------------------------------------
  # ● 刷新目前戰鬥分割圖
  #--------------------------------------------------------------------------
  def refresh_sprite_sheet
    self.src_rect.set(@now_frame*@width, @motion_id*@height, @width, @height)
  end
src_rect.set 出現了,作用是只顯示指定範圍的Bitmap內容,算是這腳本的核心
參數雖然看電子說明書就有,不過簡單說明一下:
src_rect.set(x, y, 寬, 高)
以繪圖軟體來說就是這種感覺(看X、Y、W、H的部分):
當然,因為要隨時進行變動,實際上要使用變數

所以根據上面寫的內容,
那麼當@now_frame == 0、@motion_id == 0的時候
就可以顯示站立動作的第一格(0, 0, 100, 100)了

知道運作方式後,接著就來操控那些motion變數了



Motion切換
製作一個方法:
  #--------------------------------------------------------------------------
  # ● 變更 motion  
  #      id:目標motion id
  #      wait:每個frame的等待
  #      loop:motion要不要循環播放
  #--------------------------------------------------------------------------
  def change_motion(id, wait = 5, loop = true)
    @now_frame = 0
    @frame_count = wait
    @frame_wait = wait
    @motion_id = id
    @motion_loop = loop
    @next_motion = next_data
    # 刷新目前圖像
    refresh_sprite_sheet
  end
定義了這方法之後,在適當的時機使用 change_motion(motion編號) 就可以切換到各種motion

至於後面兩個參數...
wait--如果想讓這個motion動快點或慢點就可以用(像我習慣勝利動作比較慢)
loop--預設true,大部分都是循環動作吧,如果不想循環,可以傳 false 過去,像是
change_motion(3, 5, false)



戰鬥開始時的Motion
如果以為這可以實現開戰準備動作那可能要失望了,其實這邊執行到的內容只能維持1F
就是切換成戰鬥畫面還在進行漸變的時候(那時不會進行update)
主要是進行motion初始化用
  #--------------------------------------------------------------------------
  # ● 一開始的動作
  #--------------------------------------------------------------------------
  def start_motion
    # 陣亡
    if @battler.dead?
      change_motion(5)
    # 重傷或無法行動  
    elsif @battler.hp < @battler.maxhp/4 || @battler.restriction == 4
      change_motion(7)
    # 一般狀態
    else
      change_motion(0)
    end
  end

回到追加setup_motion的地方,追加 start_motion
      @battler_name = @battler.battler_name
      @battler_hue = @battler.battler_hue
      self.bitmap = RPG::Cache.battler2(@battler_name, @battler_hue)
      setup_motion
      start_motion
現在執行戰鬥看看,角色應該已經會動了,不過目前只會一直保持這個姿勢


追加動作判斷
先找到彈出受傷數字的判斷,追加:
      # 傷害
      if @battler.damage_pop
        damage(@battler.damage, @battler.critical)
        # 受傷motion
        if @battler.damage.is_a?(Numeric) && @battler.damage > 0
          @damage_count = 20
          change_motion(3, 5, false)
        end
        @battler.damage = nil
        @battler.critical = false
        @battler.damage_pop = false
      end
注意這裡必須先進行 @battler.damage.is_a?(Numeric) 判斷,
因為傷害有可能是"Miss"或 "" (上狀態時) 這種非數字
@damage_count,這個是受傷動作的持續時間,因為之後要寫的動作更新判斷沒辦法判定受傷中的狀況,最好不要超過40(顯示傷害數字的時間)。

接下來看到內建的陣亡處理,讓我方角色陣亡時會變成陣亡motion
      if @battler.damage == nil and @battler.dead?
        if @battler.is_a?(Game_Enemy)
          $game_system.se_play($data_system.enemy_collapse_se)
          collapse
        else
          $game_system.se_play($data_system.actor_collapse_se)
          change_motion(5)  # 加上這句
        end
        @battler_visible = false
      end


這兩個比較特殊的處理完後,
    update_motion    # 追加調用一個新方法
    # 設定活動區塊的座標
    self.x = @battler.screen_x
    self.y = @battler.screen_y
    self.z = @battler.screen_z
在最後的這三行的上面追加一句 update_motion
同樣這不是內建方法,接下來要定義它的內容,結構大概是這樣:
  #--------------------------------------------------------------------------
  # ● 更新motion
  #--------------------------------------------------------------------------
  def update_motion
    # ============== 我方角色
    if @battler.is_a?(Game_Actor)
      case @battler.id
      when 100  # 100號角色
        update_actor100_motion
      else  # 其他的角色
        update_common_motion
      end
    # ============== 敵方角色  
    else
      update_common_motion
    end
  end
一樣,如果需要可以先做成依角色陣營和ID來分歧的結構
這裡又出現了兩個新方法
update_actor100_motion 這個明顯就是範例的先不管(不過至少要定義個空方法,不然100號角色一出場就會出現NameError)


我們要新增的是update_common_motion這方法
  #--------------------------------------------------------------------------
  # ● 共用motion
  #--------------------------------------------------------------------------
  def update_common_motion
    
    # 存活且在場而且不是受傷中的情況下
    if @battler_visible && @damage_count == 0
      # 目標motion
      plan_id = 0
      
      # 開始行動的場合
      if @battler.phase > 3 and @battler.phase != 6  
        # 行動種類分歧
        case @battler.current_action.kind
        when 0  # 基本
          # 不變
        when 1..2  # 技能和道具(1技能、2道具)
          if @battler.current_action.kind == 1
            # 確認技能使用對象
            scope = $data_skills[@battler.current_action.skill_id].scope
          else
            # 確認道具使用對象
            scope = $data_items[@battler.current_action.item_id].scope
          end
          # 攻撃的場合
          if scope < 3   
            plan_id = (@battler.current_action.kind == 1 ? 4 : 1)
            # 回復的場合
          else
            plan_id = (@battler.current_action.kind == 1 ? 1 : 1)
          end #  if scope < 3   
        end #  case
      end  #  if @battler.phase > 3
      
      
      # 防禦
      if @battler.guarding?
        plan_id = 2      
      end
      
      # 戰鬥勝利
      if $scene.phase == 5 && @battler.is_a?(Game_Actor)
        plan_id = 6
      end
      
      # 重傷
      if @battler.hp < @battler.maxhp/4 && (@battler.phase < 3 || @battler.phase > 5) && !@battler.guarding?
        if @battler.is_a?(Game_Actor)
          plan_id = 7
        end
      end  
      
        #無法行動的狀態       
      if @battler.restriction == 4 && $scene.phase != 5 && @battler.hp != 0
        plan_id = 7
      end
      
      # 如果需要變更motion
      if @motion_id != plan_id
        case plan_id
        when 6  #  勝利
          change_motion(plan_id, 8, false)
        else
          change_motion(plan_id)
        end
        return
      end
      
    end
    
    
    # 受傷時間計時
    @damage_count -= 1 if @damage_count > 0
    # frame計時
    @frame_count -= 1 if @frame_count >= 0
    # 需要更新 frame 時
    if @frame_count == 0 && @now_frame < @frame_max
      @frame_count = @frame_wait
      # 到達最大 frame 時
      if @now_frame + 1 == @frame_max
        # 需要循環
        @now_frame = 0  if @motion_loop
      else
        @now_frame += 1
      end
      # 刷新目前圖像
      refresh_sprite_sheet
    end
    
  end


if @battler_visible && @damage_count == 0
簡單說,如果要改變motion,必須在場上存活+不是在受傷中
裡面就是motion變化的各種判斷,簡單規模這些應該夠用了,多了也很亂
寫法的關係,優先度越高的動作要擺越後面
(但如果打算用「if...elsif...end」這種寫法記得優先度高的擺前面)

而一開始就是plan_id = 0、又是放在update,這也代表如果不符合後面的條件,一律使用站立姿勢,算是比較不靈活的地方

if @motion_id != plan_id
之後到這行判斷目前的 motion 是否和 plan_id 相同,不一樣才進行change_motion
這邊因為個人需求寫個分岐讓勝利動作播放速度慢了點

最後的部分就是減少受傷持續和frame時間,frame時間扣完就跳到下一格、到最後了要不要循環等處理,然後進行一次 refresh_sprite_sheet



製作近身攻擊
這樣一來差不多了,不過有點美中不足的是沒有近身攻擊
這邊不靠程式、直接用戰鬥動畫搞定,
利用RMXP的使用/對象方動畫、以及戰鬥動畫中的"對象消失"功能

用上一篇發的素材做兩個動畫:
使用方動畫:角色衝出去的動作,並使用"對象消失"功能,時間是使用方動畫+對象方動畫長度
對象方動畫:角色打人的動作
然後將它安排在武器和技能上即可

當然這樣做武器和近身技能一多,動畫量就會爆炸
不過比起這個應該先擔心能不能產出這麼多圖 ( ゚∀。)

然後還有個2P色問題,不過這倒有辦法簡單解決,要多一個代入目前行動者色相的動作
先修改RPG::Spriteanimation
然後追加一個 CHANGE_HUE_ANIMATION 常數,來判斷這動畫需不需要對應2P色
可以指定Array (例:[1,2,3,6,12])  或是  Range (例:11..60)
module RPG
  class Sprite < ::Sprite
    #--------------------------------------------------------------------------
    # ● 常數
    #--------------------------------------------------------------------------
    CHANGE_HUE_ANIMATION = [105, 106]  # 要跟隨使用者色相的戰鬥動畫ID
    #--------------------------------------------------------------------------
    # ● 播放戰鬥動畫
    #--------------------------------------------------------------------------
    def animation(animation, hit, hue = 0)
      dispose_animation
      @_animation = animation
      return if @_animation == nil
      @_animation_hit = hit
      @_animation_duration = @_animation.frame_max
      animation_name = @_animation.animation_name
      animation_hue = @_animation.animation_hue
      if CHANGE_HUE_ANIMATION.include?(animation.id)
        hue = animation_hue + hue
        hue -= 360 if hue > 360
        bitmap = RPG::Cache.animation(animation_name, hue)
      else
        bitmap = RPG::Cache.animation(animation_name, animation_hue)
      end
      if @@_reference_count.include?(bitmap)
        @@_reference_count[bitmap] += 1
      else
        @@_reference_count[bitmap] = 1
      end
      @_animation_sprites = []
      if @_animation.position != 3 or not @@_animations.include?(animation)
        for i in 0..15
          sprite = ::Sprite.new(self.viewport)
          sprite.bitmap = bitmap
          sprite.visible = false
          @_animation_sprites.push(sprite)
        end
        unless @@_animations.include?(animation)
          @@_animations.push(animation)
        end
      end
      update_animation
    end
    
    
  end
end

接著回到Sprite_Battlerupdate修改一下播放戰鬥動畫的部分:
      # 動畫
      if @battler.animation_id != 0
        animation = $data_animations[@battler.animation_id]
        hue = ($scene.active_battler ? $scene.active_battler.battler_hue : 0)
        animation(animation, @battler.animation_hit, hue)
        @battler.animation_id = 0
      end




好,簡易的橫向戰鬥改造教學就到這邊了
改造如果卡關的話,把這個抓回去參考參考










引用網址:https://home.gamer.com.tw/TrackBack.php?sn=4474446
All rights reserved. 版權所有,保留一切權利

相關創作

同標籤作品搜尋:RM|RGSS

留言共 1 篇留言

養樂多
回報個蟲蟲(?)
在戰鬥事件中設置動畫的話會像這樣出錯
https://imgur.com/gKGVRH1

08-09 21:53

未来ずら
啊,還真的忘了這個,因為這時沒有行動中的角色
感謝回報,我再做修改08-10 22:59
我要留言提醒:您尚未登入,請先登入再留言

4喜歡★qootm2 可決定是否刪除您的留言,請勿發表違反站規文字。

前一篇:[RMXP RGSS] ... 後一篇:【RMVA/MV】新道具...

追蹤私訊切換新版閱覽

作品資料夾

a4516【巴哈姆特事紀】
骯,《外傳.命之章》第24集〈囚徒交易〉更新完畢。可以的話,我不希望監獄成為政府用來對付平民的工具。看更多我要大聲說昨天22:48


face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】