HSP初心者が<ゆうなま>っぽいゲームを作る
■ このスレッドは過去ログ倉庫に格納されています
HSP初心者の俺がゆうなまっぽいゲームを作ろうと思う
魔物をどうやって生み出すかは考え中 ゆうなまの掘っているブロックと掘っていないブロックどう判定するのか
謎だ ゆうなまの掘っているブロックと掘っていないブロックどう判定するのか
謎だ ゆうなまの掘っているブロックと掘っていないブロックどう判定するのか
謎だ ゆうなまの掘っているブロックと掘っていないブロックどう判定するのか
謎だ ゆうなまって動画でしか知らないけど、
0:ブロックなし(掘ったブロック)
1〜6:掘っているブロック
7:掘ってないブロック
こんな感じでないの? ゆうなまってゲームしらないが、テトリスの動いてるブロックと固まってるブロックの扱いの違いとかで応用きくかも とか適当レス >>7
>>8
ありがとう
ここさえ出来れば後は大丈夫 勇者が一度いった通路と
まだいっていない通路は
どうやって判別しているのか 要は魔王のところまでどう勇者を自動でいかせるかだよね?
魔王は最初に位置決めるんだから 「A*アルゴリズム」(エースター)ってのを調べるといいと思う。
A*の読み方だよ おれが間違って読んでたから書いといた
Aはアヌスの頭文字、*はケツの穴を表すのによく使われる記号
正しい読み方知らなければ変な勘違いしたってまぁおかしくはないよな
>>13は全然悪くないよ アーッ アナル アルゴリズムと読み間違って…なわけあるかー >>1はやってるの?ゆうなまってやったことないけどようつべで見てたらつくりたくなってきたw >>19
あるが、勇者のAIがわからずつまっている おれA*を勧めたものだけど、A*は最短距離だから違ったな
あのあと興味もって、ファイナルステージの動画見たら寄り道してモンスター倒してたから。
経路探索った基本が最短距離だから、寄り道するAIずばりはないのかな?
あと厳しいこというと、ここのローカルルール的にサイトがあったほうがいいらしいよ
そのもののAIはないんだけど、ゲーム開発者のためのAIっていうオライリーの本に経路探索の話が詳しくのってたよ
動画見ただけだから正確に再現は難しそうだけど、スタート地点→経由地→魔王ってやって、魔王から直でスタート地点にいくならA*でなんとかなりそう
ただ製品は経由地まで、も最短距離じゃないんだよな。 HSPか、俺も初心者だぜー
元々の養分の設定とか考えてマップのサイズにもよるけど
マップエディタがないときつい気がするでござる HSPより前にPSPっていう言語があってな
そこで人気があったゲーム HSP使いじゃないが、ゲームシステムには興味がある。
ちょっとルールを説明してもらえないかな? 「ゆうなま」
1:ゆうなおまえが一番星
2:ゆうやけなま放出中 既に>>1が居ないようなので、スレタイの『HSP初心者が』を改め、
『C++で〈勇なま〉っぽいゲームを作る』ということで再利用しますね。 「勇なま」
1:栄勇、なまでお願い
2:勇気を出して飲んだら、なまっぽい では最初にゲーム内容の確認をしますね。適宜補足、修正をしてください。
プレイヤーは、地下世界を表すタイル状のマップに、
蟻の巣状の迷路を作成していきます。
時々、地上から勇者が冒険にくるので、プレイヤー側は作成した迷路の
どこかに魔王(防衛対象シンボル)を配置します。
以後は基本的にオートマチックにゲームが進行し、
勇者に魔王を連れ去られたらゲームオーバーになります。 ゲームのキモが抜けてるから全くゆうなまじゃない
地中に「養分」がバラ撒かれている
穴を掘る時に養分を壊すと魔物発生
養分のマスは量が増減し、レベルの高い養分マスからはレベルの高い魔物が出る
養分を吸収・運搬する下級の魔物をコントロールして
養分を一箇所に集めるのがゲームのミソ
ただし直接操作できないのでマップの掘り方を工夫する必要性が生じる >>35
慌てなさるな。
さて、地下マップの未掘削1セルごとに、「養分」「魔分」という量が設定されています。
養分、魔分が含まれるセルをプレイヤーが掘削して通路に変えたとき、
その含有量に応じてその場所に「魔物」が発生します。
養分については、ゲーム開始時に予め分布しているほか、近傍通路での魔物が活動することで
吸収、放出されるほか、勇者との戦闘の結果次第で再散布されることがあります。
つまり、倒された魔物や勇者の亡骸は、新たな魔物を産み出す養分としてリサイクルされます。
魔分については、勇者が魔法やアイテムを使うことにより、近辺の未掘削セルに散布される
ことがあります。これも規定のアルゴリズムで活動している魔物の活動により
吸収、運搬、集積の対象となります。 続けます。
魔物は、種類別のアルゴリズムに基づいた養分等の吸収、運搬および成長プログラムを
実行する以外に、他の個体と接触した際には捕食による調整が行われます。
具体的には、食べられる側が保有していた養分などが食べた側に吸収され、
食べられた個体は死亡扱いで消滅する一方で、食べた側は規定のプログラムに基づき、
繁殖して新たな個体を産み出す場合があります。
なお、この食物連鎖による生態系のなかで、魔王を求めて降りてきた勇者の肉体および
活動の影響も、結果的には養分と魔分の形で適正に処理されます。
それは良いとして、いつになったら作り始めるの?
大志を抱くのはいいけど、書いている処理をいきなり実装するのかな? 勇者が侵入を始める時点にかぎり、魔王を移動させることができます。
勇者は侵入時点では地下迷路の形も、魔王の位置も知り得ませんので、
個性的な探索アルゴリズムを駆使してさ迷いながら魔王を捜します。
その道中で勇者が魔物と接触すると、戦闘によって体力やアイテム等を消耗しますが、
その結果、体力が尽きてしまえば養分と亡骸を残して消滅します。
勇者が回復呪文等を使えば、その近辺に魔分が散布されます。
このため、プレイヤーが産み出した魔物たちは、自らの生存と繁殖を目的に
勇者を攻撃しようと働きますが、魔王は養分も魔分も持っていないため、
誰一人として魔王を守ろうとするものはありません。 プレイヤーが穴を掘るためには『掘パワー』というパラメータを消費します。
掘パワーはゲーム開始時に規定量配備されるほか、『ステージ』と呼ばれる
ミッションクリアごとに、その成績に応じて補充されます。
1つのステージは、規定の戦闘、探索能力が設定された勇者の集団の到来という
形で設定されており、彼らを全滅させることができればステージクリアになります。
ステージが進むごとに勇者は強くなっていくため、補充された掘パワーを活用し、
より強力な魔物を数多く配置できるよう迷路を拡張する必要があります。
(養分魔分の濃縮、勇者に魔分を発散させる、最後は確実に仕留めるなど、
迷路構造の設計にテクニックが要求されます)
8ステージクリアで、ゲーム終了です。 -------------------------------------------
日記スレ終了
------------------------------------------- 産み出した魔物および勇者には、各々について保有する養分と魔分に加え、
『体力』が設定されています。この数値は、以下の通り変動します。
・迷路を移動すると、わずかずつですが減少します。腹が減るような意味合いです。
・草食系魔物が土から養分を吸収すると、保有養分増加とともに、体力は回復します。
・肉食系魔物が他の魔物を捕食すると、被捕食者が保有していた魔分養分を引き継ぐとともに、
体力も回復します。
・勇者の場合、回復呪文等を使うと、保有魔分発散か所持品消費と引き換えに体力を回復できます。
・戦闘を行った場合、両者の体力は大幅に減少します。
それぞれの増減幅や体力最大値は、ゲームの難易度に直接影響するため、
テストプレイをしながら調整を行います。 さて、ゲームの概要と基本的な流れについては、これぐらいにして、
次の段階として、ゲームシステムの基礎部分の検討に移りたいと思います。
手始めに、ゲームの主要な舞台となる地下迷路のマップデータの持ち方を
考えてみます。
既に述べた通り、地下迷路には最初から散らしてある養分や、ゲームが進むと
散らされる魔分を含む土で充填されており、プレイヤーがつるはしで掘れるように
なっています。この仕様から単純に、
int youbun[Width][Height];
int mabun[Width][Height];
bool digged[Width][Height];
のような感じの2次元配列が必要であると考えられます。 Width,Heightはマップのサイズを決める定数ですが、具体的にどれぐらいにするかというと、
ゲーム中に狭苦しく感じない程度に広ければ十分で、概ね100×100ぐらいで十分だと思います。
#define Width 100
#define Height 100
開発を進めていくときに不都合を感じたら、そのとき訂正すればよいでしょう。
なお、今後のメソッド実装のときには2次元配列を頻繁に参照することになるので、
次のような引数確認用のメソッドをひとつ作っておくと便利です。
bool isInside(int x,int y){
return x>=0 && x<Width && y>=0 && y<Height;
} >>48で養分、魔分、掘削状態について、何も考えずにint,int,boolとしましたが、
土から生まれる最上級魔物のドラゴンでさえ魔分17で生み出されますので、
養分と魔分に関しては、unsigned char型で十分な気がします。
unsigned char youbun[Width][Height]; // マスごとの養分
unsigned char mabun[Width][Height]; // マスごとの魔分
けち臭い気がするかもしれませんが、セーブデータのサイズにもかかわってきますし、
過剰に集積してしまうとゲーム的に詰んでしまう可能性もあるので、マス目ごとの集積上限を
管理することを念頭におきます。
なお、これらのゲーム開始時の初期値は、mabunについてはすべて0とし、youbunについては、
乱数列を使って適当な割合でばら撒くことにします。 その一方で、diggedフラグについては、例えばyoubunかmabunの想定外の値(例えば255)を
マジックナンバーとすることで、独立の配列を持つことを省略する方法もあります。
例えば、掘削後のマスのyoubunを255とするなら、
bool isDigged(int x,int y){
return youbun[x][y]==255;
}
のような書き方が可能です。
この応用で、ちょっと工夫を凝らすため、bool型ではなくshort型の2次元配列を
持つ方法を提案します。変数名はdepthとし、未掘削マジックナンバーを -1とすると、
short depth[Width][Height];
bool isDigged(int x,int y){
return depth[x][y]!=-1;
}
のような実装になります。depth配列の全要素をゲーム開始時に-1で初期化しますが、
勇者の侵入口のみ0にしておきます。
なぜこのようにするかは、この後で説明します。 マップデータに対する主要な変更操作は、プレイヤーの指示による「掘削」です。
ただしルール上、どの地下空間も勇者が侵入する入り口とつながっていないといけませんので、
どこでも好き勝手に掘ることは出来ないように処理します。
そのためには「プレイヤーが掘削しようとした地点が、掘削可能か否か」を判定することからはじめます。
必要十分条件は、「その地点が見掘削」かつ「その4つのとなりのいずれかが掘削済み」ですので、
bool canDig(int x,int y){
if(!isInside(x,y) || isDigged(x,y)) return false;
if(isInside(x-1,y) && isDigged(x-1,y)) return true;
if(isInside(x+1,y) && isDigged(x+1,y)) return true;
if(isInside(x,y-1) && isDigged(x,y-1)) return true;
if(isInside(x,y+1) && isDigged(x,y+1)) return true;
return false;
}
というようなアルゴリズムで判定することができます。 さて、ゲームの概要と基本的な流れについては、これぐらいにして、
次の段階として、ゲームシステムの基礎部分の検討に移りたいと思います。
手始めに、ゲームの主要な舞台となる地下迷路のマップデータの持ち方を
考えてみます。
既に述べた通り、地下迷路には最初から散らしてある養分や、ゲームが進むと
散らされる魔分を含む土で充填されており、プレイヤーがつるはしで掘れるように
なっています。この仕様から単純に、
int youbun[Width][Height];
int mabun[Width][Height];
bool digged[Width][Height];
のような感じの2次元配列が必要であると考えられます。 だって生を授かるには、生で「大切なもの」を a g e なきゃいけない (>>53のつづき)
掘削可能であることが確認できたら、そのマスを掘削済み状態に更新します。
それはつまり、depth[x][y]を-1以外の値にするという意味ですが、具体的にいくつにするかというと、
「周囲の掘削済みマスのdepth値に1を足したもの」を入れていきます。
プログラムで書く場合は、例えば以下のような書き方ができます。
void dig(int x,int y){
int min=Width*Height;
int d;
d=depth[x-1][y]; if(d!=-1 && d<min) min=d;
d=depth[x+1][y]; if(d!=-1 && d<min) min=d;
d=depth[x][y-1]; if(d!=-1 && d<min) min=d;
d=depth[x][y+1]; if(d!=-1 && d<min) min=d;
depth[x][y]=min+1;
} その一方で、diggedフラグについては、例えばyoubunかmabunの想定外の値(例えば255)を
マジックナンバーとすることで、独立の配列を持つことを省略する方法もあります。
例えば、掘削後のマスのyoubunを255とするなら、
bool isDigged(int x,int y){
return youbun[x][y]==255;
}
のような書き方が可能です。
この応用で、ちょっと工夫を凝らすため、bool型ではなくshort型の2次元配列を
持つ方法を提案します。変数名はdepthとし、未掘削マジックナンバーを -1とすると、
short depth[Width][Height];
bool isDigged(int x,int y){
return depth[x][y]!=-1;
}
のような実装になります。depth配列の全要素をゲーム開始時に-1で初期化しますが、
勇者の侵入口のみ0にしておきます。
なぜこのようにするかは、この後で説明します。 (>>62のつづき)
このようなルールでdepth配列を構築すると、掘削済みの任意の地点から地上に至るまでの
比較的上りやすい経路を簡単に一意に探すことができるようになります。
参照しているマス周囲の掘削済みマスのうち、
depth値の一番小さいほうへ繰り返し移動すればよいのです。
ただし、この方法で得られるルートは必ずしも最短にはなりません。
典型的な例外としては、枝分かれさせた2本の迷路が奥のほうで貫通し、
再び合流したときに発生します。
この問題を解決するには、掘削メソッドにさらに一工夫加えます。 繰り返しになりますが、depth値の配列は、
「周囲4方向の掘削済みセルのdepth値の最小値に1を加えたもの」
というルールで統一しようとしています。
貫通するように穴を掘ったとき、最後に掘ったところのdepth値は
上の処理によって適切な値に設定されることが期待できます。
問題が発生するのは、そのセルに隣接するセルのうち、
今掘って設定したdepth値よりも大きい値を持つセルです。
少なくともこの隣接セルについては、今掘ったところのdepth値+1になるはずです。
そこで、そのように更新してやると、今度はその隣接セルのさらに周囲の
セルのうち、より大きいdepth値のセルが……、という具合に問題が波及します。 こういう問題を処理するときには、再帰的アルゴリズムというのを使うとうまくいきます。
注目しているセルのdepth値が、隣接していて更新されたセルのdepth値よりも大きいとき、
そのセルのdepth値は、隣接depth値に1を加えたものに更新しますが、
それに引き続き、注目したセルに隣接するセルに対して、更新後のdepth値を通知します。
void Cmapdata::Rdig(int x,int y,short min){
if(isInside(x,y) && isDigged(x,y) && depth[x][y]>min){
depth[x][y]= ++min;
Rdig(x-1,y,min);
Rdig(x+1,y,min);
Rdig(x,y-1,min);
Rdig(x,y+1,min);
}
}
未掘削であったり、通知されたdepth値よりもともと小さいセルでは、値の更新もされませんし、
再帰呼び出しも行われませんので、影響範囲だけ更新したら、このアルゴリズムは完結します。
なおこの関数は、上記のdig関数の最後に(掘削セルのdepth値をセットしてから)呼び出して使います。 『俺は屍をこえてゆく』が面白かった。
ファーストプレイのとき、おみくじで、ショップで売ってる
一番いい武器(10万円ぐらいするやつ)を引き当てて、
無双っぽい状況だったんだけど、別のデータを上書きしちゃった>< ■ このスレッドは過去ログ倉庫に格納されています