序言
我正在使用XNA内容项目来保存我正在使用的所有各种纹理(以及可能的其他资源)作为开发游戏的一部分。
将Content项目中的图像加载到XNA纹理对象的默认方法涉及使用硬编码字符串文字指向各种文件。
我想自动将内容项目中的目录/文件树投影到对象层次结构中,以避免直接使用字符串文字,并获得使用强类型对象的好处。
示例:
而不是使用
Texture2D tex = Content.Load("Textures/defaultTexture32");
我更喜欢这个
Texture2D tex = Content.Load(Content.Textures.defaultTexture32);
的问题:
这个问题是否已有解决方案? (我在谷歌找不到任何东西)
额外详情:
我相当肯定这可以通过T4模板完成;可能与DTE工具一起使用。我已经初步尝试这样做了,但由于我对两种工具都缺乏经验,我一直在打块,但我过去曾与T4MVC合作过类似的事情。不幸的是,它投射的是类结构而不是文件系统,并且不易调整。
我不要求解决方案使用T4或DTE,它们看起来好像很可能是解决方案的一部分。
仅包含属于VS项目的文件(而不是整个磁盘上的文件系统)将是首选,但不是必需的。
按文件类型等进行额外过滤的能力将是额外的特殊奖励。
对于任何没有立即看到这样做的好处的人;考虑重命名或删除文件会发生什么。应用程序将继续编译正常,但它会在运行时崩溃。也许直到满足某个特定文件才能访问某个文件。如果将所有文件名都投影到一个对象结构中(每次构建项目时都会重新生成,或者甚至每次修改时都会重新生成),那么您将得到编译时错误,指出缺少的资源并可能避免很多未来疼痛
感谢您的时间。
答案 0 :(得分:2)
这是一个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) #>";
<# } #>
}
}
答案 1 :(得分:2)
我创建了一个更复杂的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");
}
}
#>
答案 2 :(得分:1)
我以前从未见过这样的事情。我这样做的方法是创建一个单独的c#控制台应用程序,扫描内容文件夹或内容项目xml,并将所需的c#代码写入项目中包含的文件。然后,您可以将其作为预构建步骤(或每次添加内容时手动)运行。我不熟悉T4或DTE,所以我不能对这些选项发表评论。
请记住,扫描内容项目XML有它的缺点。您将能够提取内容项的类型(或至少分配的内容导入程序/处理器),但它可能无法获取所有内容。例如,3D模型会自动包含其引用的纹理,因此这些纹理不会列在内容项目中。这可能没问题,因为您不太可能想直接引用它们。