自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析
我在做这样一个坦克游戏,是仿照(http://game.kid.qq.com/a/20140221/028931.htm)这个游戏制作的。仅为学习Unity之用。图片大部分是自己画的,少数是从网上搜来的。您可以到我的github页面(https://github.com/bitzhuwei/TankHero-2D)上得到工程源码。
本篇主要记录关卡解析器、小地图图标和对碰撞的原理的探索,需要耐心分析。
在一个关卡里,敌方坦克应该是一波一波地出现,每波敌人出现多少个,每个敌人是什么类型的坦克、出现在什么位置都应该是可配置的。这需要一个关卡解析器,把如下的文字解析为一个数据结构 Level 。
1 level 2 { 3 tank{0 0} | 4 tank{0 1} | 5 tank{0 2} | 6 tank{0 0} tank{0 1} tank{0 2} | 7 tank{0 0} tank{0 1} tank{0 2} tank{0 0} tank{0 1} tank{0 2} | 8 tank{0 0} tank{0 1} tank{0 2} tank{0 0} tank{0 1} tank{0 2} | 9 tank{0 0} tank{0 1} tank{0 2} tank{0 0} tank{0 1} tank{0 2} tank{0 0} tank{0 1} tank{0 2} | 10 tank{0 0} tank{0 1} tank{0 2} tank{0 0} tank{0 1} tank{0 2} tank{0 0} tank{0 1} tank{0 2} tank{0 0} tank{0 1} tank{0 2} 11 }
这段文字的意思是,第一波敌人是类型编号为0,出生位置编号为0的1个坦克;第二波敌人是类型编号为0,出生位置编号为1的1个坦克;。。。
这种东西我喜欢用编译原理解决,因为我在(https://github.com/bitzhuwei/CGCompiler.git)有一个自己写的自动生成词法、语法分析器的工具。关于这个工具的介绍可参考我博客里关于编译原理的文章(在这里搜索"编译器")。
先总结一下关卡的文法
1 <Level> ::= "level" "{" <StepList> "}"; 2 <StepList> ::= <Step> <StepList> | null; 3 <Step> ::= "step" "{" <TankList> "}"; 4 <TankList> ::= <Tank> <TankList> | null; 5 <Tank> ::= "tank" "{" <TankPrefab> <BornPoint> "}"; 6 <TankPrefab> ::= number; 7 <BornPoint> ::= number;
然后用工具生成词法语法解析器代码。
剩下的就是自己写一下从语法树到数据结构的转换。代码如下。
1 public static Level GetValue(this SyntaxTree<EnumTokenTypeLevelCompiler, 2 EnumVTypeLevelCompiler, TreeNodeValueLevelCompiler> syntaxTree) 3 { 4 if (syntaxTree == null) { return null; } 5 6 var result = new Level(); 7 _GetLevel(result, syntaxTree); 8 return result; 9 } 10 11 private static void _GetLevel(Level result, SyntaxTree<EnumTokenTypeLevelCompiler, EnumVTypeLevelCompiler, TreeNodeValueLevelCompiler> syntaxTree) 12 { 13 if (syntaxTree.CandidateFunc == LL1SyntaxParserLevelCompiler.GetFuncParsecase_Level___tail_levelLeave()) 14 { 15 GetTankList(result, syntaxTree.Children[2]); 16 } 17 } 18 19 private static void GetTankList(Level level, SyntaxTree<EnumTokenTypeLevelCompiler, EnumVTypeLevelCompiler, TreeNodeValueLevelCompiler> syntaxTree) 20 { 21 if (syntaxTree.CandidateFunc == LL1SyntaxParserLevelCompiler.GetFuncParsecase_TankList___tail_tankLeave() 22 || syntaxTree.CandidateFunc == LL1SyntaxParserLevelCompiler.GetFuncParsecase_TankList___tail_or_Leave()) 23 { 24 var egg = GetTank(syntaxTree.Children[0]); 25 level.Add(egg); 26 GetTankList(level, syntaxTree.Children[1]); 27 } 28 else if (syntaxTree.CandidateFunc == LL1SyntaxParserLevelCompiler.GetFuncParsecase_TankList___tail_rightBrace_Leave()) 29 { 30 //nothing to do 31 } 32 33 } 34 35 private static TankEgg GetTank(SyntaxTree<EnumTokenTypeLevelCompiler, EnumVTypeLevelCompiler, TreeNodeValueLevelCompiler> syntaxTree) 36 { 37 if (syntaxTree.CandidateFunc == LL1SyntaxParserLevelCompiler.GetFuncParsecase_Tank___tail_tankLeave()) 38 { 39 var tankPrefab = GetTankPrefab(syntaxTree.Children[2]); 40 var bornPoint = GetBornPoint(syntaxTree.Children[3]); 41 var result = new TankEgg(tankPrefab, bornPoint); 42 return result; 43 } 44 else if (syntaxTree.CandidateFunc == LL1SyntaxParserLevelCompiler.GetFuncParsecase_Tank___tail_or_Leave()) 45 { 46 var result = new TankEgg(-1, -1); 47 return result; 48 } 49 50 return null; 51 } 52 53 private static int GetBornPoint(SyntaxTree<EnumTokenTypeLevelCompiler, EnumVTypeLevelCompiler, TreeNodeValueLevelCompiler> syntaxTree) 54 { 55 if (syntaxTree.CandidateFunc == LL1SyntaxParserLevelCompiler.GetFuncParsecase_BornPoint___numberLeave()) 56 { 57 var result = int.Parse(syntaxTree.Children[0].NodeValue.NodeName); 58 return result; 59 } 60 61 return 0; 62 } 63 64 private static int GetTankPrefab(SyntaxTree<EnumTokenTypeLevelCompiler, EnumVTypeLevelCompiler, TreeNodeValueLevelCompiler> syntaxTree) 65 { 66 if (syntaxTree.CandidateFunc == LL1SyntaxParserLevelCompiler.GetFuncParsecase_TankPrefab___numberLeave()) 67 { 68 var result = int.Parse(syntaxTree.Children[0].NodeValue.NodeName); 69 return result; 70 } 71 72 return 0; 73 }
在VS2013里你可以获得Tip,便于coding。