自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析

我在做这样一个坦克游戏,是仿照(http://game.kid.qq.com/a/20140221/028931.htm)这个游戏制作的。仅为学习Unity之用。图片大部分是自己画的,少数是从网上搜来的。您可以到我的github页面(https://github.com/bitzhuwei/TankHero-2D)上得到工程源码。

自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析

本篇主要记录关卡解析器、小地图图标和对碰撞的原理的探索,需要耐心分析。

在一个关卡里,敌方坦克应该是一波一波地出现,每波敌人出现多少个,每个敌人是什么类型的坦克、出现在什么位置都应该是可配置的。这需要一个关卡解析器,把如下的文字解析为一个数据结构 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;

然后用工具生成词法语法解析器代码。

自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析

剩下的就是自己写一下从语法树到数据结构的转换。代码如下。

 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     }
GetLevel

在VS2013里你可以获得Tip,便于coding。

相关文章: