まったくのプログラミング素人の筆者がC++/HSPを使用してSTG(シューティングゲーム)を作っていく過程を書くブログでしたが最近は脱線気味。プログラミング以外にも、ゲーム関連の記事、日々の戯言など。
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
ゲームのリプレイは是非とも導入したかったので、以前軽くリプレイデータの作成を考えてみた事がありました。
その時にわからなかったのが、ランダムを使用した時のリプレイの記録の仕方でした。
で、ちょっと調べてみると、どうもランダムとは言っても実際は本当にバラバラに数字を選んでいるのではなく
数え切れないくらいのランダム表みたいなのがあって、その表を選んでランダムっぽく見せかけているようです。
例えば、適当なスクリプトでrandomizeと実行するのではなくrandomize 1と実行すると、毎回同じ値を選びます。
つまり、ランダムを使用してリプレイデータを作成しようと思ったら、このrandomize 1の1の部分を記憶させれば良いって事になります。
それを踏まえて、ランダムを使用したリプレイスクリプトを作ってみました↓
*************************************
*get_mode
;リプレイ記録用変数
sdim replay,32000;リプレイ用にメモリを確保
sdim replay1,32000;リプレイ用にメモリを確保
sdim rando,65535;リプレイ用にメモリを確保
fname="replay.dat"
fname1="replay1.dat"
fname2="rando.dat"
timer=0;ゲームタイマー
dialog "「はい」:ゲームプレイ/「いいえ」:リプレイ再生",2
if stat=6 : playmode=0
if stat=7 : playmode=1 : gosub *load_data
*strat
screen 0,240,320,0,0,0 ;メインウインドウ0を240×320で用意
;■配列宣言
tmx=10 ;自機弾の数
dim tx,tmx ;自機弾のX座標の配列変数
dim ty,tmx ;自機弾のY座標の配列変数
dim tf,tmx ;自機弾が画面にあるか無いかを調べる配列変数
emt=1000;敵の弾
dim etx,emt : dim ety,emt ;敵弾の位置、移動量
dim etxv,emt ; etxv 敵弾x方向運動量
dim etyv,emt ; etyv 敵弾y方向運動量
dim etf,emt;敵弾のフラグ
;■初期化
px=112:py=304 ;自機の初期位置
ex=0:ey=10 ;敵の初期位置
etx(cnt)=0:ety(cnt)=10 ;敵弾の初期位置
etxv(cnt)=-10:etyv(cnt)=-10;敵弾の移動量
etf(cnt)=0;敵弾の有無,0は無し
dx=8:dy=8 ;敵の移動量
dx=1:dy=1 ;敵弾の移動量
tx(cnt)=px:ty(cnt)=py ;自機弾の初期位置
tf(cnt)=0;自機弾の有無,0は無し
shottime=0;連射速度
st=0;ショットフラグ
ky=0;自機移動
space=0;ショットボタン
ef=1;敵の有無,1は有り
eh=0;敵弾用の変数
r=0.;敵弾用の変数
;■ランダム生成
if playmode=0 : gosub *rando_load;ゲームプレイ時はrandomize実行
if playmode=1 : gosub *rando_read;リプレイ再生時はrandomize読み込み
;■メインルーチン
*main
getkey syuryo,27:if syuryo=1:end;ESCを押すと強制終了
timer=timer+(timer<32000) ;ゲームタイマー加算
if playmode=0 : gosub *key_input;ゲームプレイ時はキー入力へ
if playmode=0 : gosub *key_input1;ゲームプレイ時はキー入力へ
if playmode=1 : gosub *read_replay;リプレイ再生時はリプレイファイル読み込みへ
if playmode=1 : gosub *read_replay1;リプレイ再生時はリプレイファイル読み込みへ
redraw 0 ;仮想画面を指定
color 0,0,0 ;色に黒を指定
boxf 0,0,240,320 ;指定色で画面を塗りつぶす
;■自機移動処理
pos px,py ;自機の座標指定
color 0,0,255 : mes "▲" ;青で▲を表示
if px<0 :px=0
if px>224 :px=224
if py<0 :py=0
if py>304 :py=304
;■自機弾発射処理
shottime-- ;連射為の処理
if hassya=1 : if shottime<0: st=1 ;スペースキーが押されていて、かつshottimeが0以下の時、stに1を代入
repeat tmx ;repeat〜loop間をtmx回繰り返す
if tf(cnt)=0 : if st=1{ ;tf(cnt)が0の時=画面に弾がない時、かつstが1の時(スペースキーを押している時){}内の命令を行う
tx(cnt)=px ;tx(cnt)(弾のx座標)にpx(自機のx座標)の値を代入
ty(cnt)=py ;ty(cnt)(弾のy座標)にpy(自機のy座標)の値を代入
tf(cnt)=1 ;tf(cnt)に1を代入。つまり画面に弾があると言う事
st=0 ;連射の為の処理
shottime=3 ;連射の為の処理
}
ty(cnt)-=5 : if ty(cnt)<-16 : tf(cnt)=0 :ty(cnt)=-16: st=0;弾を進めて画面から出たら弾を消す
if tf(cnt)=1{ ;画面に弾があるとき、{}内の命令を行う
pos tx(cnt),ty(cnt) ;自機弾の座標指定
color 255,255,255 : mes "□";□を表示
}
loop
;■敵発生、移動処理
if ef=0 : ex=rnd(220) : ey=0 :ef=1;敵が画面にいない時は敵生成
if ef=1 {;敵が画面にいる時は{}内の命令を行う
ex=ex+dx ;敵座標に移動量をプラス
if(ex<0)|(ex>224) :dx=-dx ;画面端で反転
ey=ey+dy
if(ey<0)|(ey>100) :dy=-dy ;y軸0〜100ドットの間で反転
pos ex,ey
color 255,0,0 : mes "■" ;■を表示
}
;■敵弾処理
if timer¥6=0 : gosub *tama ;timer÷6の余りが0の時*tamaに移動
repeat emt ;emt回繰り返す
if etf(cnt)=1 { ;敵弾が画面に有る時{}内の命令を行う
etx(cnt)=etxv(cnt)+etx(cnt) ;敵弾のx軸の座標指定
ety(cnt)=etyv(cnt)+ety(cnt) ;敵弾のx軸の座標指定
pos etx(cnt),ety(cnt) ;敵弾の座標指定
color 255,255,0 : mes "○" ;○を表示
if etx(cnt)>240 : etf(cnt)=0 ;敵弾が画面から消えたら存在をなしにする
if etx(cnt)<-16 : etf(cnt)=0
if ety(cnt)>320 : etf(cnt)=0
if ety(cnt)<-16 : etf(cnt)=0
}
loop
;■敵弾当り判定
repeat emt ;emt回繰り返す
if etf(cnt)=0 : continue ;敵弾がない時はループを繰り返す。
ddx=(etx(cnt)+8)-(px+8) ;中心座標の計算
ddy=(ety(cnt)+8)-(py+8) ;中心座標の計算
if (abs(ddx)<4)&(abs(ddy)<4) { ;自機と敵との差が4ドット以下なら{}内の命令を行う
gosub *gameover};*gameoverに行く
loop
;■自機当り処理
repeat tmx ;repeat〜loop間をemx回繰り返す
if tf(cnt)=0 : continue ;tf(cnt)が0(画面に自機弾がない)ならばrepeatに戻る
if (abs(ex-tx(cnt))<16)&(abs(ey-ty(cnt))<16) { ;自機弾と敵との差が16ドット以下なら{}内の命令を行う
ef=0 ;敵消去
}
loop
;■インフォ表示
pos 0,0:color 255,255,255:mes "timer "+timer+"";タイマー入力を表示
pos 100,0:color 255,255,255:mes "randomize "+randn+"";randomize数を表示
;■Fps表示
fp++ ;フレーム数を覚えておく変数
time=gettime(6) ;timeに現在の秒数を代入
if time!=time2 { ;timeとtime2が等しくなければ{}内の命令を行う
fpc=fp ;fpc(表示するFps数)にfp(現在のフレーム数)を代入
fp=0 } ;フレーム数をクリア
pos 180,300
mes " Fps "+fpc ;フレーム数の表示
time2=time ;time2に現在の秒数を代入(1秒ごとに比べる為)
redraw 1 ;画面を更新
await 16
goto *main ;*mainに戻る
;以下サブルーチン
;■敵弾処理
*tama
repeat emt ;emt回繰り返す
if etf(cnt)>0 : continue ;画面に弾がある時はrepeatに戻る
eh=eh+1 ;弾の数の為の計算
etf(cnt)=1 ;弾の存在を有にする
etx(cnt)=double(ex) : ety(cnt)=double(ey) ;弾の座標を実数で設定
ds=2 ;敵弾の速さ
r=double(r+0.6) ;弾の角度を実数で計算
etxv(cnt)=cos(r)*ds : etyv(cnt)=sin(r)*ds ;弾の移動量を角度より計算
if eh>10 : eh=0 :break ;10発弾を撃った時点でrepeatを抜ける
loop
return
;■各データロード
*load_data
bload fname,replay,32000;replayデータにfnameをロード
bload fname1,replay1,32000;replay1データにfname1をロード
bload fname2,rando,32000;replay2データにfname2をロード
wait 10
return
;■方向キー入力
*key_input
;自機移動処理
stick ky,15 ;キーボードのチェック
if ky&1 :px-=3
if ky&4 :px+=3
if ky&2 :py-=3
if ky&8 :py+=3
;変数kyをリプレイデータに記録
poke replay,timer,ky
return
;■スペースキー入力
*key_input1
;自機移動処理
stick space,16 ;キーボードのチェック
if space&16 {hassya=1}else{hassya=0}
;変数spaceをリプレイデータに記録
poke replay1,timer,space
return
;■方向キー出力
*read_replay
;リプレイデータを変数kyに代入
ky=peek(replay,timer)
if ky&1 :px-=3
if ky&4 :px+=3
if ky&2 :py-=3
if ky&8 :py+=3
return
;■スペースキー出力
*read_replay1
;リプレイデータを変数spaceに代入
space=peek(replay1,timer)
if space&16 :{hassya=1}else{hassya=0}
return
;■ランダム入力
*rando_load
randn = gettime(4)*1000 + gettime(5)*100 + gettime(6)
randomize randn
wpoke rando,0,randn
return
;■ランダム出力
*rando_read
randn=wpeek(rando,0)
randomize randn
return
;■ゲームオーバー
*gameover
if playmode=0 {
bsave fname,replay,32000;fnameにreplayデータをセーブ
bsave fname1,replay1,32000;fnameにreplayデータをセーブ
bsave fname2,rando,32000
wait 10
}
goto *get_mode
*************************************
HSP3.0使用
実行するとこんな感じ↓
ちなみにスペースで弾発射です。
で、そのランダム部分をどうしたかと言うと、以前コメント欄でアドバイスを頂いたLeighさんのスクリプトを参考に↓こんな感じで発生させました。
randn = gettime(4)*1000 + gettime(5)*100 + gettime(6)
randomize randn
wpoke rando,0,randn
この方法だとスクリプトを実行した時間でランダムの数が決まるので、ほぼ同じパターンにはならない(はず)
ただ、このスクリプトの場合リプレイファイルをreplay.dat(キー入力用)
replay1.dat(スペースキー入力用)rando.dat(ランダム入力用)
の3個のファイルに分かれてしまっています。
たぶん、一つのファイルに纏める事が出来るような気がするのですが、イマイチpeek,poke,sdim命令などが理解出来ておらず
上手く出来ていません(汗)
そして、このスクリプトを作っている時に気づいたのですが、リプレイを実装する為には最初に変数などを初期化しないと同じ動きにならないので
リプレイが成立しません。
で、今作ってるSTGなんですが、リプレイの事など全く気にせず作製していたので
色んな変数を初期化する為のスクリプトを作らないといけないことに気づき途方に暮れています^^;
その時にわからなかったのが、ランダムを使用した時のリプレイの記録の仕方でした。
で、ちょっと調べてみると、どうもランダムとは言っても実際は本当にバラバラに数字を選んでいるのではなく
数え切れないくらいのランダム表みたいなのがあって、その表を選んでランダムっぽく見せかけているようです。
例えば、適当なスクリプトでrandomizeと実行するのではなくrandomize 1と実行すると、毎回同じ値を選びます。
つまり、ランダムを使用してリプレイデータを作成しようと思ったら、このrandomize 1の1の部分を記憶させれば良いって事になります。
それを踏まえて、ランダムを使用したリプレイスクリプトを作ってみました↓
*************************************
*get_mode
;リプレイ記録用変数
sdim replay,32000;リプレイ用にメモリを確保
sdim replay1,32000;リプレイ用にメモリを確保
sdim rando,65535;リプレイ用にメモリを確保
fname="replay.dat"
fname1="replay1.dat"
fname2="rando.dat"
timer=0;ゲームタイマー
dialog "「はい」:ゲームプレイ/「いいえ」:リプレイ再生",2
if stat=6 : playmode=0
if stat=7 : playmode=1 : gosub *load_data
*strat
screen 0,240,320,0,0,0 ;メインウインドウ0を240×320で用意
;■配列宣言
tmx=10 ;自機弾の数
dim tx,tmx ;自機弾のX座標の配列変数
dim ty,tmx ;自機弾のY座標の配列変数
dim tf,tmx ;自機弾が画面にあるか無いかを調べる配列変数
emt=1000;敵の弾
dim etx,emt : dim ety,emt ;敵弾の位置、移動量
dim etxv,emt ; etxv 敵弾x方向運動量
dim etyv,emt ; etyv 敵弾y方向運動量
dim etf,emt;敵弾のフラグ
;■初期化
px=112:py=304 ;自機の初期位置
ex=0:ey=10 ;敵の初期位置
etx(cnt)=0:ety(cnt)=10 ;敵弾の初期位置
etxv(cnt)=-10:etyv(cnt)=-10;敵弾の移動量
etf(cnt)=0;敵弾の有無,0は無し
dx=8:dy=8 ;敵の移動量
dx=1:dy=1 ;敵弾の移動量
tx(cnt)=px:ty(cnt)=py ;自機弾の初期位置
tf(cnt)=0;自機弾の有無,0は無し
shottime=0;連射速度
st=0;ショットフラグ
ky=0;自機移動
space=0;ショットボタン
ef=1;敵の有無,1は有り
eh=0;敵弾用の変数
r=0.;敵弾用の変数
;■ランダム生成
if playmode=0 : gosub *rando_load;ゲームプレイ時はrandomize実行
if playmode=1 : gosub *rando_read;リプレイ再生時はrandomize読み込み
;■メインルーチン
*main
getkey syuryo,27:if syuryo=1:end;ESCを押すと強制終了
timer=timer+(timer<32000) ;ゲームタイマー加算
if playmode=0 : gosub *key_input;ゲームプレイ時はキー入力へ
if playmode=0 : gosub *key_input1;ゲームプレイ時はキー入力へ
if playmode=1 : gosub *read_replay;リプレイ再生時はリプレイファイル読み込みへ
if playmode=1 : gosub *read_replay1;リプレイ再生時はリプレイファイル読み込みへ
redraw 0 ;仮想画面を指定
color 0,0,0 ;色に黒を指定
boxf 0,0,240,320 ;指定色で画面を塗りつぶす
;■自機移動処理
pos px,py ;自機の座標指定
color 0,0,255 : mes "▲" ;青で▲を表示
if px<0 :px=0
if px>224 :px=224
if py<0 :py=0
if py>304 :py=304
;■自機弾発射処理
shottime-- ;連射為の処理
if hassya=1 : if shottime<0: st=1 ;スペースキーが押されていて、かつshottimeが0以下の時、stに1を代入
repeat tmx ;repeat〜loop間をtmx回繰り返す
if tf(cnt)=0 : if st=1{ ;tf(cnt)が0の時=画面に弾がない時、かつstが1の時(スペースキーを押している時){}内の命令を行う
tx(cnt)=px ;tx(cnt)(弾のx座標)にpx(自機のx座標)の値を代入
ty(cnt)=py ;ty(cnt)(弾のy座標)にpy(自機のy座標)の値を代入
tf(cnt)=1 ;tf(cnt)に1を代入。つまり画面に弾があると言う事
st=0 ;連射の為の処理
shottime=3 ;連射の為の処理
}
ty(cnt)-=5 : if ty(cnt)<-16 : tf(cnt)=0 :ty(cnt)=-16: st=0;弾を進めて画面から出たら弾を消す
if tf(cnt)=1{ ;画面に弾があるとき、{}内の命令を行う
pos tx(cnt),ty(cnt) ;自機弾の座標指定
color 255,255,255 : mes "□";□を表示
}
loop
;■敵発生、移動処理
if ef=0 : ex=rnd(220) : ey=0 :ef=1;敵が画面にいない時は敵生成
if ef=1 {;敵が画面にいる時は{}内の命令を行う
ex=ex+dx ;敵座標に移動量をプラス
if(ex<0)|(ex>224) :dx=-dx ;画面端で反転
ey=ey+dy
if(ey<0)|(ey>100) :dy=-dy ;y軸0〜100ドットの間で反転
pos ex,ey
color 255,0,0 : mes "■" ;■を表示
}
;■敵弾処理
if timer¥6=0 : gosub *tama ;timer÷6の余りが0の時*tamaに移動
repeat emt ;emt回繰り返す
if etf(cnt)=1 { ;敵弾が画面に有る時{}内の命令を行う
etx(cnt)=etxv(cnt)+etx(cnt) ;敵弾のx軸の座標指定
ety(cnt)=etyv(cnt)+ety(cnt) ;敵弾のx軸の座標指定
pos etx(cnt),ety(cnt) ;敵弾の座標指定
color 255,255,0 : mes "○" ;○を表示
if etx(cnt)>240 : etf(cnt)=0 ;敵弾が画面から消えたら存在をなしにする
if etx(cnt)<-16 : etf(cnt)=0
if ety(cnt)>320 : etf(cnt)=0
if ety(cnt)<-16 : etf(cnt)=0
}
loop
;■敵弾当り判定
repeat emt ;emt回繰り返す
if etf(cnt)=0 : continue ;敵弾がない時はループを繰り返す。
ddx=(etx(cnt)+8)-(px+8) ;中心座標の計算
ddy=(ety(cnt)+8)-(py+8) ;中心座標の計算
if (abs(ddx)<4)&(abs(ddy)<4) { ;自機と敵との差が4ドット以下なら{}内の命令を行う
gosub *gameover};*gameoverに行く
loop
;■自機当り処理
repeat tmx ;repeat〜loop間をemx回繰り返す
if tf(cnt)=0 : continue ;tf(cnt)が0(画面に自機弾がない)ならばrepeatに戻る
if (abs(ex-tx(cnt))<16)&(abs(ey-ty(cnt))<16) { ;自機弾と敵との差が16ドット以下なら{}内の命令を行う
ef=0 ;敵消去
}
loop
;■インフォ表示
pos 0,0:color 255,255,255:mes "timer "+timer+"";タイマー入力を表示
pos 100,0:color 255,255,255:mes "randomize "+randn+"";randomize数を表示
;■Fps表示
fp++ ;フレーム数を覚えておく変数
time=gettime(6) ;timeに現在の秒数を代入
if time!=time2 { ;timeとtime2が等しくなければ{}内の命令を行う
fpc=fp ;fpc(表示するFps数)にfp(現在のフレーム数)を代入
fp=0 } ;フレーム数をクリア
pos 180,300
mes " Fps "+fpc ;フレーム数の表示
time2=time ;time2に現在の秒数を代入(1秒ごとに比べる為)
redraw 1 ;画面を更新
await 16
goto *main ;*mainに戻る
;以下サブルーチン
;■敵弾処理
*tama
repeat emt ;emt回繰り返す
if etf(cnt)>0 : continue ;画面に弾がある時はrepeatに戻る
eh=eh+1 ;弾の数の為の計算
etf(cnt)=1 ;弾の存在を有にする
etx(cnt)=double(ex) : ety(cnt)=double(ey) ;弾の座標を実数で設定
ds=2 ;敵弾の速さ
r=double(r+0.6) ;弾の角度を実数で計算
etxv(cnt)=cos(r)*ds : etyv(cnt)=sin(r)*ds ;弾の移動量を角度より計算
if eh>10 : eh=0 :break ;10発弾を撃った時点でrepeatを抜ける
loop
return
;■各データロード
*load_data
bload fname,replay,32000;replayデータにfnameをロード
bload fname1,replay1,32000;replay1データにfname1をロード
bload fname2,rando,32000;replay2データにfname2をロード
wait 10
return
;■方向キー入力
*key_input
;自機移動処理
stick ky,15 ;キーボードのチェック
if ky&1 :px-=3
if ky&4 :px+=3
if ky&2 :py-=3
if ky&8 :py+=3
;変数kyをリプレイデータに記録
poke replay,timer,ky
return
;■スペースキー入力
*key_input1
;自機移動処理
stick space,16 ;キーボードのチェック
if space&16 {hassya=1}else{hassya=0}
;変数spaceをリプレイデータに記録
poke replay1,timer,space
return
;■方向キー出力
*read_replay
;リプレイデータを変数kyに代入
ky=peek(replay,timer)
if ky&1 :px-=3
if ky&4 :px+=3
if ky&2 :py-=3
if ky&8 :py+=3
return
;■スペースキー出力
*read_replay1
;リプレイデータを変数spaceに代入
space=peek(replay1,timer)
if space&16 :{hassya=1}else{hassya=0}
return
;■ランダム入力
*rando_load
randn = gettime(4)*1000 + gettime(5)*100 + gettime(6)
randomize randn
wpoke rando,0,randn
return
;■ランダム出力
*rando_read
randn=wpeek(rando,0)
randomize randn
return
;■ゲームオーバー
*gameover
if playmode=0 {
bsave fname,replay,32000;fnameにreplayデータをセーブ
bsave fname1,replay1,32000;fnameにreplayデータをセーブ
bsave fname2,rando,32000
wait 10
}
goto *get_mode
*************************************
HSP3.0使用
実行するとこんな感じ↓
ちなみにスペースで弾発射です。
で、そのランダム部分をどうしたかと言うと、以前コメント欄でアドバイスを頂いたLeighさんのスクリプトを参考に↓こんな感じで発生させました。
randn = gettime(4)*1000 + gettime(5)*100 + gettime(6)
randomize randn
wpoke rando,0,randn
この方法だとスクリプトを実行した時間でランダムの数が決まるので、ほぼ同じパターンにはならない(はず)
ただ、このスクリプトの場合リプレイファイルをreplay.dat(キー入力用)
replay1.dat(スペースキー入力用)rando.dat(ランダム入力用)
の3個のファイルに分かれてしまっています。
たぶん、一つのファイルに纏める事が出来るような気がするのですが、イマイチpeek,poke,sdim命令などが理解出来ておらず
上手く出来ていません(汗)
そして、このスクリプトを作っている時に気づいたのですが、リプレイを実装する為には最初に変数などを初期化しないと同じ動きにならないので
リプレイが成立しません。
で、今作ってるSTGなんですが、リプレイの事など全く気にせず作製していたので
色んな変数を初期化する為のスクリプトを作らないといけないことに気づき途方に暮れています^^;
PR
この記事にコメントする
カレンダー
10 | 2024/11 | 12 |
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
リンク
カテゴリー
最新記事
(04/04)
(12/26)
(12/25)
(11/17)
(10/11)
プロフィール
HN:
kt.
性別:
男性
自己紹介:
プログラミング経験は昔ファミリーベーシックでちょっとさわったくらい。
好きなSTGは、怒首領蜂大往生、エスプガルーダ(2)等の弾幕STGやら雷電シリーズなんかの非弾幕、バトルガレッガ、グラディウスシリーズ、R-TYPE等、STGなら何でも好きです。
音楽がカッコイイSTGが特に好きで、並木学氏は最高!
好きなSTGは、怒首領蜂大往生、エスプガルーダ(2)等の弾幕STGやら雷電シリーズなんかの非弾幕、バトルガレッガ、グラディウスシリーズ、R-TYPE等、STGなら何でも好きです。
音楽がカッコイイSTGが特に好きで、並木学氏は最高!
ブログ内検索
最古記事