UrhoSharp(链接)是Urho游戏引擎(链接)的.Net的端口。 它允许开发便携式3d应用程序。 与Unity或Unreal相比,它非常粗糙,主要是因为它缺少像Unity Editor这样的体面的场景编辑器,但是当我学习Xamarin时我决定学习它,在这篇文章中,我将谈论与它的第一次接触。我构建了3d编程的Hello World-从3d建模系统导入的旋转的纹理立方体。
环境和项目设置
要开始使用UrhoSharp,您需要适当的Visual Studio项目模板(链接)。 我花了一些时间来了解此模板,在了解此模板的存在之前,我尝试了两种错误的方法:我试图自己拉包,就像UrhoSharp简介中所暗示的那样(链接)来设置项目,并且我尝试了复制和重用示例项目。 两种方法都失败了,前一种是由于在运行时缺少必需的Dll,而后一种是由于将资产复制到已构建项目的持续失败。 因此,开始使用UrhoSharp的正确方法是使用模板。 我创建了一个空白解决方案,并在该空白解决方案中创建了一个空白UrhoSharp游戏(便携式)。

该模板将创建四个项目:可移植项目和三个特定于平台的项目,一个用于Windows,一个用于Android,另一个用于iOS。 大多数游戏代码都将驻留在可移植项目中,而特定于平台的项目可用于使UrhoSharp应用程序在各自的平台上工作所需的管道。

架构
游戏的主要类别是Urho.Application衍生的类别,因为生命周期事件发生在此类中。 应用程序具有场景,丰富了应用程序的上下文。 场景内将有许多节点。 这些节点可以具有几何形状,灯光,照相机,并且具有变换。 一个节点可以具有子节点。 因此,我们通过创建一个场景并用节点填充它来填充一个世界。 在MyGame.Start方法中,执行此操作。
受保护的重写异步void Start(){base.Start(); Log.LogMessage + = e => Debug.WriteLine($“ [{{e.Level}] {e.Message}”); var cache = this.ResourceCache; // cria a cenascene = new Scene(this.Context); scene.CreateComponent (); // cria o node做cubocubeNode = scene.CreateChild(“ Cube”); cubeNode.Position = new Vector3(0,0 ,0); var cubeObject = cubeNode.CreateComponent (); cubeObject.Model = cache.GetModel(“ Models / Cube.mdl”); cubeObject.SetMaterial(cache.GetMaterial(“ Materials / Dice.xml”)) ; //从节点获得的光照节点lightNode = scene.CreateChild(“ DirectionalLight”); lightNode.SetDirection(new Vector3(0.6f,-1.0f,0.8f)); //不需要对方向向量进行归一化var light = lightNode.CreateComponent (); light.LightType = LightType.Directional; //临界节点dacâmeraCameraNode= scene.CreateChild(“ camera”); camera = CameraNode .CreateComponent (); CameraNode.Position = new Vector3(0,1,-10); // Com is eu tenho uma luz,uma camera e um objeto qqer pendurados na cena /// Viewportvar视口= new Viewport(Context ,场景,相机,null); viewport.SetClearColor(Color.Yellow); Renderer.SetViewport(0,viewport); // FPSnew MonoDebugHud(this).Show(Color.Black,25);}
首先,我创建了场景。 场景要求创建一个八叉树。 然后,我创建了多维数据集的节点,照明节点和相机的节点。 最后,我创建了视口。 在立方体节点和摄影机节点中,我设置了变形,在这种情况下,仅设置了平移,而在照明节点中则不需要。
立方体节点使用模型和材料。 这些资产是在Blender中创建的,导出到UrhoSharp引擎并添加到项目中。 有关引擎中资产的问题,请参见下文。 关于灯光,没有太多要说的-它们可以像其他任何库中的定向光一样完成定向光的预期。
另一方面,相机具有必须考虑的细节。 在其他3d库中,例如VTK(链接),像FOV这样的相机的光学特性和像方向和位置这样的空间特性都属于同一类。 在UrhoSharp中,空间属性在Node上,而光学属性在Camera类上。
最后,我创建了视口。 缺少引擎的文档,但是从它的方法(如GetScreenRay)以及构造函数的要求来看,我推断出它管理着屏幕空间和世界空间之间的渲染和转换。
用户互动
在UrhoSharp中,您必须注意程序是否在桌面上运行,因此它是否具有鼠标。 它是否在移动设备上运行且没有鼠标但有触摸事件。 在本《 Hello World》中,三种方法负责处理交互:OnUpdate,MoveCubeByTouches和MoveCubeByMouse。 OnUpdate是UrhoSharp生命周期的一部分,而其他两种方法是我为该程序创建的。
受保护的重写void OnUpdate(float timeStep){MoveCubeByTouches(timeStep); MoveCubeByMouse(timeStep); base.OnUpdate(timeStep);}
OnUpdate调用这两种方法,并调用其祖先的OnUpdate,后者了解有关更新的更多信息。
私有无效MoveCubeByTouches(float timeStep){const float touchSensitivity = 2f; var input = Input; for(uint i = 0,num = input.NumTouches; i <num; ++ i){TouchState状态= input.GetTouch(i) ; if(state.Delta.X!= 0 || state.Delta.Y!= 0){cubeYaw + = touchSensitivity * camera.Fov / Graphics.Height * state.Delta.X; cubePitch + = touchSensitivity * camera.Fov / Graphics.Height * state.Delta.Y; cubeNode.Rotation =新的四元数(cubePitch,cubeYaw,0);}}}
MoveCubeByTouches获取触摸(可以有多个触摸),并使用其X和Y来计算多维数据集节点的新Yaw和Pitch,然后使用这些值旋转多维数据集。 MoveCubeByMouse执行相同的操作,但是使用鼠标位置。
private void MoveCubeByMouse(float timeStep,float moveSpeed = 10.0f){if(!Input.GetMouseButtonDown(MouseButton.Left)){return;} else {const float mouseSensitivity = .1f; var mouseMove = Input.MouseMove; cubeYaw + = mouseSensitivity * mouseMove.X; cubePitch + = mouseSensitivity * mouseMove.Y; cubePitch = MathHelper.Clamp(cubePitch,-90,90); cubeNode.Rotation =新四元数(cubePitch,cubeYaw,0);}}
下面是MyGame类的完整代码。
使用系统;使用System.Diagnostics;使用Urho;使用Urho.Actions;使用Urho.Gui;使用Urho.Shapes;命名空间MyGame {public class MyGame:Application {[Preserve] public MyGame(ApplicationOptions options):base(options){ }静态MyGame(){UnhandledException + =(s,e)=> {if(Debugger.IsAttached)Debugger.Break(); e.Handled = true;};}私有场景场景;私有节点cubeNode;私有节点CameraNode;私有摄像机;受保护的重写异步void Start(){base.Start(); Log.LogMessage + = e => Debug.WriteLine($“ [{e.Level}] {e.Message}”); var cache = this.ResourceCache; //临界点=新场景(this.Context); scene.CreateComponent (); //临界点做cubocubeNode = scene.CreateChild(“ Cube”); cubeNode.Position =新的Vector3 (0,0,0);变量cubeObject = cubeNode.CreateComponent (); cubeObject.Model = cache.GetModel(“ Models / Cube.mdl”); cubeObject.SetMaterial(cache.GetMaterial(“ Materials / Dice。 xml“)); // cria o luda da luzvar lightNode = scene.CreateChild(” DirectionalLight“); lightNo de.SetDirection(new Vector3(0.6f,-1.0f,0.8f)); //不需要对方向向量进行归一化var light = lightNode.CreateComponent (); light.LightType = LightType.Directional; //临界节点dacâmeraCameraNode= scene.CreateChild(“ camera”); camera = CameraNode .CreateComponent (); CameraNode.Position = new Vector3(0,1,-10); CameraNode.LookAt(new Vector3(1,0,0),new Vector3(0,1,0)); //可以使用UM相机和/或Viewportvar视口= new Viewport(Context,scene,camera,null); viewport.SetClearColor(Color.Yellow); Renderer.SetViewport(0,viewport) ; // FPSnew MonoDebugHud(this).Show(Color.Black,25);}受保护的覆盖作废void OnUpdate(float timeStep){MoveCubeByTouches(timeStep); MoveCubeByMouse(timeStep); base.OnUpdate(timeStep);}私有void MoveCubeByTouches( float timeStep){const float touchSensitivity = 2f; var input = Input; for(uint i = 0,num = input.NumTouches; i <num; ++ i){TouchState状态= input.GetTouch(i); if(状态.Delta.X!= 0 || state.Delta.Y!= 0){cubeYaw + = touchSensitivity * camera.Fov / Graphics.Height * state.Delta.X; cubePitch + = touchSensitivity * camera.Fov / Graphics.Height * state.Delta.Y; cubeNode.Rotation = new Quaternion(cubePitch,cubeYaw,0);} }} private float cubeYaw = 0; private float cubePitch = 0; private void MoveCubeByMouse(float timeStep,float moveSpeed = 10.0f){if(!Input.GetMouseButtonDown(MouseButton.Left)){return;} else {const float mouseSensitivity = .1f; var mouseMove = Input.MouseMove; cubeYaw + = mouseSensitivity * mouseMove.X; cubePitch + = mouseSensitivity * mouseMove.Y; cubePitch = MathHelper.Clamp(cubePitch,-90,90); cubeNode.Rotation =新的四元数(cubePitch ,cubeYaw,0);}}}}
关于资产的注释:
UrhoSharp需要一个非常特定的资产文件夹组织。 所有资产必须位于特定文件夹内(项目模板创建此文件夹并将其命名为“ MyData”),并且资产应与模型,纹理及其他内容的文件夹一起组织。 在android项目中,MyData文件夹位于Assets文件夹内。 在iOS中,它位于Resources文件夹中。 在Windows项目中,MyData不在任何文件夹中。
对我有用的资产添加工作流程是:1)如果需要,通过可视化Studio解决方案资源管理器创建文件夹。 2)使用Windows资源管理器将资产从其所在位置复制到新创建的文件夹中。 3)将资产添加到项目中。
3d模型必须采用UrhoSharp可以理解的格式,即.mdl。 要将混合器格式和3ds max格式等更常见的格式转换为Urho格式,有两种方法:UrhoSharp派生自Urho引擎随附的资产导入器和Blender插件(链接)。 在Blender的情况下,我对UV贴图有一个问题:blender对象没有UV贴图,我必须手工创建它。