Xamarin版的C# SVG路径解析器

简介: 原文:Xamarin版的C# SVG路径解析器 Xamarin版的C# SVG路径解析器,对SVG的Path路径进行解析,其中包括:主程序SvgPathParser.cs, 相关接口定义:ISourceFormatter.cs, 辅助类:FormatterRocks.cs, 从接口派生的CSharpCoreGraphicsFormatter.cs。
原文: Xamarin版的C# SVG路径解析器

Xamarin版的C# SVG路径解析器,对SVG的Path路径进行解析,其中包括:

主程序SvgPathParser.cs,

相关接口定义:ISourceFormatter.cs,

辅助类:FormatterRocks.cs,

从接口派生的CSharpCoreGraphicsFormatter.cs。


//主程序SvgPathParser.cs:

// Authors:
//    Sebastien Pouliot  <sebastien@xamarin.com>
//
// Copyright 2012-2013 Xamarin Inc.
//
// This file is mostly based on the C++ code from once magnificent Moonlight
// https://github.com/mono/moon/blob/master/src/xaml.cpp
// Copyright 2007 Novell, Inc. (http://www.novell.com)
//
// Licensed under the GNU LGPL 2 license only (no "later versions")

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;

namespace Poupou.SvgPathConverter {

    public class SvgPathParser {

        static int i;

        public ISourceFormatter Formatter { get; set; }

        public void Parse (string svgPath, string name = null)
        {
            if (Formatter == null)
                throw new InvalidOperationException ("Missing formatter");

            if (name == null)
                name = "Unnamed_" + (++i).ToString ();

            Parse (svgPath, name, Formatter);
        }

        static void Advance (string s, ref int pos)
        {
            if (pos >= s.Length)
                return;
            char c = s [pos];
            while (!Char.IsLetterOrDigit (c) && c != '.' && c!= '-' && c != '+') {
                if (++pos == s.Length)
                    return;
                c = s [pos];
            }
        }
        
        static int FindNonFloat (string s, int pos)
        {
            char c = s [pos];
            while ((Char.IsNumber (c) || c == '.' || c == '-' || c == '+')) {
                if (++pos == s.Length)
                    return pos;
                c = s [pos];
            }
            return pos;
        }
        
        static bool MorePointsAvailable (string s, int pos)
        {
            if (pos >= s.Length)
                return false;
            char c = s [pos];
            while (Char.IsWhiteSpace (c) || c == ',')
                c = s [++pos];
            return Char.IsDigit (c) || c == '.' || c == '-' || c == '+';
        }
        
        static float GetFloat (string svg, ref int pos)
        {
            int end = FindNonFloat (svg, pos);
            string s = svg.Substring (pos, end - pos);
            float f = Single.Parse (s, CultureInfo.InvariantCulture);
            pos = end;
            return f;
        }
        
        static PointF GetPoint (string svg, ref int pos)
        {
            while (Char.IsWhiteSpace (svg [pos]))
                pos++;
            float x = GetFloat (svg, ref pos);
            
            while (Char.IsWhiteSpace (svg [pos]))
                pos++;
            if (svg [pos] == ',')
                pos++;
            while (Char.IsWhiteSpace (svg [pos]))
                pos++;
            
            float y = GetFloat (svg, ref pos);
            
            return new PointF (x, y);
        }
        
        static PointF MakeRelative (PointF c, PointF m)
        {
            return new PointF (m.X + c.X, m.Y + c.Y);
        }

        static void Parse (string svg, string name, ISourceFormatter formatter)
        {
            formatter.Prologue (name);
            
            PointF start;
            PointF cp = new PointF (0, 0);
            PointF cp1, cp2, cp3;
            PointF qbzp, cbzp;
            int fill_rule = 0;
            int pos = 0;
            bool cbz = false;
            bool qbz = false;
            while (pos < svg.Length) {
                char c = svg [pos++];
                if (Char.IsWhiteSpace (c))
                    continue;
                
                bool relative = false;
                switch (c) {
                case 'f':
                case 'F':
                    c = svg [pos++];
                    if (c == '0')
                        fill_rule = 0;
                    else if (c == '1')
                        fill_rule = 1;
                    else
                        throw new FormatException ();
                    break;
                case 'h':
                    relative = true;
                    goto case 'H';
                case 'H':
                    float x = GetFloat (svg, ref pos);
                    if (relative)
                        x += cp.X;
                    cp = new PointF (x, cp.Y);
                    formatter.LineTo (cp);
                    cbz = qbz = false;
                    break;
                case 'm':
                    relative = true;
                    goto case 'M';
                case 'M':
                    cp1 = GetPoint (svg, ref pos);
                    if (relative)
                        cp1 = MakeRelative (cp, cp1);
                    formatter.MoveTo (cp1);
                    
                    start = cp = cp1;
                    
                    Advance (svg, ref pos);
                    while (MorePointsAvailable (svg, pos)) {
                        cp1 = GetPoint (svg, ref pos);
                        if (relative)
                            cp1 = MakeRelative (cp, cp1);
                        formatter.LineTo (cp1);
                    }
                    cp = cp1;
                    cbz = qbz = false;
                    break;
                case 'l':
                    relative = true;
                    goto case 'L';
                case 'L':
                    while (MorePointsAvailable (svg, pos)) {
                        cp1 = GetPoint (svg, ref pos);
                        if (relative)
                            cp1 = MakeRelative (cp, cp1);
                        Advance (svg, ref pos);
                        
                        formatter.LineTo (cp1);
                        cp = cp1;
                    }
                    cbz = qbz = false;
                    break;
                case 'a':
                    relative = true;
                    goto case 'A';
                case 'A':
                    while (MorePointsAvailable (svg, pos)) {
                        cp1 = GetPoint (svg, ref pos);
                        // this is a width and height so it's not made relative to cp
                        Advance (svg, ref pos);

                        float angle = GetFloat (svg, ref pos);
                        Advance (svg, ref pos);

                        bool is_large = GetFloat (svg, ref pos) != 0.0f;
                        Advance (svg, ref pos);

                        bool positive_sweep = GetFloat (svg, ref pos) != 0.0f;
                        Advance (svg, ref pos);

                        cp2 = GetPoint (svg, ref pos);
                        if (relative)
                            cp2 = MakeRelative (cp, cp2);
                        Advance (svg, ref pos);
                        
                        formatter.ArcTo (cp1, angle, is_large, positive_sweep, cp2, cp);
                        
                        cp = cp2;
                        Advance (svg, ref pos);
                    }
                    qbz = false;
                    cbz = false;
                    break;
                case 'q':
                    relative = true;
                    goto case 'Q';
                case 'Q':
                    while (MorePointsAvailable (svg, pos)) {
                        cp1 = GetPoint (svg, ref pos);
                        if (relative)
                            cp1 = MakeRelative (cp, cp1);
                        Advance (svg, ref pos);
                        
                        cp2 = GetPoint (svg, ref pos);
                        if (relative)
                            cp2 = MakeRelative (cp, cp2);
                        Advance (svg, ref pos);
                        
                        formatter.QuadCurveTo (cp1, cp2);
                        
                        cp = cp2;
                        Advance (svg, ref pos);
                    }
                    qbz = true;
                    qbzp = cp1;
                    cbz = false;
                    break;
                case 'c':
                    relative = true;
                    goto case 'C';
                case 'C':
                    while (MorePointsAvailable (svg, pos)) {
                        cp1 = GetPoint (svg, ref pos);
                        if (relative)
                            cp1 = MakeRelative (cp, cp1);
                        Advance (svg, ref pos);
                        
                        cp2 = GetPoint (svg, ref pos);
                        if (relative)
                            cp2 = MakeRelative (cp, cp2);
                        Advance (svg, ref pos);
                        
                        cp3 = GetPoint (svg, ref pos);
                        if (relative)
                            cp3 = MakeRelative (cp, cp3);
                        Advance (svg, ref pos);

                        formatter.CurveTo (cp1, cp2, cp3);
                        
                        cp1 = cp3;
                    }
                    cp = cp3;
                    cbz = true;
                    cbzp = cp2;
                    qbz = false;
                    break;
                case 't':
                    relative = true;
                    goto case 'T';
                case 'T':
                    while (MorePointsAvailable (svg, pos)) {
                        cp2 = GetPoint (svg, ref pos);
                        if (relative)
                            cp2 = MakeRelative (cp, cp2);
                        if (qbz) {
                            cp1.X = 2 * cp.X - qbzp.X;
                            cp1.Y = 2 * cp.Y - qbzp.Y;
                        } else {
                            cp1 = cp;
                        }
                        formatter.QuadCurveTo (cp1, cp2);
                        qbz = true;
                        qbzp = cp1;
                        cp = cp2;
                        Advance (svg, ref pos);
                    }
                    cbz = false;
                    break;
                case 's':
                    relative = true;
                    goto case 'S';
                case 'S':
                    while (MorePointsAvailable (svg, pos)) {
                        cp2 = GetPoint (svg, ref pos);
                        if (relative)
                            cp2 = MakeRelative (cp, cp2);
                        Advance (svg, ref pos);

                        cp3 = GetPoint (svg, ref pos);
                        if (relative)
                            cp3 = MakeRelative (cp, cp3);

                        if (cbz) {
                            cp1.X = 2 * cp.X - cbzp.X;
                            cp1.Y = 2 * cp.Y - cbzp.Y;
                        } else {
                            cp1 = cp;
                        }
                        formatter.CurveTo (cp1, cp2, cp3);
                        cbz = true;
                        cbzp = cp2;
                        cp = cp3;
                        Advance (svg, ref pos);
                    }
                    qbz = false;
                    break;
                case 'v':
                    relative = true;
                    goto case 'V';
                case 'V':
                    float y = GetFloat (svg, ref pos);
                    if (relative)
                        y += cp.Y;
                    cp = new PointF (cp.X, y);
                    formatter.LineTo (cp);
                    cbz = qbz = false;
                    break;
                case 'z':
                case 'Z':
                    formatter.ClosePath ();
                    formatter.MoveTo (start);
                    cp = start;
                    cbz = qbz = false;
                    break;
                default:
                    throw new FormatException (c.ToString ());
                }
            }
            formatter.Epilogue ();
        }
    }
}


//相关接口定义:ISourceFormatter.cs

// Authors:
//    Sebastien Pouliot  <sebastien@xamarin.com>
//
// Copyright 2012 Xamarin Inc.
//
// Licensed under the GNU LGPL 2 license only (no "later versions")

using System;
using System.Drawing;

namespace Poupou.SvgPathConverter {
    
    public interface ISourceFormatter {
    
        void Prologue (string name);
        void Epilogue ();
    
        void MoveTo (PointF pt);
        void LineTo (PointF pt);
        void QuadCurveTo (PointF pt1, PointF pt2);
        void CurveTo (PointF pt1, PointF pt2, PointF pt3);
        void ArcTo (PointF size, float angle, bool isLarge, bool sweep, PointF ep, PointF sp);
        void ClosePath ();
    }
}


//辅助类:FormatterRocks.cs

// Authors:
//    Sebastien Pouliot  <sebastien@xamarin.com>
//
// Copyright 2012 Xamarin Inc.
//
// This file is mostly based on the C++ code from once magnificent Moonlight
// https://github.com/mono/moon/blob/master/src/moon-path.cpp
// Copyright 2007-2008 Novell, Inc. (http://www.novell.com)
//
// Licensed under the GNU LGPL 2 license only (no "later versions")

using System;
using System.Drawing;
using System.IO;

namespace Poupou.SvgPathConverter {
    
    public static class FormatterRocks {

        static bool IsNearZero (float value)
        {
            return Math.Abs (value) < 0.000019;
        }

        // The SVG Arc is a bit more complex than others - and also quite different than many existing API
        // This implementation will use a ISourceFormatter's CurveTo method to draw the arc
        public static void ArcHelper (this ISourceFormatter formatter, PointF size, float anglef,
            bool isLarge, bool sweep, PointF endPoint, PointF startPoint)
        {
            if (IsNearZero (endPoint.X - startPoint.X) && IsNearZero (endPoint.Y - startPoint.Y))
                return;

            // Correction of out-of-range radii, see F6.6 (step 1)
            if (IsNearZero (size.X) || IsNearZero (size.Y)) {
                // treat this as a straight line (to end point)
                formatter.LineTo (endPoint);
                return;
            }

            // Correction of out-of-range radii, see F6.6.1 (step 2)
            float rx = Math.Abs (size.X);
            float ry = Math.Abs (size.Y);
            
            // convert angle into radians
            double angle = anglef * Math.PI / 180.0f;
            
            // variables required for F6.3.1
            double cos_phi = Math.Cos (angle);
            double sin_phi = Math.Sin (angle);
            double dx2 = (startPoint.X - endPoint.X) / 2.0;
            double dy2 = (startPoint.Y - endPoint.Y) / 2.0;
            double x1p = cos_phi * dx2 + sin_phi * dy2;
            double y1p = cos_phi * dy2 - sin_phi * dx2;
            double x1p2 = x1p * x1p;
            double y1p2 = y1p * y1p;
            float rx2 = rx * rx;
            float ry2 = ry * ry;
            
            // Correction of out-of-range radii, see F6.6.2 (step 4)
            double lambda = (x1p2 / rx2) + (y1p2 / ry2);
            if (lambda > 1.0) {
                // see F6.6.3
                float lambda_root = (float) Math.Sqrt (lambda);
                rx *= lambda_root;
                ry *= lambda_root;
                // update rx2 and ry2
                rx2 = rx * rx;
                ry2 = ry * ry;
            }
            
            double cxp, cyp, cx, cy;
            double c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
            
            // check if there is no possible solution (i.e. we can't do a square root of a negative value)
            if (c < 0.0) {
                // scale uniformly until we have a single solution (see F6.2) i.e. when c == 0.0
                float scale = (float) Math.Sqrt (1.0 - c / (rx2 * ry2));
                rx *= scale;
                ry *= scale;
                // update rx2 and ry2
                rx2 = rx * rx;
                ry2 = ry * ry;
                
                // step 2 (F6.5.2) - simplified since c == 0.0
                cxp = 0.0;
                cyp = 0.0;
                
                // step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
                cx = 0.0;
                cy = 0.0;
            } else {
                // complete c calculation
                c = Math.Sqrt (c / ((rx2 * y1p2) + (ry2 * x1p2)));
                
                // inverse sign if Fa == Fs
                if (isLarge == sweep)
                    c = -c;
                
                // step 2 (F6.5.2)
                cxp = c * ( rx * y1p / ry);
                cyp = c * (-ry * x1p / rx);
                
                // step 3 (F6.5.3 first part)
                cx = cos_phi * cxp - sin_phi * cyp;
                cy = sin_phi * cxp + cos_phi * cyp;
            }
            
            // step 3 (F6.5.3 second part) we now have the center point of the ellipse
            cx += (startPoint.X + endPoint.X) / 2.0;
            cy += (startPoint.Y + endPoint.Y) / 2.0;
            
            // step 4 (F6.5.4)
            // we dont' use arccos (as per w3c doc), see http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
            // note: atan2 (0.0, 1.0) == 0.0
            double at = Math.Atan2 (((y1p - cyp) / ry), ((x1p - cxp) / rx));
            double theta1 = (at < 0.0) ? 2.0 * Math.PI + at : at;
            
            double nat = Math.Atan2 (((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
            double delta_theta = (nat < at) ? 2.0 * Math.PI - at + nat : nat - at;
            
            if (sweep) {
                // ensure delta theta < 0 or else add 360 degrees
                if (delta_theta < 0.0)
                    delta_theta += 2.0 * Math.PI;
            } else {
                // ensure delta theta > 0 or else substract 360 degrees
                if (delta_theta > 0.0)
                    delta_theta -= 2.0 * Math.PI;
            }
            
            // add several cubic bezier to approximate the arc (smaller than 90 degrees)
            // we add one extra segment because we want something smaller than 90deg (i.e. not 90 itself)
            int segments = (int) (Math.Abs (delta_theta / Math.PI)) + 1;
            double delta = delta_theta / segments;
            
            // http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
            float bcp = (float) (4.0 / 3 * (1 - Math.Cos (delta / 2)) / Math.Sin (delta / 2));
            
            double cos_phi_rx = cos_phi * rx;
            double cos_phi_ry = cos_phi * ry;
            double sin_phi_rx = sin_phi * rx;
            double sin_phi_ry = sin_phi * ry;
            
            double cos_theta1 = Math.Cos (theta1);
            double sin_theta1 = Math.Sin (theta1);

            PointF c1, c2;
            
            int i;
            for (i = 0; i < segments; ++i) {
                // end angle (for this segment) = current + delta
                double theta2 = theta1 + delta;
                double cos_theta2 = Math.Cos (theta2);
                double sin_theta2 = Math.Sin (theta2);
                
                // first control point (based on start point sx,sy)
                c1.X = startPoint.X - bcp * (float) (cos_phi_rx * sin_theta1 + sin_phi_ry * cos_theta1);
                c1.Y = startPoint.Y + bcp * (float) (cos_phi_ry * cos_theta1 - sin_phi_rx * sin_theta1);
                
                // end point (for this segment)
                endPoint.X = (float) (cx + (cos_phi_rx * cos_theta2 - sin_phi_ry * sin_theta2));
                endPoint.Y = (float) (cy + (sin_phi_rx * cos_theta2 + cos_phi_ry * sin_theta2));
                
                // second control point (based on end point ex,ey)
                c2.X = endPoint.X + bcp * (float) (cos_phi_rx * sin_theta2 + sin_phi_ry * cos_theta2);
                c2.Y = endPoint.Y + bcp * (float) (sin_phi_rx * sin_theta2 - cos_phi_ry * cos_theta2);
                
                formatter.CurveTo (c1, c2, endPoint);
                
                // next start point is the current end point (same for angle)
                startPoint = endPoint;
                theta1 = theta2;
                // avoid recomputations
                cos_theta1 = cos_theta2;
                sin_theta1 = sin_theta2;
            }
        }
    }
}


// 从接口派生的CSharpCoreGraphicsFormatter.cs

// Authors:
//    Sebastien Pouliot  <sebastien@xamarin.com>
//
// Copyright 2012-2013 Xamarin Inc.
//
// Licensed under the GNU LGPL 2 license only (no "later versions")

using System;
using System.Drawing;
using System.Globalization;
using System.IO;

namespace Poupou.SvgPathConverter {

    public class CSharpCoreGraphicsFormatter : ISourceFormatter {

        TextWriter writer;

        public CSharpCoreGraphicsFormatter (TextWriter textWriter)
        {
            writer = textWriter;
        }
        
        public void Prologue (string name)
        {
            writer.WriteLine ("\tstatic void {0} (CGContext c)", name);
            writer.WriteLine ("\t{");
        }
    
        public void Epilogue ()
        {
            writer.WriteLine ("\t\tc.FillPath ();");
            writer.WriteLine ("\t\tc.StrokePath ();");
            writer.WriteLine ("\t}");
            writer.WriteLine ();
        }
    
        public void MoveTo (PointF pt)
        {
            writer.WriteLine ("\t\tc.MoveTo ({0}f, {1}f);", pt.X.ToString (CultureInfo.InvariantCulture),
                pt.Y.ToString (CultureInfo.InvariantCulture));
        }
    
        public void LineTo (PointF pt)
        {
            writer.WriteLine ("\t\tc.AddLineToPoint ({0}f, {1}f);", pt.X.ToString (CultureInfo.InvariantCulture),
                 pt.Y.ToString (CultureInfo.InvariantCulture));
        }
    
        public void ClosePath ()
        {
            writer.WriteLine ("\t\tc.ClosePath ();");
        }
    
        public void QuadCurveTo (PointF pt1, PointF pt2)
        {
            writer.WriteLine ("\t\tc.AddQuadCurveToPoint ({0}f, {1}f, {2}f, {3}f);",
                pt1.X.ToString (CultureInfo.InvariantCulture), pt1.Y.ToString (CultureInfo.InvariantCulture),
                pt2.X.ToString (CultureInfo.InvariantCulture), pt2.Y.ToString (CultureInfo.InvariantCulture));
        }

        public void CurveTo (PointF pt1, PointF pt2, PointF pt3)
        {
            writer.WriteLine ("\t\tc.AddCurveToPoint ({0}f, {1}f, {2}f, {3}f, {4}f, {5}f);",
                pt1.X.ToString (CultureInfo.InvariantCulture), pt1.Y.ToString (CultureInfo.InvariantCulture),
                pt2.X.ToString (CultureInfo.InvariantCulture), pt2.Y.ToString (CultureInfo.InvariantCulture),
                pt3.X.ToString (CultureInfo.InvariantCulture), pt3.Y.ToString (CultureInfo.InvariantCulture));
        }

        public void ArcTo (PointF size, float angle, bool isLarge, bool sweep, PointF endPoint, PointF startPoint)
        {
            this.ArcHelper (size, angle, isLarge, sweep, endPoint, startPoint);
        }
    }
}


目录
相关文章
|
2月前
|
开发框架 C# Android开发
C#一分钟浅谈:Xamarin 移动应用开发
随着移动设备的普及,Xamarin 成为跨平台移动应用开发的重要工具,允许开发者使用 C# 编写一次代码,即可在 iOS、Android 和 Windows Phone 上运行。本文介绍 Xamarin 的基本概念、开发环境搭建、常见问题及解决方案,并通过代码示例详细讲解。
55 0
|
4月前
|
C# Windows
visual studio 2022 社区版 c# 环境搭建及安装使用【图文解析-小白版】
这篇文章提供了Visual Studio 2022社区版C#环境的搭建和安装使用指南,包括下载、安装步骤和创建C#窗体应用程序的详细图文解析。
visual studio 2022 社区版 c# 环境搭建及安装使用【图文解析-小白版】
|
13天前
|
存储 监控 算法
企业内网监控系统中基于哈希表的 C# 算法解析
在企业内网监控系统中,哈希表作为一种高效的数据结构,能够快速处理大量网络连接和用户操作记录,确保网络安全与效率。通过C#代码示例展示了如何使用哈希表存储和管理用户的登录时间、访问IP及操作行为等信息,实现快速的查找、插入和删除操作。哈希表的应用显著提升了系统的实时性和准确性,尽管存在哈希冲突等问题,但通过合理设计哈希函数和冲突解决策略,可以确保系统稳定运行,为企业提供有力的安全保障。
|
2月前
|
编译器 C# 开发者
C# 9.0 新特性解析
C# 9.0 是微软在2020年11月随.NET 5.0发布的重大更新,带来了一系列新特性和改进,如记录类型、初始化器增强、顶级语句、模式匹配增强、目标类型的新表达式、属性模式和空值处理操作符等,旨在提升开发效率和代码可读性。本文将详细介绍这些新特性,并提供代码示例和常见问题解答。
56 7
C# 9.0 新特性解析
|
7月前
|
存储 Java C#
C# 中的值类型与引用类型:内存大小解析
C# 中的值类型与引用类型:内存大小解析
|
2月前
|
C# 开发者
C# 10.0 新特性解析
C# 10.0 在性能、可读性和开发效率方面进行了多项增强。本文介绍了文件范围的命名空间、记录结构体、只读结构体、局部函数的递归优化、改进的模式匹配和 lambda 表达式等新特性,并通过代码示例帮助理解这些特性。
42 2
|
4月前
|
缓存 API 网络架构
Nuxt Kit API :路径解析工具
【9月更文挑战第20天】在 Nuxt Kit API 中,路径解析工具如 `resolvePath()`、`joinPaths()` 和 `relativePath()` 帮助开发者高效处理应用路径,确保资源准确加载,并支持动态路由与组件导入。这些工具提升了应用的灵活性和可扩展性,同时需注意路径准确性、跨平台兼容性和性能优化,以提升用户体验。
56 12
|
5月前
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
170 0
|
5月前
|
区块链 C# 存储
链动未来:WPF与区块链的创新融合——从智能合约到去中心化应用,全方位解析开发安全可靠DApp的最佳路径
【8月更文挑战第31天】本文以问答形式详细介绍了区块链技术的特点及其在Windows Presentation Foundation(WPF)中的集成方法。通过示例代码展示了如何选择合适的区块链平台、创建智能合约,并在WPF应用中与其交互,实现安全可靠的消息存储和检索功能。希望这能为WPF开发者提供区块链技术应用的参考与灵感。
73 0
|
5月前
|
开发者 C# Windows
WPF与游戏开发:当桌面应用遇见游戏梦想——利用Windows Presentation Foundation打造属于你的2D游戏世界,从环境搭建到代码实践全面解析新兴开发路径
【8月更文挑战第31天】随着游戏开发技术的进步,WPF作为.NET Framework的一部分,凭借其图形渲染能力和灵活的UI设计,成为桌面游戏开发的新选择。本文通过技术综述和示例代码,介绍如何利用WPF进行游戏开发。首先确保安装最新版Visual Studio并创建WPF项目。接着,通过XAML设计游戏界面,并在C#中实现游戏逻辑,如玩家控制和障碍物碰撞检测。示例展示了创建基本2D游戏的过程,包括角色移动和碰撞处理。通过本文,WPF开发者可更好地理解并应用游戏开发技术,创造吸引人的桌面游戏。
256 0

推荐镜像

更多
下一篇
开通oss服务