MIT 许可证 NuGet

首页 / 文档 / 项目布局

项目布局与资产

资产位于 Resources/Raw/ 中,通过其下的路径寻址。 位于 Resources/Raw/Assets/island.glb 的文件 在全部六个平台上都以 "Assets/island.glb" 加载, 你的代码中没有任何逐平台路径处理。

一个应用的形状

Hello/
  Hello.csproj
  GlobalUsings.cs
  App.cs
  Panels/
    Scene.cs
  Platforms/
    Windows/     App.xaml, App.xaml.cs, Package.appxmanifest
    Linux/       Linux.cs
    Android/     MainActivity.cs, AndroidManifest.xml
    iOS/         Program.cs, Info.plist
    MacCatalyst/ Program.cs, Info.plist
  Resources/
    AppIcon/     appicon.svg
    Splash/      splash.svg
    Raw/
      Assets/    island.glb, robot.glb, NotoSansMono-VariableFont.ttf, ...

Platforms/ 之外的一切都是共享的。Web 构建是一个独立的 Blazor WebAssembly 项目,引用同一个 App 类, 因为浏览器宿主只是项目 SDK 不同,而不是源码文件不同 — 参见 环境要求中的三项目结构。

让资产工作的两个项组

移动端与 Windows 通过 MAUI 资产系统读取资产;Windows 与 Linux 桌面构建 还希望它们位于可执行文件旁边。两个组声明了相同的 LogicalName/Link 模式, 这正是让查找路径处处一致的原因。

<ItemGroup>
  <MauiAsset Include="Resources\Raw\**"
             LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

<ItemGroup Condition="'$(IsWindows)' == 'true' or '$(IsLinux)' == 'true'">
  <Content Include="Resources\Raw\**"
           CopyToOutputDirectory="PreserveNewest"
           Link="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
如果模型在 Windows 上能加载但在 Android 上不能

先检查这两个组。缺失的 MauiAsset 条目 会产生完全相同的症状:桌面构建通过 Content 复制在磁盘上找到文件, 而打包构建中没有任何东西可打开。加载是异步的,缺失的资产只会让控件 不处于 Ready 状态而不是抛出异常, 所以故障看起来像一个看不见的对象,而不是一个异常。

让 Linux 源码远离 MAUI 构建

Linux 目标是纯 net10.0,所以它的入口点不能 被编译进 MAUI 目标框架。示例项目用两个小 target 而不是 #if 块来完成这件事:

<Target Name="IncludeLinuxFilesForLinuxTarget" BeforeTargets="BeforeCompile"
        Condition="'$(IsLinux)' == 'true'">
  <ItemGroup>
    <Compile Include="Platforms\Linux\**\*.cs" />
  </ItemGroup>
</Target>

<Target Name="ExcludeLinuxFilesForNonLinuxTargets" AfterTargets="BeforeCompile"
        Condition="'$(IsLinux)' != 'true'">
  <ItemGroup>
    <Compile Remove="Platforms\Linux\**\*.cs" />
  </ItemGroup>
</Target>

这些条件中用到的 IsWindows、IsLinux、 IsAndroid 等是普通 MSBuild 属性, 在项目文件顶部由 TargetFramework 推导而来。 连同 targets 一起复制它们是最不易出错的开局方式。

全局 using

应用通常一次性声明它们的命名空间,这就是示例代码几乎不携带 using 行的原因,也是本站代码片段不重复它们的原因:

global using System.Numerics;

global using Season.Basic;
global using Season.Utils;
global using Season.Storage;
global using Season.Models;
global using Season.Controls;
global using Season.Panels;
global using Season.Rendering;
命名空间里面有什么
Season.BasicBaseApp、DeviceServices、Graphics、Colors、Platform。
Season.ControlsModel、Mesh3D、InstancedModel、Sprite2D、Sprite3D、Shape、Texts,以及你派生实例类型所用的 MeshInstanceTransform。
Season.PanelsPanel 与现成面板:FrameButton、Input、Picker、ObjectPicker。
Season.RenderingRenderQuality、FrameSchedule、Atmosphere、WorldSettings、SceneLighting。
Season.ModelsglTF 加载与动画:GltfAsset、GLTFAnimationPlayer、ModelAnimationInfo、PickMesh。
Season.StorageStorageService 与质量界面使用的设置持久化。
Season.FontsFont。通常不作为全局 using,因为你只在启动时碰它一次。