银河

SKYIV STUDIO

  博客园 :: 首页 ::  ::  :: 订阅 订阅 :: 管理 ::
  105 随笔 :: 2 文章 :: 753 评论 :: 22 引用
    这是“使用 C# 开发智能手机软件:推箱子”系列文章的第十一篇。在这篇文章中,介绍 Common/Env.cs 源程序文件。这个源程序文件中包含表示“工作环境”的密封类 Env 。也就是说,主程序中重要的变量都封装在这个类中,作为整个程序的“工作环境”。她还起着桥梁作用,其中两个字段:

    DataFile db;       // 数据文件
    ConfigFile cfg;    // 配置文件

    正是我们以前介绍过的管理数据文件的密封类 DataFile 和管理配置文件的密封类 ConfigFile 的实例。密封类 Env 中的不少属性和方法是通过这两个字段调用其各自的属性和方法。
    下面对密封类 Env 中的一些方法作点说明:
    GetClientSize 方法用来计算当使用标准箱子尺寸时主窗体客户区的尺寸。该方法仅当程序运行在计算机上时才会被调用,使主窗体的尺寸根据当前关的尺寸自动调整。程序运行在智能手机时是不会被调用的,因为在智能手机上本程序并不改变主窗体的尺寸。
    SetBoxInfo 方法的作用是根据客户区尺寸计算箱子的尺寸,并相应设定要显示的图形单元。图形单元共有 24x24、20x20、16x16 和 12x12 四种尺寸,如下所示:





  



    如果使用 12x12 的图形单元还不能在客户区完整显示地图的话,可能这一关的游戏就无法玩了。
    Draw 方法用来更新主窗体客户区,也就是在主窗体客户区画出本关的地图,并根据玩家的动作随时更新地图。
    Design 方法实现在设计模式下,当鼠标点击时要采取的动作。
    StepIt 方法实现工人往指定方向前进一步(可能推着箱子)。
    Back 方法实现工人后退一步(可能连带箱子一起后退)。
    GetMoveInfo 方法寻找一条将工人移动到鼠标点击的位置的路线。她调用我们以前介绍过的静态类 FindPath 的 Seek 方法来寻找最短路线。
    GetPushInfo 方法给出将箱子推动到鼠标点击的位置所需的信息。
    到此为止,Common 目录下所有源程序文件都介绍完了,这些源程序文件中包含的类是实现整个程序功能的基础。在随后的文章中将介绍 Windows 目录下的源程序文件,她们包含的类实现整个程序的用户界面。
    下面就是密封类 Env 的源程序代码,虽然稍微长了一点,但是里面的注释比较详细,应该不难理解。
  1using System;
  2using System.Drawing;
  3using System.Collections.Generic;
  4
  5namespace Skyiv.Ben.PushBox.Common
  6{
  7  /// <summary>
  8  /// 工作环境
  9  /// </summary>

 10  sealed class Env : IDisposable
 11  {
 12    DataFile db;       // 数据文件
 13    ConfigFile cfg;    // 配置文件
 14    string errorMsg;   // 错误信息
 15    string debugMsg;   // 调试信息
 16    bool isReplay;     // 是否正在回放
 17    Action active;     // 模式: 正常 新建 编辑 删除
 18    byte pen;          // 设计时的笔
 19    Bitmap img;        // 图形单元, 横向被均匀分为八份
 20    Stack<Step> stack; // 历史路线, 用于后退功能
 21    Size clientSize;   // 工作区域尺寸(以像素为单位)
 22    Size boxSize;      // 图形元素尺寸(以像素为单位)
 23    Point toPixel;     // 将要到达的位置(以像素为单位)
 24    Point worker;      // 当前工人位置(以单元格为单位)
 25    int pushSteps;     // 推动着箱子走的步数
 26    int levelOem;      // 原来的关数,仅用于“菜单 -> 数据 -> 设计 -> 新建”放弃后恢复现场
 27
 28    public string ErrorMsg get return errorMsg; } }
 29    public string DebugMsg get return debugMsg; } }
 30    public string[] Groups get return cfg.Groups; } }
 31    public int Group get return cfg.Group; } set { cfg.Group = value; } }
 32    public int Level get return cfg.Levels[Group]; } set { cfg.Levels[Group] = value; } }
 33    public int LeveLOem get return levelOem; } }
 34    public int MaxLevel get return db.MaxLevel; } }
 35    public string Steps get return cfg.Steps; } }
 36    public Size LevelSize get return db.LevelSize; } }
 37    public Size ClientSize set { clientSize = value; } }
 38    public Point ToPixel set { toPixel = value; } }
 39    public int MaxLevelSize get return cfg.MaxLevelSize; } set { cfg.MaxLevelSize = value; } }
 40    public int StepDelay get return cfg.StepDelay; } set { cfg.StepDelay = value; } }
 41    public int ReplayDelay get return cfg.ReplayDelay; } set { cfg.ReplayDelay = value; } }
 42    public Action Active get return active; } set { active = value; } }
 43    public byte Pen get return pen; } set { pen = value; } }
 44    public bool HasError get return !string.IsNullOrEmpty(errorMsg); } }
 45    public bool HasWorker get return db.HasWorker; } }
 46    public bool CanUndo get return stack.Count != 0; } }
 47    public bool CanReplay get return db.IsFinished && !CanUndo; } }
 48    public bool IsSave get return cfg.IsSave; } set { cfg.IsSave = value; } }
 49    public bool IsFinish get return db.Tasks == db.Boths; } }
 50    public bool IsReplay get return isReplay; } set { isReplay = value; } }
 51    public bool IsDesign get return active != Action.None; } }
 52
 53    public Env()
 54    {
 55      stack = new Stack<Step>();
 56      cfg = new ConfigFile();
 57      db = new DataFile();
 58      Init();
 59    }

 60
 61    /// <summary>
 62    /// 状态栏信息
 63    /// </summary>

 64    public string StatusMessage
 65    {
 66      get
 67      {
 68        return HasError ? "请点击“菜单 -> 帮助 -> 错误信息”" : string.Format(
 69          "{0} {1}/{2} {3} {4} {5} [{6}] {7}",
 70          (active == Action.Create) ? '+' : (active == Action.Edit) ? '=' : isReplay ? "|/-\\"[stack.Count % 4] : '>',
 71          Level + 1, MaxLevel, Pub.ToString(LevelSize),
 72          IsDesign ? string.Format("{0}={1}", db.Boxs, db.Slots) : string.Format("{0}/{1}", db.Boths, db.Tasks),
 73          IsDesign ? Block.GetPenName(pen) : string.Format("{0}({1})", stack.Count, pushSteps),
 74          IsDesign ? (active == Action.Create ? "新建" : "编辑") : db.IsFinished ? 
 75          string.Format("{0}({1})", db.MovedSteps, db.PushedSteps) : string.Empty,
 76          db.GroupName);
 77      }

 78    }

 79
 80    public void Dispose()
 81    {
 82      db.Dispose();
 83    }

 84
 85    public void Init()
 86    {
 87      active = Action.None;
 88      pen = Block.Land;
 89      stack.Clear();
 90      SetExceptionMessage(null);
 91    }

 92
 93    void SetExceptionMessage(Exception ex)
 94    {
 95      errorMsg = Pub.GetMessage(ex, false);
 96      debugMsg = Pub.GetMessage(ex, true);
 97    }

 98
 99    /// <summary>
100    /// 计算当使用标准箱子尺寸时主窗体客户区的尺寸
101    /// </summary>
102    /// <param name="statusBarHeight">状态条的高度</param>
103    /// <returns>客户区的尺寸</returns>

104    public Size GetClientSize(int statusBarHeight)
105    {
106      int width = (Properties.Resources.PushBox24.Width / 8* LevelSize.Width;
107      int height = Properties.Resources.PushBox24.Height * LevelSize.Height + statusBarHeight;
108      if (width < 240) width = 240;
109      if (height < 48) height = 48;
110      if (width > 1008) width = 1008;
111      if (height > 672) height = 672;
112      return new Size(width, height);
113    }

114
115    /// <summary>
116    /// 根据客户区尺寸,计算箱子的尺寸,并相应设定要显示的图形单元
117    /// </summary>

118    public void SetBoxInfo()
119    {
120      if (HasError) return;
121      if (LevelSize.IsEmpty) return;
122      int rX = clientSize.Width / LevelSize.Width;
123      int rY = clientSize.Height / LevelSize.Height;
124      int r = Math.Min(rX, rY);
125      if (r >= 24) img = Properties.Resources.PushBox24;
126      else if (r >= 20) img = Properties.Resources.PushBox20;
127      else if (r >= 16) img = Properties.Resources.PushBox16;
128      else img = Properties.Resources.PushBox12;
129      boxSize = new Size(img.Height, img.Width / 8);
130    }

131
132    /// <summary>
133    /// 装入配置文件
134    /// </summary>

135    public void LoadConfig()
136    {
137      if (HasError) return;
138      try
139      {
140        cfg.LoadConfig();
141      }

142      catch (Exception ex)
143      {
144        SetExceptionMessage(ex);
145      }

146    }

147
148    /// <summary>
149    /// 保存组信息到配置文件
150    /// </summary>
151    /// <param name="groups">组信息</param>

152    public void SaveConfig(string[] groups)
153