忍者ブログ
なんとなくだらだらと。方向性はまだ決まってない。 当方のプログラムでは、山田巧さん作成のDXライブラリを利用させていただいてます。 本サイト http://homepage2.nifty.com/natupaji/DxLib/index.html DX Library Copyright (C) 2001-2008 Takumi Yamada.
[32] [31] [1] [30] [29] [28] [27] [26] [25] [24] [23]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

なんかまたキモチワルいプログラム出来た。
作ってみたら出来るもんだねえ。

今日は、霧の中のゴールじゃなく、そこに至る道筋が見えてた。
ホントに数年来行き詰ってた壁を乗り越えることができた感。
頭ん中で、レベルアップのファンファーレが鳴りまくってるぜ。



e021.png



ダウンロード  Entry021.zip (2009.08.02)




敵も味方も行動入力を受け付けて、行動順に並び変えないといけないんだから
なんらかのリスト構造が必要になるだろうと勝手に予測。
いちばんメジャーな、 std::list なんて持ち出してみる。
使い方なんてさっぱりわからないけど。参考書が手元に無いと手を出す気にもなれない。


前から気にしてた「STL標準講座」、5章リストコンテナの「クラスオブジェクトを
リストに格納する」のページをがっつり穴があくまで視姦。
というよりむしろ、ろくに読みもせずコードの真似っこ。

「クラスオブジェクトをリストに格納する」ためには、デフォルトコンストラクタと
比較演算子のオーバーロードが必要ということらしい。
まあ、整数とかならともかく、クラスの何を見て優劣つければいいかの判断材料を
提供してあげないと、コンピューターの中の人が悲しい顔するよな。


新しいクラス、OrderList を作る。
OrderList.h

// OrderList.h

#pragma once

class Unit;

class OrderList
{
public:
    int ActionPoint;    // 行動力
    Unit *Arry;         // 自分自身
    Unit *Other;        // 相手側

    OrderList(){
        ActionPoint = 0;
    }
    OrderList(int ActionPoint, Unit *Arry, Unit *Other){
        this->ActionPoint = ActionPoint;
        this->Arry = Arry;
        this->Other = Other;
    }
    int getActP(){return ActionPoint;}
    string getArryName(){return this->Arry->getName();}
    string getOtherName(){return this->Other->getName();}
};

bool operator<(OrderList &a, OrderList &b){
    // 降順ソートにするために、わざと比較を逆にしている。
    return a.getActP() > b.getActP();
}

void ShowList(list<OrderList> &lst)
{
    list<OrderList>::iterator p;
    for(p=lst.begin(); p!=lst.end(); p++){
        cout << p->getArryName() << "->" << p->getOtherName();
        cout << "  ActP:(" << p->getActP() << ")\n";
    }
}


使い方よくわからないから、とりあえず全public で宣言。
行動力と攻撃側ユニットと防御側ユニットを登録できるようにする。

今までの俺なら、ユニットのIDを渡して「10番が2番に攻撃」とかそんな感じで
いちど行動リストを作ってから、再度翻訳しなおしたりしてた。
その方法が今までで一番正解に近い方法だった。

今回、このコンソールRPGを作りはじめたときから「クラスの参照渡しで
ユニットの実体そのもの(インスタンスのポインタ)を登録、活用することが
できる!」って根拠の無い自信があった。


見よう見真似でとりあえず形を整えてみる。
クラス宣言の中にメソッド全部書いてるけどいいのかな。いいんだよな。
値を入れたり見たりしてるだけだから、これでいいと思う。


あとは、比較演算子「<」のオーバーロードと、確認用にリスト表示を用意。


さっそく使ってみる。
main.cpp


#include <iostream>
#include <string>
#include <sstream>
#include <stdlib.h>     // 乱数取得に必要
#include <time.h>       // rand()の初期化に必要
#include <windows.h>    // 表示のウェイトのためだけに。
#include <list>

using namespace std;

#include "Unit.h"
#include "Player.h"
#include "OrderList.h"
#include "Histgram.h"





Result Battle(Player &player, Unit &enemy)
{
    int cnt = 1, cmd, cmdcnt;
    Result res;
    list<OrderList> order;
    list<OrderList>::iterator p;


    while(1){
        cmdcnt = 0;
        order.clear();
        Sleep(200);
        cout << "\ncnt:" << cnt << "\n";
        player.ShowStatus();
        enemy.ShowStatus();
        while(1){
            cout << "どうする?(1:攻撃 2:逃げる 9:終了):";
            if(cmdcnt){
                cin.clear();
                cin.ignore();
            }
            cmd = 0;
            cin >> cmd;
            cout << "\n";
            if(cmd == 1) break;
            if(cmd == 2) {
                cout << player.getName() << "は逃げ出した!\n";
                break;
            }
            if(cmd == 9)break;
            if((++cmdcnt)>10){cmd = 9;break;}   //無限ループ防止
        }
        if(cmd == 9){res = ABORT;   break;}
        if(cmd == 2){res = RUNAWAY; break;}

        Unit * pPlayer = &player;
        Unit * pEnemy = &enemy;

        // 自分の行動を登録
        order.push_back(OrderList(player.GetActionPoint(),pPlayer,pEnemy));

        // 敵の行動を登録
        order.push_back(OrderList(enemy.GetActionPoint(),pEnemy,pPlayer));

        // リスト登録できてるか、表示してみる。
        ShowList(order);
        cout << "sort.\n";
        order.sort();

        ShowList(order);
        cout << "\n";

        for(p=order.begin(); p!=order.end(); p++){
            if(pNormalAttack(p->Arry,p->Other)){
                // なんらかの形で決着ついた
                break;
            }
        }
        if(enemy.isDead()){res = WIN;  break;}
        if(player.isDead()){res = LOSE;  break;}

        if((++cnt)>100)break;       // 無条件ループなので、無限ループ防止
    }

    return res;
}




OrderList型を std::list<OrderList> で宣言する。
リストのイテレーターもいっしょに宣言。

紆余曲折いろいろありました。
だけど、リストに登録して一覧表示するまではホントに早かったんだよ。

いろいろやって、結局この形に落ち着いた。
ポインタとか参照とか、分かってるようであまり理解してない。
出たとこ勝負な部分があるのが自分でもわかってるから怖い。

Unit のポインタ型を宣言して、pPlayer・pEnemy にplayer・enemy の参照を登録。
自分と敵の行動を登録。簡単に書けるようになるもんだねえ。

で、確認用に一覧表示して、ソート。もういちど一覧表示。
ちゃんと並べ替えできてるか確認しておく。


並べ替えできたら、オーダー順に行動を解決していく。
これがなかなかうまくいかない。ソースをいじりたおす。心が折れる。
結局、うまくいかなかったソースも公開しないと、ただの愚痴だよなあ。
さんざん迷子になって泣きながら歩いてたら、不意に目的地の目の前に出たって感じ。
もういちどスタート地点に戻って同じ道を歩こうとしても、すでに答えを知ってるから
これがどうして、なかなか同じ道を通れないんだよ。

今なら、ちょっと目についた、気になった店まではもういちど行ける気がする。
このプログラムでは意識してポインタを使わずに参照渡しで組んでたんだけど、どうも
いつの間にか中の人がニセモノ(コピー)になってしまう。

で、コピーのplayerインスタンスから Attack() とかは呼べるんだけど、HPが減らない。
影武者のHPをいくら減らしても本体にはまったくダメージが入らない。



最初はポインタで管理しないといけないんだろう。

あれ? 参照渡しでもちゃんと渡せるなあ。

むう・・・。影武者強いぞ。

やっぱりポインタ使わないとだめか。


main.cpp


int pNormalAttack(Unit *lst, Unit *rst)
{
    int dmg;
    dmg = (lst->Attack() - rst->Defence());
    if(dmg <= 0) dmg = 1;

    cout << lst->getName() << "のこうげき!\n";
    cout << rst->getName() << "に " << dmg << "のダメージを与えた。\n";

    if(rst->Hit(dmg)){
        cout << lst->getName() << "は、" << rst->getName() << "にとどめをさした!\n";
        return(-1);
    }

    return(0);
}




ポインタにするか参照でいけるのか評価が行ったりきたりしてたんで、ポインタ型の
NormalAttack() 関数なんて作ってしまった。

今までの書き方なら クラス名.インスタンス だったのが、クラスポインタを使うときは
クラス名->インスタンス と、アロー演算子「->」を使って書くようになる。
あっちこっちで表記が混ざるのはイヤなんだおれは。どっちかに統一したいんだよ。

今までの NormalAttack() 関数も一応残してあるけど、次バージョンで撤去の予定。



ま、そんなこんなあったわけだ。
そして新たな問題が浮上。
NormalAttack()関数で、敵味方どちらが倒れたのか判断できない!むしろしてない!

その攻撃で相手が倒れたって情報は返ってくるので、その時点でループを抜ける。
ループ終了ごとに両者の現在HPを確認して、死んでたらフラグ立てて終了する。
HP確認のための、Unit.isDead() なんていう、なんともいえないメソッド作る。


Unit.h


class Unit
{
protected:
    string Name;    // 名前
    int MaxHP;      // 体力最大値
    int HP;         // 体力(現在値)
    int Atp;        // 攻撃力
    int Dfp;        // 防御力
    int Agi;        // 素早さ
    int Money;      // 所持金、モンスターは獲得金額
public:
    void Init(string Name, int HP,int Atp,int Dfp, int Agi, int Money);
    void ShowStatus();
    int Attack();
    int Defence();
    int GetActionPoint();
    string getName(){return(Name);}
    int Hit(int dmg);
    void HealMax(){HP = MaxHP;}         // 体力最大まで回復
    int GetMoney(){return(Money);}
    bool isDead(){return(HP==0);}

};





e021.png



ちゃんと動いてる気がする。

ダウンロード  Entry021.zip (2009.08.02)



PR


忍者ブログ [PR]
カレンダー
12 2025/01 02
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 31
フリーエリア
バーコード
ブログ内検索
P R
アクセス解析
カウンター