2022年 7月 8日
まだ完全では無いけど、セーブ、ロードを実装した。
だいぶ時間がかかったけど、たしかに仕事しながらだと進まないというのもあるけど、セーブ機能も案外ややこしいので手間取った、というのもある。
SAVE、LOAD機能を実装するくらいになると製作も終盤になる、なぜかと言うとセーブするデータがある程度そろわないと作りにくいから。
今回のセーブデータは、システムデータ sys.dat、ゲームデータ data.dat、初期配置データ sdat.dat、リプレイデータ rep_data.datがある。
セーブ初期
////////// save load ////////// *save_ini datb=1+1+3+1+1+1+1 ; lx、scf、dbpl、mb、plb、pn、padn datb+=mb*2 ; map、hwd datb+=lddb ; 領土 datb+=(ldb+1)*ldlb+1 ; 隣接国 datb+=(ldb+1)*10 datb+=(plb+1)*7 datb+=21 ; カラー dim dtf ,8 ; データフラグ dim dat ,datb ; セーブデータ dim sdat,datb ; 初期セーブデータ smpdb=mb*8+1 ; セーブマップ表示データ dim sys,30+smpdb+10 ; システムデータ ; 表示座標 slwx1=22 : slwxn=62 slwx2=slwx1+8 slwx3=slwx1+(8*(slwxn-1)) slwy1=106 slwyk=103 ; slwyn=12 dim slwy,9 repeat 8 slwy(cnt+1)=slwy1+(slwyk*cnt) ;slwy(cnt+1)=slwy1+(slwyn*8)*cnt loop slwy(8)=846 ; キ-座標 dim sl_kx1,17 dim sl_kx2,17 dim sl_ky1,17 dim sl_ky2,17 x=slwxn*8 : xk1=390 : xk2=x-xk1 y=90 ; y=slwyn*8 repeat 8,1 sl_kx1(cnt) =slwx1 sl_kx2(cnt) =slwx1+xk1 sl_kx1(cnt+8)=slwx1+xk1 sl_kx2(cnt+8)=slwx1+x sl_ky1(cnt) =slwy(cnt) sl_ky2(cnt) =slwy(cnt)+y sl_ky1(cnt+8)=slwy(cnt) sl_ky2(cnt+8)=slwy(cnt)+y loop ; 確認座標 smx0=30 : smy0=200 smx1=26 : smy1=719 smx2=277 : smy2=719 return
dtf データフラグは読み込みの際に、その番のデータがあるか。
datがセーブするデータだけど、セーブの仕方がこれで良いのかどうか分からないけど、自分はベタに一つの変数 datに全部放り込んで bsaveする方法にしてる。
効率は悪い(ややこしい)と思うけど、他に方法を知らない。
セーブするデータの総量を算出して datbにし、dim dat,datbで定義してる。
sdatは初期配置データで「始めからやりなおし RESTART」の時のデータ、まだ実装してない。
sysはシステムデータ。
システムデータセーブ
; システムデータ、セーブ #deffunc sys_save sys(1)=bpwt1f ; Map display speed sys(2)=bpwt2f ; Battle display speed sys(3)=btl_putf ; 戦闘表示 sys(4)=glv ; GAAME LEVEL sys(5)=cld(55) ; USER COLOR sys(6)=cld(56) sys(7)=cld(57) ; セーブマップ表示データ n=30+saven*55*hyb repeat mb sys(n+cnt)=map(cnt) loop sys(30+8*55*hyb+1+saven)=hxb ; xの長さ bsave "sys.dat",sys ; システムデータ、セーブ return
1~7までは、いわいるコンフィング関係のデータで、例えば表示速度とかゲームレベルとか、ゲーム中に変えられるようなデータ。
音関係はまだ実装してないので入ってない。
30~はセーブ画面のミニマップ表示用のマップデータを 8つ分。
ミニマップというのはこれ
今回、苦労したものの一つがこれで、これが無いとセーブデータの内容が具体的に分かりにくいと思ったので作った。
表示速度的にどうかとも思ったけど、表示毎フレーム 8マップ分、2ドットの大きさで 1つずつ描く仕様になってる。
copyが使えればねえ。
表示
#deffunc save_put gmode 2 : gmulcolor 255,255,255 ; 背景 pos px,py : celput opt1w pos 100+px,29+py : celput gsft35w if smf=0 { repeat 8,1 pos sl_kx1(cnt)+px,sl_ky1(cnt)+py : celput sl_winw ; ウィンドウ loop ; ミニマップ x1=slwx1+96+px repeat 8 if dtf(cnt)=0 : continue cn=cnt ; n番目 mn=30+cn*55*hyb ; マップデータ座標 if cnt=0 : cn=8 mnmx=sys(30+8*55*hyb+1+cnt) ; xの長さ repeat hyb : cy=cnt ; y座標 x2=x1+cy\2 if mnmx=19 : x2+=36 if mnmx=37 : x2+=18 y=slwy(cn)+15+cy*2+py repeat mnmx : mpn=sys(mn) : mn+ ; x座標 ; repeat hxb : mn=hxb*cy+cnt : mpn=smpd(mn) ; x座標 if mpn=0 or mpn=-1 : continue if mpn=-2 : gmulcolor 8, 24,160 ; 海 if mpn=-3 : gmulcolor 72,104,176 ; 湖 if mpn=-4 : gmulcolor 208,136, 56 ; 山 if mpn>0 { pn=lpd(mpn) if pn>0 { ; プレイヤー領土 cln=(clpn(pn)-1)*3 gmulcolor cld(cln+1)+16,cld(cln+2)+16,cld(cln+3)+16 } } pos x2+cnt*2,y : celput smhbw loop loop loop gmulcolor 255,255,255 ; アイコン repeat 8 pos slwx1+30+px ,slwy(cnt+1)+21+py : celput s_icw if dtf(cnt)=0 : continue cn=cnt : if cnt=0 : cn=8 pos slwx1+417+px,slwy(cn)+21+py : celput l_icw loop sl_mj2="AUTO SAVE" repeat 8,1 : cn=cnt if cnt<8 { sl_mj1="DATA "+str(cn) bfp sl_mj1,240+px,slwy(cnt)+12+py } if cnt=8 : bfp sl_mj2+px,240,slwy(cnt)+12+py loop repeat 8 if dtf(cnt)=0 : continue cn=cnt : if cnt=0 : cn=8 n=gettime(0) : s=str(n) ; 年 bfp s,250+px,slwy(cn)+38+py n=gettime(1) : s=str(n) ; 月 bfp s,300+px,slwy(cn)+38+py n=gettime(3) : s=str(n) ; 日 bfp s,330+px,slwy(cn)+38+py n=gettime(4) : s=str(n) ; 時 bfp s,360+px,slwy(cn)+38+py n=gettime(5) : s=str(n) ; 分 bfp s,390+px,slwy(cn)+38+py bfp "TARN",260+px,slwy(cn)+64+py loop } if smf=1 { ;pos 90,200 : celput sinfow pos smx0+px,smy0+py : celput sinfow;,0,0.8,0.8 ; yes pos smx1+px ,smy1+py : celput gsbt1w pos smx1+80+px,smy2+18+py : celput slft1w ; no pos smx2+px ,smy1+py : celput gsbt1w pos smx2+95+px,smy2+18+py : celput slft2w } if smf=2 { ;pos 90,200 : celput sinfow pos smx0+px,smy0+py : celput sinfow;,0,0.8,0.8 ; yes pos smx1+px ,smy1+py : celput gsbt1w pos smx1+80+px,smy2+18+py : celput slft1w ; no pos smx2+px ,smy1+py : celput gsbt1w pos smx2+95+px,smy2+18+py : celput slft2w } return
ミニマップの所が、そのミニマップ表示の部分。
ゲーム画面が 1画面~3画面と 3種類あるので、その HEXの横 xの数を、システムデータ sysの最後に 8マップ分格納している。
smfは、セーブモードフラグで、0=通常、1や2はセーブしますか、ロードしますかの画面。
これもまだ出来てないけど「セーブしますか?」のような文字を描く。
YESを押せばセーブされます、それ以外だと smf=0に戻ります。
データセーブ
#deffunc save int _saven,int _savef saven=_saven ; セーブNo. savef=_savef ; フラグ sys_save ; システムデータ、セーブ if savef=1 : return ; システムデータのみ ; データ格納 n=0 dat(n)=lx : n+ ; スクロール表示位置 dat(n)=scf : n+ ; スクロールフラグ dat(n)=dbpl : n+ ; 最大戦力プレイヤー dat(n)=plb : n+ ; プレイヤー数 dat(n)=pn : n+ ; 現在のプレイヤー dat(n)=padn : n+ ; 現在の順番 dat(n)=mb : n+ ; 全HEX数 dat(n)=ldb : n+ ; 国数 repeat mb : dat(n)=map(cnt) : n+ : loop ; マップ repeat mb : dat(n)=hwd(cnt) : n+ : loop ; HEXの外周枠情報 repeat lddb : dat(n)=ld(cnt) : n+ : loop ; 領土 repeat (ldb+1)*ldlb+1 : dat(n)=ldl(cnt) : n+ : loop ; 隣接国 repeat ldb+1 : dat(n)=lpd(cnt) : n+ : loop ; 領土のプレイヤー repeat ldb+1 : dat(n)=ldcd(cnt) : n+ : loop ; ダイス数 repeat ldb+1 : dat(n)=ldcu(cnt) : n+ : loop ; キャラクターユニット repeat ldb+1 : dat(n)=dpd(cnt) : n+ : loop ; 国のダイス表示順番 repeat ldb+1 : dat(n)=lcx(cnt) : n+ : loop ; 中間座標x repeat ldb+1 : dat(n)=lcy(cnt) : n+ : loop ; 中間座標y repeat ldb+1 : dat(n)=ldhx1(cnt): n+ : loop ; 最左ライン repeat ldb+1 : dat(n)=ldhx2(cnt): n+ : loop ; 最右ライン repeat ldb+1 : dat(n)=ldhy1(cnt): n+ : loop ; 最上ライン repeat ldb+1 : dat(n)=ldhy2(cnt): n+ : loop ; 最下ライン repeat plb+1 : dat(n)=pfd(cnt) : n+ : loop ; プレイヤー操作 repeat plb+1 : dat(n)=pld(cnt) : n+ : loop ; プレイヤーの領土数 repeat plb+1 : dat(n)=pdca(cnt) : n+ : loop ; 増ダイス数 repeat plb+1 : dat(n)=pad(cnt) : n+ : loop ; 行動順 repeat plb+1 : dat(n)=dp(cnt) : n+ : loop ; 全ダイス戦力数 repeat plb+1 : dat(n)=dcpn(cnt) : n+ : loop ; 表示ダイスグラNo. repeat plb+1 : dat(n)=pdsd(cnt) : n+ : loop ; ストックダイス数 repeat 21 : dat(n)=clpn(cnt) : n+ : loop ; カラー s="data"+saven+".dat" bsave s,dat ; データセーブ ; リプレイデータセーブ dim repd2,repc repeat repc repd2(cnt)=repd(cnt) loop repd2(0)=repc ; カウンター repd2(1)=repk ; データ位置 bsave "rep_data"+saven+".dat",repd2 return
見ての通り、dat データ n番目にデータを次々と放り込むベタなやり方になってます。
ロードはこの逆です。
システムデータとリプレイデータもセーブしてます、後は初期配置データもセーブされるよう後で実装します。
ロードはこうなります。
; データ引き出し s="data"+loadn+".dat" exist s if strsize!-1 { bload s,dat ; データロード n=0 lx=dat(n) : n+ ; スクロール表示位置 scf=dat(n) : n+ ; スクロールフラグ if scf=1 : hxb=19 ; 横のマスの数 if scf=2 : hxb=37 if scf=3 : hxb=55 dbpl=dat(n) : n+ ; 最大戦力プレイヤー plb=dat(n) : n+ ; プレイヤー数 pn=dat(n) : n+ ; 現在のプレイヤー padn=dat(n)-1 : n+ ; 現在の順番 mb=dat(n) : n+ ; 全HEX数 ldb=dat(n) : n+ ; 国数 repeat mb : map(cnt)=dat(n) : n+ : loop ; マップ repeat mb : hwd(cnt)=dat(n) : n+ : loop ; HEXの外周枠情報 repeat lddb : ld(cnt)=dat(n) : n+ : loop ; 領土 repeat (ldb+1)*ldlb+1 : ldl(cnt)=dat(n) : n+ : loop ; 隣接国 repeat ldb+1 : lpd(cnt) =dat(n) : n+ : loop ; 領土のプレイヤー repeat ldb+1 : ldcd(cnt)=dat(n) : n+ : loop ; ダイス数 repeat ldb+1 : ldcu(cnt)=dat(n) : n+ : loop ; キャラクターユニット repeat ldb+1 : dpd(cnt) =dat(n) : n+ : loop ; 国のダイス表示順番 repeat ldb+1 : lcx(cnt)=dat(n) : n+ : loop ; 中間座標x repeat ldb+1 : lcy(cnt)=dat(n) : n+ : loop ; 中間座標y repeat ldb+1 : ldhx1(cnt)=dat(n): n+ : loop ; 最左ライン repeat ldb+1 : ldhx2(cnt)=dat(n): n+ : loop ; 最右ライン repeat ldb+1 : ldhy1(cnt)=dat(n): n+ : loop ; 最上ライン repeat ldb+1 : ldhy2(cnt)=dat(n): n+ : loop ; 最下ライン repeat plb+1 : pfd(cnt)=dat(n) : n+ : loop ; プレイヤー操作 repeat plb+1 : pld(cnt)=dat(n) : n+ : loop ; プレイヤーの領土数 repeat plb+1 : pdca(cnt)=dat(n) : n+ : loop ; 増ダイス数 repeat plb+1 : pad(cnt)=dat(n) : n+ : loop ; 行動順 repeat plb+1 : dp(cnt)=dat(n) : n+ : loop ; 全ダイス戦力数 repeat plb+1 : dcpn(cnt)=dat(n) : n+ : loop ; 表示ダイスグラNo. repeat plb+1 : pdsd(cnt)=dat(n) : n+ : loop ; ストックダイス数 repeat 21 : clpn(cnt)=dat(n) : n+ : loop ; カラー } ; リプレイデータロード s="rep_data"+loadn+".dat" exist s : n=strsize if n!-1 { dim repd2,n bload s,repd2 repc=repd2(0) ; カウンター repk=repd2(1) ; データ位置 repeat repc repd(cnt)=repd2(cnt) loop repd(repc)=0 ; 終端処理 } return
リプレイデータのセーブファイルの大きさは「可変」にしました、量が大きいので。
existでファイルがあるか調べ、無ければ strsizeに-1が入り、有れば strsizeにそのデータ量が入ります。
dim定義で、その strsizeを指定してやります。
dim repd2,strsizeですね。
repdは実際に使うリプレイのデータで、100000個で固定です。
ロードした repd2のデータを repdに移して使います。
saveの時も使用している repdデータだけを repd2に移してセーブしてます、ファイルのデータ量を抑えられます。
つまり、ゲーム中で使ってる repdデータは 100000個で固定して使い、セーブファイルだけ必要な量だけにしてセーブしてます。
セーブメイン
*save_main ; セーブ smf=0 ; モードフラグ ; データフラグ repeat 8 : cn=cnt dtf(cn)=0 exist "data"+cn+".dat" if strsize!-1 : dtf(cn)=1 ; データが有る loop ; システムデータ exist "sys.dat" if strsize!-1 : bload "sys.dat",sys ; システムデータ、ロード optgf=optf kwc=5 : kf=0 repeat k=0 : mx=0 : my=0 ; 入力リセット repeat 1 if kwc>0 : kwc- : break ; ウエイト mx=mousex : my=mousey ; カーソル位置 stick k,256 ; マウスボタン、スマホクリック loop ; 押してる repeat 1 if k!256 : break if kf=0 { kf=1 kepx=mx : kepy=my } if mx-kepx>18 { ; 右へ操作、左の画面へ opt_scf=4 ospf=4 ; 元の optf=2 ; オプション2へ break } if kepx-mx>18 { ; 左へ操作、右の画面へ opt_scf=3 ospf=4 ; 元の optf=3 ; PCチェンジへ break } if kepy-my>18 { ; 上へスクロールして戻る opt_scf=1 ospf=4 ; 元の optf=0 ; メインゲームへ break } if my-kepy>18 { ; 下へスクロールして戻る opt_scf=2 ospf=4 ; 元の optf=0 ; メインゲームへ break } loop ; 押してない repeat 1 if k=256 : break ; 放してない if kf=0 : break ; 一度も押してない kf=0 sl_keyn=0 switch smf case 0 repeat 16,1 ; キー入力 if sl_kx1(cnt)<mx and sl_ky1(cnt)<my and sl_kx2(cnt)>mx and sl_ky2(cnt)>my { sl_keyn=cnt : break } loop if 1<=sl_keyn and sl_keyn<=7 { ; SAVE smf=1 : sl_keyn2=sl_keyn dtf(sl_keyn2)=1 } if 9<=sl_keyn and sl_keyn<=16 { ; LOAD sl_keyn2=sl_keyn-8 if sl_keyn=16 : sl_keyn2=0 smf=2 } swbreak case 1 ; SAVE if smx1+2<mx and smy1+2<my and smx1+235>mx and smy1+66>my { ; YES opt_scf=2 ospf=4 ; 元の if msf=2 : te_kf=3 : optf=0 : swbreak ; NPC save sl_keyn2 ; セーブ } smf=0 swbreak case 2 ; LOAD if smx1+2<mx and smy1+2<my and smx1+235>mx and smy1+66>my { ; YES load sl_keyn2 ; ロード opt_scf=2 ospf=4 ; 元の optf=0 ; メインゲームへ msf=0 ; 抜け用 break } smf=0 swbreak swend loop if optf!4 : break ; 戻る redraw 0 save_put pos 10,600 : mes sl_keyn redraw 1 await 1000/50 loop return
操作関係で、横スワイプだとオプション画面等の切り替えになります。
縦スワイプでゲームに戻ります。
左側のファイル表示を押せばセーブ、右側がロードです。
一番下はオートセーブで、ゲ-ムのターンエンド時に自動でセーブされます、データ的には dara0になります。
一般セーブは 7箇所で data1~data7になります。
セーブ関係のソースでは以上ですけど、これの難しいのは「どのタイミングでセーブするか」で、これもまだ完全では無いんですけど、プレイヤー操作時のセーブは問題ありませんけど、CPUの場合はどの時点でセーブするのか難しいです。
オプションと並列になってるので、オプションはいつでも操作出来る方が望ましいのでしょうけど、セーブのタイミングを同じにはできません、操作アルゴリズム中の中断デ-タなんてできる訳ありません。
そこで、セーブ操作をした後、その CPUの勢力が操作を終えた時点でセーブされるようにしてます。
セーブを決定すると、その CPUの操作が終わるまで表示をスキップするようにしてます。
操作が終わった時点でセーブします。