如何创建用于访问XNA内容项目中的文件的强类型结构
本文关键字:项目 文件 结构 强类型 XNA 何创建 创建 访问 用于 | 更新日期: 2023-09-27 18:05:27
序言:
我正在与一个XNA内容项目合作,以保存我在开发游戏时使用的所有各种纹理(可能还有其他资源(。
将图像从Content项目加载到XNA纹理对象的默认方法包括使用硬编码字符串文字来指向各种文件。
我想自动将内容项目中的目录/文件树投影到对象层次结构中,以避免直接使用字符串文字,并获得使用强类型对象的好处。
示例:
而不是使用Texture2D tex = Content.Load("Textures/defaultTexture32");
我更喜欢Texture2D tex = Content.Load(Content.Textures.defaultTexture32);
问题:
这个问题是否已有解决方案?(我在谷歌上找不到任何东西(
其他详细信息:
我相当确定这可以通过T4模板来完成;可能与DTE工具结合使用。我最初尝试过这样做,但由于我对这两个工具集都缺乏经验,我一直遇到障碍,但我过去使用过T4MVC,它也做过类似的事情;不幸的是,它投影的是类结构,而不是文件系统,而且不容易适应。
我不要求解决方案使用T4或DTE,它们看起来很可能是解决方案的一部分。
最好只包含VS项目的一部分文件(而不是整个磁盘上的文件系统(,但不是必须的。
能够额外地按文件类型等进行过滤将是一个额外的特殊奖励。
对于那些没有立即看到这样做的好处的人;考虑一下如果重命名或删除文件会发生什么。应用程序将继续进行良好编译,但在运行时会崩溃。也许直到遇到一组非常特殊的情况才能访问某个文件。如果所有文件名都被投影到一个对象结构中(每次构建项目时,甚至每次修改时都会重新生成(,那么编译时就会出现错误,指出丢失的资源,并可能避免未来的许多痛苦。
感谢您抽出时间
这里有一个T4模板,它将从项目目录中读取"Textures"文件夹中的所有文件。然后,它们将作为字符串写入类中,如果您希望限制文件搜索,只需更改Directory.GetFiles((即可。
添加/删除文件后,您可以在解决方案资源管理器中单击"转换所有模板"来生成一个新类。
希望这能有所帮助!
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ import namespace="System.IO" #>
<# var files = Directory.GetFiles(Host.ResolvePath("Textures"), "*.*"); #>
namespace Content
{
class Textures
{
<# foreach (string fileName in files) { #>
public const string <#= Path.GetFileNameWithoutExtension(fileName) #> = @"Textures/<#= Path.GetFileNameWithoutExtension(fileName) #>";
<# } #>
}
}
我创建了一个更复杂的T4模板,它使用DTE扫描VS项目。
我将把Ronny Karlsson的答案标记为已接受的答案,因为他用更简单的解决方案帮助我达到了这一点,但我想让任何可能觉得有用的人都能使用这个答案。
在您自己使用此代码之前,请记住,我是T4和DTE的新手,因此请谨慎使用,尽管它对我来说似乎很好,但您的里程数可能会有所不同。
该模板将为项目及其内部的所有文件夹创建嵌套的命名空间…在这些命名空间中,它将为每个文件创建一个类。这些类包含一组字符串,用于显示有关文件的有用信息。
还要注意顶部附近的两个变量。。。它们定义了为哪个项目扫描和构建对象,第二个List<string> AcceptableFileExtensions
按照您的预期进行,并指示了创建对象时要考虑的文件扩展名。例如,您可能不希望包含任何.cs或.txt等文件。或者你可以。根据需要进行调整。我现在只包含png,因为这就是我现在所需要的。
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE"#>
<#@ import namespace="System"#>
<#@ import namespace="System.IO"#>
<#@ import namespace="System.Collections.Generic"#>
<#
// Global Variables (Config)
var ContentProjectName = "GameContent";
List<string> AcceptableFileExtensions = new List<string>(){".png"};
// Program
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
DTE dte = (DTE) serviceProvider.GetService(typeof(DTE));
Project activeProject = null;
foreach(Project p in dte.Solution.Projects)
{
if(p.Name == ContentProjectName)
{
activeProject = p;
break;
}
}
emitProject(activeProject, AcceptableFileExtensions);
#>
<#+
private void emitProject(Project p, List<string> acceptableFileExtensions)
{
this.Write("namespace GameContent'r'n{'r'n");
foreach(ProjectItem i in p.ProjectItems)
{
emitProjectItem(i, 1, acceptableFileExtensions);
}
this.Write("}'r'n");
}
private void emitProjectItem(ProjectItem p, int indentDepth, List<string> acceptableFileExtensions)
{
if(String.IsNullOrEmpty(Path.GetExtension(p.Name)))
{
emitDirectory(p, indentDepth, acceptableFileExtensions);
}
else if(acceptableFileExtensions.Contains(Path.GetExtension(p.Name)))
{
emitFile(p, indentDepth);
}
}
private void emitDirectory(ProjectItem p, int indentDepth, List<string> acceptableFileExtensions)
{
emitIndent(indentDepth);
this.Write("/// Directory: " + Path.GetFullPath(p.Name) + "'r'n");
emitIndent(indentDepth);
this.Write("namespace " + Path.GetFileNameWithoutExtension(p.Name) + "'r'n");
emitIndent(indentDepth);
this.Write("{" + "'r'n");
foreach(ProjectItem i in p.ProjectItems)
{
emitProjectItem(i, indentDepth + 1, acceptableFileExtensions);
}
emitIndent(indentDepth);
this.Write("}" + "'r'n" + "'r'n");
}
private void emitFile(ProjectItem p, int indentDepth)
{
emitIndent(indentDepth);
this.Write("/// File: " + Path.GetFullPath(p.Name) + "'r'n");
emitIndent(indentDepth);
this.Write("public static class " + Path.GetFileNameWithoutExtension(p.Name) + "'r'n");
emitIndent(indentDepth);
this.Write("{" + "'r'n");
emitIndent(indentDepth + 1);
this.Write("public static readonly string Path = @'"" + Path.GetDirectoryName(Path.GetFullPath(p.Name)) + "'";" + "'r'n");
emitIndent(indentDepth + 1);
this.Write("public static readonly string Extension = @'"" + Path.GetExtension(p.Name) + "'";" + "'r'n");
emitIndent(indentDepth + 1);
this.Write("public static readonly string Name = @'"" + Path.GetFileNameWithoutExtension(p.Name) + "'";" + "'r'n");
emitIndent(indentDepth);
this.Write("}" + "'r'n" + "'r'n");
}
private void emitIndent(int depth)
{
for(int i = 0; i < depth; i++)
{
this.Write("'t");
}
}
#>
我以前从未见过这样的事情。我的方法是创建一个单独的c#控制台应用程序,该应用程序扫描内容文件夹或内容项目xml,并将所需的c#代码写入项目中包含的文件中。然后,您可以将其作为预构建步骤运行(或者每次添加内容时手动运行(。我不熟悉T4或DTE,所以我不能对这些选项发表评论。
请记住,扫描内容项目XML有其缺点。您可以提取内容项的类型(或者至少提取指定的内容导入器/处理程序(,但可能无法提取所有内容。例如,三维模型会自动包含其引用的纹理,因此这些纹理不会在内容项目中列出。这可能很好,因为你不太可能想直接引用它们。