Chatterã«ã¯äŸ¡å€ããããŸããã ã³ãŒããèŠããŠãã ããã
-ã©ã€ãã¹ã»ããŒãã«ãº
ã¿ãªããããã«ã¡ã¯ïŒ ç§ã¯å°ããªïŒãããèªãã«æãïŒgamedevãªãã£ã¹ã§ããã°ã©ããŒãšããŠåããŠããŸãã éå»æ°å¹Žéãå瀟ã¯match3ãžã£ã³ã«ã®ã¢ãã€ã«ã²ãŒã çšã®ã«ãžã¥ã¢ã«ã²ãŒã ããªãªãŒã¹ããŠããŸããã ç§ãã¡ã¯CïŒïŒããã¯åã°ããããåŸãªãïŒã§èšè¿°ããUnityã䜿çšããŸããïŒãŸããã»ãšãã©ïŒã ç§ã®è²¬ä»»ã®äž»ãªé åã¯ãã²ãŒã ãã¬ã€ãšUIã§ãã CïŒãšãã®ãšã³ã·ã¹ãã ã«ã倢äžã§ãã ä»æ¥ã¯ãã²ãŒã ã³ã³ãã³ãã®ç·šéã«Roslynã³ãŒãåââæããã³å€æŽããŒã«ãã©ã®ããã«äœ¿çšãããã説æããŸãã 誰ãæ°ã«ãã-ç§ã¯ç«ããé¡ãããŸãã
ã泚æ å®è£ æ¹æ³ãç解ããã³ãŒãäŸãæäŸããã»ã¯ã·ã§ã³ã«ã¯ãèŠåºãã®[æè¡ã»ã¯ã·ã§ã³]ãä»ããŠããŸãã ãã®ã¬ãã«ã®è©³çŽ°ã«é£ã³èŸŒã¿ãããªãå Žåã¯ãã¹ãããããŠãã ããã ããã¯ãã¢ã€ãã¢ã®äžè¬çãªç解ã«ã¯åœ±é¿ããŸããã
ããã¯ã°ã©ãŠã³ãã 1ã€ã®Match3ã§ãã£ã±ãã«ãªãããšã¯ãããŸãã
match3ãžã£ã³ã«ã®å€ãã®ã²ãŒã ã«ã¯ãã²ãŒã ã®äžçãè¡šãå¥ã®ãšã³ãã£ãã£ããããŸãã ãããã«ãŒããšåŒã³ãŸãã ã«ãŒãã®æ©èœã¯ç°ãªãå ŽåããããŸããéåžžã«åçŽãªïŒã²ãŒã ã®é²è¡ç¶æ³ã®ã€ã³ãžã±ãŒã¿ãŒïŒããæ¬æ Œçãªã¢ã¯ã·ã§ã³ã·ãŒã³ïŒç¬èªã®å éšã¹ããŒãªãŒãããããããã£ã©ã¯ã¿ãŒãªã©ïŒãŸã§ã§ãã
ç§ãåãçµãã§ãããããžã§ã¯ãã®1ã€ã§ã¯ããããããšãããã«ç¹å¥ãªæ³šæãæãããŠããŸãã ååãªèŠä»¶ããããŸãïŒå€§éã®ã³ã³ãã³ããå€æ§æ§ãé åãããæŽçãããã«ããã·ãŒã³ãªã©ã ãŸãããããã¯ãã¹ãŠç°¡åã«ç·šéããã³å±éã§ããå¿ èŠããããŸãã ä»ãŸã§ãç§ãã¡ã®ã¹ã¿ãžãªã®ã²ãŒã ãããžã§ã¯ãã«ã¯ãã®ãããªãã®ã¯ãããŸããã§ããã ããããã·ã¹ãã ãšãã®ããŒã«ã®äž¡æ¹ããŒãããéçºããå¿ èŠããããŸããã
ããããã¢ãŒããã¯ãã£[æè¡ã»ã¯ã·ã§ã³]
ããããå šäœãå¥ã ã®éé-ã¯ãšã¹ãã«åå²ããŸãã ã¯ãšã¹ãã¯äžé£ã®ã¹ããŒãžïŒç¶æ ïŒã§ãããããããã«ç¬èªã®ããžãã¯ããããŸãã ã¹ã¿ãã¯ãšã¹ãã®ã³ãŒããèŠãŠã¿ãŸãããã
enum StubQuestState { Initial, } class Stub : SectorComponent<StubQuestState> {} class StubQuest : SectorBehaviour<Stub, StubQuestState> { [State(StubQuestState.Initial)] private IEnumerator<object> InitialState() { yield break; } }
ã ããç§ãã¡ã¯èŠãïŒ
- åæåStubQuestState-ã¯ãšã¹ãã®ã¹ããŒã¿ã¹ã
- ã¯ã©ã¹ã¹ã¿ã-ã¯ãšã¹ãã®ã³ã³ããŒãã³ããç¶æ ãšãªãã·ã§ã³ã®ããŒã¿ãä¿åããŸãã
- StubQuestã¯ã©ã¹-ã¯ãšã¹ãããžãã¯ã ã¯ãšã¹ãã®åç¶æ ã¯ãã€ãã¬ãŒã¿ãè¿ãã¡ãœããã«å¯Ÿå¿ããå¿ èŠããããŸãïŒã¡ãœããã¯Stateå±æ§ãä»ããŠæ¥ç¶ããããã®ç¶æ ã§çŽæ¥å®è¡ãããã¢ã¯ã·ã§ã³ã決å®ããŸãïŒã
ãŸããåã¯ãšã¹ãã«ã¯ã¢ã¯ãã£ããŒã·ã§ã³æ¡ä»¶ããããŸã-èµ·åããå¿ èŠãããå ŽåïŒã³ãŒãã§ã¯FuncãšããŠè¡šç€ºãããååã¯ReachedConditionã«ãªããŸãïŒã
ã¿ã€ãã«ã«ã€ããŠ
ç§ã¯ååãããŸãããéžã°ããªãã£ãããšãèªããªããã°ãªããŸãã-ããã¯ãNPCã«è©±ããããã-ã10åã®æçãæã«å
¥ããŸããã-ãåæ Œããnishtyakã«ãªããŸããããšããæ£èŠã®ã·ãŒã±ã³ã¹ã§ã¯ãããŸããã ã¯ãšã¹ããéããŠããããããä¿é²ãããšãã®æ©èœã®çºèŠãåã²ãŒã ã®å Žæã«åºæã®è³ã®çºè¡ãªã©ãããªãå€ãã®ç°ãªããã®ãå®è£
ãããŸãã ãBehaviorãomïŒUnityãªã©ïŒãåŒã³åºãæ¹ãé©åã§ãããéåžžã®ååã®ãŸãŸã«ããããšã«ããŸããã
ã¯ãšã¹ãã¯ãã»ã¯ã¿ãŒïŒSectorNãªã©ã®ååã®éçã¯ã©ã¹ïŒã®çšèªã§ãå Žæã«ãã£ãŠåå²ãããŠããŸãã åã»ã¯ã¿ãŒã«ã¯ããããïŒããã§ã¯ã°ã©ãã£ãã¯ãã¢ãã¡ãŒã·ã§ã³ãæå³ããïŒããã¥ãŒããªã¢ã«ïŒäžéšã¯ã¯ãšã¹ããéããŠå®è£ ããããç¬ç«ãããšã³ãã£ãã£ã§ãïŒãããã³ã¯ãšã¹ãèªäœãåæåããInitializeã¡ãœããããããŸãã åæåã³ãŒãã®äŸã次ã«ç€ºããŸãã
var fixSubmarineQuest = new FixSubmarineQuest { ReachedCondition = () => plot.Levels.Completed > 3, }; var removeSeaweedQuest = new RemoveSeaweedQuest { ReachedCondition = () => fixSubmarineQuest.Component.IsFinished, }; var fixGateStatuesQuest = new FixGateStatuesQuest { ReachedCondition = () => removeSeaweedQuest.Component.IsFinished && fixSubmarineQuest.Component.IsFinished, };
ã¯ãšã¹ãã®æ¥ç¶ãèŠèŠåããŸãïŒ
![ç»å](https://habrastorage.org/webt/y0/z0/mk/y0z0mk9p1zhvk8caavvy12lqnww.png)
ã芧ã®ããã«ãã¯ãšã¹ãã¯éé£çµã®é埪ç°æåã°ã©ãã圢æããŸãã
ã¯ãšã¹ãã®ã¹ããŒã¿ã¹ã®ããžãã¯ãåæããŸãããã åç¶æ ã¯ãåºæ¬çã«ãã®ç¶æ ã§å®è¡ããå¿ èŠãããäžé£ã®ã¢ã¯ã·ã§ã³ã§ãã ã¢ã¯ã·ã§ã³ã¯å®å šã«ç°ãªãå ŽåããããŸããã¢ãã¡ãŒã·ã§ã³ã®ã¢ã¯ãã£ãåãã·ãŒã³å ã§ã®ã«ã¡ã©ã®ç§»åãã²ãŒã ã¬ãã«ã®èµ·åãã«ããã·ãŒã³ã®èµ·åãšç®¡çãªã©ã äŸïŒ
yield return WaitForMapScreenOnTop(); MapScreen.HideUi(); yield return MapScreen.ScrollToMapNodeTask( MapScreen.Map._Sector02._RadioTower.It, zooming: MapZooming.Minimal, screenPivot: new Vector2(0.5f, 0.4f) ); yield return MapScreen.Map._Sector02._RadioTower.RunAnimationFixTower(); yield return MapScreen.Map._Sector02._RadioTower.RunAnimationCommunicate(); var dialog = new CutScene(CharacterEmotion.Playfulness); dialog.AddBubble("Bla-bla-bla"); yield return Task.WaitWhile(() => !dialog.IsNearlyHidden); MapScreen.ShowUiWithDelay(); SetState(FixCommunicationCrystalQuestState.Final);
ãã®äŸã§ã¯ã次ã®ããšãè¡ããŸãã
- ããããäžçªäžã®ãã€ã¢ãã°ãšããŠè¡šç€ºããããŸã§åŸ ã¡ãŸãã
- UIãé衚瀺ã«ããŠãã«ã¡ã©ã移åããŸãã
- ããããã¢ãã¡ãŒã·ã§ã³ãé 次起åããŸãã
- ãBla-bla-blaããšãã幞ããªãã£ã©ã¯ã¿ãŒã®ã«ããã·ãŒã³ã玹ä»ããŸãã
- UIã衚瀺ããŠã次ã®ç¶æ ã«é²ã¿ãŸãã
ããºãªã³ãšã³ã³ãã³ã
ãåãã®ããã«ãgamedevã¯ãœãããŠã§ã¢éçºã®ããªã極端ãªåéã§ãã ã²ãŒã ã¯ã·ã¹ãã ãšã³ã³ããŒãã³ãã®ãå¯ãéããã§ããã ãã§ãªãããããã®éã®é¢ä¿ã¯é»å ç³ç«ã®é床ã«ãã£ãŠå€åããŸãïŒå Žåã«ãã£ãŠã¯ãæ¯ãåã®åçã«ãã£ãŠãïŒåé€ãè¿åŽââãååã®åé€...ïŒã 掻çºãªéçºã®æ®µéã§ãããããã¯çµ¶ããå€åããŠããŸãã ããã¯ãèšè¿°ãããã¢ãŒããã¯ãã£ã®ãã¹ãŠã®ã¬ãã«ã«åœ±é¿ããŸãïŒã¯ãšã¹ãéã®é¢ä¿ïŒãããã®é åºïŒãã¯ãšã¹ãã®ã»ã¯ã¿ãŒãžã®åå²ããããŠçŽæ¥ã¯ãšã¹ãããžãã¯ïŒç¶æ ãšã¢ã¯ã·ã§ã³ïŒã ç§ã¯æ£çŽãªãšããããã¹ãŠãæã§æžãïŒãŸãã¯Ctrl + CãCtrl + Vãè¡ã£ãŠããååãå€æŽãããªã©ïŒã®ã«ããããããŠãããããæåã«WinFormsã§å°ããªã¢ããªã±ãŒã·ã§ã³ãäœæããŠãæå®ãããååãšã¹ããŒã¿ã¹ã®StubQuestãçæããŸããã ãããŠãRoslynã«ã€ããŠæãåºããããããç·šéããŒã«ãäœæããããšã«ããŸããã
äžã§èª¬æããããã«ãããããå šäœã¯ã³ãŒãã«ãã£ãŠèšå®ãããŸãã ããã°ã©ãã³ã°ã¹ãã«ããªããã°ç·šéã§ããŸããã äžèšã®ãã¹ãŠã®ã³ã³ããŒãã³ãïŒã¯ãšã¹ããé åºãããžãã¯ãã¢ã¯ã·ã§ã³ïŒãä»ã®äººïŒãããã¥ãŒãµãŒãã²ãŒã ãã¶ã€ããŒïŒã«å€æŽããæ©èœãæäŸããäœåãéãäœæ¥ããããã«ãRoslynãéèŠãªåœ¹å²ãæ ããšãã£ã¿ãŒãäœæãããŸããã ãã®ä»äºã®åçïŒ
- RoslynïŒç¹ã«ãMicrosoft.CodeAnalysis.CSharpïŒã䜿çšããŠãã²ãŒã ã³ãŒããåæãããè«çã¢ãã«ãæ§ç¯ãããŸãã
- è«çã¢ãã«ãå³ã«è¡šç€ºãããŸãã
- ãã€ã¢ã°ã©ã ãžã®å€æŽãã¢ãã«ã«è¡šç€ºããããã®åŸïŒåã³Roslynãä»ããŠããã ãAPIã䜿çšããŠã³ãŒããçæããã³å€æŽããŠïŒã³ãŒãããŒã¹ã«æ»ããŸãã
æŠç¥çã«ã¯ãããã¯æ¬¡ã®ããã«è¡šçŸã§ããŸãã
![ç»å](https://habrastorage.org/webt/kt/0r/on/kt0roniddsgqbmfdhum1urcokna.png)
è«çè¡šçŸèªäœã¯éåžžã«åçŽã§ã-ã²ãŒã ãšã³ãã£ãã£ãè¡šãã¯ã©ã¹/æ§é ã®ã»ãããããããèšè¿°/è£è¶³ã§ããããŸããŸãªããŒã¿ãããã³ãããã®ãšã³ãã£ãã£éã®é¢ä¿ã è«çã¢ãã«ã®ãããã§ãæ¯ãèãã®æ£ç¢ºããç°¡åã«ç¢ºèªã§ããŸãïŒããšãã°ãã°ã©ãå ã®ãµã€ã¯ã«ã®ååšããã«ããã·ãŒã³ã®åŸã«UIã衚瀺ããéçºè ã®å¿ããããïŒã ã³ãŒãåââæãšè«çæ§é ã®æ§ç¯ã«ã€ããŠè©³ãã説æããŸãã
éããã°ã©ããŒã«ããVisual Studioã®å€éšã§ã®ã³ãŒãç·šéã®æ±ºå®ã«ã€ããŠ
æçè«è
ã¯å°ãããããããŸããïŒãªãããããã¹ãŠãªã®ãïŒ ããã°ã©ã以å€ã®äººãã³ãŒããç·šéã§ããããã«ããã«ã¯ã©ãããã°ããã§ããïŒ
çãã¯æ¬¡ã®ãšããã§ããéåžžã«ç°¡åã§ãã ãã®ããŒã«ã䜿çšãããšãã³ãŒãã®å³å¯ã«å®çŸ©ãããéšåã®ã¿ãç·šéã§ããæ£ç¢ºæ§ã確èªã§ããŸãã ããã䜿çšãããšããããžã§ã¯ãã®ã¢ã»ã³ããªãå£ãããšã¯ã§ããŸããã
ã絶ããæ¥éã«å€åããèŠä»¶ãã«ã€ããŠã¯ã©ãã§ããïŒ ãã¹ãŠãç°¡åã§ããå¿ èŠã«å¿ããŠæ°ããæ©èœãè¿œå ãããŸãã ã³ã³ã»ãããå®è£ ãããæ¿èªãããããããã®ããŸããŸãªå Žæã§æºãããããšããã«ãããŒã«ãå®æããŸãã
çãã¯æ¬¡ã®ãšããã§ããéåžžã«ç°¡åã§ãã ãã®ããŒã«ã䜿çšãããšãã³ãŒãã®å³å¯ã«å®çŸ©ãããéšåã®ã¿ãç·šéã§ããæ£ç¢ºæ§ã確èªã§ããŸãã ããã䜿çšãããšããããžã§ã¯ãã®ã¢ã»ã³ããªãå£ãããšã¯ã§ããŸããã
ã絶ããæ¥éã«å€åããèŠä»¶ãã«ã€ããŠã¯ã©ãã§ããïŒ ãã¹ãŠãç°¡åã§ããå¿ èŠã«å¿ããŠæ°ããæ©èœãè¿œå ãããŸãã ã³ã³ã»ãããå®è£ ãããæ¿èªãããããããã®ããŸããŸãªå Žæã§æºãããããšããã«ãããŒã«ãå®æããŸãã
ã³ãŒãåââæãšã¢ãã«æ§ç¯[æè¡ã»ã¯ã·ã§ã³]ã
ã³ãŒããšã¯äœã§ããïŒ æå§ãã«ãããã¯åãªãããã¹ãã§ãã 次ã«ã æ§æ解æ段éã§ãASTïŒ Abstract Syntax Tree ïŒãååŸãããŸãã ã»ãã³ãã£ãã¯åæã®åŸãã·ã³ãã«ããŒãã«ã衚瀺ãããŸãã Roslynã䜿çšããŠãå¿ èŠãªããŒã¿æ§é ãååŸããããšã¯é£ãããããŸããïŒäŸãããããåããŸã ïŒã
var tree = CSharpSyntaxTree.ParseText(@" public class MyClass { int MyMethod() { return 0; } }" ); var Mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); var compilation = CSharpCompilation.Create( "MyCompilation", syntaxTrees: new[] { tree }, references: new[] { Mscorlib } ); //Note that we must specify the tree for which we want the model. //Each tree has its own semantic model var model = compilation.GetSemanticModel(tree);
ãã®ãããåæã«äŸ¿å©ãªåœ¢åŒã®ã³ãŒãããããŸãã ãããããããã®è«çæ§é ãšãªã³ã¯ããããšã¯æ®ã£ãŠããŸãã ãããžã§ã¯ãã®ãã¡ã€ã«æ§é ãšäžèšã®ã¢ãŒããã¯ãã£ã¯ã次ã®ç¹ã§åœ¹ç«ã¡ãŸãã
![ç»å](https://habrastorage.org/webt/qr/ye/l6/qryel6ol1hzpg_iayc3ejxujiaa.png)
public void LoadSectorCode(string dirPath) { var sectorFile = Directory.EnumerateFiles(dirPath) .FirstOrDefault(p => new FileInfo(p).Name.StartsWith("Sector")); if (sectorFile == null) { throw new SectorFileNotFoundException(); } code.ReadFromFile(Path.Combine(dirPath, sectorFile), CodeBulkType.Sector); var questsDir = Path.Combine(dirPath, "Quests"); if (Directory.Exists(questsDir)) { var files = Directory.EnumerateFiles(questsDir); foreach (var filePath in files) { code.ReadFromFile(filePath, CodeBulkType.Quest); } } }
ã³ãŒãããŒã¹ã®åãã¡ã€ã«ã¯ãã¿ã€ãïŒenum CodeBulkTypeïŒã«é¢é£ä»ããããŠããŸãïŒã¯ãšã¹ããã»ã¯ã¿ãŒãæ§æãã¡ã€ã«ãªã©ã ã¿ã€ãããããã°ãããé«ãã¬ãã«ã®åæãè¡ãããšãã§ããŸãã ããšãã°ãã¯ãšã¹ãããŒã¿ãèªã¿åããŸãã
class QuestReader : CodeReader { public override CodeBulkType[] AcceptedTypes => new[] { CodeBulkType.Quest, }; public override void Read(CodeBulk codeBulk, Code code, ref Flow flow) { var quest = FromCodeTransformer.ReadQuest(codeBulk.Tree); //... sector.Quests.Add(quest); //... } }
ããŒã¿ã¢ãŒããã¯ãã£ãšèªã¿åãã«ã€ããŠå°ã
ãããžã§ã¯ãã®å€æŽã«äŒŽããéçºçšã®ããŒã«ãå€æŽããããããæ¡åŒµæ§ãšæè»æ§ãã¢ãŒããã¯ãã£ã®å¿
é èŠä»¶ã§ããã è«çã¢ãã«ã¯ãªããžããªãšèŠãªãããŸããããŒã¿ãè¿œå ãå€æŽãåé€ã§ããŸãã ããŒã¿ã®æ¡åŒµæ§ïŒ
èªæžïŒ
äœãèªãå¿ èŠããããŸããïŒ ã¯ã©ã¹ãååŸããCodeReaderããç¶æ¿ããICodeReaderãå®è£ ããŸãã è¿œå ããå¿ èŠããããŸãã åæçšããŒã¿ïŒ çŽ æŽããããIDataããç¶æ¿ããå¿ èŠãªå Žæããããããèªã¿åããå¿ èŠãªå Žæã«è¿œå ããŸãã
public interface IData {} public class DataStorage { public List<IData> Records = new List<IData>(); //âŠ. } // - , , ⊠public DataStorage Data { get; } = new DataStorage();
èªæžïŒ
interface ICodeReader { CodeBulkType[] AcceptedTypes { get; } void Read(CodeBulk codeBulk, Code code, ref Flow flow); } abstract class CodeReader : ICodeReader { public abstract CodeBulkType[] AcceptedTypes { get; } public abstract void Read(CodeBulk codeBulk, Code code, ref Flow flow); }
äœãèªãå¿ èŠããããŸããïŒ ã¯ã©ã¹ãååŸããCodeReaderããç¶æ¿ããICodeReaderãå®è£ ããŸãã è¿œå ããå¿ èŠããããŸãã åæçšããŒã¿ïŒ çŽ æŽããããIDataããç¶æ¿ããå¿ èŠãªå Žæããããããèªã¿åããå¿ èŠãªå Žæã«è¿œå ããŸãã
æ§æããªãŒã®åæãšç·šéã«ã¯ã Visitorãã¿ãŒã³ïŒ LINQ ïŒãç©æ¥µçã«äœ¿çšãããŸãã åŸè ã¯éåžžã«äžäŸ¿ã§ãéåžžã«åçŽãªåæã«é©ããŠããŸãã ãããžã§ã¯ãã³ãŒãã§ã¯ã SyntaxVisitor ã SyntaxWalkerããã³SyntaxRewriterã¯ã©ã¹ãç©æ¥µçã«äœ¿çšããŸãã ã æåã®2ã€ã®ãŠãŒã¹ã±ãŒã¹-æ§æããªãŒã®åæã ããšãã°ãã¯ãšã¹ããããããã«è¿œå ããããéãã«æšªã«ãªã£ãŠèª°ã«ã觊ããªãããã«ããããšãã§ããŸãã ãã®ç¹æ§ãå€æããã«ã¯ãã¯ãšã¹ãã³ã³ã¹ãã©ã¯ã¿ãŒãã©ããã§åŒã³åºããããã©ããã確èªããå¿ èŠããããŸãã Roslynã䜿çšãããšãããã¯éåžžã«ç°¡åã§ããSyntaxWalkerããç¶æ¿ãããã¹ãŠã®ObjectCreationExpressionãã蚪åãããŸãã
// SyntaxWalker - CSharpSyntaxWalker'a . private class QuestInitializationFinder : SyntaxWalker { //... public override void VisitObjectCreationExpression(ObjectCreationExpressionSyntax node) { base.VisitObjectCreationExpression(node); var model = Code.Compilation.GetSemanticModel(node.SyntaxTree); if (model.GetTypeInfo(node).Type == questTypeToFind) { //âŠ. } } //... }
æ§ç¯ãããã¢ãã«ã®è¡šç€ºã
ããã§ãã¢ãã«ãäœæããŸããã ããã衚瀺ãããŸãŸã«ããŠãç·šéæ¹æ³ãåŠã³ãŸãã
ã¢ãã«ãããã³ã°ã¯ã NShapeãªãŒãã³ã©ã€ãã©ãªã䜿çšããŠå®è£ ãããŸããã ãã®ãããžã§ã¯ãã¯ç¿åŸãæãç°¡åã§ãæ©èœãè±å¯ã§ããããšãå€æããŸããïŒãã ããæ¡åŒµã®æ©äŒããã£ãšå¿ èŠãªå ŽåããããŸã...ïŒã ã¯ãšã¹ãã°ã©ãã®é ç¹ãé 眮ããããã«ã Microsoft Automatic Graph Layoutã©ã€ãã©ãªã®Sugiyamaã¢ã«ãŽãªãºã ã䜿çšããŸããããŸããããã€ãã®ãã¥ãŒãªã¹ãã£ãã¯ãäœæããå¿ èŠããããŸããã
æåã®ã»ã¯ã¿ãŒã§ã®ã¯ãšã¹ãã®äŸãæããŸãã
![ç»å](https://habrastorage.org/webt/5-/hf/w-/5-hfw-fhl5cc04k2ub_rh7pxcgm.png)
åæåã³ãŒããããã¯ããã«ãŠãŒã¶ãŒãã¬ã³ããªãŒã«èŠããŸãããïŒ ïŒããã¯ãŸã éåžžã«åçŽãªäŸã§ãã¯ãšã¹ãã¯ã»ãšãã©ãªããç·åœ¢ã®é åºã§åæåãããŠããŸããïŒãããŠãããã«ã¯ãšã¹ãããžãã¯ã®è¡šç€ºããããŸãïŒãããã°ã©ããŒçšãã¢ãŒãã¯ã³ãŒãã衚瀺ããããã§ãã
![ç»å](https://habrastorage.org/webt/km/bd/pn/kmbdpnxagynue_g_8gbhuwkswti.png)
ã¢ãã«ã®ç·šé[æè¡ã»ã¯ã·ã§ã³]ã
NShapeã©ã€ãã©ãªã¯ ããã€ã¢ã°ã©ã ãäœæããã³ç·šéããããã«èšèšãããŠããŸãã è«çæ§é ãå³åœ¢åŒã§è¡šç€ºããã®ã§ããã®ïŒæ§é ïŒèŠèŠçç·šéã®å¯èœæ§ããããŸãã äž»ãªããšã¯ãå€æŽãæ£ãã解éããç¡å¹ãªå€æŽãçŠæ¢ããããšã§ãã
ãã€ã¢ã°ã©ã äžã®ã¢ã¯ã·ã§ã³ã®è«çã¢ã¯ã·ã§ã³ãžã®å€æã¯ãNShapeã©ã€ãã©ãªã®ããŒã«ã®ç¹å¥ãªã©ãããŒã§è¡ãããŸãïŒãã®ã³ãŒããæžãã®ã¯ãŸã åã³ã§ããïŒã è«çã¢ãã«ã®ã³ã³ããã¹ãã§äœããã®æå³ãæã€ã¢ã¯ã·ã§ã³ã®ã¿ãå€æãããŸãïŒããšãã°ãã¯ãšã¹ãã瀺ã圢ç¶ã®å転ã¯ã衚瀺æ¹æ³ã®æ§é çãªå€æŽãæå³ããŸããïŒã ãã¹ãŠã®ã¢ã¯ã·ã§ã³ã¯Commandã¿ã€ãã®ãªããžã§ã¯ãã«ã«ãã»ã«åããïŒ ãã¿ãŒã³ãèªèããŸãããïŒïŒãå ã«æ»ãããšãã§ããŸãã
delegate void DoneHandler(bool firstTime); delegate void UndoneHandler(); interface ICommand { DoneHandler Done { get; set; } UndoneHandler Undone { get; set; } void Do(); void Undo(); }
ãã®ã€ã³ã¿ãŒãã§ã€ã¹ïŒããã³ãããå®è£ ããæœè±¡ã¯ã©ã¹ïŒã¯ãè«çã¢ãã«ãšã³ãŒãã®å€æŽãæ åœããŸãã èŠèŠçãªãã¬ãŒã³ããŒã·ã§ã³ã¯ãDone / Undoneããªã²ãŒããžã®ãµãã¹ã¯ãªãã·ã§ã³ãéããŠæŽæ°ãããŸãã
è€åã¢ã¯ã·ã§ã³ã®åå²
äžéšã®ã¢ã¯ã·ã§ã³ã¯ãä»ã®ããã€ãã®ã¢ã¯ã·ã§ã³ã§æ§æãããŠããŸãã ããšãã°ã2ã€ã®ã¯ãšã¹ãããªã³ã¯ããŸãã äž¡æ¹ãã¢ã¯ãã£ãã«ãªã£ãŠããå Žåããã®1ã€ã®ã¢ã¯ã·ã§ã³ã¯æ¥ç¶ã®çŽæ¥è¿œå ã§ãã ãããã®å°ãªããšã1ã€ãéã¢ã¯ãã£ãã§ããå Žåãæ¥ç¶ã確ç«ããåã«ãæå¹åãããå¿
èŠããããŸãã ãŸãã¯å¥ã®äŸ-ã¯ãšã¹ãã®åé€ã ã¯ãšã¹ããåé€ããåã«ãåå ããŠãããã¹ãŠã®ãªã³ã¯ãåé€ããã¯ãšã¹ããç¡å¹ã«ããå¿
èŠããããŸãã ãã®ãããªåå²ã«ãããè€éãªã¢ã¯ã·ã§ã³ãã³ã³ããŒãã³ãã«åå²ãããããããä»ã®ã¢ã¯ã·ã§ã³ãèšèšã§ããŸãã
ã³ãŒãã§ã¯ãããã¯ã³ãã³ãã®ç¹å®ã®ã±ãŒã¹-CompositeCommandãä»ããŠå®è£ ãããŸãã
ããã§ã¯ããã®ã³ãã³ãã®[å ã«æ»ã]ããããã£ã®ç·šéã匷調衚瀺ããŸãã å®éã«ã¯ãã³ãã³ãã¯è¿œå ã®é åºã§å®è¡ãããããŒã«ããã¯ãããŸã-éã«ã ãããã£ãŠãå ã«æ»ãããªã²ãŒãã¯éã®é åºã§çµåããå¿ èŠããããŸãã
ã³ãŒãã§ã¯ãããã¯ã³ãã³ãã®ç¹å®ã®ã±ãŒã¹-CompositeCommandãä»ããŠå®è£ ãããŸãã
class CompositeCommand : Command { private readonly List<ICommand> commands = new List<ICommand>(); public override void Do() { foreach (var cmd in commands) { cmd.Do(); } } public override void Undo() { foreach (var cmd in Enumerable.Reverse(commands)) { cmd.Undo(); } } }
ããã§ã¯ããã®ã³ãã³ãã®[å ã«æ»ã]ããããã£ã®ç·šéã匷調衚瀺ããŸãã å®éã«ã¯ãã³ãã³ãã¯è¿œå ã®é åºã§å®è¡ãããããŒã«ããã¯ãããŸã-éã«ã ãããã£ãŠãå ã«æ»ãããªã²ãŒãã¯éã®é åºã§çµåããå¿ èŠããããŸãã
public void AddCommands(IEnumerable<ICommand> commandsToAdd) { //... foreach (var cmd in commandsToAdd) { Done += cmd.Done; Undone = (UndoneHandler)Delegate.Combine(cmd.Undone, Undone); } }
æ§æããªãŒã®ç·šéã«ã¯ãäžèšã®ããã«CSharpSyntaxRewriterã¯ã©ã¹ã®æ¡åŒµã䜿çšãããŸãïŒææ³ã«ã€ããŠã¯ã ãã¡ãã§è©³ãã説æãããŠããŸã ïŒã ã¯ãšã¹ããç¡å¹ã«ããäŸïŒã³ã³ã¹ãã©ã¯ã¿ãŒåŒã³åºããåé€ããïŒïŒ
class DeactivateQuestCommand : Command { //⊠public override void Do() { var classDecls = codeBulk.Tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>(); foreach (var classDecl in classDecls) { var typeRemover = new ClassConstructorCallRemover( Context.CodeEditor.GetSymbolFor(classDecl, codeBulk), Context.Code ); Context.CodeEditor.ApplySyntaxRewriter(typeRemover); } //... } //... } class ClassConstructorCallRemover : SyntaxRewriter { //⊠public override SyntaxNode VisitExpressionStatement(ExpressionStatementSyntax node) { var model = Compilation.GetSemanticModel(node.SyntaxTree); var expr = node.Expression; if (expr is ObjectCreationExpressionSyntax oces) { if (ReferenceEquals(model.GetTypeInfo(expr).Type, typeToRemove)) { return null; } } return base.VisitExpressionStatement(node); } public override SyntaxNode VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node) { if (node.Declaration.Variables.Count == 1) { var model = Compilation.GetSemanticModel(node.SyntaxTree); var type = model.GetTypeInfo(node.Declaration.Variables.First().Initializer.Value).Type; if (ReferenceEquals(type, typeToRemove)) { return null; } } return base.VisitLocalDeclarationStatement(node); } //... }
Roslynã«ã¯ãããã«äœ¿çšã§ããç·šéæ©èœãããã€ãçšæãããŠããŸãã é¡èãªäŸã¯ãååã®å€æŽïŒ Renamer ïŒããã³ã¹ããŒã¹ã®ãã©ãŒãããïŒ Formatter ïŒã§ãã ãããŠãããã¯äººçã倧ãã«ç°¡çŽ åããŸãã
çŸæç¹ã§ã¯ãæ¢åã®ã³ãŒããåé€/å€æŽãããšããäºå®ã®ã¿ãæ±ããŸããã ãããŠãæ°ããäžä»£ã¯ã©ãã§ããïŒ ããã¯ã SyntaxFactory .Parse *ããã³ããŒââãå€æŽé¢æ°ãä»ããŠç°¡åã«å®è¡ãããŸãã
var linkExprText = $"ReachedCondition = {newLinkText}"; var newInitializer = node.Initializer.AddExpressions(SyntaxFactory.ParseExpression(linkExprText));
ãæ°ã¥ããããããŸãããïŒ2è¡ç®ã泚ææ·±ãèŠããšïŒãAddExpressionsé¢æ°ã¯æ°ããããŒããè¿ããŸãã äžè¬ã«ãRoslynå šäœã¯ã¢ãŒããã¯ãã£ã®æ©èœçã¢ãããŒãã«åŸããŸããäœããå€æŽããå Žåã¯ãå¿ èŠãªãã©ã¡ãŒã¿ãŒã䜿çšããŠæ°ãããã®ãäœæããŸãã æåã¯äžäŸ¿ã§ãããããããããªãã¯ãããæãå§ããŸãã
ã³ãŒãä¿®æ£ã«ã€ããŠ
Roslynã¯è¯ãããŒã«ã§ãããå
šèœã§ã¯ãããŸããã ããšãã°ãã³ãŒãã®å€æŽåŸãäºçŽ°ãªã³ã³ãã€ã«ãšã©ãŒãçºçããå ŽåããããŸãã ãããŠãããããèªåã§ä¿®æ£ããå¿
èŠããããŸãã ãã®ãããç§ã¯ã»ã¯ã¿ãŒå
ã®ã¯ãšã¹ãã®åæåã®é åºã®ããã«ã³ãŒãä¿®æ£ãæžããªããã°ãªããŸããã§ããã
ãã®ãããªã³ãŒããäœæããã«ã¯ãææ³ïŒç¢ºèª/ç·šé/äœæããå¿ èŠãããæ§æåäœã®çš®é¡ïŒãšRoslyn APIã®ååãªç¥èãå¿ èŠã§ãã ããããå¿ èŠãªãã®ããã¹ãŠæã£ãåŸãRoslynã䜿çšããããã°ã©ãã³ã°ã¯æ¥œãããã®ã«ãªããŸãïŒç¹ã«ã¢ãŒããã¯ãã£ã®ç¹ã§ãç§ã¯çŸããã³ãŒããšæ©èœã®å€§ãã¡ã³ã§ãïŒã
ãšãã£ã¿ãŒã®æ©èœãšãã¬ãŒããªãã å®çšçãªäŸã
ãããŸã§ããšãã£ã¿ãŒãšãã®æ©èœã«ã€ããŠã¯ã»ãšãã©èšåãããŠããŸããã§ããã ç¶æ³ãä¿®æ£ããŸãããïŒæåŸã«ãæ£ç¢ºã«ç·šéã§ããå 容ãæ©èœã®è±å¯ããšè€éãã®ãã©ã³ã¹ããšãããã«ã©ã®ãããªåŠ¥åãå¿ èŠããè°è«ããå®éã®å®è·µããã®äŸãæ€èšããŸãïŒå€å°ç°¡ç¥åãããŠããŸããïŒã
ããã§ãã³ãŒããç·šéããããšã«ããŸããã èšèé£ãã¯éåžžã«æãããšãèªããŠããŸãã ããã§ã¯ã人ã ã¯ãã€ãæ¬æ¥ã®ããã«æžããšã¯éããŸããããä»ã®ã³ãŒããåæããŠå€æŽ ããããã°ã©ã ãæžãå¿ èŠããããŸãã ãããŠãããã¯å¿ ãããç§ãã¡ã«ãã£ãŠæžãããŠããããã§ã¯ãããŸããã å¶éãªãã§ã¯ã§ããŸããã
ç§ãæåã«èª¬æãããã®ã¯ã解éæ¹æ³ãããããªãã³ãŒããèŠã€ããå Žåã®ãšãã£ã¿ãŒã®åäœã§ãïŒããã°ã©ããŒã®å éšã®åé¡ããããã³ã°ããããžã§ã¯ãã«å ¥ããã©ãããæ£ç¢ºã«èšãããšãã§ããªãæ°æ©èœãªã©ã ã ãã®ç¶æ³ã¯éåžžã«æ éã«ã解決ãããå¿ èŠããããŸãã ããããªããšãããŒã«ã¯è¯ãããšããã害ã«ãªããŸãã
ãã®åé¡ãã©ã®ããã«è§£æ±ºããŸãããïŒ ããªããç解ããŠããªãããšã§éã°ãªãã§ãã ããã ç解ãããšããããšã¯ãã³ãŒãã®è«ççãªæå³ã«é¢ããããã°ã©ã ã®ç¥èãæå³ããŸãã æœè±¡çãããïŒ ä»¥äžã«äŸãããã€ã瀺ããŸãã
æåã®äŸã ã¯ãšã¹ãã¯ã次ã®ã¬ãã«ã ãã§ãªãã3ã€ã®ã¬ãã«ãå®äºããåŸã«ãã¢ã¯ãã£ãã«ããå¿ èŠããããŸãã ãŸãã¯5ã ãããŠããŠãŒã¶ãŒãæ¯æ¥ã®å ±é ¬ãäžããããåŸã§ãã äžè¬çã«ãããã€ãã®è¿œå æ¡ä»¶ããããŸãã äžè¬çãªã±ãŒã¹ã§ã¯ããšãã£ã¿ãŒãããã®ãããªæ¡ä»¶ãæå®ããããšã¯ã§ããŸãããããã°ã©ããŒã®ä»å ¥ãå¿ èŠã§ãã ããŠãããã°ã©ããŒã¯ã€ãã«èªåèªèº«ã解æŸããã³ãŒãã«æ°è¡ãè¿œå ããŸããã 次ã¯ïŒ ãŸãããšãã£ã¿ãŒã¯ãããå³ã«è¡šç€ºããŸãã
![ç»å](https://habrastorage.org/webt/-q/31/je/-q31jegqlkhfrex0dy76hqqv-vw.png)
ãŠãŒã¶ãŒã¯ããã®ã¯ãšã¹ãã§ã¯ããã»ã©ç°¡åã§ã¯ãªãããšãããã«ããããŸãã 第äºã«ãæªç¥ã®ïŒãšãã£ã¿ãŒã«ãã£ãŠãœãŒããããŠããªãïŒæ¡ä»¶ãç·šéããããšã¯çŠæ¢ãããŠããŸãã ã§ããããšã®æ倧ã¯ããœãŒã¹ã³ãŒããšããã«æ®ãããã³ã¡ã³ããèªãããšã§ãïŒäŸ¿å©ãªæ©èœ-ããã°ã©ããŒãã³ãŒãã®æå³ãæªçµéšè ã«èª¬æã§ããããã«ããŸãïŒã 第äžã«ã æ°ããæ¡ä»¶ã®è¿œå ã®ã¿ãèš±å¯ãããŸãïŒãããŠãã¡ããããããã®ç·šéãšåé€ïŒã ããªãã¡ æ¡ä»¶ãããå³ãããªãããšãã£ã¿ãŒãèš±å¯ããç¯å²å ã«ãªãå¯èœæ§ããããŸãã
2çªç®ã®äŸã ã¯ãšã¹ãã®ããžãã¯ïŒã¢ã¯ã·ã§ã³ïŒã®åæ§ç¯ãããŠããªãã³ãŒãã ããããæãäžè¬çãªç¶æ³ã ããã§ã®å²åŠã¯åãã§ãïŒç·šéãèš±å¯ãããéæ¿ã«ç®ç«ããªãèŠåã衚瀺ããŸã ïŒäžè¬ã«ãã¢ã»ã³ãã«ãããŠããªãã³ãŒããé衚瀺/衚瀺ãããã§ãã¯ããŒã¯ããããŸãïŒã
2çªç®ã«è°è«ããããšã¯ãå®è¡ã¹ã¬ããã®ç·šéã§ãã ããšãã°ãã¯ãšã¹ãã®1ã€ã«ã¯ããžãã¯ã®äžéšããããããèªäœã¯ãªãã·ã§ã³ã§ãããç¹å®ã®äžå¯åãªæ¡ä»¶ã«ãã£ãŠã®ã¿èµ·åãããŸãã ç·šéãå®å šã«çŠæ¢ããã®ã¯æããªããšã§ãã ãããã£ãŠããã®å¯èœæ§ããŠãŒã¶ãŒã«ä»»ããããšã決å®ãããŸããããããã€ãã®æ³šæäºé ããããŸãïŒéå§æ¡ä»¶ïŒifãwhileã®æ¡ä»¶ïŒã®ç·šéã¯èš±å¯ããããäžè¬çãªã±ãŒã¹ã§ã¯ãããã¯å šäœã移åããŸãïŒã¯ããèŠãçå®-移åã¯ã³ã³ãã€ã«ãäžæãããããã«æªãããšã«ãããžãã¯ãå€æŽããããšã¯æããã§ã¯ãããŸããïŒã ããžãã¯ã移åããå¿ èŠããããŸããïŒ 2ã€ã®ãªãã·ã§ã³ïŒãŠãããããªãã«ããŠé©åãªå Žæã«æ°ãããŠããããäœæããããããã°ã©ãã«ã¿ã¹ã¯ãäžããŸãã
次ã«ãç°¡åãªäŸã瀺ããŠèª¬æããŸãã ã²ãŒã ãã¶ã€ããŒã¯ãFindKeyã¯ãšã¹ããšOpenTheDoorã¯ãšã¹ãã®éã«TalkToLeeroyã¯ãšã¹ããæ¿å ¥ããŸãã ã¯ãšã¹ãã§ã¯ãã«ããã·ãŒã³ã衚瀺ããã¯ãšã¹ãã®çŽæ¥ééãåŸ ã£ãŠããã2çªç®ã®ã«ããã·ãŒã³ã衚瀺ããŠãã¯ãšã¹ããå®äºããŸãã ã²ãŒã ãã¶ã€ããŒã®ã¢ã¯ã·ã§ã³ã®ã¢ã«ãŽãªãºã ïŒåœŒã«ä»£ãã£ãŠïŒïŒ
- æ°ããã¯ãšã¹ããæ¿å
¥ããå¿
èŠãããã¯ãšã¹ããèŠã€ããŸãïŒ
- ç®çã®ååã§æ°ããã¯ãšã¹ããè¿œå ããæ¥ç¶ãã¹ããŒããŸãã
- ã¯ãšã¹ãç·šéãŠã£ã³ããŠãéããå¿
èŠãªç¶æ
ãè¿œå ããŸãã
- 次ã«ãåç¶æ
ã«å¿
èŠãªã¢ã¯ã·ã§ã³ãå
¥åããŸãã ããšãã°ãåæç¶æ
ã®ã¿ã¹ã¯ã¯ãæåã®ã«ããã·ãŒã³ã衚瀺ããããšã§ãã
- é¡æšã«ãããæ®ãã®ç¶æ ãæºããããŸãã
- ãšã©ãŒãçªç¶æ€åºãããå ŽåïŒããšãã°ãã€ã³ã¿ãŒãã§ã€ã¹ãé ããŠè¡šç€ºããã®ãå¿ããå ŽåïŒãããã«ã€ããŠèŠèŠçã«èŠåãããŸãïŒå³ã«ç¹å¥ãªããŒã¯ã衚瀺ãããŸãïŒã ãšã©ãŒãä¿®æ£ããŸãã
- ã²ãŒã ãéå§ãããã¹ãŠãæå³ãããšããã«æ©èœããããšã確èªããŸãã
ã ãããã¿ã¹ã¯ã¯å®äºããããããããªãã¶ã€ããŒã¯éãã«ãã¹ãã®ããã«åœŒå¥³ãéãããè¶ã飲ãããã«å»ããŸãã
ãšãã£ã¿ãŒã®ç®çã¯ãã³ã³ãã³ãã®ãããã¿ã€ãããã°ããäœæããŠå ¥åããããšã§ãããæçµçã«ã¯ãã²ãŒã ã®ããžãã¯ïŒä»®æ³äžçã®å°ããªçãšç¥ïŒã«å¯Ÿãã絶察çãªåãæã€ã®ã¯ããã°ã©ãã ãã§ããããŒã«ãå€§å¹ ã«è€éåããã®ã§ã¯ãªããã²ãŒã ã®æ©èœãæŽç·Ž/æ¹è¯ããã¿ã¹ã¯ã圌ã«äžããæ¹ãå®äŸ¡ã§ãïŒãããã£ãŠãããé©åã§ãïŒããããŠãããã¯ãã®ãããªç¶æ³ã«ããã»ã©è¿ããããŸããïŒ
![ç»å](https://habrastorage.org/webt/f8/fe/nj/f8fenjgxemkxpbtyplzkujqbzxy.png)
ãããã«
ç§ã¯ãã¹ãŠãšã¯ã»ã©é ãããšãè¿°ã¹ãŠããŸãããããã€ãã®ç¹ïŒãšã©ãŒã®åæãšèŠèŠåãã»ã¯ã¿ãŒã®æäœãªã©ïŒãçç¥ããªããã°ãªããŸããã§ãããèå³ãããã°ãã³ã¡ã³ããæžããŠãããããå¥ã®èšäºãæžãã§ããããããã®ç®çã¯ãã²ãŒã éçºã«ããŸãé¢ä¿ããªããšæããããã®ãããªããŒã«ã§ãããå®éã«ããŸãé©çšã§ããããšã瀺ãããšã§ããã
æçµçã«è¡ããããã®ïŒ
- æã«ã¯ä»äºãå é
- ã²ãŒã ã®æ§é ãšã¢ãŒããã¯ãã£ã®æ¹å
- èè ã¯Roslynãç 究ããCïŒã®ç¥èãæ·±ããèšèšã¹ãã«ãåäžãããŸããã
RoslynãåŠç¿ãšäœ¿çšã«æšå¥šããŸããïŒ ãã¡ããã èŸæ±åŒ·ãè¡ããŸãããã