RPGを作りたい。というか作った(1)
一応Programです。Windows用を一応公開します。
RPGをつくります。仕様を決めます。と言っても2ヶ月のみの期間で作れっていうのですからしかも一人で(ボッチー)っていうのですから機能的にも妥協が必要です。しかも使ったことのないJavaベースのProcessingということですのであまり機能を増やしすぎると崩壊します。
だからといってこれは忘れてはいけません。Programerは楽をすること。これはいつだって同じですからね〜ということでこんな感じの仕様になりました。
- ローグライクな感じで一方方向で階段をひたすらおりるゲーム。
- Gillとかなしで
- モンスタースポーンもなしで階段降りた時にスポーンするだけで
- 相手は最短ルートのみでこっちに近づく
- 相手はワンパターンで魔法とかはなしで
- アイテムも使うだけで装備系も装備Onlyで
- 一応視界システムは搭載ということで(主人公のみで敵は関係なし
- アイテムは設置済みで敵のDropはなしで
- 一応ターン制は実装で
- 敵ステータスなどConstanceなDataはcsvに退避させる
- セーブデータとmapはJSONで
という感じですかね。結構ざっくり絞ったつもりですが、これでもコードは3000行位ですかね。初めてなので妥当なコード量だと思います。
では実装した項目についてはなします。
マップはTiledをつかいました。簡単でいいですよ〜お勧めです。json形式で出力できるのでかなり手軽だと思います。
レイヤーは全3つでevent,objects,groundです。これらのレイヤは下から順に描画していきます。まあ色々意見などはあると思いますが私はマップを書くセンスのかけらもありません。ということはさておきレイヤーの説明に入ります。
Eventでは障害物や敵の位置の候補、アイテムなどの情報を書き込みます。もちろん階段もね。ですがこれらを全て書くとなると二度手間なのでAutoMap機能を使うといいと思います。
次にobjectです。まあ名の通りオブジェクトを起きます。石や階段、(そのくらいかな?)ですかね。これは手動で置きました。これを使ってAutoMap機能を使って大体のEventを生成すると楽だと思います。
groundでは床を敷きます。まあ何も働きを持たなそうですがみためがよくなります。これらはjsonで出力すればProcessingでパースすることができます。
次に4方向を向いた主人公をおいて矢印キーなどで操作できるようにします。もう何も言うことが無いです。が、出来上がってから思ったのですが、キー関係はまとまったTaskとして登録しておくべきです。分散させてしまったせいでメンテ性が最悪です。コールバックするなどすべきでした。まあいまさら遅いけど。。。
そしたら私の場合敵の最短ルート検索の実装をします。A starが楽かと思います。最短ルートを早く検索してくれます。まあググればいいのですが解説ページはわかりやすいのですがね。
[java]
class Astar_node { Astar_node parent; stat State = stat.Open; int cost; int hsrc; int x, y; int dirIndex=2; int getScore() { return cost+hsrc; }
Astar_node(int x, int y, int cost, int hsrc, Astar_node n, int dirIndex) { this.x = x; this.y = y; this.cost = cost; this.hsrc = hsrc; this.parent = n; this.dirIndex = dirIndex; } }
class Astar { int gx; int gy; Astar_node parent; ArrayList<Astar_node> nodes = new ArrayList<Astar_node>();
direction nextStep() { Astar_node n = calc(); if (n.parent != null) { while (n.parent.parent != null) { n = n.parent; }//while(n.parent != null); if (n.dirIndex == 0) { return direction.DOWN; } else if (n.dirIndex == 1) { return direction.LEFT; } else if (n.dirIndex==2) { return direction.RIGHT; } else { return direction.UP; } } return direction.NOP; }
Astar(int x, int y, int gx, int gy) { this.gx = gx; this.gy = gy; parent = new Astar_node(x, y, 0, distance(x, y), null, 0); nodes.add(parent); }
Astar_node calc() { int idx; int trytimes = 30; do { idx = getMinScoreIndex(); openNodes(nodes.get(idx)); trytimes --; } while (any_node(gx, gy) == false && trytimes >= 0); if (!(trytimes >= 0)) { } return nodes.get(idx); }
boolean any_node(int x, int y) { for (Astar_node n : nodes) { if (x == n.x&&y == n.y) return true; } return false; }
int distance(int x, int y) { return abs(x-gx)+abs(y-gy); }
int getMinScoreIndex() { int min = 99999; int index = 0; Astar_node n;
for (int i = 0; i < nodes.size(); i++) { n = nodes.get(i); if (n.State == stat.Close || n.State == stat.Close) continue; if (min > n.getScore()) {
min = n.getScore();
index = i;
}
if (min == nodes.get(i).getScore()) {
if (nodes.get(i).cost > nodes.get(index).cost) {
min = nodes.get(i).getScore();
index = i;
}
}
}
return index;
}
void openNodes(Astar_node a) { int nx, ny; boolean canMove = true;
int x, y, cost;
x = a.x;
y = a.y;
cost = a.cost;
for (int i = 0; i < 4; i++) {
canMove = true;
nx = x + tablex[i];
ny = y + tabley[i];
if ((nx != g.p.pos.x||ny != g.p.pos.y)&&isCollision(nx, ny)) {
continue;
}
for (Astar_node n : nodes) {
if (n.x == nx&&n.y==ny) {
canMove = false;
}
}
if (!canMove)
continue;
int c;
int h;
c = cost+1;
h = distance(nx, ny);
nodes.add(new Astar_node(nx, ny, c, h, a, i));
}
a.State = stat.Close;
} void draw() {
for (Astar_node n : nodes) {
if (n.State==stat.Open) {
fill(0, 200/(n.hsrc/3.0f+1), 200/(n.hsrc/3.0f+1), 50);
}
if (n.State==stat.Close) {
fill(200/(n.hsrc/3.0f+1), 200/(n.hsrc/3.0f+1), 0, 50);
}
rect(n.x*16*scale, n.y*16*scale, 16*scale, 16*scale);
}
} } [/java]
今回ははここまでで。
あとenum使わなかったのはなぜだと思った人は「Processing2.x enum」ってググればProcessingの闇を知ることになります。そうすれば納得してくれるかな?