ツクール変数・配列操作


メニュー
ツクールのスイッチや変数をスクリプトで操作する (2004/8/14)
乱数の一括代入でばらばらの数値を入れる (2005/7/24)
部分的ソート (2004/7/24)
最大値・最小値を取得する (2005/7/24)
指定した数値を範囲の前後に移動する (2004/8/18)
ランダムに並び替える (2005/7/24)
重複のない乱数を一括代入する (2005/7/24)

ソースコード(Arrayクラス拡張用)



● ツクールのスイッチや変数をスクリプトで操作する

200Xからの感覚からするとXPではイベントコマンドでのスイッチや変数の操作が
やや使いづらくなっています。一番痛いのはやはりスイッチや変数の番号を
変数で指定できなくなったことです。この辺りの処理はスクリプトで代用しましょう。

ツクールのスイッチと変数は以下のグローバル変数で管理されています。
$game_switches[n] #スイッチn番
$game_variables[n] #変数n番


まずスイッチのON/OFFはtrueとfalseを代入することで切り替えます。
$game_switches[1] = true #スイッチ1番をON
$game_switches[10] = false #スイッチ10番をOFF


変数は言うまでもなく…
$game_variables[1] = 2 #変数1番に2を代入
もちろん数値の代わりに文字列を代入することも可能です。

そして大切なのは[]に変数を入れることもできるということです。
$game_switches[$game_variables[5]] #変数5番目の値のスイッチの操作
$game_variables[$game_variables[5]] #変数5番目の値の変数の操作

これで200X並の処理が可能ですね。

さらに200Xではできなかった「変数n番のスイッチがONのとき」という条件分岐も作れます。
if $game_switches[$game_variables[5]] == true #変数5番目の値のスイッチがONの時
ちなみにtrueの場合は"== true"を省略しても構いません。


メニューへ


● 乱数の一括代入でばらばらの数値を入れる

200XではできたのにXPではできなくなったことの一つに乱数の一括代入があります。
一応できるにはできるのですが、200Xでは指定した範囲にそれぞれ別の数値が入るのに
XPではすべて同じ値が入ってしまいます。そこでXPでも一括してそれぞればらばらの数値を
代入するようにメソッドを作りました。


  #--------------------------------------------------------------------------
  # ● 乱数まとめ入れ
  #--------------------------------------------------------------------------
  def randpack(min, max, start, goal)
    #min=乱数の最小値,max=乱数の最大値,start=範囲の最初, goal=範囲の最後
    for pointer in start..goal
      #変数pointerの値を変数startから変数goalまで変動させながらループ
      $game_variables[pointer] = rand(max - min + 1) + min
      #変数pointerで指定したツクール変数に変数minから変数maxまでの乱数を代入
    end
  end

ループしながら変数範囲の最初から最後まで別個に乱数を代入するという代物です。
まず感動したのが"for X in Y..Z 〜 end"の命令文です。
これを使うと変数Xの値を変数Yから変数Zまで1ずつ加算しながらループします。
おかげですっきりまとめることができました。

"rand(n)"は0以上n未満の整数を返す関数です。
rand(10)と書くと0〜9の乱数を生成します。
21〜30を指定したい時はrand(10) + 21という感じで使います。

さて肝心の使い方ですが、このメソッドでは「変数のstart番からgoal番まで乱数aa〜bbを代入」
という形で4つの引数をとります。
次のように実行します。

◆スクリプト:randpack(-999999, 999999, 1, 1000,)

この場合は-999999から999999までの乱数を1〜1000番の変数に代入します。


ところで、スクリプトからではなくイベントコマンドをこのような動作に変えることもできます。
"Interpreter 4"の141行目辺り("一括操作のためにループ"の中)に以下の3行を追加しましょう。

if @parameters[3] == 2
  value = @parameters[4] + rand(@parameters[5] - @parameters[4] + 1)
end


慣れてくるとツクールの変数ではなく配列を直接使うようになってきます。
そこでこのスクリプトを配列一般に使えるように改造します。
配列はArrayクラスのオブジェクトですから、Arrayクラスのメソッドにします。

class Array
  #--------------------------------------------------------------------------
  # ● 乱数まとめ入れ
  #--------------------------------------------------------------------------
  def randpack(min, max,start, goal)
    #min=乱数の最小値,max=乱数の最大値,start=範囲の最初, goal=範囲の最後
    for pointer in start..goal
      #変数pointerの値を変数startから変数goalまで変動させながらループ
      self[pointer] = rand(max - min + 1) + min
      #変数pointerで指定したツクール変数に変数minから変数maxまでの乱数を代入
    end
  end
end

$game_variablesだった部分がselfに代わっています。
Arrayクラスではselfの中に呼び出し元の配列が入る仕組みになっています。

使い方はこうなります。
変数(配列)名.randpack(-999999, 999999, 0, 1000)

これで-999999から999999までの乱数を配列の0から1000に代入します。


メニューへ


● 部分的ソート

変数並び替えパッケージみたいなものを作ろうと思っていたら
配列の中にすでにソート関数が用意してありました。
しかもスクリプトで1から組んだのと比べものにならないほど高速です。

というわけで、次に紹介するのはツクール変数を一度配列に押し込んでから
ソートして再びツクール変数に返すスクリプトです。


  #--------------------------------------------------------------------------
  # ● 部分的昇順ソート
  #--------------------------------------------------------------------------
  def partialsort_up(start, goal)   #start=範囲の最初, goal=範囲の最後
    array = []                      #配列初期化
    for pointer in start..goal      #startからgoalまで繰り返す
      array.push $game_variables[pointer]  #配列に順次代入
    end
    array.sort!                     #配列をソートする
    for pointer in start..goal      #startからgoalまで繰り返す
      $game_variables[pointer] = array[pointer - start]  #元の配列に代入
    end
  end

ここで目新しいものはpushとsort!くらいですね。
push : 配列の末尾に新しい要素を追加するメソッド
sort! : 配列の要素を小さい方から順に並び替えるメソッド

降順に並べるにはarray.sort!の下にarray.reverse!を入れましょう。

次のように実行します。

◆スクリプト:partialsort_up(1, 1000)

これでツクール変数の1〜1000番が昇順に並びます。


では、これも一般的な配列用に改造してみます。
class Array
  #--------------------------------------------------------------------------
  # ● 部分的昇順ソート
  #--------------------------------------------------------------------------
  def partialsort_up(start, goal)   #start=範囲の最初, goal=範囲の最後
    array = self[start..goal]       #配列初期化
    array.sort!                     #配列をソートする
    self[start..goal] = array       #元の配列に代入
  end
end

ずいぶんと簡単になりましたね。
ツクール変数は通常の配列と違って範囲式やらArrayのメソッドやらが使えないので
スクリプトから動かそうとしたら結構大変です。

ところで直接配列を使う人が部分的ソートを必要とすることなんてあるのでしょうか…??


メニューへ


● 変数の最大値・最小値を取得する

何かと使い道が多い(かもしれない)スクリプトです。
素早さが一番大きいキャラから行動したり、HPが一番小さいキャラを狙ったり、
信頼度が一番高いキャラの特殊イベントを発生させたり…という判定に使えます。


  #--------------------------------------------------------------------------
  # ● 最大値検索
  #--------------------------------------------------------------------------
  def search_max(start, goal)
    #start=範囲の最初, goal=範囲の最後
    max_variable = []                #最大値とその番号を記録する配列
    for pointer in start..goal
    #変数pointerをstartからgoalまで繰り返す
      if max_variable[0] == nil or max_variable[0] < $game_variables[pointer] 
      #最大値がまだ記録されてないか、現在の変数が記録した最大値より大きい時
        max_variable = [$game_variables[pointer],pointer]
        #最大値に現在の変数とその変数の番号を代入
      elsif max_variable[0] == $game_variables[pointer]
        #現在の変数が記録した最大値と同じ時
        max_variable.push pointer  #配列に変数の番号を追加
      end
    end
    return max_variable              #最大値とその番号の配列を返す
  end

変数を1個ずつ順番に調べながらmax_variableより大きい値があれば
その値と変数の番号を代入しています。
これを最小値検索に変えるには6行目の"<"を">"にするだけです。

次のように実行します。

◆スクリプト:a = search_max(1, 1000)

これで変数1〜1000のなかから最大値と変数番号が分かります。
結果は配列aの0番目(a[0])に最大値が、
それ以降に最大値をとる変数の番号が入っています。
最大値が同値で複数あった場合にはその変数の番号が全部記録されるわけです。

例えば以下のような並びなら、
変数の番号12345
変数の値225615630
p a #=> [56, 2, 4]
という配列になります。

この中でもしも必要な変数番号が1個だけなら、

a = search_max(1, 1000) b = rand(a.size - 1) + 1
p a[b]


という感じでランダムに1つを選んで取り出せます。


さて、これだけでも十分使えるとは思いますが、「HPが一番小さいキャラを狙う」といった場合、
死んだキャラ(HP=0)は対象にしたくないですね。
そこで「これ以下の中で最大」とか「これ以上の中で最小」という値を調べるための
ボーダーラインを加えてみます。


  #--------------------------------------------------------------------------
  # ● 最大値検索
  #--------------------------------------------------------------------------
  def search_max(start, goal, border = nil)
    #start=範囲の最初, goal=範囲の最後, border=調べる数値の最大値
    max_variable = []                #最大値とその番号を記録する配列
    for pointer in start..goal
      #変数pointerをstartからgoalまで繰り返す
      if border == nil or border >= $game_variables[pointer]
        #ボーダーが無いか、現在の変数がボーダー以下の時
        if max_variable[0] == nil or max_variable[0] < $game_variables[pointer] 
          #最大値がまだ記録されてないか、現在の変数が記録した最大値より大きい時
          max_variable = [$game_variables[pointer],pointer]
            #最大値に現在の変数とその変数の番号を代入
        elsif max_variable[0] == $game_variables[pointer]
          #現在の変数が記録した最大値と同じ時
          max_variable.push pointer  #配列に変数の番号を追加
        end
      end
    end
    return max_variable              #最大値とその番号の配列を返す
  end

引数にborderを加えました。必要ないときのためにデフォルト値を設定しておきます。
そして6行目の条件分岐でボーダーより大きい数を排除しています。

ボーダーラインを加えて調べる時は以下のようにします。
◆スクリプト:p search_max(1, 1000, 100)

これで変数1〜1000の中から100以下で最大の数とその変数番号が分かります。

ちなみに、2番目に大きな数を知りたい時は、最大値から1つ小さい数をボーダーにしましょう。

◆スクリプト:a = search_max(1, 1000)
:       :p search_max(1, 1000, a[0] - 1)


同様の手順で3番目、4番目…も取得できます。
ボーダー以下の数がなくなると空の配列[]が返ってくるので注意しましょう。


一般的な配列に使うにはスクリプト全体をArrayクラスの中に入れて
$game_variablesをselfに変えるだけです。


メニューへ


● 指定した数値を範囲の前後に移動する

ツクール変数の中から指定した数値だけを前、または後ろへ移動します。
他の数値の並び順は変えません。

例えば、
[2,9,3,0,5,7,0,6,1,4]という並びが、0を指定して後方移動すると
[2,9,3,5,7,6,1,4,0,0]となります。

2000で自作メニューを作ったときにアイテム欄を前から順に詰めるために
アイテムが存在しないことを表す0を後ろへ移動させる必要からこういう処理を使いました。
XPで他に使い道があるかどうかは不明です。


  #--------------------------------------------------------------------------
  # ● 後方移動
  #--------------------------------------------------------------------------
  def move_back(target, start, goal)
    #target=移動する数値, start=範囲の最初, goal=範囲の最後
    pointer = start                   #範囲の最初から順番に移動する変数
    k = 0                             #ターゲットの個数を記録する変数
    while (pointer + k <= goal)
      #変数pointer + kの値が範囲内にある間ループ
      while ($game_variables[pointer + k] == target && pointer + k <= goal) 
        #k個分先の変数がターゲットなら
        k += 1                        #ターゲットの個数1個追加
      end                             #以上繰り返し
      $game_variables[pointer] = $game_variables[pointer + k] 
        #変数k個分先の変数を代入(前に詰める)
      pointer += 1                    #変数を1つ移動する
    end                               #以上繰り返し
    while (k > 0) 
      #見つかったターゲットの個数だけ繰り返す
      k -= 1                          #ターゲット数を1つ減らす
      $game_variables[goal - k] = target  
        #範囲の後ろをtargetで埋める
    end                               #以上繰り返し
  end                                 #メソッド定義終了


範囲の最初から数値を調べていって、ターゲットの数値が見つかったら
個数を記録する"k"を加算。その数だけ離れた変数番の数を順次代入していって、
最後の余った空間にターゲットの数値を代入しています。
これを前方移動にするには逆に範囲の後ろから前へ向かって
調べるように書き換える必要があります。

少しイメージしづらいかもしれませんので
[2,9,3,0,5,7,0,6,1,4]を0の後方移動する場合で流れを表にします。

1つ目のループ
1[2,9,3,0,5,7,0,6,1,4]k = 0
2[2,9,3,0,5,7,0,6,1,4]k = 0
3[2,9,3,0,5,7,0,6,1,4]k = 0
4[2,9,3,5,5,7,0,6,1,4]k = 1
5[2,9,3,5,7,7,0,6,1,4]k = 1
6[2,9,3,5,7,6,0,6,1,4]k = 2
7[2,9,3,5,7,6,1,6,1,4]k = 2
8[2,9,3,5,7,6,1,4,1,4]k = 2
2つ目のループ
1[2,9,3,5,7,6,1,4,0,4]k = 1
2[2,9,3,5,7,6,1,4,0,0]k = 0

次のように実行します。

◆スクリプト:move_back(5, 1, 1000)

これでツクール変数の1〜1000番の中からすべての5を範囲の後ろへ移動します。


メニューへ


● ランダムに並び替える

カードゲームを作るときなんかに使えるかもしれません。
「変数1〜10に数値1〜10のどれかを重複なくランダムに代入」とかいうことをやりたい時には
一度範囲内に順番に数値を代入してからシャッフルすれば簡単です。
…というより、そういう使い方しか思い当たりません。


  #--------------------------------------------------------------------------
  # ● シャッフル
  #--------------------------------------------------------------------------
  def shuffle(start, goal)                  #start=範囲の最初, goal=範囲の最後
    for pointer in start...goal
      #変数pointerの値を変数startから変数goalまで変動させながらループ
      other = rand(goal - pointer + 1) + pointer 
      #現在の位置以降の範囲からランダムに1つ選択
      $game_variables[pointer], $game_variables[other] = 
      $game_variables[other], $game_variables[pointer] #2変数を交換
    end
  end

範囲の最初の変数とそれ以降の範囲内のどれかを交換、2番目と
それ以降の範囲内のどれかと交換…。という処理を繰り返しています。
これで全ての組み合わせが等しい確率で出現します。
2変数の交換は"a, b = b, a"という式でできます。便利ですね。

次のように実行します。

◆スクリプト:shuffle(1, 1000)

これでツクール変数の1〜1000番がランダムに入れ替わります。


メニューへ


● 重複のない乱数を一括代入する

で、結局作ってしまいました。shuffleメソッドの存在意義が…。(別の使い道があればご一報を)


  #--------------------------------------------------------------------------
  # ● 重複無しの乱数一括代入
  #--------------------------------------------------------------------------
  def randpackplus(min, max, start, goal)
    #min=乱数の最小値,max=乱数の最大値,start=範囲の最初, goal=範囲の最後
    array = []  #配列初期化
    for pointer in min..max
      #変数pointerの値を変数minから変数maxまで変動させながらループ
      array.push pointer  #pointerの値を順次代入
    end
    for pointer in 0...array.size - 1
      #変数pointerの値を変数startから変数array.size - 1まで変動させながらループ
      other = rand(array.size - pointer) + pointer
      #現在の位置以降の範囲からランダムに1つ選択
      array[pointer], array[other] = array[other], array[pointer] #2変数を交換
    end
    while array.size <= goal - start
      #arrayの要素数が部分配列と同じ数になるまで
      array.push 0 #配列の末尾に0を追加する
    end
    for pointer in start..goal
      #変数pointerの値を変数startから変数goalまで変動させながらループ
      $game_variables[pointer] = array[pointer - start]  #元の変数に順次代入
    end
  end

乱数範囲の数値を順番に配列に入れてからシャッフルし、前から順にツクール変数に移し変えています。
代入範囲が乱数範囲より大きい場合は足りない部分が0で埋まります。(このときの0は重複します)

次のように実行します。

◆スクリプト:randpackplus(100, 200, 300, 400)

これで100〜200の値が変数の300〜400番に重複なくランダムに入ります。



メニューへ


一覧へ戻る
トップページへ戻る


更新日時:2005/7/24 18:00
管理人:すっぴぃ ◆ADGYPSYxII