スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

DXRubyでノベルエンジン作ってみた(AC用記事)

〈完成!>

こんにちは、みれいゆですー
今回の記事はDXRubyのアドベントカレンダー に向けて作られた記事となっています。
http://www.adventar.org/calendars/345
今回の私は12月23日を担当しました!!もうクリスマスも近い!!

前回は 役に立ちそうで役に立たない少し役に立つかも知れないDXRubyローグライクのノウハウ hoshi_sano さんでした!!
ローグライクは私もかなりはまってたゲーム種別ですね!!ちゃんとYAMLで内容をユーザーが変更できるのもうれしいところ
これまでのAC記事の集大成のような記事でした!!

さて、当の私はこれまでの自身の成長?の集大成(にはならなかったかもしれない)なものを作ろうと思って、考えたところ、
様々なゲームジャンルを探っていくとどうもフリーゲームで一人専用だと
「ノベルADV] これは非常に人気で数が多い事がわかりました!!

私にはノベルを書くような文学才能はありませんが型なら作れるぞ!!と思って今記事にとりかかった次第です。では、どうぞー




[DXRubyノベルエンジン作ってみた]

目標:ファイルの簡単な一部をユーザーが加えることで簡単にノベルを編集できる!!

用意する(した)もの(ソース)
以下の適当なファイルを用意しておく。
・背景画像 ・背景じゃない前に置くような画像、
・キャラクターの顔を含めない画像 ・キャラクターの顔の画像
<それぞれ拡張子は png !!>
・効果音に使うWAVファイル
・音楽に使うMP3ファイル

をそれぞれ1つ以上あるといいです!

今回作るソース・ファイルは以下の3つになります。

・main.rb ・・・メインですね。ここにノベルクラスを動かす為の記述を入れます。
・page.rb・・・Sceneクラス。これは汎用sceneモジュール
(http://dxruby.sourceforge.jp/cgi-bin/hiki.cgi?%C8%C6%CD%D1scene%A5%AF%A5%E9%A5%B9)
を改造させていただきました!(パブリックドメイン)
・class.b・・・今回最も重要な、Classを管理しているファイルになります。




①:では、まずはpage.rb のソースだけちゃっと載せましょう!!

http://pastebin.com/7fMhfrET

#メインの定常処理はrun->render->update->exitとなる。
step.times do
break if page.Next_page
page.__send__ :__run
end

page.render

step.times do
break if page.Next_page
page.__send__ :__update
page.__send__ :__exit
end



ここが重要でして、フレームで動かすdefの順番を run->render->update->exit (描写前の処理、描写処理、描写後処理、終了用処理)としています。




②:class.rbを作ろう!!

さて、今回はユーザーが比較的に?簡単にコードを編集して音楽も画像も、もちろんノベルの文章も追加できるようにと考えていきました。そこで音楽、画像の導入には、”全てを配列に突っ込み、番号で処理できる”ようにしました。
つまりこうです。

@body , @face= [],[]
@body << @sample_body1 = Image.load('sample_pass/body_sample1.png')



画像をファイルパスからロードした画像を適当な変数で定義し、そのまま@body配列に突っ込みます。
この要領で音楽等もやっていきました。
次に文章はクラスに引数を設定する事で、非常に可変的に作ります。
ボタンですが(ここでは文字をボタンとした)、ボタンのサイズを得るためにこのようなコードを書きました。

button_size = [x..x + font.getWidth(sentence) , y..y + font.size]


として、

if button_size[0].include?($xpoint) && button_size[1].include?($ypoint)


これは、文字の大きさをrangeクラスで取り、マウスカーソルがその範囲に入ったかを意味しています!

なるべく抽象化する事で、立ち絵、背景等画像、音楽、文章、ボタンを下のようなクラスに纏めてどれも引数で処理ができるようになりました!!

http://pastebin.com/vfTRHa9S

本当に、抽象化を覚えてよかったと思いました!!

ここの引数は後に作る、ノベルを構成するクラスで全て設定ができるようになります・・・





本番のノベルを構成するクラスに入る前にここで息抜き?のクラス、
固定データの変更に用いる。Param_edit_class を作っていきます。
なぜ必要になったかはノベルクラスの際に紹介します・・・
結果は20行でできました。

http://pastebin.com/EZe9aCXt
ここのところ、 演算子を変数化できればもっと短く出来たところです!!(やり方があったらお願いします!!)




さて、本番のクラスです。ノベルクラスを紹介します。
ノベルに必要な事柄はなにか?と考えていったところ、必要な事柄をまとめました(あくまで個人の結果なので、もうちょっといいまとめ方があったらお願いします!)

・イベント開始条件 ・・・開始条件によって複数のイベントを分岐させる
・イベント結果分岐 ・・・正答度の概念によってイベント全体の結果を変える
・分岐判断待ちタイム ・・・これはボタンのタイムに同期させない理由は、瞬間的に出てくる分岐とかも作りたかった
・サウンドエフェクトタイム ・・・サウンドノベルはいつでも音を鳴らせるようにしたい!
・画像描写 ・・・画像、音楽も条件分岐によって滑らかに変えたい
・文章描写 ・・・大量の文章を構造化して編集がし易いように
・分岐ボタン描写 ・・・大量の条件分岐を構造化して編集がし易いように


にまとめ上げました。




class.rb の完成形がこちらになります!!

http://pastebin.com/fj0D15uQ

ここで最初んっと思ったところがあって、それは、
"一つの変数に対し、each メソッド上の変数を使ってeachした値は、基の一つ変数と関連性が無くなる"
という事です。つまりこのような事です。
sample_ary = [[2,"up",[[$sample_data[0],3],[$sample_data[1],-4]],-1],[2,"down",[[$sample_data[1],3],[$sample_data[2],-4]],-1]]
という配列があって、

sample_ary.each do |p|
p[2].each{|d| d[0] += d[1]}
end


このやり方では、$sample_dataのどの値も変化しないようですね!
何かというと、一回目のeachの時点で本来$sample_data各種であったはずの変数はp[2]配下のp[2][0]に姿を替えてしまったために、関連性が無くなってしまったのだと思います。

そのため上のような事をする為に息抜き?でやっていたparam_edit_classを呼び出して引数で直接変更させる変数を設定するようにしました。

今回はeachメソッドを使う機会が非常に多かったので、この仕様を見つけることができて感慨深いです。




③:main.rbで戻り値を使った総管理

最後にmain.rbと題して、上のClass_novel内の操作をどういった方法で使用するのかを決めます。
Class_novelの def initialize と def reinit 以外はこれからいかなる編集をする上では全く操作する必要が無いと入っていいでしょう!

http://pastebin.com/ABvHwWtK

Class_novel内のdefの使用順番は、
ifs->draw->effects->sentence->result->(ifs)->time->mind->reinit
となります。

main.rbのユーザーが手を加える時は、ゲームオーバー条件を定める必要がある場合です。
もしゲームオーバーが存在しないように作るならばこれも編集の必要がありません。




④:まとめ

今回のノベルエンジンコードでできる事は
・タイムワークで文章や効果音を流せる
・ボタンを押すことで物語の分岐を作る
・複数の開始条件と結果を作れる。(ゲームオーバーも用意できる)

やろうと思えば拡張はどんどんできます!!拡張しても負担がそれほどかからないのが抽象化されたクラスの特長ですね!!


最後に、このノベルエンジンを作る事は、DXRuby、Ruby言語で作る方法が非常に簡単でした!!まずほかの言語ではGUIを出すところから、(できれば高機能にできるかもしれないが)難しいところがあり、それをコマ送りのように動かすことはとても準備がいる作業でした。
DXRuby(Ruby)のイイトコロはぱっと思いついた構想をぱっと実現できるところだと思いました!!特にDXRubyはGUIアプリケーションを作る時にはかなり使いやすいですよ!!
最後に、ゲームのジャンルはノベルやシューティング、ローグライク以外にももっと様々あります。想像すればいつでも実現がしやすいDXRubyで、グラフィカルに再現してみませんか??

コードソースリンクがあちらこちらにあったので、完成形をここにまとめます。

・page.rb
http://pastebin.com/7fMhfrET

・class.rb
http://pastebin.com/fj0D15uQ

・main.rb
http://pastebin.com/ABvHwWtK




さて、次回は土屋つかささん t_tutiya さんです!!またDXRubyに纏わる短編物語を書いてくださるのでしょうか??
それとも・・・


ご閲覧ありがとうございました!!

スポンサーサイト

DXRubyで成長曲線と父母遺伝の再現、描写

こんにちはーれゆーですーっ



(まちがったみれいゆーだ)


今日は DXRuby Advent Calender 2014 の10日目という事で、私が担当します!!

前回は、Vivit_jc さんの 「〜ゲーム制作者だってテストがしたい!〜DXRubyユーザー向けRspec入門」でした!!
まだ記事は続きがあるようで・・・楽しみですね!!
さて、今回は、DXRubyで成長曲線と父母遺伝の再現、描写
となっておりますっ。これは私が遺伝子計算を勉強した中で得た計算式の詳細や、その曲線を描写する為の処理、他様々に加え、父と母のグラフを定義して合成するといった内容を行います!
(あまりDXRubyに関係が無いかもしれませんがご了承くださいすいません><)


使用するパロメータを定義する
今回成長曲線を描く上で必要になってくる要素を遺伝子生物学から考えていきましょう。
まず、人間にも、いかなる生物にもそうですが、思春期ありますよねー。この思春期の時には体、そして心も急成長します。これを第二次性徴期といいます。今回はこの第二次性徴期を再現しようと思います。最も成長する歳を名づけて成長頂点歳、変数は
peakage
としましょう。
只、性徴期が訪れた時、人によってその成長の進み具合、言い換えれば傾斜が違うと思います。人間同士ならわかりにくいですが特にネズミとゾウを比べるとはっきりわかります。この時必要となってくる要素を、私は成長幅と名付けます。
今回変数表すとこれは
peakrange
となります。
でも、人間もそうですが、皆さん寿命が存在します。この定められた寿命を超える、体力が衰え、もう体を成長させようにも出来ないことになります。これを決定づける値、ここでは変数で
activeage
とします。
最後に、皆さん体を成長させるとき、それには二つの方法が存在すると思います。それは、体が自然に成長していく「自然成長」と、ジムなどによって人為的に鍛えて成長させる「訓練成長」です。
これも今回は再現しようと思います。変数はそれぞれ、
naturalpower
trainpower
としましょう。
尚、上記二つの成長力を総合して
totalpower
とします。




成長曲線グラフを考える

さてグラフの時間となりました!
まず、今回の成長曲線を描くにおいての必要な動きの四大要素を掲示します!!
A:成長頂点歳に向けて指数関数的に成長力が上昇する事
B:成長幅の広さに応じてその成長力の傾斜をゆるめ、成長力最大値を可変する。
C:成長頂点歳を過ぎた場合一定の成長力に収まる
D:寿命を超えると成長力が0に進む。。


これらを叶える為に必要な計算式を順々に取り出して行きましょう!

A:成長頂点歳に向けて指数関数的に成長力が上昇する事
まずこれは、成長頂点歳の時には成長力が最大になるということの再現を行います。

Rubyでは指数関数を扱うモジュール/メソッドとして Mathモジュールの expメソッドがあります!
(組み込みライブラリ)

よって単純に表したい場合こうなります。

grow = Math.exp(time)


timeは0..60 つまり現在の年齢を指定します。 grow に出力されます。
これが成長頂点歳の場合、

peakgrow = Math.exp(peakage)


となります。

B:成長幅の広さに応じてその成長力の傾斜をゆるめ、成長力最大値を可変する。
これを表現する為に、二箇所の計算式を加える必要が出てきます。
まずは成長力の傾斜をゆるめこれに着目します。
確かに最大の値はA:で述べた成長頂点歳式peakgrowに達したいけどそこまでの傾斜を決めたい場合、必要なメソッドは、そう、 Math.sinです。
この三角関数Sin(正弦関数)の持つ値は、角度にして0..180と言われており、そのグラフは、
○の上半円を切り取ったようなものとなります。
角度ではなくラジアンで表現した場合、その単位はπとなりますが、πをrubyではMath::PIで取得する事ができます。
そして、以上の三角関数式に影響したい値は、ここまでの流れから当然、成長幅、peakrangeとなります。
以上を纏めると入る式は、

Math.sin(peakrange * Math::PI/ 180.0)



次に、成長力最大値を可変するに着目します。
成長力の最大値を決めるにあたって、必ず「成長頂点歳と成長幅によって"大きく"総合成長力を変えない」という事を前提に置きます。
ここの"大きく"と表現した理由は、人間誰しもが同じ成長力を持ってそれが早熟であれ晩成であれ同じだとすると、特に晩成は「報われない」からです。今回はこの事も視野に入れた式を作ります。
こうして作りたい式は、
「成長歳が遅ければ晩成として成長力の総合が大きい」
「成長幅が成長歳と釣り合っていればさらに成長力の総合が大きい」

これを再現する為、得た式はこうなります。

((totalpower / peakage ) / peakrange) / (1.0 + peakage * Math.exp(-1.0 * (Math.sin(peakrange * Math::PI/ 180.0)) * time))


最早自分でも意味の分からない式となりました。

C:成長頂点歳を過ぎた場合一定の成長力に収まる
前述の式は成長頂点歳に向かうまでの再現をとりました。次は成長頂点歳を過ぎた場合の式を作ります。
マイナスの方向に進む式の為、まず、最大成長力から引く式を立てます。

peakgrow - 式



さて、次に考えるのは、最大成長力からの「減衰力」です。
すなわち、成長頂点歳を境にマイナス式を大きくするという考えで行くので、加える式は

式 * (time - peakage )


さあこれで再現しよう!という時に問題が一つあります。それは、
「減衰が無い。」
この状況を回避する為に「成長頂点歳を過ぎたら約半分程の成長力を残そう」
という考えです。これを考慮して完成した2式がこちらです。

grow = ((totalpower / peakage ) / peakrange) / (1.0 + peakage * Math.exp(-1.0 * (Math.sin(peakrange * Math::PI/ 180.0)) * time)) if time <= peakage and time <= activeage
peakgrow = ((totalpower / peakage ) / peakrange) / (1.0 + peakage * Math.exp(-1.0 * (Math.sin(peakrange * Math::PI/ 180.0)) * peakage))
grow = peakgrow - (((totalpower / peakage ) / peakrange) / (1.0 + peakage * Math.exp(-1.0 * (Math.sin(peakrange * Math::PI/ 180.0)) * (time - peakage )))) / 2.0 if time > peakage and time <= activeage



型崩れが激しいです。申し訳ございません。この上記にある条件式は双方とも、「寿命を超えていない」事を書いております。さて、超えるとどうなるのか・・・・

D:寿命を超えると成長力が0に進む。。
先ほどに書いた式の減衰に、小幅な修正を加えます。すなわち、先ほどは値を"2"としたことで減衰したら残りの成長力は成長頂点歳に比べ"1/2"で安定しました。ならば、値が2より大きく、そして∞に向かうなら・・・

/ (2.0 * (activeage / time) ** 4.0)


尚、** は累乗を意味します。

こうして成長曲線を描く3式が出来上がりました。列挙すると、


for time in 0..60
grow = ((totalpower / peakage ) / peakrange) / (1.0 + peakage * Math.exp(-1.0 * (Math.sin(peakrange * Math::PI/ 180.0)) * time)) if time <= peakage and time <= activeage
peakgrow = ((totalpower / peakage ) / peakrange) / (1.0 + peakage * Math.exp(-1.0 * (Math.sin(peakrange * Math::PI/ 180.0)) * peakage))
grow = peakgrow - (((totalpower / peakage ) / peakrange) / (1.0 + peakage * Math.exp(-1.0 * (Math.sin(peakrange * Math::PI/ 180.0)) * (time - peakage )))) / 2.0 if time > peakage and time <= activeage
grow = peakgrow - ((totalpower / peakage ) / peakrange) / (1.0 + peakage * Math.exp(-1.0 * (Math.sin(peakrange * Math::PI/ 180.0)) * (time - peakage ))) / (2.0 * (activeage / time) ** 4.0) if time > activeage
grow = 0.0 if grow <= 0.0
totalgrow += grow
end



後ほど完成されたコードは pastebinにあるのでご心配なく!!




成長曲線グラフを描写する。

Window.draw_font(10 + time * 10 , (-1 * grow* 100) + 440 , "●" , font,hash={color: [255,66,66]})


これを前述のFOR文に加えます。
ここで、-1 が入ってる理由は、Windowのy座標は大きければ↓に向かうからです。今回は上に山なりとするため、440をベースに-1 * としました。
年齢が1進むことでx座標は10進みます。

こうして、完成されたグラフはこう映ります。

grarh4.png

赤が自然成長力のみ、水色が訓練成長力のみ、黄色が総合成長力です。
また後の話にはなりますが、分かりやすいように左上に各重要値の配列を表示しています。
ここでは左から順に、
自然成長力、訓練成長力、成長頂点歳、成長幅、成長寿命、生命寿命。
成長寿命の事を今までは単純に寿命と言ってきました。実際の遺伝ゲームとなると生命寿命を別に設け、その歳に死亡するようにします。




おまけ?父母遺伝を書く。

この項は最早おまけです。
一つ言うならば、父と母で引き継ぐ"比率"が重要という事ですね。
あとは、新しい子を人間の配列に pushします。




こうして、すべてのコードを導入した完成品をpastebinに貼ったのでどうぞ!!!

http://pastebin.com/CdVGVjzt




grarh1.png
grarh2.png
grarh3.png

最後までご視聴ありがとうございました!!
さて、次回は、unohanaTさんの記事です。お楽しみにーーー

新製品:「フリースタイルメモ_0.8」

実用性のあるもの作りたいなと思って作りました。

すべての操作はF1キーを押すとわかります。
フリースタイルメモ0_8_0

ダウンロードはこちらからどうぞ(流石にサイトに上げるほどの機能ではない・・・)
http://www2.axfc.net/uploader/so/3370293

ミニゲーム:かのんの信仰あつめ

作っちゃいました!!

ルールは簡単!マウスとWASDキーで飛んでるのを叩くだけ!!めっちゃ簡単だよ!! Eキーで魔法!!

F4、F5で画面サイズ変更!!ESCキーで終了!!
暇つぶしにどうぞ!!

ふりーむ!のみに投稿しますー

sinkou.png

Dxruby談:サイドビュー自動戦闘システムの紹介その弐(オブジェクト使用版) + 小物 #Advent Calender 用記事

前回の続きです。前回はオブジェクト指向なしに、動きだけをっ再現してみました。
それはいいのですが、やはりコードとして見ると、見づらすぎるという方がいらっしゃいます。
Ruby を初めて4ヶ月なのですが、ここに来て自分のコーティングを完璧に改めることになりそうです・・・!


今回の記事はDxruby #Advent Calender 用記事となっております。
前回はそう、DXRuby開発者、 mirichiさんの「DXRuby機能リクエストにお答えする」でした!!
特に私にはマイナーすぎてまずちんぷんかんぷんです!でもいずれそんな機能達にもうまくふれあいたいですね・・・

前回の記事もあります! http://mirenoteu.blog.fc2.com/blog-entry-40.html

上を見て、そして今回を見るとまず「成長したなー」と感じる人と「低レベルだなー」と思う方といらっしゃるかも知れませんw

ではでは、オブジェクト指向ばりのコードを用いたやり方で記述しましょう。
まずは結果論として、コードを載せました。




#クラスをよく理解していない例。module Flamework に playerが管理しきれて
#ダメージモーションが変な人にある。つまりターゲットにキャラが向かわない。

#! ruby -Ku
require 'dxruby'

#グローバル変数の定義
$turn = 0

#メンバー変数の為のクラス定義
class Person
def initialize(side,mem)
@side = side
@mem = mem
end

def side
@side
end

def mem
@mem
end

end

#インクルード用モジュールの定義
module Flamework

def self.description # モジュールメソッドの定義
"戦闘フレームワーク"
end

#initは初回動作用
def init
#変数群

@@font3 = Font.new( 15,'HGP行書体') #フォントの定義

@@b_x = [[100,100,100,100],[540,540,540,540]] #表示X始点座標 を、4*2箱作る。
@@b_y = [[100,160,220,280],[100,160,220,280]] #表示Y始点座標 を、4*2箱作る。

@@b_sx = [[100,100,100,100],[540,540,540,540]] #表示X始点座標 を、4*2箱作る。
@@b_sy = [[100,160,220,280],[100,160,220,280]] #表示Y始点座標 を、4*2箱作る。


@@image = Image.loadTiles('mireiyu.png', 3,10) #画像のリンク。ここでは横が3個分、縦が10個分と認識する。
@@b_motion = [[0,0,0,0],[0,0,0,0]]
@@b_motioned = [[0,0,0,0],[0,0,0,0]]

@@b_wtime = [[0,0,0,0],[0,0,0,0]]
@@b_doskill = [[0,0,0,0],[0,0,0,0]]

@@b_time = [[0,0,0,0],[0,0,0,0]] #コマ送りの時間 を、4*2箱作る。
@@b_stats = [[0,0,0,0],[0,0,0,0]]
@@b_speed = [[5,10,15,20],[25,30,35,40]]

@@b_attack = [[10,20,30,40],[10,20,30,40]]
@@b_hp = [[300,200,400,100],[300,200,400,100]]
@@b_shp = [[300,200,400,100],[300,200,400,100]]
@@b_target = [[0,0,0,0],[0,0,0,0]]
@@b_id = [[0,1,2,3],[0,1,2,3]]

end
#変数群

#アクションインスタンスを定義
#ターン処理をするもの

def action

if @@b_doskill[self.side].all?{|a| a == 0 } && @@b_doskill[1 - self.side].all?{|b| b == 0 } #誰もが移動行動を取らなければ実行する条件
@@b_stats[self.side][self.mem] += @@b_speed[self.side][self.mem]
@@b_stats[self.side][self.mem] = @@b_stats[self.side][self.mem].floor
end

ok = 0
ok = 1 if @@b_stats[self.side][self.mem] >= 400
if ok != 1
@@b_motion[self.side][self.mem] = 0

elsif ok == 1

case @@b_wtime[self.side][self.mem]
when 0

@@b_doskill[self.side][self.mem] = 2
@@b_target[self.side][self.mem] = @@b_hp[1 - self.side].index(@@b_hp[1 - self.side].select{|c| c > 0}.sample)
@@b_wtime[self.side][self.mem] = 79 if @@b_target[self.side][self.mem] == nil

when 20..39

@@b_x[self.side][self.mem] += 20 if self.side == 0 && @@b_x[self.side][self.mem] <= @@b_x[1 - self.side][self.mem]
@@b_x[self.side][self.mem] -= 20 if self.side == 1 && @@b_x[self.side][self.mem] >= @@b_x[1 - self.side][self.mem]

@@b_y[self.side][self.mem] += 20 if @@b_y[self.side][self.mem] <= @@b_y[1 - self.side][@@b_target[self.side][self.mem]]
@@b_y[self.side][self.mem] -= 20 if @@b_y[self.side][self.mem] <= @@b_y[1 - self.side][@@b_target[self.side][self.mem]]


@@b_motion[self.side][self.mem] = 1

when 40..58

@@b_x[self.side][self.mem] += 20 if self.side == 0 && @@b_x[self.side][self.mem] <= @@b_x[1 - self.side][self.mem]
@@b_x[self.side][self.mem] -= 20 if self.side == 1 && @@b_x[self.side][self.mem] >= @@b_x[1 - self.side][self.mem]

@@b_y[self.side][self.mem] += 20 if @@b_y[self.side][self.mem] <= @@b_y[1 - self.side][@@b_target[self.side][self.mem]]
@@b_y[self.side][self.mem] -= 20 if @@b_y[self.side][self.mem] <= @@b_y[1 - self.side][@@b_target[self.side][self.mem]]

@@b_motion[self.side][self.mem] = 1

when 59

@@b_hp[1 - self.side][@@b_target[self.side][self.mem]] -= @@b_attack[self.side][self.mem]
@@b_motion[1 - self.side][@@b_target[self.side][self.mem]] = 2

when 60..79

@@b_x[self.side][self.mem] += -40 if self.side == 0 && @@b_x[self.side][self.mem] >= @@b_sx[self.side][self.mem]
@@b_x[self.side][self.mem] -= -40 if self.side == 1 && @@b_x[self.side][self.mem] <= @@b_sx[self.side][self.mem]

@@b_y[self.side][self.mem] += 20 if @@b_y[self.side][self.mem] <= @@b_sy[self.side][self.mem]
@@b_y[self.side][self.mem] -= 20 if @@b_y[self.side][self.mem] <= @@b_sy[self.side][self.mem]

@@b_motion[self.side][self.mem] = 1

when 80

@@b_doskill[self.side][self.mem] = 0
@@b_motion[self.side][self.mem] = 0
@@b_motion[1 - self.side][@@b_target[self.side][self.mem]] = 0 #敵のモーションも戻してあげる
@@b_wtime[self.side][self.mem] = -1
@@b_stats[self.side][self.mem] = -100
end
@@b_wtime[self.side][self.mem] += 1
end

$turn += 1 if @@b_doskill[self.side][self.mem] == 0 #ターンは、プレイヤーの行動順番を決める。
$turn = 0 if $turn == @@b_time.flatten.length #一周して戻す
end

#描写。action とわけないと何者かの移動発生中に描写が起こらない。
def render

#画面外対策
@@b_time[self.side][self.mem] = 0 if @@b_motioned[self.side][self.mem] != @@b_motion[self.side][self.mem]
@@b_x[self.side][self.mem] = 0 if @@b_x[self.side][self.mem] < 0
@@b_x[self.side][self.mem] = 640 if @@b_x[self.side][self.mem] > 640

#モーションタイムリセット
@@b_time[self.side][self.mem] = 0 if @@b_motion[self.side][self.mem] != @@b_motioned[self.side][self.mem]
#死亡画像を起こすために常にモーション画像固定
@@b_motion[self.side][self.mem] = 3 if @@b_hp[self.side][self.mem] <= 0
#モーション画像の設定
imagenum = 0
imagenum = 0 if @@b_motion[self.side][self.mem] >= 0 && @@b_motion[self.side][self.mem] <= 0
imagenum = 3 if @@b_motion[self.side][self.mem] >= 1 && @@b_motion[self.side][self.mem] <= 1
imagenum = 6 if @@b_motion[self.side][self.mem] >= 2 && @@b_motion[self.side][self.mem] <= 2
imagenum = 9 if @@b_motion[self.side][self.mem] >= 3 && @@b_motion[self.side][self.mem] <= 3

case @@b_time[self.side][self.mem]
when 120..999
@@b_time[self.side][self.mem] = 0
Window.draw_scale(self.side == 0 ? @@b_x[self.side][self.mem] - 32 : @@b_x[self.side][self.mem], @@b_y[self.side][self.mem], @@image[imagenum] ,self.side == 0 ? 1 : -1 ,1 , 16 ,16 , 1)
when 90..119
Window.draw_scale(self.side == 0 ? @@b_x[self.side][self.mem] - 32 : @@b_x[self.side][self.mem], @@b_y[self.side][self.mem], @@image[imagenum] ,self.side == 0 ? 1 : -1 ,1 , 16 ,16 , 1)
@@b_time[self.side][self.mem] += 2

when 60..89
Window.draw_scale(self.side == 0 ? @@b_x[self.side][self.mem] - 32 : @@b_x[self.side][self.mem], @@b_y[self.side][self.mem], @@image[imagenum + 1] ,self.side == 0 ? 1 : -1 ,1 , 16 ,16 , 1)
@@b_time[self.side][self.mem] += 2

when 30..59
Window.draw_scale(self.side == 0 ? @@b_x[self.side][self.mem] - 32 : @@b_x[self.side][self.mem], @@b_y[self.side][self.mem], @@image[imagenum + 2] ,self.side == 0 ? 1 : -1 ,1 , 16 ,16 , 1)
@@b_time[self.side][self.mem] += 2

when 0..29
Window.draw_scale(self.side == 0 ? @@b_x[self.side][self.mem] - 32 : @@b_x[self.side][self.mem], @@b_y[self.side][self.mem], @@image[imagenum + 1] ,self.side == 0 ? 1 : -1 ,1 , 16 ,16 , 1)
@@b_time[self.side][self.mem] += 2
end

@@b_time[self.side][self.mem] += 1
Window.draw_font(self.side * 200 + 20 , self.mem * 20 + 400 ,"400:" + @@b_stats[self.side][self.mem].to_s + "/" + @@b_speed[self.side][self.mem].to_s , @@font3, hash={color: [122,255,122]})

Window.draw_font(self.side * 200 + 100 , self.mem * 20 + 400 ,"400:" + @@b_hp[self.side][self.mem].to_s + "/" + @@b_shp[self.side][self.mem].to_s , @@font3, hash={color: [255,122,122]})
end
end

#チーム所属とエントリー番号を持つ人のクラス定義
class PersonInBattle < Person #Personで定義したものの継承
include Flamework # Flameworkモジュールを読み込む
def initialize(side,mem)
super(side,mem) #Personで定義したものの継承
end
end

#今回は4対4を再現する。新しいプレイヤーの定義
#()内の意味=>チーム所属:エントリー

player1 = PersonInBattle.new(0,0)
player2 = PersonInBattle.new(0,1)
player3 = PersonInBattle.new(0,2)
player4 = PersonInBattle.new(0,3)

player9 = PersonInBattle.new(1,0)
player10 = PersonInBattle.new(1,1)
player11 = PersonInBattle.new(1,2)
player12 = PersonInBattle.new(1,3)

#初回定義
player1.init
player2.init
player3.init
player4.init
player9.init
player10.init
player11.init
player12.init

Window.loop do #ループを起こす為の記述
#タイムラインシステム
#行動システム
#ミスクシステム
case $turn #順番ごとに実行する。直列動作を起こせる。
when 0 then player1.action
when 1 then player2.action
when 2 then player3.action
when 3 then player4.action
when 4 then player9.action
when 5 then player10.action
when 6 then player11.action
when 7 then player12.action
end
#表示システムは並列。
player1.render
player2.render
player3.render
player4.render
player9.render
player10.render
player11.render
player12.render

end

#さあ、実行!!


team_sample.png

http://pastebin.com/wDUEb05x

これは、クラスオブジェクト初心者のみれいゆーが、なんとかかんとかで使った場合のコードとなります。
できることは、起動すればまた分かるのですが
・4対4の擬似対戦
・体力を削り合う対戦をランダムターゲットで行う
・体力を失った敵は倒れ、ターゲットにされなくなる


ここまで出来ることが出来ました。

(かわいそうなことに)このコードを書く上で初めてクラス変数 @@ の意義を知りました・・・wインスタンス変数@ とも使い分けが重要なのですね!

今回、冗長な事に 各player の action だけを直列処理で実行したくて、 $turn なる変数を使ったりもしました。

また、コードにはあるのに
・敵のダメージモーションが変な人にある
・つまりターゲットの方向に動いていない
等の問題がまだあります。


こう作っていくと、上級者のコードはものすごくエレガントで、奥が深いと改めて感じます。
このチーム戦サイドビューはすでに私の処女作にも使われていて、そこではしっかりターゲットを狙うよう機能しております!!

今回題目にしたサイドビュー自動戦闘システムの他にも私は二個作品を作ってみました。いずれもDXRubyの勉強がてら作ったものです。

①:DXRuby 1.5devより追加されたIME機能とSpriteの組み合わせ
http://pastebin.com/kdq6fb5V
imput_sample.png
②:3つのクラスをそれぞれ操作して拡張、色変更、移動する。(本当は一部同期をしたかった)
http://pastebin.com/XTf9ex9y
class_sample.png

では、次回は、wannabe53 さんの DXRuby を Wine から使う ですーー
私の記事の前も後ろもすごい人ばかり!頑張らなくちゃ!!ではまたーー
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。