エビフライの唐揚げ

今までのことや、技術的なこと、その他を書いていこうと思います。

Twitter上で出来るゲームを作成した件について

Twitter上で遊べるゲームを作成した(過去)

【概要】

リプライを送ると戦闘結果が返ってくる非同期オンラインRPG

f:id:littlemore:20200526005757g:plain

 

【言語】

C#

【サーバー】

自宅PC

【DB】

SQLite

 

大まかな内部処理

f:id:littlemore:20200526010307p:plain

 

SVNの履歴を見る限り、約1年前につくったもの。

なので、あまりコードは覚えてない・・・。

Twitterに挙げられるGifのサイズが3MB以下だったりしたので、2ターンで決着を付けさせたり、画像サイズを圧縮したり、見えないところで努力をした記憶がある。

 

このプログラムで使った主なライブラリは、

CoreTweet:TwitterAPIを簡単に使用できる

Magick.NET:BitmapリストからGifアニメーションを作れる

 

一応、これが戦闘計算用クラスにあったメソッド

ソースコードはあまり綺麗とは言えない・・・。

 

public static List<BattleData> BattleDataCreator(BattleData battleData, List<CommandSkill> actorSkills)
{
	List<BattleData> battleDataList = new List<BattleData>();
	battleDataList.Add(battleData);
	var characterData = battleData.CharacterData;
	characterData.ForEach(p => InitializeCharacterData(p));
	bool extinction = false;

	int trun = 0;
	while (trun < 2)
	{
		trun++;
		Logger.write("ターン" + trun.ToString(), LogType.Debug);
		//素早さが早い順に並び替え
		characterData = characterData.OrderByDescending(p => p.Parameters.SPD).ThenBy(a => Guid.NewGuid()).ToList();
		for (int k = 0; k < characterData.Count; k++)
		{
			characterData[k].Status.Order = k;
		}

		//全員行動済みになるまで
		for (int i = 0; i < characterData.Count; i++)
		{
			//行動準備初期化
			var battleDataTemp = DeepClone(battleDataList.Last()) as BattleData;
			battleDataTemp.CharacterData.ForEach(p => p.Status.Animetion = Animetion.Defalt);
			battleDataTemp.CharacterData.ForEach(p => p.Status.Damage = 0);
			battleDataTemp.CharacterData.ForEach(p => p.Status.PictureMessage = string.Empty);

			//HPが0の場合戦闘不能フラグ
			battleDataTemp.CharacterData.Where(p => p.Parameters.HP <= 0).ToList().ForEach(q => q.Status.Death = true);

			characterData = battleDataTemp.CharacterData;
			//スキルコマンドをキャラにセット
			SetActorSkill(ref characterData, actorSkills, trun);

			//行動準備順に並び替え
			characterData = characterData.OrderBy(p => p.Status.Order).ToList();
			Logger.write("処理," + i.ToString() + "," + characterData[i].Parameters.Name, LogType.Debug);
			//戦闘不能の場合スキップ
			if (characterData[i].Status.Death) continue;
			Logger.write("行動," + i.ToString() + "," + characterData[i].Parameters.Name, LogType.Debug);

			//行動準備					
			var skill = characterData[i].Status.Skills[new RandomEx().Next(0, characterData[i].Status.Skills.Count)];
			Logger.write("スキル," + i.ToString() + "," + characterData[i].Parameters.Name + skill.SkillName, LogType.Debug);
			if (characterData[i].Parameters.HP - skill.Cost < 1) skill = new Skill_Attack();
		
characterData[i].Status.Animetion = skill.UserAnime; battleDataTemp.CommandMessage = characterData[i].Parameters.Name + skill.SkillMessage + characterData[i].Status.ActorMessage; battleDataList.Add(battleDataTemp); //行動準備初期化 battleDataTemp = DeepClone(battleDataList.Last()) as BattleData; battleDataTemp.CharacterData.ForEach(p => p.Status.Animetion = Animetion.Defalt); var actionCharaData = battleDataTemp.CharacterData; //行動準備順に並び替え actionCharaData = actionCharaData.OrderBy(p => p.Status.Order).ToList(); //行動(全体攻撃対応したので、ターゲットが複数の場合がある) var targetDatas = TargetSeacher(actionCharaData, skill.Target, actionCharaData[i].Actor); if (targetDatas.Where(p => p != null).Count() == 0) { continue; } for (int k = 0; k < targetDatas.Count; k++) { targetDatas[k].Status.Animetion = skill.TargetAnime; } for (int j = 0; j < targetDatas.Count; j++) { var charaData = actionCharaData[i]; var targetData = targetDatas[j]; DamageCalculation(ref targetData, ref charaData, skill); actionCharaData[i] = charaData; targetDatas[j] = targetData; Logger.write("対象," + i.ToString() + "," + targetData.Parameters.Name, LogType.Debug); } string deathMes = BattleMessageCreator.DeathMessage(targetDatas); string damageMessage = BattleMessageCreator.BattleMessage(targetDatas); battleDataTemp.CommandMessage = targetDatas.FirstOrDefault().Parameters.Name + damageMessage + deathMes; battleDataList.Add(battleDataTemp); //全滅判定 if (battleDataList.Last().CharacterData.Where(p => p.Actor && p.Parameters.HP > 0).Count() == 0 || battleDataList.Last().CharacterData.Where(p => p.Actor == false && p.Parameters.HP > 0).Count() == 0) { extinction = true; break; } } if (extinction) break; } //行動準備初期化(リザルト画面用) var battleDataLast = DeepClone(battleDataList.Last()) as BattleData; //HPが0の場合戦闘不能フラグ battleDataLast.CharacterData.Where(p => p.Parameters.HP <= 0).ToList().ForEach(q => q.Status.Death = true); battleDataLast.CharacterData.ForEach(p => p.Status.Animetion = Animetion.Defalt); battleDataLast.CharacterData.ForEach(p => p.Status.Damage = 0); battleDataList.Add(battleDataLast); return battleDataList; }

 

プロジェクトの中を除く限り、自分で作ったものの中では大規模な部類で、かつ綺麗にまとめられている。

f:id:littlemore:20200526010152p:plain

 

何故、やめてしまったか。

オンラインゲームなのにプレイしてくれるプレイヤーが少なかったこと。

定期的に敵が出現した!みたいな画像を自動でツイートしていたが、それがTwitterの自動化なんとかに引っかかって、アカウントが凍結されてしまったことが要因。

 

ソースコードはあって、アカウントを作れば動かせる。

誰かマネタイズ出来るなら譲渡します。

もちろん、半分くらいもらいます。

 

1年前はクラウドを使うという考えがなかったようで

今で考えたら、Azure使って動かせばよかったなぁと

でも、Gif画像を作るときにすべての画像をメモリで持つからかなりメモリ消費していた気がする・・・。

 

 

説明書もあったので、載せておこう

f:id:littlemore:20200526010655p:plain

f:id:littlemore:20200526010720p:plain