ゲーム作ってます5

投稿者:User icon mini taki 投稿日:2016/11/14 01:52

自作戦闘の話です。
前回ブログが通常よりも反響があったので
興味のある人が多い分野なのかもしれません。
ブログ閲覧数を稼ぐチャンス。

というわけで今回はとりあえず制作過程の一部と作ったものを公開して
次回もう一回ぐらい続きをブログに書こうと思います。

長いので戻るボタンを押すなら今ですよ。

1.何の為に我々は戦うのか
ゲームに戦闘を入れる必要はあるのかをまず考えます。
面倒くさいことはなるべく無しで済ませたいのです。

ストーリーメインのゲームだと自分で育てたキャラがボスを倒すことで
達成感が得られるので楽しみに繋がる気がします。

戦闘システムが独特なものはそれ自体がゲームの楽しみの中心だったりします。

今回は上記のどっちでもねーなーとは思いつつ、プレーヤーにゲームを進める上で
必要な呪文がどんなものかヒントを出すために、半ば仕方なく実装することにしました。

スタートはこんなですが作り始めると割と楽しいです。

2.仕様をどうしよう
実装することは決まったのでもう少し具体的に
どんなシステムにするか考えます。

理想はこの段階でどんなスクリプトにするかまでキチンと決めてから
スクリプトの編集に入ることですが、私はそれが可能な境地には達していないので
ざっくりとだけ決めて詳細は手を動かしながら考えることにします。

必要な機能としては
・キーボードからコマンドを入力できること
・コマンドに応じて効果の異なる呪文が使えること
・HPの情報をもとに勝敗が判定できること

なるべく簡単に済ませたいなーというわけで
・1対1でプレイヤーが必ず先攻
(複数対複数は1対1よりべらぼうに面倒くさいのです。)

このぐらい考えると頭がショートするので
まずは1対1で互いに攻撃するだけの簡単なものを作ってみます。

簡単なものを作って後から付け加えていくやり方だと
とりあえず動かすことができるのでモチベーションが保ちやすい気がします。

一番効率がいいのは最初に仕様を固めるやり方だと思いますが
残念ながらレベルが足りません。

3.頼むから動いてくれよ
ターン制で交互に攻撃するだけのものをまずは作ってみます。

後で応用が利くように攻撃によるダメージ計算は別の自作関数で作ります。
この時「〇〇の攻撃、XXはダメージ」というのをどうするかで悩みます。

ダメージの計算やメッセージの表示を二つ作ってフラグで切り替えるのは
呪文が増えた数だけ手間が2倍になるので避けたかったのです。

ここでやってみたのは攻撃するキャラクタと受けるキャラクタを表す変数を作って
ターンによってその変数の中身を入れ替える方法です。

おしゃれに(?)配列とか使ってますgetVariable("tp")というやつです。
tpが何の略だったかは忘れました。

後にこの方法を使えば複数対複数も作れるのではという思いつきが
長い寄り道につながります。

とりあえずここでどうやってターンを切り替えるか、勝敗を判定するか
どのタイミングで攻撃するキャラにステータスを入れて、
攻撃を受けたキャラのステータスをもとのキャラに反映するかといった部分を
つかむことができたので、次はキーボードから入力したコマンドを使えるようにします。
def attack()
   getVariable("tp")[1][1]=getVariable("tp")[1][1]-getVariable("tp")[0][2]*(1+0.1*rand(6))
   if getVariable("tp")[1][1]<0
     getVariable("tp")[1][1]=0
   end
   return
end

def battle()
turn=0
a=createArray()
a[0]=createArray()
a[1]=createArray()
a[0][0]="主人公"
a[0][1]=100
a[0][2]=10
a[1][0]="enemy"
a[1][1]=50
a[1][2]=10
setFlag("battle",true)

setVariable("tp",createArray())
getVariable("tp")[0]=createArray()
getVariable("tp")[1]=createArray()

while getFlag("battle")
speak("HPP",a[0][1],"\nhpe",a[1][1],"\nturn",turn)

if turn==0
  i=0
  while i<2
     j=0
    while j<3
       getVariable("tp")[i][j]=a[i][j]
       j=j+1
    end
    i=i+1
  end 
elsif turn==1
  i=1
  ii=0
  while i>-1
     j=0
    while j<3
       getVariable("tp")[i][j]=a[ii][j]
       j=j+1
    end
    i=i-1
    ii=ii+1
  end 
end

hpas_old=getVariable("tp")[1][1]
attack()
speak(getVariable("tp")[0][0],"の攻撃","\n",getVariable("tp")[1][0],"は",hpas_old-getVariable("tp")[1][1],"のダメージ")

if getVariable("tp")[1][1]==0
  setFlag("battle",false)
end

if turn==0
  i=0
  while i<2
     j=0
    while j<3
       a[i][j]=getVariable("tp")[i][j]
       j=j+1
    end
    i=i+1
  end 
elsif turn==1
  i=1
  ii=0
  while i>-1
     j=0
    while j<3
       a[ii][j]=getVariable("tp")[i][j]
       j=j+1
    end
    i=i-1
    ii=ii+1
  end 
end

case turn
when 0
  turn=1
when 1
  turn=0
end

end
end


4.ちからつきるとはなさけない
やたらめったら長くなってしまったので続きは次回にしたいと思います。
(正直にいうとブログを書くのに力尽きました)

とりあえず現在実装しているものを貼っておきます。
コマンドを入力できるようにして、状態異常を作って
経験値制レベルアップもできるようになっています。

頼むからそうなっていてくれ。

質問頂ければなるべくさっさと答えます。
def battle(ne)
turn=0
a=createArray()
a[0]=createArray()
a[1]=createArray()
i=0
while i<14
  a[0][i]=getVariable("status")[0][i]
  a[1][i]=getVariable("status")[ne][i]
  i=i+1
end

a[0][14]=0
a[1][14]=0

setFlag("battle",true)

setVariable("tp",createArray())
getVariable("tp")[0]=createArray()
getVariable("tp")[1]=createArray()

while getFlag("battle")
speak("HPP",a[0][2],"\nhpe",a[1][2],"\nturn",turn)

if turn==0
  i=0
  while i<2
     j=0
    while j<15
       getVariable("tp")[i][j]=a[i][j]
       j=j+1
    end
    i=i+1
  end 
elsif turn==1
  i=1
  ii=0
  while i>-1
     j=0
    while j<15
       getVariable("tp")[i][j]=a[ii][j]
       j=j+1
    end
    i=i-1
    ii=ii+1
  end 
end


if turn==0
  command()
else
  setVariable("command",getVariable("status")[ne][floor(9+rand(3))])
end

if getVariable("command")!="CURE"
  case getVariable("tp")[0][14]
    when 0
    when 1
      if rand(3)!=0
        setVariable("command","PARALYSIS")
      end
    when 2
      if getVariable("command")!="FIRE"
        setVariable("command","FROZEN")
      end
  end
end

judge_battle()

if getVariable("tp")[1][2]==0||getVariable("tp")[0][2]==0
  setFlag("battle",false)
end

if turn==0
  i=0
  while i<2
     j=0
    while j<15
       a[i][j]=getVariable("tp")[i][j]
       j=j+1
    end
    i=i+1
  end 
elsif turn==1
  i=1
  ii=0
  while i>-1
     j=0
    while j<15
       a[ii][j]=getVariable("tp")[i][j]
       j=j+1
    end
    i=i-1
    ii=ii+1
  end 
end

case turn
  when 0
    turn=1
  when 1
    turn=0
end

end
#____result_____
i=0
while i<14
  getVariable("status")[0][i]=a[0][i]
  i=i+1
end
if a[0][2]>0&&a[1][2]==0
  speak("playerは勝利した")
  speak("playerは",getVariable("status")[ne][8],"経験値を得た")
  getVariable("status")[0][8]=getVariable("status")[0][8]+getVariable("status")[ne][8]
  if getVariable("status")[0][8]>=getVariable("status")[0][9]
    getVariable("status")[0][1]=getVariable("status")[0][1]+1
    speak("レベル",getVariable("status")[0][1],"になった")
    getVariable("status")[0][12]=floor(getVariable("status")[0][12]*1.5)
    getVariable("status")[0][13]=floor(getVariable("status")[0][13]*1.2)
    getVariable("status")[0][2]=getVariable("status")[0][12]
    getVariable("status")[0][3]=getVariable("status")[0][13]
    i=4
    while i<8
      getVariable("status")[0][i]=getVariable("status")[0][i]+20
      i=i+1
    end
    getVariable("status")[0][9]=floor(getVariable("status")[0][9]*2.5)
  else
    speak("次のレベルに必要な経験値は",getVariable("status")[0][9]-getVariable("status")[0][8])
  end
elsif a[0][2]>0&&a[1][2]>0
  setVariable("px",getVariable("px")-getVariable("vx"))
  setVariable("py",getVariable("py")-getVariable("vy"))
else
  speak("playerは力尽きた")
  setPartyMemberDeathAll()
end
deleteAllSprite()
px=getVariable("px")
py=getVariable("py")
pass=getVariable("map")[px][py][0]
cout=getVariable("counter")
wall=getVariable("wall")[pass][cout]
setSpriteRect(createSprite(wall), 0,0 , 512, 384, 0, 0, 512, 384)
drawCanvas()
end


def judge_battle()
hpas_old=getVariable("tp")[1][2]
hact_old=getVariable("tp")[0][2]
mpas_old=getVariable("tp")[1][3]
mact_old=getVariable("tp")[0][3]
stanm=createArray()
stanm[0]=getVariable("tp")[0][14]
stanm[1]=getVariable("tp")[1][14]

  case getVariable("command")
    when "ATTACK"
      speak(getVariable("tp")[0][0],"の攻撃")
      getVariable("tp")[1][2]=getVariable("tp")[1][2]-floor(getVariable("tp")[0][4]*(1+0.1*rand(6))-getVariable("tp")[1][5]*0.5)
    when "ESCAPE"
      speak(getVariable("tp")[0][0],"は逃亡を試みた")
      if getVariable("tp")[0][0]==getVariable("status")[0][0]
        #if rand(3)==0
         setFlag("battle",false)
         speak("うまく逃げ切った")
        #else
          #speak("逃亡に失敗した")
        #end
      else
         speak("魔王からは逃げられない")
      end
    when "PARALYSIS"
      speak(getVariable("tp")[0][0],"はマヒしていて動けない")
    when "FROZEN"
      speak(getVariable("tp")[0][0],"は氷漬けで動けない")
    else
       speak(getVariable("tp")[0][0],"は",getVariable("command"),"を唱えた")
       case getVariable("command")
          when "CONVERT"
              speak(getVariable("tp")[0][0],"から光があふれる")
              getVariable("tp")[0][3]=getVariable("tp")[0][3]+floor(getVariable("tp")[0][2]*0.1)
              getVariable("tp")[0][2]=floor(getVariable("tp")[0][2]*0.9)
              if getVariable("tp")[0][3]>getVariable("tp")[0][13]
                getVariable("tp")[0][3]=getVariable("tp")[0][13]
              end
          when "EXPLOSION"
              if  getVariable("tp")[0][3]<30
                speak("しかし魔力が足りなかった")
              else
                getVariable("tp")[0][3]=getVariable("tp")[0][3]-30
                speak("激しい爆発とともに轟音が鳴り響く")
                getVariable("tp")[1][2]=getVariable("tp")[1][2]-floor(50*getVariable("tp")[0][6]/getVariable("tp")[1][7])
              end
          when "FIRE"
              if  getVariable("tp")[0][3]<20
                speak("しかし魔力が足りなかった")
              else
                getVariable("tp")[0][3]=getVariable("tp")[0][3]-20
                speak("火柱が立ち上る")
                getVariable("tp")[1][2]=getVariable("tp")[1][2]-floor(20*getVariable("tp")[0][6]/getVariable("tp")[1][7])
                if stanm[0]==2
                  stanm[0]=0
                end
              end
          when "BLIZZARD"
              if  getVariable("tp")[0][3]<30
                speak("しかし魔力が足りなかった")
              else
                getVariable("tp")[0][3]=getVariable("tp")[0][3]-30
                speak("猛吹雪が吹き荒れる")
                getVariable("tp")[1][2]=getVariable("tp")[1][2]-floor(20*getVariable("tp")[0][6]/getVariable("tp")[1][7])
                stanm[0]=2
                stanm[1]=2
              end
          when "THUNDER"
              if  getVariable("tp")[0][3]<30
                speak("しかし魔力が足りなかった")
              else
                getVariable("tp")[0][3]=getVariable("tp")[0][3]-30
                speak("激しい稲妻が周囲を駆ける")
                getVariable("tp")[1][2]=getVariable("tp")[1][2]-floor(30*getVariable("tp")[0][6]/getVariable("tp")[1][7])
                if rand(3)==0
                  stanm[1]=1
                end
              end
          when "FREEZE"
              if  getVariable("tp")[0][3]<30
                speak("しかし魔力が足りなかった")
              else
                getVariable("tp")[0][3]=getVariable("tp")[0][3]-30
                speak("辺りが冷気に包まれる")
                getVariable("tp")[1][2]=getVariable("tp")[1][2]-floor(20*getVariable("tp")[0][6]/getVariable("tp")[1][7])
                if rand(3)==0
                  stanm[1]=2
                end
              end
          when "POISON"
              if  getVariable("tp")[0][3]<30
                speak("しかし魔力が足りなかった")
              else
                getVariable("tp")[0][3]=getVariable("tp")[0][3]-30
                speak("毒の霧がたちこめる")
                stanm[1]=3
              end
          when "HEAL"
              if  getVariable("tp")[0][3]<10
                speak("しかし魔力が足りなかった")
              else
                getVariable("tp")[0][3]=getVariable("tp")[0][3]-10
                speak("癒しの光が",getVariable("tp")[0][0],"を包む")
                getVariable("tp")[0][2]=getVariable("tp")[0][2]+floor(getVariable("tp")[0][12]*0.2)
                if getVariable("tp")[0][2]>getVariable("tp")[0][12]
                  getVariable("tp")[0][2]=getVariable("tp")[0][12]
                end
              end
          when "CURE"
              if  getVariable("tp")[0][3]<20
                speak("しかし魔力が足りなかった")
              else
                getVariable("tp")[0][3]=getVariable("tp")[0][3]-20
                speak("浄化の光が",getVariable("tp")[0][0],"を包む")
                stanm[0]=0
              end
          else
            speak("しかし何も起こらなかった")
    end
  end
   if getVariable("tp")[1][2]<0
     getVariable("tp")[1][2]=0
   end

if hact_old-getVariable("tp")[0][2]>0
  speak(getVariable("tp")[0][0],"は",hact_old-getVariable("tp")[0][2],"のダメージ")
end

if hpas_old-getVariable("tp")[1][2]>0
  speak(getVariable("tp")[1][0],"は",hpas_old-getVariable("tp")[1][2],"のダメージ")
end

if mpas_old-getVariable("tp")[1][3]>0
  speak(getVariable("tp")[1][0],"は魔力を",mpas_old-getVariable("tp")[1][3],"失った")
end

if  hact_old-getVariable("tp")[0][2]<0
  speak(getVariable("tp")[0][0],"は",getVariable("tp")[0][2]-hact_old,"回復した")
end

if  mact_old-getVariable("tp")[0][3]<0
  speak(getVariable("tp")[0][0],"は魔力を",getVariable("tp")[0][3]-mact_old,"回復した")
end


i=0
while i<2
  if stanm[i]!=getVariable("tp")[i][14]
    case stanm[i]
      when 0
        speak(getVariable("tp")[i][0],"は状態異常から回復した")
      when 1
        speak(getVariable("tp")[i][0],"はマヒした")
      when 2
        speak(getVariable("tp")[i][0],"は凍りついた")
      when 3
        speak(getVariable("tp")[i][0],"は毒をあびた")
      when 4
        speak(getVariable("tp")[i][0],"は魔法を封じられた")
    end
  end
getVariable("tp")[i][14]=stanm[i]
i=i+1
end

if stanm[0]==3
  damage=floor(getVariable("tp")[0][12]*0.1)
  if damage>getVariable("tp")[0][2]
    damage=getVariable("tp")[0][2]
  end
  speak(getVariable("tp")[0][0],"は毒によって",damage,"のダメージ")
  getVariable("tp")[0][2]=getVariable("tp")[0][2]-damage
end

コメントする

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

コメント一覧

Cdv30200 aoi icon mini aoihikawa(投稿日:2016/11/19 09:54, 履歴)
getVariable関数は
そこそこ重いので、

保管用のold以外にも
内部処理用のwrkを予め
準備しておいても良いかも

ラストで代入すれば
呼び出しが2回ですみますし
文字数自体も短く出来ます
User icon mini taki(投稿日:2016/11/20 18:32, 履歴)
アドバイスありがとうございます

ここまでスクリプトを見ていただけて嬉しいです。

関数によって処理に必要な時間の幅があるのですね。
wrkを準備すれば確かに文字数もぐっと減らすことができて
変数の意味も分かりやすくなっていいですね。
Material 204926 1 mini 井戸乃博士(投稿日:2016/11/16 01:26, 履歴)
お恥ずかしながら、
わたくしも戦闘のプログラムを考えておるのですが、
こんなふうに整然とはしておりません・・・
やっぱりプログラムに熟練している方は違うよね、ラヴィ君・・・

汎用性を意識してスクリプトを組まれているところが、
すごいですね。
経験値制のシステムの作り方や、
ダメージの計算式など参考になる点がたくさんあります。
User icon mini taki(投稿日:2016/11/20 18:19, 履歴)
コメントありがとうございます

スクリプトに関して未熟で無駄の多い部分もありますが
参考になるものがあれば幸いです。
(MPが足りない場合をいちいち全ての呪文に書いているのは
 我ながらどうかと思います)

ダメージ計算ですが作ってみると
バリエーションが多くて意外に奥が深くて楽しいです。
工夫の余地が多い、個性の出せる部分かもしれません。
            mini mosmoss(投稿日:2016/11/15 13:12, 履歴)
技の言いまわしがいいですね
テンション上がります
User icon mini taki(投稿日:2016/11/20 18:11, 履歴)
コメントありがとうございます

>>技の言い回しがいいですね
そう言っていただけて嬉しいです。
演出面で他に盛り上げる要素がないので
苦労しているポイントです。

あくまで個人的なイメージに基づいたものですが
やや古目のゲームのテキストを意識してみました。

Cdv30200 aoi icon mini aoihikawa(投稿日:2016/11/14 20:26, 履歴)
様々な種類の、特殊な状態も
再現できるようになるのは
自作ならではの魅力ですね
User icon mini taki(投稿日:2016/11/20 18:07, 履歴)
コメントありがとうございます

特殊状態は思いつきで色々と追加できるので
自作していて楽しいです。

余裕があれば変身も入れてみたいです。
(変身はロマン)