Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9308

複合属性を考慮した三すくみ の関係を enumのキレイなコードに書き直した話 in 英語学習ソシャゲの英語物語

$
0
0

どうも毎日2万人弱がプレイする
英語学習ソーシャルゲーム、英語物語の開発者、ゴン氏です。

英語物語では火属性・水属性・風属性、火水属性、水風属性、風火属性、火水風属性
の7つの属性があるのですが、
AttributeRef.png

そのダメージ計算や、属性の演算(火水パネルに反応するのは、火属性と水属性)を定義するのに、
もともと下記のような汚いコードを書いてたんですが、
一念発起してきれいなコードに書き直したのでその備忘録です。

もし、もっとCoolな方法あったら、ぜひ教えてください😊

属性を表すenum

OneEventEnumAttribute.cs
publicenumOneEventEnumAttribute{Dami=0,Fire=1,Water=2,Wind=3,FireWater=4,WaterWind=5,WindFire=6,FireWaterWind=7,}

以前の汚いコード

OneEventAttributeDamageLogic.cs
privatefloatOneEventAttributeDamageLogic(OneEventEnumAttributefrom,OneEventEnumAttributeto){if(from==to){return1;}switch(from){caseOneEventEnumAttribute.Fire:switch(to){caseOneEventEnumAttribute.Fire:return1f;caseOneEventEnumAttribute.Water:return0.5f;caseOneEventEnumAttribute.Wind:return1.5f;caseOneEventEnumAttribute.FireWater:return0.75f;caseOneEventEnumAttribute.WaterWind:return1f;caseOneEventEnumAttribute.WindFire:return1.25f;caseOneEventEnumAttribute.FireWaterWind:return1f;default:break;}break;caseOneEventEnumAttribute.Water:switch(to){caseOneEventEnumAttribute.Fire:return1.5f;caseOneEventEnumAttribute.Water:return1f;caseOneEventEnumAttribute.Wind:return0.5f;caseOneEventEnumAttribute.FireWater:return1.25f;caseOneEventEnumAttribute.WaterWind:return0.75f;caseOneEventEnumAttribute.WindFire:return1f;caseOneEventEnumAttribute.FireWaterWind:return1f;default:break;}break;caseOneEventEnumAttribute.Wind:switch(to){caseOneEventEnumAttribute.Fire:return0.5f;caseOneEventEnumAttribute.Water:return1.5f;caseOneEventEnumAttribute.Wind:return1f;caseOneEventEnumAttribute.FireWater:return1f;caseOneEventEnumAttribute.WaterWind:return1.25f;caseOneEventEnumAttribute.WindFire:return0.75f;caseOneEventEnumAttribute.FireWaterWind:return1f;default:break;}break;caseOneEventEnumAttribute.FireWater:switch(to){caseOneEventEnumAttribute.Fire:return1.25f;caseOneEventEnumAttribute.Water:return0.75f;caseOneEventEnumAttribute.Wind:return1f;caseOneEventEnumAttribute.FireWater:return1f;caseOneEventEnumAttribute.WaterWind:return0.875f;caseOneEventEnumAttribute.WindFire:return1.125f;caseOneEventEnumAttribute.FireWaterWind:return1f;default:break;}break;caseOneEventEnumAttribute.WaterWind:switch(to){caseOneEventEnumAttribute.Fire:return1f;caseOneEventEnumAttribute.Water:return1.25f;caseOneEventEnumAttribute.Wind:return0.75f;caseOneEventEnumAttribute.FireWater:return1.125f;caseOneEventEnumAttribute.WaterWind:return1f;caseOneEventEnumAttribute.WindFire:return0.875f;caseOneEventEnumAttribute.FireWaterWind:return1f;default:break;}break;caseOneEventEnumAttribute.WindFire:switch(to){caseOneEventEnumAttribute.Fire:return0.75f;caseOneEventEnumAttribute.Water:return1f;caseOneEventEnumAttribute.Wind:return1.25f;caseOneEventEnumAttribute.FireWater:return0.875f;caseOneEventEnumAttribute.WaterWind:return1.125f;caseOneEventEnumAttribute.WindFire:return1f;caseOneEventEnumAttribute.FireWaterWind:return1f;default:break;}break;caseOneEventEnumAttribute.FireWaterWind:return1f;default:return1f;}return1f;}
OneEventAttributeLogic.cs
publicboolCanAttack(OneEventEnumAttributeattacker,OneEventEnumAttributepanelAttributle){switch(panelAttributle){caseOneEventEnumAttribute.Fire:switch(attacker){caseOneEventEnumAttribute.Fire:caseOneEventEnumAttribute.FireWater:caseOneEventEnumAttribute.WindFire:caseOneEventEnumAttribute.FireWaterWind:returntrue;}break;caseOneEventEnumAttribute.Water:switch(attacker){caseOneEventEnumAttribute.Water:caseOneEventEnumAttribute.FireWater:caseOneEventEnumAttribute.WaterWind:caseOneEventEnumAttribute.FireWaterWind:returntrue;}break;caseOneEventEnumAttribute.Wind:switch(attacker){caseOneEventEnumAttribute.Wind:caseOneEventEnumAttribute.WaterWind:caseOneEventEnumAttribute.WindFire:caseOneEventEnumAttribute.FireWaterWind:returntrue;}break;caseOneEventEnumAttribute.FireWater:switch(attacker){caseOneEventEnumAttribute.Fire:caseOneEventEnumAttribute.Water:caseOneEventEnumAttribute.FireWater:caseOneEventEnumAttribute.WaterWind:caseOneEventEnumAttribute.WindFire:caseOneEventEnumAttribute.FireWaterWind:returntrue;}break;caseOneEventEnumAttribute.WaterWind:switch(attacker){caseOneEventEnumAttribute.Water:caseOneEventEnumAttribute.Wind:caseOneEventEnumAttribute.FireWater:caseOneEventEnumAttribute.WaterWind:caseOneEventEnumAttribute.WindFire:caseOneEventEnumAttribute.FireWaterWind:returntrue;}break;caseOneEventEnumAttribute.WindFire:switch(attacker){caseOneEventEnumAttribute.Fire:caseOneEventEnumAttribute.Wind:caseOneEventEnumAttribute.FireWater:caseOneEventEnumAttribute.WaterWind:caseOneEventEnumAttribute.WindFire:caseOneEventEnumAttribute.FireWaterWind:returntrue;}break;caseOneEventEnumAttribute.FireWaterWind:returntrue;default:Debug.LogError("no attribute like "+attacker.ToString());break;}returnfalse;}}

今回きれいにしたポイント

  • enumの拡張メソッドとして定義する。
  • 複合属性は、単属性に分けてから計算する。

コードの行数的にも半分以下になったし、なにより、読みやすくなりました。
きもちいぃ!

今回きれいにしたコード

OneEventEnumAttribute.cs
publicenumOneEventEnumAttribute{Dami=0,Fire=1,Water=2,Wind=3,FireWater=4,WaterWind=5,WindFire=6,FireWaterWind=7,}publicstaticpartialclassOneEventEnumAttributeExtend{publicstaticboolContains(thisOneEventEnumAttributeparam,OneEventEnumAttributetarget){varthisAttributes=param.ConvertSingleAttributeArray();vartargetAttributes=target.ConvertSingleAttributeArray();foreach(vartargetAttributeintargetAttributes){foreach(varthisAttributeinthisAttributes){if(targetAttribute==thisAttribute){returntrue;}}}returnfalse;}publicstaticfloatGetDamageRate(thisOneEventEnumAttributeparam,OneEventEnumAttributetarget){varthisAttributes=param.ConvertSingleAttributeArray();vartargetAttributes=target.ConvertSingleAttributeArray();intcounta=0;floatsumRate=0;foreach(vartargetAttributeintargetAttributes){foreach(varthisAttributeinthisAttributes){sumRate+=GetSingleDamageRate(thisAttribute,targetAttribute);counta++;}}returnsumRate/counta;}privatestaticfloatGetSingleDamageRate(thisOneEventEnumAttributeparam,OneEventEnumAttributetarget){if(param==target){return1f;}if(param.IsStrongFor(target)){return1.5f;}return0.5f;}privatestaticboolIsStrongFor(thisOneEventEnumAttributeparam,OneEventEnumAttributetarget){if(param==target){returnfalse;}switch(param){caseOneEventEnumAttribute.Fire:returntarget==OneEventEnumAttribute.Wind;caseOneEventEnumAttribute.Water:returntarget==OneEventEnumAttribute.Fire;caseOneEventEnumAttribute.Wind:returntarget==OneEventEnumAttribute.Water;default:thrownewArgumentOutOfRangeException(nameof(param),param,null);}}privatestaticOneEventEnumAttribute[]ConvertSingleAttributeArray(thisOneEventEnumAttributeparam){switch(param){caseOneEventEnumAttribute.Dami:returnnewOneEventEnumAttribute[0];caseOneEventEnumAttribute.Fire:returnnewOneEventEnumAttribute[1]{OneEventEnumAttribute.Fire};caseOneEventEnumAttribute.Water:returnnewOneEventEnumAttribute[1]{OneEventEnumAttribute.Water};caseOneEventEnumAttribute.Wind:returnnewOneEventEnumAttribute[1]{OneEventEnumAttribute.Wind};caseOneEventEnumAttribute.FireWater:returnnewOneEventEnumAttribute[2]{OneEventEnumAttribute.Fire,OneEventEnumAttribute.Water};caseOneEventEnumAttribute.WaterWind:returnnewOneEventEnumAttribute[2]{OneEventEnumAttribute.Water,OneEventEnumAttribute.Wind};caseOneEventEnumAttribute.WindFire:returnnewOneEventEnumAttribute[2]{OneEventEnumAttribute.Wind,OneEventEnumAttribute.Fire};caseOneEventEnumAttribute.FireWaterWind:returnnewOneEventEnumAttribute[3]{OneEventEnumAttribute.Fire,OneEventEnumAttribute.Water,OneEventEnumAttribute.Wind};default:thrownewArgumentOutOfRangeException(nameof(param),param,null);}}}

参考:修正したコードを検証するテストコード(NUnit)

OneEventEnumAttributeTest.cs
usingNUnit.Framework;publicclassOneEventEnumAttributeTest{[Test]publicvoidTestGetDamageRate(){OneEventEnumAttribute[]attackerAttributes=newOneEventEnumAttribute[7]{OneEventEnumAttribute.Fire,OneEventEnumAttribute.Water,OneEventEnumAttribute.Wind,OneEventEnumAttribute.FireWater,OneEventEnumAttribute.WaterWind,OneEventEnumAttribute.WindFire,OneEventEnumAttribute.FireWaterWind};OneEventEnumAttribute[]targetAttributes=newOneEventEnumAttribute[7]{OneEventEnumAttribute.Fire,OneEventEnumAttribute.Water,OneEventEnumAttribute.Wind,OneEventEnumAttribute.FireWater,OneEventEnumAttribute.WaterWind,OneEventEnumAttribute.WindFire,OneEventEnumAttribute.FireWaterWind};foreach(varattackerAttributeinattackerAttributes){foreach(vartargetAttributeintargetAttributes){varexpectedResult=newOneEventAttributeDamageLogic().Caluculate(attackerAttribute,targetAttribute);vardamageRate=attackerAttribute.GetDamageRate(targetAttribute);Assert.AreEqual(expectedResult,damageRate,"from:"+attackerAttribute+" to:"+targetAttribute);}}}[Test]publicvoidTestContains(){OneEventEnumAttribute[]attackerAttributes=newOneEventEnumAttribute[7]{OneEventEnumAttribute.Fire,OneEventEnumAttribute.Water,OneEventEnumAttribute.Wind,OneEventEnumAttribute.FireWater,OneEventEnumAttribute.WaterWind,OneEventEnumAttribute.WindFire,OneEventEnumAttribute.FireWaterWind};OneEventEnumAttribute[]targetAttributes=newOneEventEnumAttribute[7]{OneEventEnumAttribute.Fire,OneEventEnumAttribute.Water,OneEventEnumAttribute.Wind,OneEventEnumAttribute.FireWater,OneEventEnumAttribute.WaterWind,OneEventEnumAttribute.WindFire,OneEventEnumAttribute.FireWaterWind};foreach(varattackerAttributeinattackerAttributes){foreach(vartargetAttributeintargetAttributes){varexpectedResult=newOneEventAttributeLogic().CanAttack(attackerAttribute,targetAttribute);varnowResult=attackerAttribute.Contains(targetAttribute);Assert.AreEqual(expectedResult,nowResult,"from:"+attackerAttribute+" to:"+targetAttribute);}}}}

おわりに

開発者のツイッターはこちら
プログラマさん。ゲーム開発者さん。仲良くしたいです。

英語学習ゲーム 英語物語で遊んでみてね♪

713ikWQGVJL.png


Viewing all articles
Browse latest Browse all 9308

Trending Articles