CoRでゲームをつくってみよう / 第06回 ゲームとして必要な機能を追加する

投稿者:Cdv30200 aoi icon mini aoihikawa 投稿日:2022/08/15 22:33

CoRでゲームをつくってみよう

 第06回 ゲームとして必要な機能を追加する


  こんにちは。
 フリーデザイナープログラマー(自称)の
 簸川 葵(ひかわ あおい)と申します。


 第05回 では、弾の表示と当たり判定
 音楽や効果音の再生方法までスクリプトを実装しました。
 第06回はスコアや残機など文字を表示する方法、
 タイトル画面やゲームクリアの作り方など
 ゲームとして必要な機能を実装する方法についてやっていきますね。



06-01 文字を表示しよう


  ゲームには画像の表示だけでなく
 文字の表示も大事な要素となっています。

  まずは、文字オブジェクトを生成して
 スコアや残機を表示させるところまでつくってみましょう。




  新CoRでは、文字オブジェクトも
 画像オブジェクトのように、素材を定義して使用します。

  定義の項目は異なりますが、それ以降の内容は
 画像オブジェクトのスクリプトの書き方と似ているので
 arrpと比較して、扱いやすくなっています。


  それでは、文字の定義から。

  スクリプトの最後、画像素材の定義より後に
 文字の定義を行います。

  プレイヤーキャラクターには
 待機状態のアニメーションが存在しなかったため、
 こちらの定義も追加しておきます。

#_/_/_/_/_/_/ 素材の定義 _/_/_/_/_/_/

#----- 画像の定義 -----

#メイン背景
sprite 'spr_bg_00' do
  image 'img_bg_00'
  origin :left_top
end

#プレイヤーキャラクター
sprite 'spr_char' do
  image 'img_char'
  origin :left_top
  #---------- ここから ----------
  animation 'wait',    [3],    60, true
  #---------- ここまで ----------
  animation 'neutral', [0, 3], 10, true
  animation 'left',    [1, 4], 10, true
  animation 'right',   [2, 5], 10, true
end

#~~~~~~~~~~ 中略 ~~~~~~~~~~

#---------- ここから ----------
#----- 文字の定義 -----

text 'spr_txt_info' do
  origin :left_top
  font_size 18
  color '#FFFFFF'
end
#---------- ここまで ----------

  文字の場合、定義の命令がspriteからtextに変更されます。

  originは画像のときと同様に
 画面上に表示するときの基準点を決めています。

  font_sizeは表示する文字のフォントサイズ。

  colorは表示する文字の色をカラーコードで指定しています。

  カラーコードについては
 WEB色見本などの情報を参考にすると分かりやすいです。



  文字オブジェクトで表示する値の
 ローカル変数を定義します。

scene 'start' do
  #_/_/_/ ローカル変数の定義 _/_/_/
  
  #キー入力用のフラグ
  key_flg_up_st    = nil;  key_flg_up_do    = nil;  key_flg_up_en    = nil
  key_flg_down_st  = nil;  key_flg_down_do  = nil;  key_flg_down_en  = nil
  key_flg_left_st  = nil;  key_flg_left_do  = nil;  key_flg_left_en  = nil
  key_flg_right_st = nil;  key_flg_right_do = nil;  key_flg_right_en = nil
  
  key_flg_z_st = nil;      key_flg_z_do = nil;      key_flg_z_en = nil
  
  #---------- ここから ----------
  #システム関連
  score = nil;      score_view = nil
  enemy_score = nil
  
  char_zan = nil;   char_zan_view = nil
  enemy_zan = nil;  enemy_zan_view = nil
  #---------- ここまで ----------

  score、score_viewは現在のスコア表示。

  enemy_scoreは敵キャラクターを倒したときに獲得するスコア値。

  char_zan、char_zan_viewは残機表示。

  enemy_zan、enemy_zan_viewは残り敵キャラクター出現数表示に使用します。



  文字オブジェクトを格納するローカル変数を定義します。

scene 'start' do
  #_/_/_/ ローカル変数の定義 _/_/_/
#~~~~~~~~~~ 中略 ~~~~~~~~~~
  obj_enemy_next      = nil
  obj_enemy_next_rnd  = nil
  obj_enemy_next_time = nil
  
  #---------- ここから ----------
  #文字オブジェクト
  obj_minienemy_spr = nil
  obj_txt_enemyzan_spr = nil;    obj_txt_enemyzan_no_spr = nil
  
  obj_txt_score_spr = nil;       obj_txt_score_no_spr = nil
  
  obj_minichar_spr = nil
  obj_txt_charzan_spr = nil;     obj_txt_charzan_no_spr = nil
  #---------- ここまで ----------
  
  #音楽オブジェクト
  obj_bgm_main = nil
  
  
  #効果音オブジェクト
  obj_se_hit00 = nil

  obj_minienemy_sprは縮小した敵キャラクター画像

  obj_txt_enemyzan_spr、obj_txt_enemyzan_no_sprは
 残り敵キャラクター出現数表示。

  obj_txt_score_spr、obj_txt_score_no_sprはスコア表示。

  obj_minichar_sprは縮小したプレイヤーキャラクター表示

  obj_txt_enemyzan_spr、obj_txt_enemyzan_no_sprは
 残機数表示に使用します。



  文字オブジェクトで表示する値の
 ローカル変数を初期化します。

  create do
    #----- シーンロード時に実行される処理 -----
    #_/_/_/ 変数の初期化 _/_/_/
    
    #キー入力用のフラグ
    key_flg_up_st    = false;  key_flg_up_do    = false;  key_flg_up_en    = false
    key_flg_down_st  = false;  key_flg_down_do  = false;  key_flg_down_en  = false
    key_flg_left_st  = false;  key_flg_left_do  = false;  key_flg_left_en  = false
    key_flg_right_st = false;  key_flg_right_do = false;  key_flg_right_en = false
    
    key_flg_z_st = false;      key_flg_z_do = false;      key_flg_z_en = false
    
    #---------- ここから ----------
    #システム関連
    score = 0;         score_view = score
    enemy_score = 500
    
    char_zan = 5;      char_zan_view = char_zan
    enemy_zan = 20;    enemy_zan_view = enemy_zan
    #---------- ここまで ----------



  文字オブジェクトを生成します。

  まずは、敵キャラクターの残り出現数から表示してみましょう。

    #_/_/_/ 素材の初期化 _/_/_/
    
    #メイン背景
    obj_bg_00_spr = put_sprite 'spr_bg_00' do
      position obj_bg_pos_x, obj_bg_pos_y
      src_rect 0, 0, 800, 600
    end
    
#~~~~~~~~~~ 中略 ~~~~~~~~~~
    #敵キャラクター
    i = 0; ilen = obj_enemy_max
    while i < ilen do
      obj_enemy_spr[i] = put_sprite 'spr_enemy' do
        position obj_enemy_pos_x[i], obj_enemy_pos_y[i]
        frame_index 0
      end
      
      #アニメーションの開始
      obj_enemy_spr[i].start_animation('wait')
      
      i += 1
    end
    
    #---------- ここから ----------
    #残敵数
    obj_minienemy_spr = put_sprite 'spr_enemy' do
      position 16, 0
      frame_index 0
    end
    #アニメーションの開始
    obj_minienemy_spr.start_animation('wait')
    #サイズの変更
    obj_minienemy_spr.scale (32 / obj_enemy_size_w), (32 / obj_enemy_size_h)
    
    obj_txt_enemyzan_spr = put_text 'spr_txt_info' do
      position 52, 6
    end
    obj_txt_enemyzan_spr.text '×'
    
    obj_txt_enemyzan_no_spr = put_text 'spr_txt_info' do
      position 78, 6
    end
    obj_txt_enemyzan_no_spr.text (format('%02d',enemy_zan_view)).to_s
    #---------- ここまで ----------

  obj_minienemy_sprは敵キャラクターの画像オブジェクトを生成し
 scaleで縮小して表示しています。

  obj_txt_enemyzan_spr、obj_txt_enemyzan_no_sprが文字オブジェクトの生成。
  put_textでオブジェクトを生成し、
 各オブジェクトに対してtextを指定することで
 表示するテキストを書き換えています。

  なお、obj_txt_enemyzan_no_sprのテキストで使用されている
 format関数は、数値型の変数を特定の長さの0で埋め、
 文字型の変数に変更しています。
 数値、文字列の0埋め



  スコア、プレイヤーの残機の文字オブジェクトも同様に生成します。

    #残敵数
    obj_minienemy_spr = put_sprite 'spr_enemy' do
      position 16, 0
      frame_index 0
    end
    #アニメーションの開始
    obj_minienemy_spr.start_animation('wait')
    #サイズの変更
    obj_minienemy_spr.scale (32 / obj_enemy_size_w), (32 / obj_enemy_size_h)
    
    obj_txt_enemyzan_spr = put_text 'spr_txt_info' do
      position 52, 6
    end
    obj_txt_enemyzan_spr.text '×'
    
    obj_txt_enemyzan_no_spr = put_text 'spr_txt_info' do
      position 78, 6
    end
    obj_txt_enemyzan_no_spr.text (format('%02d',enemy_zan_view)).to_s
    
    #---------- ここから ----------
    #スコア
    obj_txt_score_spr = put_text 'spr_txt_info' do
      position 320, 6
    end
    obj_txt_score_spr.text 'SCORE'
    
    obj_txt_score_no_spr = put_text 'spr_txt_info' do
      position 400, 6
    end
    obj_txt_score_no_spr.text (format('%06d',score_view)).to_s
    
    
    #残機
    obj_minichar_spr = put_sprite 'spr_char' do
      position 696, 4
      frame_index 0
    end
    #アニメーションの開始
    obj_minichar_spr.start_animation('wait')
    #サイズの変更
    obj_minichar_spr.scale (32 / obj_char_size_w), (32 / obj_char_size_h)
    
    obj_txt_charzan_spr = put_text 'spr_txt_info' do
      position 732, 6
    end
    obj_txt_charzan_spr.text '×'
    
    obj_txt_charzan_no_spr = put_text 'spr_txt_info' do
      position 758, 6
    end
    obj_txt_charzan_no_spr.text (format('%02d',char_zan_view)).to_s
    #---------- ここまで ----------




  残敵キャラクター数、スコア、プレイヤー残機に変更があった場合
 文字オブジェクトのテキストを書き換える処理を追加します。

    #--- 背景 ---
    #背景のスクロール
    obj_bg_pos_y += obj_bg_speed
    
    #背景のスクロールをループさせる
    if obj_bg_pos_y > 600
      obj_bg_pos_y -= 600
    end
    
    #背景のオブジェクトを移動する
    obj_bg_00_spr.position obj_bg_pos_x, obj_bg_pos_y
    obj_bg_01_spr.position obj_bg_pos_x, (obj_bg_pos_y - 600)
    
    #---------- ここから ----------
    #--- 残敵数 ---
    if enemy_zan < 0
      enemy_zan = 0
    end
    
    if enemy_zan != enemy_zan_view
      enemy_zan_view = enemy_zan
      obj_txt_enemyzan_no_spr.text (format('%02d',enemy_zan_view)).to_s
    end
    
    
    #--- スコア ---
    #スコアの最大値
    if score > 999999
      score = 999999
    end
    
    #スコア表示の変更
    if score_view < score
      score_view += 17
      if score_view > score
        score_view = score
      end
      obj_txt_score_no_spr.text (format('%06d',score_view)).to_s
    end
    
    
    #--- 残機 ---
    if char_zan < 0
      char_zan = 0
    end
    
    if char_zan != char_zan_view
      char_zan_view = char_zan
      obj_txt_charzan_no_spr.text (format('%02d',char_zan_view)).to_s
    end
    #---------- ここまで ----------

  スコア表示の変更では
 一度に表示スコアを変更するのではなく、
 実際のスコアに表示スコアが徐々に近づいていくように
 換算する処理を加えることで、演出を上げているところがポイントです。



  メインループ内、各条件が整ったとき
 残り敵キャラクター数、スコア、プレイヤー残機が変更されるよう
 処理を追加します。

    #--- 敵キャラクター ---
    #敵キャラクターの出現カウントダウン
    obj_enemy_next_time -= 1
    
    #カウントダウンが0以下になったら敵を出現させる
    if obj_enemy_next_time < 0
      obj_enemy_next_time = obj_enemy_next + rand(obj_enemy_next_rnd)
      
      i = 0; ilen = obj_enemy_max
      while i < ilen do
        #待機状態の敵キャラクターを探す
        if obj_enemy_anime[i] == 'wait'
          #---------- ここから ----------
          #敵キャラクターの残数を変更
          enemy_zan -= 1
          #---------- ここまで ----------
          #敵キャラクターを行動状態にする
          obj_enemy_pos_x[i] = rand(obj_enemy_w_max)
          obj_enemy_pos_y[i] = (obj_enemy_size_h * (-2))
          
          obj_enemy_speed_r_x[i] = obj_enemy_speed * (1 + ((-2) * rand(1)))
          obj_enemy_speed_r_y[i] = obj_enemy_speed - obj_enemy_speed_rnd + rand(obj_enemy_speed_rnd)
          
          #アニメーションの開始
          obj_enemy_anime[i] = setChangeAnime(obj_enemy_spr[i], obj_enemy_anime[i], 'move')
          
          #ループを終了させる
          i = ilen
        end
        i += 1
      end
    end
    
#~~~~~~~~~~ 中略 ~~~~~~~~~~
        
        #プレイヤーキャラクターとの当たり判定
        if (obj_char_hit_time < 0) && 
           ((obj_char_pos_x + obj_char_hit_x) < (obj_enemy_pos_x_old[i] + obj_enemy_hit_w)) && 
           ((obj_char_pos_x + obj_char_hit_w) > (obj_enemy_pos_x_old[i] + obj_enemy_hit_x)) && 
           ((obj_char_pos_y + obj_char_hit_y) < (obj_enemy_pos_y_old[i] + obj_enemy_hit_h)) && 
           ((obj_char_pos_y + obj_char_hit_h) > (obj_enemy_pos_y_old[i] + obj_enemy_hit_y))
          #---------- ここから ----------
          #プレイヤーキャラクターの残機を変更
          char_zan -= 1
          #---------- ここまで ----------
          #プレイヤーキャラクターに無敵時間を設定
          obj_char_hit_time = obj_char_invincible_time
        end
        
        #プレイヤー弾との当たり判定
        j = 0; jlen = obj_char_tama_max
        while j < jlen do
          #移動状態のプレイヤー弾を探す
          if obj_char_tama_flg[j] && 
             ((obj_char_tama_pos_x[j] + obj_char_tama_hit_x) < (obj_enemy_pos_x_old[i] + obj_enemy_hit_w)) && 
             ((obj_char_tama_pos_x[j] + obj_char_tama_hit_w) > (obj_enemy_pos_x_old[i] + obj_enemy_hit_x)) && 
             ((obj_char_tama_pos_y[j] + obj_char_tama_hit_y) < (obj_enemy_pos_y_old[i] + obj_enemy_hit_h)) && 
             ((obj_char_tama_pos_y[j] + obj_char_tama_hit_h) > (obj_enemy_pos_y_old[i] + obj_enemy_hit_y))
            
            #効果音を再生する
            obj_se_hit00.play()
            #---------- ここから ----------
            #スコアの加算
            score += enemy_score
            #---------- ここまで ----------
            #敵キャラクターに無敵時間を設定
            obj_enemy_hit_time[i] = obj_enemy_invincible_time



  ここまでで、
 画面上にスコアや残機を表示するスクリプトが出来ました。

  画面上部メニューにある、「保存をして実行」を押して
 実際のゲーム画面を確認してみましょう。





06-02 オープニングやエンディングを設定しよう


  ゲームの内容は一通り完成しましたが、
 ゲームを遊んでもらうためには、外見、つまり「タイトル画面」は
 重要な要素のひとつです。

  また、このまま無限に遊び続けられるのも良いですが
 ゲームの終了となる、ゲームクリアやゲームオーバーもつくってみましょう。



  今回もまた、恒例のサンプルの素材を準備いたしました。
 音楽素材は、助教授さまと、rerofumiさまの音楽素材を利用いたします。











  クリップをしたら、ゲームエディタ画面で「OP,ED,全滅時設定」を押します。

  オープニング画面の項目の、「クリップした画像を設定する」を押し、
 設定する画像の「設定する」を押します。


  同様に、エンディング画面も設定します。

  新CoRでは、ゲームオーバー画面が存在しないため、
 ここまで設定できたら、COREシーン一覧からCOREシーンエディタに戻ります。



  新CoRでは、上記の設定だけだと
 ゲーム一覧にはオープニング画面が反映されますが
 実際のゲーム内では表示されません。

  そのため、タイトル画面、ゲームオーバー画面は
 スクリプトで実装する必要があります。

  COREシーンエディタのリソースに
 先ほどの素材の中から、
 タイトル画面、ゲームオーバー画面、
 2種類の音楽素材を追加しておきましょう。




  ここからstart.rb内に、タイトル用のシーンを追加することもできますが、
 今回は、ゲームメイン画面と、タイトル画面でソースファイルを分け
 スクリプトが見やすくなるようにつくってみましょう。


  ソースファイル一覧に戻り。
 タイトル画面用のソースファイルを作成します。

  ソースファイル一覧横の新規作成を押します。


  タイトル画面用のソースファイル名を入力し、
 「新規作成する」を押します。


  ソースファイル一覧にtitle.rbという
 ソースファイルが追加されました。


  「start.rb」を押して、スクリプトの編集画面を開きます。

  画面の一番右に、先ほどと同様にtitle.rbが追加されています。
  編集したいスクリプトを切り替える際は、
 この一覧からクリックすることでも切り替えることができます。



  ソースファイルを作成しただけでは、
 他のソースファイルのスクリプトを読み込んでくれません。

  まずは「start.rb」から「title.rb」を読み込んでもらえるよう
 外部ソースファイルの読み込み設定を行います。

#---------------------------------------
#シューティングサンプルゲーム(新CoR)
#---------------------------------------

#_/_/_/_/_/_/ 外部ソースファイルの読み込み _/_/_/_/_/_/
#---------- ここから ----------
require_code 'title.rb'
#---------- ここまで ----------

  これで、外部ソースファイルの読み込みの準備が完了しました。

  画面上部メニューの「保存する」押してから
 「title.rb」のスクリプト編集画面に移動します。



  全く何も書かれていないスクリプトが表示されますので、
 第01回を参考に、空のスクリプトを作成します。

  このとき、title.rb側では、
 外部ソースファイルの読み込み、ゲームロード後最初のシーンを開始する、という箇所は
 必要ないので削除しておきましょう。

#---------- ここから ----------
#---------------------------------------
#シューティングサンプルゲーム(新CoR)
#---------------------------------------

#_/_/_/_/_/_/ グローバル変数の定義 _/_/_/_/_/_/



#_/_/_/_/_/_/ ユーザ関数の定義 _/_/_/_/_/_/



#********** titleシーン ここから **********
scene 'title' do
  #_/_/_/ ローカル変数の定義 _/_/_/
  
  
  
  preload do
    #----- ゲームロード時に実行される処理 -----
    #_/_/_/ 素材のロード _/_/_/
    
    
  end
  
  create do
    #----- シーンロード時に実行される処理 -----
    #_/_/_/ 変数の初期化 _/_/_/
    
    
    #_/_/_/ 素材の初期化 _/_/_/
    
    
  end
  
  update do
    #----- シーンのメインループ処理 -----
    #_/_/_/ 入力の判定 _/_/_/
    
    
    #_/_/_/ メイン処理 _/_/_/
    
    
  end
  
  render do
    #----- シーンのメイン描画処理 -----
    #_/_/_/ 描画の変更 _/_/_/
    
    
  end
end
#********** titleシーン ここまで **********

#********** gameoverシーン ここから **********
scene 'gameover' do
  #_/_/_/ ローカル変数の定義 _/_/_/
  
  
  
  preload do
    #----- ゲームロード時に実行される処理 -----
    #_/_/_/ 素材のロード _/_/_/
    
    
  end
  
  create do
    #----- シーンロード時に実行される処理 -----
    #_/_/_/ 変数の初期化 _/_/_/
    
    
    #_/_/_/ 素材の初期化 _/_/_/
    
    
  end
  
  update do
    #----- シーンのメインループ処理 -----
    #_/_/_/ 入力の判定 _/_/_/
    
    
    #_/_/_/ メイン処理 _/_/_/
    
    
  end
  
  render do
    #----- シーンのメイン描画処理 -----
    #_/_/_/ 描画の変更 _/_/_/
    
    
  end
end
#********** gameoverシーン ここまで **********

#_/_/_/_/_/_/ 素材の定義 _/_/_/_/_/_/




#---------- ここまで ----------

  titleシーンはそこまで大きなスクリプトにはならないため
 同じソースファイルにgameoverシーンも追加しています。

  このように、ソースファイルを分けずに
 複数のシーンを実装する方法もあります。



  titleシーンとgameoverシーンに
 メイン画像オブジェクトを表示します。

  これまでのおさらいもかねて
 一気に実装してみましょう。

#********** titleシーン ここから **********
scene 'title' do
  #_/_/_/ ローカル変数の定義 _/_/_/
  #---------- ここから ----------
  #タイトル背景オブジェクト
  obj_bg_title_spr = nil
  
  #文字オブジェクト
  obj_txt_title_spr = nil
  #---------- ここまで ----------
  
  preload do
    #----- ゲームロード時に実行される処理 -----
    #_/_/_/ 素材のロード _/_/_/
    #---------- ここから ----------
    #----- 画像 -----
    #タイトル背景
    image 'img_bg_title', id: 78741
    #---------- ここまで ----------
  end
  
  create do
    #----- シーンロード時に実行される処理 -----
    #_/_/_/ 変数の初期化 _/_/_/
    
    
    #_/_/_/ 素材の初期化 _/_/_/
    #---------- ここから ----------
    #タイトル背景
    obj_bg_title_spr = put_sprite 'spr_bg_title' do
      position 0, 0
      src_rect 0, 0, 800, 600
    end
    
    #タイトル文字
    obj_txt_title_spr = put_text 'spr_txt_title' do
      position 400, 550
    end
    obj_txt_title_spr.text 'PLEASE PUSH Z KEY'
    #---------- ここまで ----------
  end
  
  update do
    #----- シーンのメインループ処理 -----
    #_/_/_/ 入力の判定 _/_/_/
    
    
    #_/_/_/ メイン処理 _/_/_/
    
    
  end
  
  render do
    #----- シーンのメイン描画処理 -----
    #_/_/_/ 描画の変更 _/_/_/
    
    
  end
end
#********** titleシーン ここまで **********

#********** gameoverシーン ここから **********
scene 'gameover' do
  #_/_/_/ ローカル変数の定義 _/_/_/
  #---------- ここから ----------
  #タイトル背景オブジェクト
  obj_bg_gameover_spr = nil
  #---------- ここまで ----------
  
  preload do
    #----- ゲームロード時に実行される処理 -----
    #_/_/_/ 素材のロード _/_/_/
    #---------- ここから ----------
    #----- 画像 -----
    #ゲームオーバー背景
    image 'img_bg_gameover', id: 78769
    #---------- ここまで ----------
  end
  
  create do
    #----- シーンロード時に実行される処理 -----
    #_/_/_/ 変数の初期化 _/_/_/
    
    
    #_/_/_/ 素材の初期化 _/_/_/
    #---------- ここから ----------
    #ゲームオーバー背景
    obj_bg_gameover_spr = put_sprite 'spr_bg_gameover' do
      position 0, 0
      src_rect 0, 0, 800, 600
    end
    #---------- ここまで ----------
  end
  
  update do
    #----- シーンのメインループ処理 -----
    #_/_/_/ 入力の判定 _/_/_/
    
    
    #_/_/_/ メイン処理 _/_/_/
    
    
  end
  
  render do
    #----- シーンのメイン描画処理 -----
    #_/_/_/ 描画の変更 _/_/_/
    
    
  end
end
#********** gameoverシーン ここまで **********

#_/_/_/_/_/_/ 素材の定義 _/_/_/_/_/_/

#----- 画像の定義 -----
#---------- ここから ----------
#タイトル背景
sprite 'spr_bg_title' do
  image 'img_bg_title'
  origin :left_top
end

#ゲームオーバー背景
sprite 'spr_bg_gameover' do
  image 'img_bg_gameover'
  origin :left_top
end

#----- 文字の定義 -----

text 'spr_txt_title' do
  origin :center
  font_size 18
  color '#FFFFFF'
end
#---------- ここまで ----------

  素材の定義は、全てのソースファイルで共通のため
 同じ名前が重複して使用されいように注意しましょう。



  titleシーンとgameoverシーンに
 音楽が再生されるようにします。

#********** titleシーン ここから **********
scene 'title' do
  #_/_/_/ ローカル変数の定義 _/_/_/
  
  #タイトル背景オブジェクト
  obj_bg_title_spr = nil
  
  #文字オブジェクト
  obj_txt_title_spr = nil
  #---------- ここから ----------
  #音楽オブジェクト
  obj_bgm_title = nil
  #---------- ここまで ----------
  preload do
    #----- ゲームロード時に実行される処理 -----
    #_/_/_/ 素材のロード _/_/_/
    #----- 画像 -----
    #タイトル背景
    image 'img_bg_title', id: 78741
    
    #---------- ここから ----------
    #----- 音楽 -----
    #メイン音楽
    music 'bgm_title',  id: 63621
    #---------- ここまで ----------
  end
  
  create do
    #----- シーンロード時に実行される処理 -----
    #_/_/_/ 変数の初期化 _/_/_/
    
    
    #_/_/_/ 素材の初期化 _/_/_/
    #タイトル背景
    obj_bg_title_spr = put_sprite 'spr_bg_title' do
      position 0, 0
      src_rect 0, 0, 800, 600
    end
    
    #タイトル文字
    obj_txt_title_spr = put_text 'spr_txt_title' do
      position 400, 550
    end
    obj_txt_title_spr.text 'PLEASE PUSH Z KEY'
    
    #---------- ここから ----------
    #音楽
    obj_bgm_title = add_music('bgm_title')
    volBGM(obj_bgm_title, 0.5)
    
    #音楽の再生を開始する
    obj_bgm_title.play()
    #---------- ここまで ----------
  end
  
  update do
    #----- シーンのメインループ処理 -----
    #_/_/_/ 入力の判定 _/_/_/
    
    
    #_/_/_/ メイン処理 _/_/_/
    
    
  end
  
  render do
    #----- シーンのメイン描画処理 -----
    #_/_/_/ 描画の変更 _/_/_/
    
    
  end
end
#********** titleシーン ここまで **********

#********** gameoverシーン ここから **********
scene 'gameover' do
  #_/_/_/ ローカル変数の定義 _/_/_/
  
  #タイトル背景オブジェクト
  obj_bg_gameover_spr = nil
  #---------- ここから ----------
  #音楽オブジェクト
  obj_bgm_gameover = nil
  #---------- ここまで ----------
  preload do
    #----- ゲームロード時に実行される処理 -----
    #_/_/_/ 素材のロード _/_/_/
    #----- 画像 -----
    #ゲームオーバー背景
    image 'img_bg_gameover', id: 78769
    
    #---------- ここから ----------
    #----- 音楽 -----
    #メイン音楽
    music 'bgm_gameover',  id: 48990
    #---------- ここまで ----------
  end
  
  create do
    #----- シーンロード時に実行される処理 -----
    #_/_/_/ 変数の初期化 _/_/_/
    
    
    #_/_/_/ 素材の初期化 _/_/_/
    #ゲームオーバー背景
    obj_bg_gameover_spr = put_sprite 'spr_bg_gameover' do
      position 0, 0
      src_rect 0, 0, 800, 600
    end
    
    #---------- ここから ----------
    #音楽
    obj_bgm_gameover = add_music('bgm_gameover')
    volBGM(obj_bgm_gameover, 0.5)
    
    #音楽の再生を開始する
    obj_bgm_gameover.play()
    #---------- ここまで ----------
  end
  
  update do
    #----- シーンのメインループ処理 -----
    #_/_/_/ 入力の判定 _/_/_/
    
    
    #_/_/_/ メイン処理 _/_/_/
    
    
  end
  
  render do
    #----- シーンのメイン描画処理 -----
    #_/_/_/ 描画の変更 _/_/_/
    
    
  end
end
#********** gameoverシーン ここまで **********



  タイトル画面でZキーの入力を受け付けるようにします。

#********** titleシーン ここから **********
scene 'title' do
  #_/_/_/ ローカル変数の定義 _/_/_/
  #---------- ここから ----------
  #キー入力用のフラグ
  key_flg_z_st = nil;  key_flg_z_do = nil;  key_flg_z_en = nil
  
  change_scene_flg = nil
  #---------- ここまで ----------
  #タイトル背景オブジェクト
  obj_bg_title_spr = nil
  
  #文字オブジェクト
  obj_txt_title_spr = nil
  
  #音楽オブジェクト
  obj_bgm_title = nil
  
  preload do
    #----- ゲームロード時に実行される処理 -----
    #_/_/_/ 素材のロード _/_/_/
    #----- 画像 -----
    #タイトル背景
    image 'img_bg_title', id: 78741
    
    
    #----- 音楽 -----
    #メイン音楽
    music 'bgm_title',  id: 63621
    
  end
  
  create do
    #----- シーンロード時に実行される処理 -----
    #_/_/_/ 変数の初期化 _/_/_/
    #---------- ここから ----------
    #キー入力用のフラグ
    key_flg_z_st = false;  key_flg_z_do = false;  key_flg_z_en = false
    
    change_scene_flg = false
    #---------- ここまで ----------
    
    #_/_/_/ 素材の初期化 _/_/_/
    #タイトル背景
    obj_bg_title_spr = put_sprite 'spr_bg_title' do
      position 0, 0
      src_rect 0, 0, 800, 600
    end
    
    #タイトル文字
    obj_txt_title_spr = put_text 'spr_txt_title' do
      position 400, 550
    end
    obj_txt_title_spr.text 'PLEASE PUSH Z KEY'
    
    
    #音楽
    obj_bgm_title = add_music('bgm_title')
    volBGM(obj_bgm_title, 0.5)
    
    #音楽の再生を開始する
    obj_bgm_title.play()
    
  end
  
  update do
    #----- シーンのメインループ処理 -----
    #_/_/_/ 入力の判定 _/_/_/
    #---------- ここから ----------
    #キー入力判定
    if keyboard.down?('Z')
      key_flg_z_st = true
    else
      key_flg_z_st = false
      key_flg_z_do = false
    end
    #---------- ここまで ----------
    
    #_/_/_/ メイン処理 _/_/_/
    #---------- ここまで ----------
    #--- キー入力処理 ---
    #Zキー
    if (key_flg_z_st && !key_flg_z_do)
      key_flg_z_do = true
      #--- 押された時に実行する処理 ---
      change_scene_flg = true
    elsif key_flg_z_do
      key_flg_z_en = true
      #--- 押されつづけている時に実行する処理 ---
      
    elsif key_flg_z_en
      key_flg_z_en = false
      #--- 離された時に実行する処理 ---
      
    end
    #---------- ここまで ----------
  end
  
  render do
    #----- シーンのメイン描画処理 -----
    #_/_/_/ 描画の変更 _/_/_/
    
    
  end
end
#********** titleシーン ここまで **********

  Zキーの入力を受け付けた場合、
 ローカル変数のchange_scene_flgがtrueになります。



  change_scene_flgがtrueになったとき
 ゲームメイン画面のstartシーンに移動するようにします。

#********** titleシーン ここから **********
scene 'title' do
#~~~~~~~~~~ 中略 ~~~~~~~~~~
    #_/_/_/ メイン処理 _/_/_/
    
    #--- キー入力処理 ---
    #Zキー
    if (key_flg_z_st && !key_flg_z_do)
      key_flg_z_do = true
      #--- 押された時に実行する処理 ---
      change_scene_flg = true
    elsif key_flg_z_do
      key_flg_z_en = true
      #--- 押されつづけている時に実行する処理 ---
      
    elsif key_flg_z_en
      key_flg_z_en = false
      #--- 離された時に実行する処理 ---
      
    end
    #---------- ここから ----------
    #シーンの移動
    if change_scene_flg
      #音楽の再生を停止する
      obj_bgm_title.stop()
      
      #画像素材を開放する
      obj_bg_title_spr.destroy
      
      obj_txt_title_spr.destroy
      
      #startシーンに移動
      change_scene "start"
    end
    #---------- ここまで ----------

  現在のシーンを終了する前に
 使用した画像素材をdestroyの指示で開放します。

  今回のような短いゲームでは
 画像の開放をしていなくても影響はほぼありませんが、
 長編のゲームになった場合
 ゲーム自体がフリーズしてしまう恐れがあるためです。

  change_sceneで別のシーンに移動します。
  移動先はシーン名の文字列で指定します。



  ゲーム開始時にタイトル画面から始まるように
 ゲームロード後最初のシーンを変更します。

  title.rbを保存してから、start.rbに移動しましょう。


  start.rbのスクリプトの最後にある
 ゲームロード後最初のシーンを書き換えます。

#_/_/_/_/_/_/ ゲームロード後最初のシーンを開始する _/_/_/_/_/_/
start_scene 'title' #------- 変更



  ゲームメイン画面で特定の条件を満たしたとき、
 ゲームクリア画面、ゲームオーバー画面に移動するように
 処理を追加します。

  まずは、ゲームクリア、ゲームオーバーを判定する
 ローカル変数の定義から行います。

#********** startシーン ここから **********
scene 'start' do
  #_/_/_/ ローカル変数の定義 _/_/_/
#~~~~~~~~~~ 中略 ~~~~~~~~~~
  #システム関連
  #---------- ここから ----------
  gameclear_flg = nil
  gameover_flg  = nil
  #---------- ここまで ----------
  score = nil;      score_view = nil
  enemy_score = nil
  
  char_zan = nil;   char_zan_view = nil
  enemy_zan = nil;  enemy_zan_view = nil



  定義したローカル変数を初期化します。

  create do
    #----- シーンロード時に実行される処理 -----
    #_/_/_/ 変数の初期化 _/_/_/
#~~~~~~~~~~ 中略 ~~~~~~~~~~
    #システム関連
    #---------- ここから ----------
    gameclear_flg = false
    gameover_flg  = false
    #---------- ここまで ----------
    score = 0;         score_view = score
    enemy_score = 500
    
    char_zan = 5;      char_zan_view = char_zan
    enemy_zan = 20;    enemy_zan_view = enemy_zan



  プレイヤーキャラクターの残機がなくなった場合
 入力操作を停止するようにします。

    #--- プレイヤーキャラクター ---
    #プレイヤーキャラクター移動方向のフラグによって処理を分岐
    if (obj_char_flg_up && obj_char_flg_left) && (char_zan > 0) #------- 変更
      #プレイヤーキャラクターのアニメーションを変更
      obj_char_anime = setChangeAnime(obj_char_spr, obj_char_anime, 'left')
      
      #プレイヤーキャラクターの座標を変更
      obj_char_pos_x -= obj_char_speed_r_x;  obj_char_pos_y -= obj_char_speed_r_y
      
    elsif (obj_char_flg_down && obj_char_flg_left) && (char_zan > 0) #------- 変更
      #プレイヤーキャラクターのアニメーションを変更
      obj_char_anime = setChangeAnime(obj_char_spr, obj_char_anime, 'left')
      
      #プレイヤーキャラクターの座標を変更
      obj_char_pos_x -= obj_char_speed_r_x;  obj_char_pos_y += obj_char_speed_r_y
      
    elsif (obj_char_flg_up && obj_char_flg_right) && (char_zan > 0) #------- 変更
      #プレイヤーキャラクターのアニメーションを変更
      obj_char_anime = setChangeAnime(obj_char_spr, obj_char_anime, 'right')
      
      #プレイヤーキャラクターの座標を変更
      obj_char_pos_x += obj_char_speed_r_x;  obj_char_pos_y -= obj_char_speed_r_y
      
    elsif (obj_char_flg_down && obj_char_flg_right) && (char_zan > 0) #------- 変更
      #プレイヤーキャラクターのアニメーションを変更
      obj_char_anime = setChangeAnime(obj_char_spr, obj_char_anime, 'right')
      
      #プレイヤーキャラクターの座標を変更
      obj_char_pos_x += obj_char_speed_r_x;  obj_char_pos_y += obj_char_speed_r_y
      
    elsif obj_char_flg_up && (char_zan > 0) #------- 変更
      #プレイヤーキャラクターのアニメーションを変更
      obj_char_anime = setChangeAnime(obj_char_spr, obj_char_anime, 'neutral')
      
      #プレイヤーキャラクターの座標を変更
      obj_char_pos_y -= obj_char_speed
      
    elsif obj_char_flg_down && (char_zan > 0) #------- 変更
      #プレイヤーキャラクターのアニメーションを変更
      obj_char_anime = setChangeAnime(obj_char_spr, obj_char_anime, 'neutral')
      
      #プレイヤーキャラクターの座標を変更
      obj_char_pos_y += obj_char_speed
      
    elsif obj_char_flg_left && (char_zan > 0) #------- 変更
      #プレイヤーキャラクターのアニメーションを変更
      obj_char_anime = setChangeAnime(obj_char_spr, obj_char_anime, 'left')
      
      #プレイヤーキャラクターの座標を変更
      obj_char_pos_x -= obj_char_speed
      
    elsif obj_char_flg_right && (char_zan > 0) #------- 変更
      #プレイヤーキャラクターのアニメーションを変更
      obj_char_anime = setChangeAnime(obj_char_spr, obj_char_anime, 'right')
      
      #プレイヤーキャラクターの座標を変更
      obj_char_pos_x += obj_char_speed
      
    else
      #プレイヤーキャラクターのアニメーションを変更
      obj_char_anime = setChangeAnime(obj_char_spr, obj_char_anime, 'neutral')
    end



  プレイヤーキャラクターがダメージを受けている間は
 弾の発射が行えないようにします。

    #プレイヤー弾の発射
    if obj_char_tama_flg_z && (obj_char_tama_next_time < 0) && (obj_char_hit_time < 0) #------- 変更
      obj_char_tama_next_time = obj_char_tama_next
      
      i = 0; ilen = obj_char_tama_max
      while i < ilen do
        #待機状態のプレイヤー弾を探す
        if !obj_char_tama_flg[i]
          #プレイヤー弾を移動状態にする
          obj_char_tama_pos_x[i] = obj_char_pos_x + ((obj_char_size_w - obj_char_tama_size_w) / 2)
          obj_char_tama_pos_y[i] = obj_char_pos_y
          
          obj_char_tama_scale_x[i] = 1;  obj_char_tama_scale_y[i] = 0
          obj_char_tama_scale_count[i] = 0
          
          obj_char_tama_flg[i] = true
          
          #ループを終了させる
          i = ilen
        end
        i += 1
      end
    end



  ゲームオーバーの条件が揃ったら
 ゲームオーバーのフラグをtrueにします。

        #プレイヤー弾のオブジェクトを移動する
        if (obj_char_tama_pos_x_old[i] != obj_char_tama_pos_x[i]) || 
           (obj_char_tama_pos_y_old[i] != obj_char_tama_pos_y[i])
          
          obj_char_tama_pos_x_old[i] = obj_char_tama_pos_x[i]
          obj_char_tama_pos_y_old[i] = obj_char_tama_pos_y[i]
          
          obj_char_tama_spr[i].position obj_char_tama_pos_x[i], obj_char_tama_pos_y[i]
          
          obj_char_tama_spr[i].scale obj_char_tama_scale_x[i], obj_char_tama_scale_y[i]
        end
      end
      i += 1
    end
    #---------- ここから ----------
    #ゲームオーバー
    if (char_zan == 0) && (obj_char_hit_time < 0)
      gameover_flg = true
    end
    #---------- ここまで ----------
    #--- 敵キャラクター ---
    #敵キャラクターの出現カウントダウン
    obj_enemy_next_time -= 1



  残り敵キャラクター出現数が0になったら
 次の敵キャラクターを出現させないようにします。

    #--- 敵キャラクター ---
    #敵キャラクターの出現カウントダウン
    obj_enemy_next_time -= 1
    
    #カウントダウンが0以下になったら敵を出現させる
    if obj_enemy_next_time < 0 && enemy_zan > 0 #------- 変更
      obj_enemy_next_time = obj_enemy_next + rand(obj_enemy_next_rnd)
      
      i = 0; ilen = obj_enemy_max
      while i < ilen do
        #待機状態の敵キャラクターを探す
        if obj_enemy_anime[i] == 'wait'
          #敵キャラクターの残数を変更
          enemy_zan -= 1



  ゲームクリア条件を追加するため
 敵キャラクターが全ていなくなったか判定する処理を追加します。

    #敵キャラクターの行動ループ
    #---------- ここから ----------
    wrk_enemy_count = 0
    #---------- ここまで ----------
    i = 0; ilen = obj_enemy_max
    while i < ilen do
      #移動状態の敵キャラクターを探す
      if obj_enemy_anime[i] == 'move'
        #---------- ここから ----------
        wrk_enemy_count += 1
        #---------- ここまで ----------
        #敵キャラクターの座標値を移動させる
        obj_enemy_pos_x[i] += obj_enemy_speed_r_x[i]
        obj_enemy_pos_y[i] += obj_enemy_speed_r_y[i]
        
#~~~~~~~~~~ 中略 ~~~~~~~~~~
      
      #ダメージ状態の敵キャラクターを探す
      if obj_enemy_anime[i] == 'damage'
        #---------- ここから ----------
        wrk_enemy_count += 1
        #---------- ここまで ----------
        #敵キャラクターの無敵時間をカウント
        if obj_enemy_hit_time[i] > (-1)
          obj_enemy_hit_time[i] -= 1
          

  wrk_enemy_countというカウンター変数を用意して
 敵キャラクターがmoveまたはdamageの行動状態が存在した場合
 カウンターを追加しています。

  敵キャラクターの行動を全て確認したとき
 カウンターが0のままであれば、
 全ての敵キャラクターがいなくなったと判断しています。



  ゲームクリアの条件が揃ったら、
 ゲームクリアのフラグをtrueにします。

            #敵キャラクターのオブジェクトを移動する
            obj_enemy_spr[i].position obj_enemy_pos_x[i], obj_enemy_pos_y[i]
          elsif ((obj_enemy_hit_time[i] % 10) == 0)
            setAlpha(obj_enemy_spr[i], 1)
          elsif ((obj_enemy_hit_time[i] % 5) == 0)
            setAlpha(obj_enemy_spr[i], 0)
          elsif ((obj_enemy_hit_time[i] % 3) == 0)
            setAlpha(obj_enemy_spr[i], 0.5)
          end
        end
      end
      i += 1
    end
    #---------- ここから ----------
    #ゲームクリア
    if (enemy_zan == 0) && (wrk_enemy_count == 0)
      gameclear_flg = true
    end
    #---------- ここまで ----------



  ゲームクリアフラグまたはゲームオーバーフラグがtrueの場合
 シーンを移動する処理を追加します。

    #--- 残機 ---
    if char_zan < 0
      char_zan = 0
    end
    
    if char_zan != char_zan_view
      char_zan_view = char_zan
      obj_txt_charzan_no_spr.text (format('%02d',char_zan_view)).to_s
    end
    
    #---------- ここから ----------
    #シーンの移動
    if gameclear_flg || gameover_flg
      #音楽の再生を停止する
      obj_bgm_main.stop()
      
      
      #画像素材を開放する
      obj_bg_00_spr.destroy;  obj_bg_01_spr.destroy
      
      obj_char_spr.destroy
      
      i = 0; ilen = obj_char_tama_max
      while i < ilen do
        obj_char_tama_spr[i].destroy
        i += 1
      end
      
      i = 0; ilen = obj_enemy_max
      while i < ilen do
        obj_enemy_spr[i].destroy
        i += 1
      end
      
      obj_minienemy_spr.destroy
      obj_txt_enemyzan_spr.destroy;    obj_txt_enemyzan_no_spr.destroy
      
      obj_txt_score_spr.destroy;       obj_txt_score_no_spr.destroy
      
      obj_minichar_spr.destroy
      obj_txt_charzan_spr.destroy;     obj_txt_charzan_no_spr.destroy
      
      
      #配列を開放する
      obj_char_tama_spr.clear
      
      obj_char_tama_pos_x.clear;       obj_char_tama_pos_y.clear
      
      obj_char_tama_scale_x.clear;     obj_char_tama_scale_y.clear
      obj_char_tama_scale_count.clear
      
      obj_char_tama_pos_x_old.clear;   obj_char_tama_pos_y_old.clear
      
      obj_char_tama_flg.clear
      
      obj_enemy_spr.clear
      
      obj_enemy_pos_x.clear;           obj_enemy_pos_y.clear
      obj_enemy_pos_x_old.clear;       obj_enemy_pos_y_old.clear
      
      obj_enemy_speed_r_x.clear;       obj_enemy_speed_r_y.clear
      
      obj_enemy_hit_time.clear
      
      obj_enemy_anime.clear
      
      
      if gameclear_flg
        #ゲームクリア画面に移動
        game_clear
      else
        #gameoverシーンに移動
        change_scene "gameover"
      end
    end
    #---------- ここまで ----------
  end
  
  render do
    #----- シーンのメイン描画処理 -----
    #_/_/_/ 描画の変更 _/_/_/
    
    
  end

  シーンを移動する前に
 destroyで画像素材の開放と、clearで配列の開放を行っています。

  配列の開放も画像素材の開放と同様に
 ゲーム自体がフリーズしてしまう恐れがあるため
 必ず行うようにしておきましょう。


  素材を開放すると、当然ながら
 素材を扱う処理がこの後に存在しているとエラーとなってしまいますので
 メインループ内の途中ではフラグを立てておき
 updateの一番最後で、シーンを移動するように順番を組み立てています。


  game_clearはゲームクリア画面に移動します。
  この命令で、Rmakeのゲームクリア条件も達成したことになります。

  ゲームオーバーの場合は
 title.rb内に作成したgameoverシーンに移動しています。



  ここまでで、タイトル画面、ゲームクリア画面、ゲームオーバー画面を
 表示するスクリプトが出来ました。

  画面上部メニューにある、「保存をして実行」を押して
 実際のゲーム画面を確認してみましょう。



06-03 おわりに


  いかがでしたでしょうか。
 これで、立派にゲームとして必要な要素が揃いました。

  さて、次回は、いよいよ最終回。
 「より面白くするために」と題しまして、
 更なるテクニックをご紹介します。


  今回までのスクリプト全体
CoRでゲームをつくってみよう / 第06回



  第01回 CoRの開発準備をしよう
  第02回 画面に背景やキャラクターを描画する
  第03回 背景やキャラクターを動かす
  第04回 敵キャラクターを登場させる
  第05回 弾を発射する
  第06回 ゲームとして必要な機能を追加する
  第07回 より面白くするために

コメントする

コメントするには、ログインする必要があります。

コメント一覧

Material 71926 1 mini なとおとき(投稿日:2022/08/27 21:49, 履歴)
0埋め便利!

タイトル画面をスクリプトで作る機会ってRmakeでは
あんまり無かったと思うので助かるなと思いました。

シーンの移り方も分かってありがたいです。
Cdv30200 aoi icon mini aoihikawa(投稿日:2022/08/28 11:55, 履歴)
この0埋め処理は
Rubyと同じ仕組みを利用しています。

CoRはRubyをベースに拡張したつくりになっているため
一部のRuby関数は同様に利用することができるのです。