亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

在C#中使用適配器Adapter模式和擴(kuò)展方法解決面向?qū)ο笤O(shè)計(jì)問題記錄

 更新時間:2024年10月08日 08:56:12   作者:dax.net  
在開發(fā)基于MonoGame的游戲框架時,面臨SpriteFont和DynamicSpriteFont兼容問題,SpriteFont在內(nèi)容管道中編譯確定字號,導(dǎo)致不同字號需加載多個字體資源,本文給大家介紹在C#中使用適配器Adapter模式和擴(kuò)展方法解決面向?qū)ο笤O(shè)計(jì)問題,感興趣的朋友一起看看吧

之前有陣子在業(yè)余時間拓展自己的一個游戲框架,結(jié)果在實(shí)現(xiàn)的過程中發(fā)現(xiàn)一個設(shè)計(jì)問題。這個游戲框架基于MonoGame實(shí)現(xiàn),在MonoGame中,所有的材質(zhì)渲染(Texture Rendering)都是通過SpriteBatch類來完成的。舉個例子,假如希望在屏幕的某個地方顯示一個圖片材質(zhì)(imageTexture),就在Game類的子類的Draw方法里,使用下面的代碼來繪制圖片:

protected override void Draw(GameTime gameTime)
{
    // ...
    spriteBatch.Draw(imageTexture, new Vector2(x, y), Color.White);
    // ...
}

那么如果希望在屏幕的某個地方用某個字體來顯示一個字符串,就類似地調(diào)用SpriteBatchDrawString方法來完成:

protected override void Draw(GameTime gameTime)
{
    // ...
    spriteBatch.DrawString(spriteFont, "Hello World", new Vector2(x, y), Color.White);
    // ...
}

暫時可以不用管這兩個代碼中spriteBatch對象是如何初始化的,以及DrawDrawString兩個方法的各個參數(shù)是什么意思,在本文討論的范圍中,只需要關(guān)注spriteFont這個對象即可。MonoGame使用一種叫“內(nèi)容管道”(Content Pipeline)的技術(shù),將各種資源(聲音、音樂、字體、材質(zhì)等等)編譯成xnb文件,之后,通過ContentManager類,將這些資源讀入內(nèi)存,并創(chuàng)建相應(yīng)的對象。SpriteFont就是其中一種資源(字體)對象,在GameLoad方法中,可以通過指定xnb文件名的方式,從ContentManager獲取字體信息:

private SpriteFont? spriteFont;
protected override void LoadContent()
{
    // ...
    spriteFont = Content.Load<SpriteFont>("fonts\\arial"); // Load from fonts\\arial.xnb
    // ...
}

OK,與MonoGame相關(guān)的知識就介紹這么多。接下來,就進(jìn)入具體問題。由于是做游戲開發(fā)框架,那么為了能夠更加方便地在屏幕上(確切地說是在當(dāng)前場景里)顯示字符串,我封裝了一個Label類,這個類大致如下所示:

public class Label : VisibleComponent
{
    private readonly SpriteFont _spriteFont;
    public Label(string text, SpriteFont spriteFont, Vector2 pos, Color color)
    {
        Text = text;
        _spriteFont = spriteFont;
        Position = pos;
        TextColor = color;
    }
    public string Text { get; set; }
    public Vector2 Position { get; set; }
    public Color TextColor { get; set; }
    protected override void ExecuteDraw(GameTime gameTime, SpriteBatch spriteBatch)
        => spriteBatch.DrawString(_spriteFont, Text, Position, TextColor);
}

這樣實(shí)現(xiàn)本身并沒有什么問題,但是仔細(xì)思考不難發(fā)現(xiàn),SpriteFont是從Content Pipeline讀入的字體信息,而字體信息不僅包含字體名稱,而且還包含字體大?。ㄗ痔枺⑶以赑ipeline編譯的時候就已經(jīng)確定下來了,所以,如果游戲中希望使用同一個字體的不同字號來顯示不同的字符串時,就需要加載多個SpriteFont,不僅麻煩而且耗資源,靈活度也不高。

經(jīng)過一番搜索,發(fā)現(xiàn)有一款開源的字體渲染庫:FontStashSharp,它有MonoGame的擴(kuò)展,可以基于字體的不同字號,動態(tài)加載字體對象(稱之為“動態(tài)精靈字體(DynamicSpriteFont)”),然后使用MonoGame原生的SpriteBatch將字符串以指定的動態(tài)字體顯示在場景中,比如:

private readonly FontSystem _fontSystem = new();
private DynamicSpriteFont? _menuFont;
public override void Load(ContentManager contentManager)
{
    // Fonts
    _fontSystem.AddFont(File.ReadAllBytes("res/main.ttf"));
    _menuFont = _fontSystem.GetFont(30);
}
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
    spriteBatch.DrawString(_menuFont, "Hello World", new Vector2(100, 100), Color.Red);
}

在上面的Draw方法中,仍然是使用了SpriteBatch.DrawString方法來顯示字符串,不同的地方是,這個DrawString方法所接受的第一個參數(shù)為DynamicSpriteFont對象,這個DynamicSpriteFont對象是第三方庫FontStashSharp提供的,它并不是標(biāo)準(zhǔn)的MonoGame里的類型,所以,這里有兩種可能:

  • DynamicSpriteFont是MonoGame中SpriteFont的子類
  • FontStashSharp使用了C#擴(kuò)展方法,對SpriteBatch類型進(jìn)行了擴(kuò)展,使得DrawString方法可以使用DynamicSpriteFont來繪制文本

如果是第一種可能,那問題倒也簡單,基本上自己開發(fā)的這個游戲框架可以不用修改,比如在創(chuàng)建Label實(shí)例的時候,構(gòu)造函數(shù)第二個參數(shù)直接將DynamicSpriteFont對象傳入即可。但不幸的是,這里屬于第二種情況,也就是FontStashSharp中的DynamicSpriteFontSpriteFont之間并沒有繼承關(guān)系。

現(xiàn)在總結(jié)一下,目前的現(xiàn)狀是:

  • DynamicSpriteFont并不是SpriteFont的子類
  • 兩者提供相似的能力:都能夠被SpriteBatch用來繪制文本,都能夠基于給定的文本字符串來計(jì)算繪制區(qū)域的寬度和高度(兩者都提供MeasureString方法)
  • 我希望在我的游戲框架中能夠同時使用SpriteFontDynamicSpriteFont,也就是說,我希望Label可以同時兼容SpriteFontDynamicSpriteFont的文本繪制能力

很明顯,可以使用GoF95的適配器(Adapter)模式來解決目前的問題,以滿足上述3的條件。為此,可以定義一個IFontAdapter接口,然后基于SpriteFontDynamicSpriteFont來提供兩種不同的適配器實(shí)現(xiàn),最后,讓框架里的類型(比如Label)依賴于IFontAdapter接口即可,UML類圖大致如下:

DynamicSpriteFontAdapter被實(shí)現(xiàn)在一個獨(dú)立的包(C#中的Assembly)里,這樣做的目的是防止Mfx.Core項(xiàng)目對FontStashSharp有直接依賴,因?yàn)镸fx.Core作為整個游戲框架的核心組件,會被不同的游戲主體或者其它組件引用,而這些組件并不需要依賴FontStashSharp。

此外,同樣可以使用C#的擴(kuò)展方法特性,讓SpriteBatch可以基于IFontAdapter進(jìn)行文本繪制:

public static class SpriteBatchExtensions
{
    public static void DrawString( 
        this SpriteBatch spriteBatch, 
        IFontAdapter fontAdapter, 
        string text) => fontAdapter.DrawString(spriteBatch, text);
}

 其它相關(guān)代碼類似如下:

public interface IFontAdapter
{
    void DrawString(SpriteBatch spriteBatch, string text);
    Vector2 MeasureString(string text);
}
public sealed class SpriteFontAdapter(SpriteFont spriteFont) : IFontAdapter
{
    public Vector2 MeasureString(string text) => spriteFont.MeasureString(text);
    public void DrawString(SpriteBatch spriteBatch, string text)
        => spriteBatch.DrawString(spriteFont, text);
}
public sealed class FontStashSharpAdapter(DynamicSpriteFont spriteFont) : IFontAdapter
{
    public void DrawString(SpriteBatch spriteBatch, string text)
        => spriteBatch.DrawString(spriteFont, text);
    public Vector2 MeasureString(string text) => spriteFont.MeasureString(text);
}
public class Label(string text, IFontAdapter fontAdapter) : VisibleComponent
{
    // 其它成員忽略
    public string Text { get; set; } = text;
    protected override void ExecuteDraw(GameTime gameTime, SpriteBatch spriteBatch)
        => spriteBatch.DrawString(fontAdapter, Text);
}

總結(jié)一下:本文通過對一個實(shí)際案例的分析,討論了GoF95設(shè)計(jì)模式中的Adapter模式在實(shí)際項(xiàng)目中的應(yīng)用,展示了如何使用面向?qū)ο笤O(shè)計(jì)模式來解決實(shí)際問題的方法。Adapter模式的引入也會產(chǎn)生一些邊界效應(yīng),比如本案例中FontStashSharp的DynamicSpriteFont其實(shí)還能夠提供更多更為豐富的功能特性,然而Adapter模式的使用,使得這些功能特性不能被自制的游戲框架充分使用(因?yàn)榻涌诮y(tǒng)一,而標(biāo)準(zhǔn)的SpriteFont并不提供這些功能),一種有效的解決方案是,擴(kuò)展IAdapter接口的職責(zé),然后使用空對象模式來補(bǔ)全某個適配器中不被支持的功能特性,但這種做法又會在框架設(shè)計(jì)中,讓某些類型的層次結(jié)構(gòu)設(shè)計(jì)變得特殊化,也就是為了迎合某個外部框架而去做抽象,使得設(shè)計(jì)變得不那么純粹,所以,還是需要根據(jù)實(shí)際項(xiàng)目的需求來決定設(shè)計(jì)的方式。

到此這篇關(guān)于在C#中使用適配器Adapter模式和擴(kuò)展方法解決面向?qū)ο笤O(shè)計(jì)問題記錄的文章就介紹到這了,更多相關(guān)C#面向?qū)ο笤O(shè)計(jì)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論