多重継承で、本当はオススメできない使い方で文字しか出ないゲームっぽいもの作ってしまったのでこっそり置いとく話 —勉強しなおしの記録というか学習ノート-番外編その2-
Unreal Engin4すごい!(語彙力)ってなったので、少しいじりつつ、むかーしかじった事あるC,C++の学びなおしの学習ノートの番外編その2。この連載記事の詳しい趣旨と注意事項は1日目をご覧ください。
この記事について
多重継承をやった時に、少し(だいぶ)脱線して、何か作ってしまったのです脇道に逸れてすみませんm(_ _;)m
せっかくだからどこかしらに置いておこうと思ったので番外編として書いて本編から切り離して置いておきます。
※本来、『多重継承』は、『純粋仮想関数』のような特殊な事例を除き、使用することは推奨されていません。このような使い方は真似してはいけないということを、まず初めにご留意ください。
“植物クラス”と”人間クラス”使いまわししつつ、”叫びクラス”を追加して、”植物クラス”と”叫びクラス”の『多重継承』をした”マンドレイククラス”を作ってみました。 なので、今回もソースコード長いですので、ご容赦ください(’~`;)
完全に趣味みたいな感じで作成しましたので、決して『多重継承』をこのような使い方はしないでください。
悪い例としてでも、娯楽として流して読む程度でも、どなたかにお楽しみいただければ幸いですm(_ _)m
ソースコード一覧と実行結果
plant.h
#pragma once
#include <string>
// 外部にあるクラス名
class Human;
using namespace std;
/******************************************
植物クラス(基底クラス)の宣言
*/
class Plant {
protected:/**植物クラスと派生クラス(ユウガオクラス)のみアクセス可********/
//Human* master; // マスターの情報
string name;//植物の名前
int water; // 現在の水の量
int exp;// 現在の経験値
int level; // レベル
int max_exp; // 現在レベルの上限経験値
public:/**どこからでもアクセス可***************************/
//Plant(Human* _master); // コンストラクタ
Plant();//コンストラクタ
virtual ~Plant(); // デストラクタ
void watered(int _water); // 水を与えられる
void absorb_water(int _water); // 水を吸収する
int get_water(); // 現在の水の量の値のゲッター
int get_level(); // 現在のレベルのゲッター
void raise_exp(int _water); // 経験値を上げる
void level_up(); // レベルアップさせる
virtual void info_status(); //現在のステータス(経験値やレベル)を出力
};
plant.cpp
#include "plant.h"
//#include "human.h"
#include <iostream>
using namespace std;
/*植物クラスのメンバの初期化・定義***************/
//コンストラクタ
Plant::Plant() :water(5), exp(0), level(1), max_exp(10) {
//master = _master;//マスター登録
int select = 0;
cout << "あなたの為の植物が生まれました。名前を付けてあげましょう。"
<< endl << "名前:";
while (1) {
// 名前を入力してもらう
cin >> name;
cout << "名前は " << name << " でよろしいですか?" << endl
<< "この名前で決定なら'1'、違う名前にしたいなら'0':";
cin >> select;
if (select) break;
else {
cout << "もう一度植物に付けたい名前を入力してください。" << endl
<< "名前:";
}
}
cout << "名前は " << name << " に決まりました。"
<< endl << "可愛がってあげてください" << endl;
}
//デストラクタ
Plant::~Plant() {
cout << "おきのどくですが、あなたの"
<< name << "は枯れてしまいました。" << endl
<< "どうか次の植物は大切に育ててあげてください。"
<< endl;
}
// 水を与えられる
void Plant::watered(int _water) {
water += _water;
raise_exp(_water);//経験値を上げる
}
// 水を吸収する
void Plant::absorb_water(int _water) {
water -= _water;
if (water >= 0) {
cout << "植物が水を吸収しました。" << endl;
raise_exp(1); //経験値を上げる
}
else cout << "植物が水を吸収しようとしましたが、吸収できる水がありません!" << endl;
}
//現在の水の量の値のゲッター
int Plant::get_water() {
return water;
}
//現在のレベルのゲッター
int Plant::get_level() {
return level;
}
// 経験値を上げる
void Plant::raise_exp(int _water) {
exp += _water;
info_status(); // 現在のステータス通知
if (water >= 0 && exp >= max_exp) {//expが上限を超えたら
//レベルアップ
level_up();
}
}
// レベルアップさせる
void Plant::level_up() {
cout << "レベルアップ!" << endl;
level++;
max_exp *= 2.5;
max_exp = (int)max_exp; // キャストして整数に丸める
info_status(); // 現在のステータス通知
cout << "レベルアップしたので、現在レベル分、水分を吸収します。" << endl;
absorb_water(level);//レベル数に応じて水分を吸収する
cout << "育てている植物がレベルアップしたので、育てているマスターにもボーナス経験値が入ります。" << endl;
//master->raise_exp(level); // レベルに応じてマスターのexpもアップ
}
//現在のステータス(経験値やレベル)を出力
void Plant::info_status() {
cout << name << "の現在のレベル:" << level
<< "、現在の経験値:" << exp
<< "、次のレベルアップまで後:" << max_exp - exp
<< "、現在の水の量:" << water << endl;
}
scream.h
#pragma once
#include <string>
using namespace std;
/***********************
* 叫びクラス
*/
class Scream
{
private:
string serif;//セリフ(叫ぶ内容)
int voice_size; // 声の大きさ
public:
Scream(); // コンストラクタ
virtual ~Scream(); // デストラクタ
void set_serif(string _serif);// セリフのセッター
string get_serif(); //セリフのゲッター
void set_voice_size(int _size); // 声の大きさのセッター
int get_voice_size();//声の大きさのゲッター
void show_scream(); // 叫ぶ
};
scream.cpp
#include "scream.h"
#include <iostream>
#include <string>
using namespace std;
/*叫びクラスのメンバの初期化・定義********************************/
//コンストラクタ
Scream::Scream() :serif(string("ぴぎゃーーーーーー")), voice_size(5) {
cout << "感情が芽生えました。『叫びたい』という感情です。" << endl;
}
// デストラクタ
Scream::~Scream() {
cout << "感情は崩壊して、跡形もなく消え去りました。" << endl;
}
//セリフのセッター
void Scream::set_serif(string _serif) {
serif = _serif;
}
// セリフのゲッター
string Scream::get_serif() {
return serif;
}
// 声の大きさのセッター
void Scream::set_voice_size(int _size) {
voice_size = _size;
}
// 声の大きさのゲッター
int Scream::get_voice_size() {
return voice_size;
}
// 叫ぶ
void Scream::show_scream() {
string a;
cout << serif;
for (int i = 0; i < voice_size;i++) {
a.append("!");
}
cout << serif << a << endl;
}
mandrake.h
#pragma once
#include "plant.h"
#include "scream.h"
#include <iostream>
using namespace std;
/***************************
* マンドレイククラス(多重継承クラス。継承元…植物クラス、叫びクラス)
*/
class Mandrake :public Plant, public Scream {
private:
int time_limit; // 叫びを聞いていられるタイムリミット
int max_time_limit; //タイムリミット上限値
public:
//コンストラクタ
Mandrake():time_limit(5),max_time_limit(5){
cout << "この植物はマンドレイクです。※お取り扱いには十分ご注意ください。" << endl;
}
//デストラクタ
virtual ~Mandrake() {
cout << "この植物は萎れてしまいました。" << endl;
}
//カウントダウン
void limit_countdown() {
time_limit--;
}
// タイムリミット値のゲッター
int get_time_limit() {
return time_limit;
}
// タイムリミット上限値のゲッター
int get_max_time_limit() {
return max_time_limit;
}
//タイムリミットをリセット
void reset_time_limit() {
time_limit = max_time_limit;
}
// 叫びを聞いていられるタイムリミット通告
void show_time_limit() {
cout << "危険!マンドレイクが泣き叫んでいます!" << endl;
show_scream(); // 植物クラスから引っ張ってきたメンバ関数
cout << "今から後" << time_limit << "ターンであなたは覚めない眠りに就くでしょう…。" << endl;
}
};
human.h
#pragma once
#include <string>
// 外部にあるクラス名
class Mandrake;
using namespace std;
/********************************
* 人間クラス(植物を育てるマスター)
*/
class Human
{
private:
Mandrake* mandrake; // 育てる植物
string name; // 名前
int water; //保有している水の量
int exp; // 経験値
int level; // レベル
int max_water; // 保有できる水の上限値
int max_exp;//現在レベルの上限経験値
public:
Human(); // コンストラクタ
virtual ~Human(); // デストラクタ
int get_water(); //保有している水の量のゲッター
int get_level(); // 現在のレベルのゲッター
bool check_plant_pointer();//plantが存命かどうかのチェック
void give_water(); // 植物に水やり
void fill_the_water(); // 保有できる最大値まで水を満たす
void raise_exp(int _water); // 経験値を上げる
void level_up(); // レベルアップさせる
void info_status();// ステータスを出力
void tick_master_plant_status();//マスターと植物のターン毎の始めにステータス通知
void tick_plant(); // 植物のターンごとの決まった行動
void throw_away();//植物を捨てる
};
human.cpp
#include "human.h"
#include "mandrake.h"
#include <iostream>
#include <string>
using namespace std;
/*人間クラスのメンバの初期化・定義***************/
//コンストラクタ
Human::Human() :water(10), exp(0), level(1), max_water(10), max_exp(20) {
int select = 0;
mandrake = 0;
cout << "あなたの名前は?"
<< endl << "名前:";
while (1) {
// 名前を入力してもらう
cin >> name;
cout << "あなたの名前は " << name << " ですか?" << endl
<< "この名前なら'1'、違う名前なら'0':";
cin >> select;
if (select) break;
else {
cout << "もう一度名前を入力してください。" << endl
<< "名前:";
}
}
cout << name << " さんには、植物を育てていただきます。" << endl;
// 植物生成
mandrake = new Mandrake();
}
// デストラクタ
Human::~Human() {
cout << "おお" << name << "よ、しんでしまうとはなさけない…。" << endl;
if (mandrake != NULL) {
delete mandrake;
mandrake = NULL;
}
}
//保有している水の量のゲッター
int Human::get_water() {
return water;
}
// 現在のレベルのゲッター
int Human::get_level() {
return level;
}
//plantが存命かどうかのチェック
bool Human::check_plant_pointer() {
bool p = (mandrake != NULL);
return p;
}
// 植物に水やり
void Human::give_water() {
int select;
while (1) {
cout << "どのくらいあげますか?" << endl
<< "水の量:";
cin >> select;
if (water > select) {//水が足りれば
water -= select;
mandrake->watered(select);//水を与える
cout << name << "も与えた水の分だけ経験値獲得します。" << endl;
raise_exp(select); // 水を与えた分だけexpアップ
break;
}
else {// 水が足りなければ
cout << "水が足りません。" << endl;
}
}
}
// 保有できる最大値まで水を満たす
void Human::fill_the_water() {
int _water;
cout << "水を溜めました。" << endl;
_water = max_water - water;
water = max_water;
// 増やした水の量だけexpをアップ
raise_exp(_water);
}
// 経験値を上げる
void Human::raise_exp(int _water) {
exp += _water;
info_status();// 現在のステータスを通知
if (exp >= max_exp) {//expが上限を超えたら
//レベルアップ
level_up();
}
}
// レベルアップさせる
void Human::level_up() {
cout << "レベルアップ!" << endl;
level++;
max_exp *= 2.5;
max_water *= 2.5;
max_exp = (int)max_exp; // キャストして整数に丸める
max_water = (int)max_water; // キャストして整数に丸める
info_status(); // 現在のステータス通知
}
// ステータスを出力
void Human::info_status() {
cout << "あなたの現在のレベル:" << level
<< "、現在の経験値:" << exp
<< "、次のレベルアップまで後:" << max_exp - exp
<< "、現在保有している水の量:" << water
<< "/" << max_water << endl;
}
//マスターのターン毎の決まった行動
void Human::tick_master_plant_status() {
info_status();// ステータスを表示
mandrake->info_status();
}
// 植物のターン毎の決まった行動
void Human::tick_plant() {
cout << "植物がターン毎の水の吸収をします。" << endl;
mandrake->absorb_water(mandrake->get_level());//水を吸収
bool p = (mandrake->get_water() < 0) ? true : false;
if (p) {// 水が0を下回っていた場合
if (mandrake->get_time_limit() <= 0) { // リミットオーバー
delete mandrake;
mandrake = NULL;
}else{// タイムリミットがまだセーフ
mandrake->show_time_limit();// 叫ぶ
mandrake->limit_countdown();// リミットを1つずつ減らす
}
}
else { // 水が0を下回っていない時
if (mandrake->get_max_time_limit()
!= mandrake->get_time_limit()) {
// マンドレイクの叫びのタイムリミットが上限値ではない場合
mandrake->reset_time_limit();// タイムリミットリセット
}
}
}
// 植物を捨てる
void Human::throw_away() {
int select;
cout << "植物を捨てます。本当に良いですか?" << endl
<< "捨てる(Yes:1、No:0):";
cin >> select;
if (select) {
cout << "植物を捨てました。" << endl;
// 植物を消去
delete mandrake;
mandrake = NULL;
cout << "あなた…『覚悟して来てる人』…ですよね" << endl;
}
}
main.cpp
#include "human.h"
//#include "plant.h"
#include <iostream>
#include <string>
using namespace std;
// グローバル変数
int master_water = 10;
int main() {
Human* pM = 0;
pM = new Human;
while (1) {
pM->tick_master_plant_status();
int select = 0;
cout << "どうしますか?" << endl
<< "1:水をあげる。" << endl
<< "2:放置する。" << endl
<< "3:水を溜める。" << endl
<< "4:終了する。" << endl
<< "選択肢を入力してください:";
cin >> select;
switch (select) {
case 1:
pM->give_water();
break;
case 2:
cout << "放置しました。" << endl;
break;
case 3:
pM->fill_the_water();
break;
default:
pM->throw_away();
if (pM->check_plant_pointer() == false) {
delete pM;//マスターのインスタンスを消去
return 0;
}
break;
}
pM->tick_plant();//水を吸収&水の量チェック
if (pM->check_plant_pointer() == false) {
delete pM;//マスターのインスタンスを消去
break;
}
cout << "-----------------------" << endl;
}
cout << "終了します。" << endl;
return 0;
}
実行結果
あなたの名前は?
名前:ますた
あなたの名前は ますた ですか?
この名前なら'1'、違う名前なら'0':1
ますた さんには、植物を育てていただきます。
あなたの為の植物が生まれました。名前を付けてあげましょう。
名前:まんどら
名前は まんどら でよろしいですか?
この名前で決定なら'1'、違う名前にしたいなら'0':1
名前は まんどら に決まりました。
可愛がってあげてください
感情が芽生えました。『叫びたい』という感情です。
この植物はマンドレイクです。※お取り扱いには十分ご注意ください。
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:10、現在の水の量:5
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しました。
まんどらの現在のレベル:1、現在の経験値:1、次のレベルアップまで後:9、現在の水の量:4
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:1、次のレベルアップまで後:9、現在の水の量:4
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しました。
まんどらの現在のレベル:1、現在の経験値:2、次のレベルアップまで後:8、現在の水の量:3
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:2、次のレベルアップまで後:8、現在の水の量:3
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しました。
まんどらの現在のレベル:1、現在の経験値:3、次のレベルアップまで後:7、現在の水の量:2
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:3、次のレベルアップまで後:7、現在の水の量:2
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しました。
まんどらの現在のレベル:1、現在の経験値:4、次のレベルアップまで後:6、現在の水の量:1
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:4、次のレベルアップまで後:6、現在の水の量:1
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しました。
まんどらの現在のレベル:1、現在の経験値:5、次のレベルアップまで後:5、現在の水の量:0
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:5、次のレベルアップまで後:5、現在の水の量:0
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しようとしましたが、吸収できる水がありません!
危険!マンドレイクが泣き叫んでいます!
ぴぎゃーーーーーーぴぎゃーーーーーー!!!!!
今から後5ターンであなたは覚めない眠りに就くでしょう…。
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:5、次のレベルアップまで後:5、現在の水の量:-1
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しようとしましたが、吸収できる水がありません!
危険!マンドレイクが泣き叫んでいます!
ぴぎゃーーーーーーぴぎゃーーーーーー!!!!!
今から後4ターンであなたは覚めない眠りに就くでしょう…。
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:5、次のレベルアップまで後:5、現在の水の量:-2
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しようとしましたが、吸収できる水がありません!
危険!マンドレイクが泣き叫んでいます!
ぴぎゃーーーーーーぴぎゃーーーーーー!!!!!
今から後3ターンであなたは覚めない眠りに就くでしょう…。
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:5、次のレベルアップまで後:5、現在の水の量:-3
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しようとしましたが、吸収できる水がありません!
危険!マンドレイクが泣き叫んでいます!
ぴぎゃーーーーーーぴぎゃーーーーーー!!!!!
今から後2ターンであなたは覚めない眠りに就くでしょう…。
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:5、次のレベルアップまで後:5、現在の水の量:-4
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しようとしましたが、吸収できる水がありません!
危険!マンドレイクが泣き叫んでいます!
ぴぎゃーーーーーーぴぎゃーーーーーー!!!!!
今から後1ターンであなたは覚めない眠りに就くでしょう…。
-----------------------
あなたの現在のレベル:1、現在の経験値:0、次のレベルアップまで後:20、現在保有している水の量:10/10
まんどらの現在のレベル:1、現在の経験値:5、次のレベルアップまで後:5、現在の水の量:-5
どうしますか?
1:水をあげる。
2:放置する。
3:水を溜める。
4:終了する。
選択肢を入力してください:2
放置しました。
植物がターン毎の水の吸収をします。
植物が水を吸収しようとしましたが、吸収できる水がありません!
この植物は萎れてしまいました。
感情は崩壊して、跡形もなく消え去りました。
おきのどくですが、あなたのまんどらは枯れてしまいました。
どうか次の植物は大切に育ててあげてください。
おおますたよ、しんでしまうとはなさけない…。
終了します。
大して読まなくて良いあとがき
結構入力系に問題あったりするのですが、全体的な流れというか、そういうところは割と転用出来そうな雰囲気なので、このシステムをいつかどこかで、ちゃんとゲームって形で出すかもです(が、脱線はすれど暫くはC++学習ですよ!)。
ご意見・ご感想・ご質問、また、ここ間違ってるよとか、もっといい方法あるよといったご指摘などございましたら、お手数ですがコメント欄やtwitterアカウントほろほろり(@_horo_horori)へお願いしますm(_ _)m