Silverlight數學引擎(15)——數據存取


這幾天年終績效考核,瑣事比較多所以更新得沒有以前快了,絕對不是因為網上說的什么微軟關閉Silverlight網站的影響啦,就算微軟說現在馬上拋棄SL,我也要把這個游戲寫完,對於那些一邊玩着Windows系統,一邊大罵.net framework是微軟的骷髏的人我一般也沒有什么可說的。

說到游戲,數據存取這個功能是必須的,不管是游戲設置還是狀態什么的,都是可以存成數據文件,然后可以從文件加載。本節就來實現將我們畫的圖形存成Xml文件,然后可以從Xml文件中加載圖形,也只有這樣,我們的過關游戲才能實現。

要實現數據保存就要現有文件的內容格式,對於我們的坐標系上各個圖形元素而言,就是要把他們按照依賴順序寫到Xml中,這樣才能保存加載的時候才能不出意外,因此需要做的事情有以下幾點:

  1.一個坐標系中的每個圖形都必須有唯一的ID或者Name

 2.必須按照圖形被添加到坐標系的順序保存。

 3.必須記錄每個圖形的上層依賴(比如線AB依賴於點A和點B)

 4.圖形的屬性也需要保存(例如交點索引Index以及樣式等)

 綜合以上,我們可以給出一個Xml文件格式:

<?xml version="1.0" encoding="utf-16"?>
<CS>
    <Shapes>
        <FreePoint Name="A" Style="00,11,1.00,1.00,False" P="1.00,2.00"/>
        <FreePoint Name="B" Style="00,11,1.00,1.00,False" P="-2.00,3.00"/>
        <FreePoint Name="C" Style="00,11,1.00,1.00,False" P="0.00,0.00"/>
        <LineShape Name="AB" Style="08,00,4.00,1.00,False" D="A,B"/>
        <LineShape Name="BC" Style="08,00,4.00,1.00,False" D="B,C"/>
        <LineShape Name="CA" Style="08,00,4.00,1.00,False" D="C,A"/>
        <CircleShape Name="OA" Style="00,02,0.50,1.00,True" D="A,A,B"/>
        <CircleShape Name="OB" Style="00,02,0.50,1.00,True" D="B,A,B"/>
        <IntersectionPointOfCircles Name="D" Style="00,08,0.50,1.00,True" D="OA,OB" I="1"/>
        <IntersectionPointOfCircles Name="E" Style="00,08,0.50,1.00,True" D="OA,OB" I="2"/>
        <LineShape Name="DE" Style="08,00,0.50,1.00,True" D="D,E"/>
        <CircleShape Name="OB1" Style="00,02,0.50,1.00,True" D="B,B,C"/>
        <CircleShape Name="OC" Style="00,02,0.50,1.00,True" D="C,B,C"/>
        <IntersectionPointOfCircles Name="F" Style="00,08,0.50,1.00,True" D="OB1,OC" I="1"/>
        <IntersectionPointOfCircles Name="G" Style="00,08,0.50,1.00,True" D="OB1,OC" I="2"/>
        <LineShape Name="FG" Style="08,00,0.50,1.00,True" D="F,G"/>
        <IntersectionPointOfLines Name="H" Style="00,08,1.00,1.00,False" D="DE,FG"/>
        <LineShape Name="HA" Style="08,00,0.50,1.00,True" D="H,A"/>
        <CircleShape Name="OH" Style="00,02,2.00,1.00,False" D="H,H,A"/>
    </Shapes>
</CS>

Xml的好處是有目共睹的,看到上面的Xml格式,你是否對數據保存的功能實現胸有成竹了呢?當然,你可以已經發現了還有些細節工作需要做的:

第一,我們要格式化樣式:

        public static string GetStyleString(Shape shape)
        {
            var result = string.Format("{0},{1},{2},{3},{4}",
                                       BrushPicker.GetBrushId(shape.Stroke as SolidColorBrush).ToString("00"),
                                       BrushPicker.GetBrushId(shape.Fill as SolidColorBrush).ToString("00"),
                                       shape.StrokeThickness.ToString("0.00"),
                                       shape.Opacity.ToString("0.00"),
                                       shape.StrokeDashArray.Count > 0);
            return result;
        }

第二我們要添加圖形的上層依賴列表(Parents):

        public List<ICoordinate> Parents { get; set; }
        public void RegisterDependencies(params ICoordinate[] parents)
        {
            if (Parents == null)
                Parents = new List<ICoordinate>();
            foreach (var parent in parents)
            {
                Parents.Add(parent);
                if (parent.Children == null)
                    parent.Children = new List<ICoordinate>();
                parent.Children.Add(this);
            }
        }

第三我們要在ICoordinate中添加新的接口WriteXmlReadXml,這兩個方法的功能恰好相反,因此放在一起實現比較直觀,例如FreePoint中時這樣實現的這兩個方法的:

        public override void WriteXml(XmlWriter writer)
        {
            base.WriteXml(writer);
            writer.WriteAttributeString("P", Center.X.ToString("0.00") + "," + Center.Y.ToString("0.00"));
        }

        public override void ReadXml(XElement element)
        {
            base.ReadXml(element);
            var ps = element.ReadString("P").Split(',');
            Center.SetPosition(double.Parse(ps[0]), double.Parse(ps[1]));
        }

OK,這些工作完成后,數據保存就十分簡單了,如果你對XML文件比較熟悉的話:

        private static void Write(CoordinateSystem cs, XmlWriter writer)
        {
            if (cs.Shapes.Count == 0)
                return;

            writer.WriteStartElement("CS");
            writer.WriteStartElement("Shapes");
            foreach (var shape in cs.Shapes)
            {
                writer.WriteStartElement(shape.GetType().Name);
                writer.WriteAttributeString("Name", shape.Name);
                writer.WriteAttributeString("Style", ShapeStyleEditor.GetStyleString(shape.Shape));
                if (shape.Parents != null)
                    writer.WriteAttributeString("D", shape.Parents.Select(d => d.Name).ToListString(","));
                shape.WriteXml(writer);
                writer.WriteEndElement();
            }
            writer.WriteEndElement(); //Shapes
            writer.WriteEndElement(); //CS
        }

數據保存成XML看來比較簡單,反過來解析就不那么容易了,在此我們需要用到反射機制,而反射的實例創建是調用默認的無參構造函數創建的,如:

var shape = (ICoordinate) Activator.CreateInstance(typeof(FreePoint));

但是我們定義的所有圖形都是嚴格按照依賴創建的,也就是說沒有無參的構造函數,如果直接使用我們現有的方法把Xml解析成Shapes,只能使用無窮的if else if else了,這無疑是非常不雅的,為了實現高度自治,我們還是為每個Shape類實現無參的構造函數吧,有點麻煩,但是長痛不如短痛。

首先為了能夠在構造函數中使用Xml節點等一些信息,我們需要定義一個靜態的類ShapeSerializer

    public static class ShapeSerializer
    {
        private static CoordinateSystem CS { get; set; }
        private static XElement XmlNode { get; set; }
        public static int Index { get { return (int)XmlNode.ReadDouble("I"); } }
        public static double Ratio { get { return XmlNode.ReadDouble("R"); } }
        public static string Style { get { return XmlNode.ReadString("Style"); } }
        public static List<ICoordinate> Parents { get; private set; }
}

這樣就能方便的實現無參構造函數了,拿LineShape舉例吧:

    public sealed class LineShape : CoordinateBase
    {
        public LineShape()
            : this(ShapeSerializer.Parents[0] as PointShape, ShapeSerializer.Parents[1] as PointShape) { }

        public LineShape(PointShape p1, PointShape p2)
        {
            Line = new LogicalLine(p1.Center, p2.Center);
            P1 = p1;
            P2 = p2;
            RegisterDependencies(p1, p2);
        }
}

我們把完整的解析代碼頁放在ShapeSerializer類中,如下:

        public static void Parse(string xml, CoordinateSystem cs)
        {
            try
            {
                CS = cs;
                CS.Children.Clear();
                var element = XElement.Parse(xml);
                var figuresNode = element.Element("Shapes");
                var types = ReflectorUtils.DiscoverTypes<CoordinateBase>();
                if (figuresNode != null)
                {
                    foreach (var figureNode in figuresNode.Elements())
                    {
                        var type = types.FirstOrDefault(t => t.Name == figureNode.Name);
                        if (type == null)
                            throw new Exception("Type not found:" + figureNode.Name);
                        XmlNode = figureNode;
                        Parents = GetParents();
                        var shape = (ICoordinate) Activator.CreateInstance(type);
                        shape.Name = figureNode.ReadString("Name");
                        shape.CS = CS;
                        shape.ReadXml(XmlNode);
                        ShapeStyleEditor.ApplyStyle(shape.Shape, Style);
                    }
                }
                cs.Shapes.OfType<IMovable>().ToList().ForEach(m => m.Move(((ICoordinate) m).Center));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

這樣,整個保存和解析的功能就完成了,如何測試呢?對了,我們不是有了右鍵菜單嗎,我們再加兩個菜單選項【復制】和【粘貼】,分別實現將內容保存到剪貼板和從剪貼板加載圖形,這樣就能很好地測試了,而且還省去了打開和保存文件那么麻煩。

運行效果如圖:

【源代碼和演示地址】

 http://www.diyuexi.com/webpages/query/ShareRes.aspx

 大功告成,算是完成了里程碑的一頁!前面講到,我的一個目的就是做一個幾何作圖的過關游戲,根據過關的等級顯示不同的技能,比如初始技能只有【筆】、【尺】、【規】,當你過了用這些初始竟能畫平行線這一關,就會多一個技能【平行線】,所以以后就可以更方便地去畫平行線了,下節我們就來看看如何實現【平行線】這類組合技能的實現!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM