I tried following this Unity ECS Entity Manager Tutorial from Turbo Makes Games.
I'm using Unity 2020.3.20f1.1086.
The Code works initially but I noticed, that Line 20 in SpawnEntitiesSystem.cs EntityManager.SetComponentData(newEntity, newPosition); stops working when I change or remove the namespace. After tinkering a bit with the code I can reproduce that the specific namespace is not important but the namespace has to be at least 18 characters long for the code to work as expected. If I change the namespace to 17 characters all entities are spawned at the same place. Other code after the faulty line 20 was executed but for readability I removed it from the code example. (Originally user input was also handled and in OnStartRunning a component was added to the new entitiy)
I only heard of similar problems in e.g. C++ when using pointer maths but I thought something like this shouldn't happen in C#. Does anyone have an idea what the core problem could be?
What I already tried without succes
restart Unity
delete Library, Obj and Logs folders
write code from scratch in new project
restart PC
SpawnEntitiesSystem.cs
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
namespace ALongNamespaceABCD {
public class SpawnEntitiesSystem : SystemBase {
private int2 _entitySpacing;
protected override void OnStartRunning() {
var entitySpawnData = GetSingleton<EntitySpawnData>();
var gridSize = entitySpawnData.SpawnGrid;
_entitySpacing = entitySpawnData.EntitySpacing;
for (int x = 0; x < gridSize.x; x++) {
for (int y = 0; y < gridSize.y; y++) {
var newEntity = EntityManager.Instantiate(entitySpawnData.EntityPrefab);
var newPosition = new LocalToWorld { Value = CalculateTransform(x, y) };
EntityManager.SetComponentData(newEntity, newPosition);
}
}
}
private float4x4 CalculateTransform(int x, int y) {
return float4x4.Translate(new float3 {
x = x * _entitySpacing.x,
y = 1f,
z = y * _entitySpacing.y
});
}
protected override void OnUpdate() {}
}
}
EntitySpawnData
using Unity.Entities;
using Unity.Mathematics;
[GenerateAuthoringComponent]
public struct EntitySpawnData : IComponentData {
public Entity EntityPrefab;
public int2 SpawnGrid;
public int2 EntitySpacing;
}
Edit:
I tried around a bit more and found out that it seems that the correct position will be set correctly at first. But it will instantly be overwritten by the prefabs Transform component. So maybe it has to do with converting the Prefab to an entity (I use the default Convert to Entity component) or a problem with some internal multithreading occurs.
Well, we finally got a response from Unity. Here is what they said:
This is intended behavior and will therefore be set to "won't fix".
The reasons are this:
Changing the name (and namespace) of a system changes its default position in the frame, this is expected behavior.
The SpawnEntitiesSystem from the provided repro project is not using any explicit system ordering attribute like
UpdateAfter/UpdateBefore/UpdateInGroup, so it follows the default.
Because it is expected behavior but nonetheless surprising, we have a task in the backlog about making the default order not depend on the
name in the future.
The prefab being instantiated has a Translation component and a Rotation component in addition to the LocalToWorld component.
The transform system from entities updates LocalToWorld based on the values of Translation and Rotation.
A direct modification of LocalToWorld conflicts with the transform system, and that modification will be overwritten.
Because Translation and Rotation are still at their default values, the expected outcome is that the transform system will compute an
identity LocalToWorld and put the instances back to the origin.
Unfortunately there is a bug in that particular setup (where the system doing the modification does it in OnStartRunning and executes
immediately after the transform system, if the namespace is long
enough to put it after the transform system) that causes the newly
spawned instances to never be noticed by the transform system. And it
is because of that bug that the cubes would show in the intended grid
layout by accident.
Source: https://issuetracker.unity3d.com/issues/entity-localtoworld-position-is-overwritten-by-prefab-position-when-namespace-is-17-characters-long-or-shorter
So, apparently, this whole situation happens because the LocalToWorld is sometimes being overwritten due to operation ordering that the length of the namespace is able to change. But this won't be the case in a future version.
A workaround is to set the Translation instead of the LocalToWorld Data.
Working code:
EntityManager.SetComponentData(newEntity, new Translation {
Value = new float3(
10.0f,
1.0f,
10.0f
)
});
Scenario
I'm planning to learn the basics about how to develop a custom HUD for a 3rd party PC-game (free, legal), my intention is to develop an application that will draw/show additional info on the screen (by reading mem addresses).
So I've researched for profesional projects and I've found TurboHUD that uses SharpDX library to draw objects/overlays/text over the screen using that DirectX based library and the results are very good (it does not loose performancy at any moment when drawing multiple objects on the screen),
but, since the author of that project does not provide a source to understand how they did it then I'm trying to learn by myself to use the same professional technics.
Question
If I'm on the wrong way or I missed some other better alternatives than SharpDX to develop this, please let me know.
My main question is:
In C# or preferably VB.Net, how I could just draw efficiently a custom string over the screen using SharpDX?.
Note that I could set the Form's oppacity to 0 but I think it should exists a proper way and I'm asking to know that proper way to draw directlly on the "desktop" screen.
My expects are to launch that PC-Game, then launch my custom HUD that will draw over the "desktop" screen surface to add additional info on the game, I hope you understand me.
Research
I should clarify that I'm totally unexperienced whit this kind of DirectX libs, and I'm using the SharpDX samples package to try learn its usage.
Since all the samples are in C# its more difficult to learn its usage under VB.Net.
Inside the SharpDX samples package there is an 'AdvancedTextRenderingApp' C# project, but as its name says it is an advanced example and also it instances a custom Form (a SharpDX.Windows.RenderForm) to draw on that Form.
This is the an VB.Net code translation of that C# project that I mentioned:
http://pastebin.com/KG2c3v09
UPDATE:
Just with the intention to comment about a research I did:
I recentlly discovered this useful GitHub repository, however, it fails to compile in Visual Studio 2015 (because missing namespace usings, that when added they generate more compiler errors), also it is oriented for advanced SharpDX users, and after analyzed the full sample I still don't have any idea of how to write/draw over the surface of a 3rd part process window ...also the C# syntax difficults me the overall understanding of SharpDX usage, because also the author did custom implementations a big variety of SharpDX members, then... I'm more than lost with all those examples.
The official SharpDX samples are another of those things that seems very useful ...maybe for advanced users. Some samples seems that demonstrates how to render a custom window/surface (with 500 tedious and incomprehensible lines of code to do it. And their "Hello world" sample is a nightmare for me.), however, what I would like to acchieve as I explained in my question is to draw on the surface of an existing window of another process, and I'm aware that probablly for that I would need to render a "surface" from scratch with SharpDX, then positionate it in the target window, then make invisible the surface, then draw on it, but I don't know how to do those things.
It took me a while to find how to load the font in XNA to draw the text but everything works fine.
You need 4 things:
Make form topmost
Extend aero glass style to whole form (for transparency to work)
Initialize XNA, Microsoft XNA Game Studio 4.0
Draw the texture and text
One limitation
The game must not be in fullscreen mode. The same as the TurboHUD limitation
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using System.Runtime.InteropServices;
using System.Drawing;
namespace XNATransparentWindow
{
public partial class Form1 : Form
{
private ContentBuilder contentBuilder;
public Form1()
{
InitializeComponent();
TopMost = true;
FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
MARGINS margins = new MARGINS();
margins.leftWidth = 0;
margins.rightWidth = 0;
margins.topHeight = this.Width;
margins.bottomHeight = this.Height;
// Extend aero glass style to whole form
DwmExtendFrameIntoClientArea(this.Handle, ref margins);
//Load XNA directX
this.contentBuilder = new ContentBuilder();
graphicsDeviceService = GraphicsDeviceService.AddRef(Handle, ClientSize.Width, ClientSize.Height);
//Register the service, so components like ContentManager can find it.
services.AddService<IGraphicsDeviceService>(graphicsDeviceService);
//Get the Graphics device
dev = graphicsDeviceService.GraphicsDevice;
if(dev == null){/*error message*/}
//Load texture
int bufferSize;
System.IO.MemoryStream memoryStream;
Bitmap img;
using (img = new Bitmap(#"C:\...\.png"))
{
bufferSize = img.Height * img.Width * 4;
memoryStream = new System.IO.MemoryStream(bufferSize);
img.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
memoryStream.Seek(0, SeekOrigin.Begin);
texture = Texture2D.FromStream(dev, memoryStream, img.Width, img.Height, false);
memoryStream.Close();
if(texture == null){/*error message*/}
}
//Create sprite
s_Batch = new SpriteBatch(dev);
if(s_Batch == null){/*error message*/}
FontPos = new Vector2(270.0F, 110.0F);
//Load font
contentManager = new ContentManager(services, this.contentBuilder.OutputDirectory);
this.contentBuilder.Clear();
this.contentBuilder.Add(#"C:\...\my_font1.spritefont","my_font1", "FontDescriptionImporter", "FontDescriptionProcessor");
//Build spritefont to get the .xbn file
string error = this.contentBuilder.Build();
//If build fail
if (!String.IsNullOrEmpty(error))
{
MessageBox.Show(error);
return;
}
//Load the .xbn file
Font1 = contentManager.Load<SpriteFont>("my_font1");
if(Font1 == null){/*error message*/}
}
[DllImport("dwmapi.dll")]
static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margin);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);
[StructLayout(LayoutKind.Sequential)]
public struct MARGINS
{
public int leftWidth;
public int rightWidth;
public int topHeight;
public int bottomHeight;
}
public ServiceContainer Services
{
get { return services; }
}
ServiceContainer services = new ServiceContainer();
GraphicsDevice dev;
SpriteFont Font1;
Vector2 FontPos;
SpriteBatch s_Batch;
Texture2D texture;
ContentManager contentManager;
GraphicsDeviceService graphicsDeviceService;
private const UInt32 WM_NCLBUTTONDOWN = 0xA1;
private const Int32 HTCAPTION = 0x2;
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
this.Close();
}
else //to move the form
{
this.Capture = false;
SendMessage(this.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
//There are two buffers. One offscreen-backbuffer and the
//frontbuffer which is the actual form in this example. The
//drawings are done to the backbuffer and in the end the two
//buffers flip. The backbuffer becomes frontbuffer and the
//frontbuffer becomes backbuffer.
//The drawing should start when the last resource is loaded.
//Since Font1 is the last one in this example I check for this
if (Font1 == null)
{
return;
}
//clear the backbuffer with transparent color.
dev.Clear(Microsoft.Xna.Framework.Color.Transparent);
//Do all your drawings here
//draw texture and text with the sprite
s_Batch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
s_Batch.Draw(texture, new Microsoft.Xna.Framework.Rectangle(0, 0, this.Width, this.Height), Microsoft.Xna.Framework.Color.White);
s_Batch.DrawString(Font1, #"XNA FRAMEWORK", FontPos, Microsoft.Xna.Framework.Color.Black);
s_Batch.End();
//here the flip is performed
dev.Present();
}
//Release resources
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
graphicsDeviceService.GraphicsDevice.Dispose();
graphicsDeviceService.Release(true);
s_Batch.Dispose();
texture.Dispose();
}
}
}
The following classes are just copy-paste from an example XNA 4.0 Content Compiler I found(with minor adjustments). They are used just to load the font for drawing the text:
GraphicsDeviceService.cs
#region File Description
//-----------------------------------------------------------------------------
// GraphicsDeviceService.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Threading;
using Microsoft.Xna.Framework.Graphics;
#endregion
// The IGraphicsDeviceService interface requires a DeviceCreated event, but we
// always just create the device inside our constructor, so we have no place to
// raise that event. The C# compiler warns us that the event is never used, but
// we don't care so we just disable this warning.
#pragma warning disable 67
namespace XNATransparentWindow
{
/// <summary>
/// Helper class responsible for creating and managing the GraphicsDevice.
/// All GraphicsDeviceControl instances share the same GraphicsDeviceService,
/// so even though there can be many controls, there will only ever be a single
/// underlying GraphicsDevice. This implements the standard IGraphicsDeviceService
/// interface, which provides notification events for when the device is reset
/// or disposed.
/// </summary>
class GraphicsDeviceService : IGraphicsDeviceService
{
#region Fields
// Singleton device service instance.
static GraphicsDeviceService singletonInstance;
// Keep track of how many controls are sharing the singletonInstance.
static int referenceCount;
#endregion
/// <summary>
/// Constructor is private, because this is a singleton class:
/// client controls should use the public AddRef method instead.
/// </summary>
GraphicsDeviceService(IntPtr windowHandle, int width, int height)
{
parameters = new PresentationParameters();
parameters.BackBufferWidth = Math.Max(width, 1);
parameters.BackBufferHeight = Math.Max(height, 1);
parameters.BackBufferFormat = SurfaceFormat.Vector4; // SurfaceFormat.Color;
parameters.DeviceWindowHandle = windowHandle;
parameters.PresentationInterval = PresentInterval.Immediate;
parameters.IsFullScreen = false;
graphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, parameters);
}
/// <summary>
/// Gets a reference to the singleton instance.
/// </summary>
public static GraphicsDeviceService AddRef(IntPtr windowHandle,
int width, int height)
{
// Increment the "how many controls sharing the device" reference count.
if (Interlocked.Increment(ref referenceCount) == 1)
{
// If this is the first control to start using the
// device, we must create the singleton instance.
singletonInstance = new GraphicsDeviceService(windowHandle,
width, height);
}
return singletonInstance;
}
/// <summary>
/// Releases a reference to the singleton instance.
/// </summary>
public void Release(bool disposing)
{
// Decrement the "how many controls sharing the device" reference count.
if (Interlocked.Decrement(ref referenceCount) == 0)
{
// If this is the last control to finish using the
// device, we should dispose the singleton instance.
if (disposing)
{
if (DeviceDisposing != null)
DeviceDisposing(this, EventArgs.Empty);
graphicsDevice.Dispose();
}
graphicsDevice = null;
}
}
/// <summary>
/// Resets the graphics device to whichever is bigger out of the specified
/// resolution or its current size. This behavior means the device will
/// demand-grow to the largest of all its GraphicsDeviceControl clients.
/// </summary>
public void ResetDevice(int width, int height)
{
if (DeviceResetting != null)
DeviceResetting(this, EventArgs.Empty);
parameters.BackBufferWidth = Math.Max(parameters.BackBufferWidth, width);
parameters.BackBufferHeight = Math.Max(parameters.BackBufferHeight, height);
graphicsDevice.Reset(parameters);
if (DeviceReset != null)
DeviceReset(this, EventArgs.Empty);
}
/// <summary>
/// Gets the current graphics device.
/// </summary>
public GraphicsDevice GraphicsDevice
{
get { return graphicsDevice; }
}
GraphicsDevice graphicsDevice;
// Store the current device settings.
PresentationParameters parameters;
// IGraphicsDeviceService events.
public event EventHandler<EventArgs> DeviceCreated;
public event EventHandler<EventArgs> DeviceDisposing;
public event EventHandler<EventArgs> DeviceReset;
public event EventHandler<EventArgs> DeviceResetting;
}
}
ServiceContainer.cs
#region File Description
//-----------------------------------------------------------------------------
// ServiceContainer.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
#endregion
namespace XNATransparentWindow
{
/// <summary>
/// Container class implements the IServiceProvider interface. This is used
/// to pass shared services between different components, for instance the
/// ContentManager uses it to locate the IGraphicsDeviceService implementation.
/// </summary>
public class ServiceContainer : IServiceProvider
{
Dictionary<Type, object> services = new Dictionary<Type, object>();
/// <summary>
/// Adds a new service to the collection.
/// </summary>
public void AddService<T>(T service)
{
services.Add(typeof(T), service);
}
/// <summary>
/// Looks up the specified service.
/// </summary>
public object GetService(Type serviceType)
{
object service;
services.TryGetValue(serviceType, out service);
return service;
}
}
}
ContentBuilder.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
namespace XNATransparentWindow
{
public class ContentBuilder : IDisposable
{
#region Fields
// What importers or processors should we load?
const string xnaVersion = ", Version=4.0.0.0, PublicKeyToken=842cf8be1de50553";
static string[] pipelineAssemblies =
{
"Microsoft.Xna.Framework.Content.Pipeline.FBXImporter" + xnaVersion,
"Microsoft.Xna.Framework.Content.Pipeline.XImporter" + xnaVersion,
"Microsoft.Xna.Framework.Content.Pipeline.TextureImporter" + xnaVersion,
"Microsoft.Xna.Framework.Content.Pipeline.EffectImporter" + xnaVersion,
"Microsoft.Xna.Framework.Content.Pipeline.AudioImporters" + xnaVersion,
"Microsoft.Xna.Framework.Content.Pipeline.VideoImporters" + xnaVersion,
// If you want to use custom importers or processors from
// a Content Pipeline Extension Library, add them here.
//
// If your extension DLL is installed in the GAC, you should refer to it by assembly
// name, eg. "MyPipelineExtension, Version=1.0.0.0, PublicKeyToken=1234567812345678".
//
// If the extension DLL is not in the GAC, you should refer to it by
// file path, eg. "c:/MyProject/bin/MyPipelineExtension.dll".
};
// MSBuild objects used to dynamically build content.
Project buildProject;
ProjectRootElement projectRootElement;
BuildParameters buildParameters;
List<ProjectItem> projectItems = new List<ProjectItem>();
//ErrorLogger errorLogger;
// Temporary directories used by the content build.
string buildDirectory;
string processDirectory;
string baseDirectory;
// Generate unique directory names if there is more than one ContentBuilder.
static int directorySalt;
// Have we been disposed?
bool isDisposed;
#endregion
#region Properties
/// Gets the output directory, which will contain the generated .xnb files.
public string OutputDirectory
{
get { return Path.Combine(buildDirectory, "bin/Content"); }
}
#endregion
#region Initialization
/// Creates a new content builder.
public ContentBuilder()
{
CreateTempDirectory();
CreateBuildProject();
}
/// Finalizes the content builder.
~ContentBuilder()
{
Dispose(false);
}
/// Disposes the content builder when it is no longer required.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// Implements the standard .NET IDisposable pattern.
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
isDisposed = true;
DeleteTempDirectory();
}
}
#endregion
#region MSBuild
/// Creates a temporary MSBuild content project in memory.
void CreateBuildProject()
{
string projectPath = Path.Combine(buildDirectory, "content.contentproj");
string outputPath = Path.Combine(buildDirectory, "bin");
// Create the build project.
projectRootElement = ProjectRootElement.Create(projectPath);
// Include the standard targets file that defines how to build XNA Framework content.
projectRootElement.AddImport("$(MSBuildExtensionsPath)\\Microsoft\\XNA Game Studio\\" +
"v4.0\\Microsoft.Xna.GameStudio.ContentPipeline.targets");
buildProject = new Project(projectRootElement);
buildProject.SetProperty("XnaPlatform", "Windows");
buildProject.SetProperty("XnaProfile", "Reach");
buildProject.SetProperty("XnaFrameworkVersion", "v4.0");
buildProject.SetProperty("Configuration", "Release");
buildProject.SetProperty("OutputPath", outputPath);
// Register any custom importers or processors.
foreach (string pipelineAssembly in pipelineAssemblies)
{
buildProject.AddItem("Reference", pipelineAssembly);
}
// Hook up our custom error logger.
//errorLogger = new ErrorLogger();
buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection);
//buildParameters.Loggers = new ILogger[] { errorLogger };
}
/// Adds a new content file to the MSBuild project. The importer and
/// processor are optional: if you leave the importer null, it will
/// be autodetected based on the file extension, and if you leave the
/// processor null, data will be passed through without any processing.
public void Add(string filename, string name, string importer, string processor)
{
ProjectItem item = buildProject.AddItem("Compile", filename)[0];
item.SetMetadataValue("Link", Path.GetFileName(filename));
item.SetMetadataValue("Name", name);
if (!string.IsNullOrEmpty(importer))
item.SetMetadataValue("Importer", importer);
if (!string.IsNullOrEmpty(processor))
item.SetMetadataValue("Processor", processor);
projectItems.Add(item);
}
/// Removes all content files from the MSBuild project.
public void Clear()
{
buildProject.RemoveItems(projectItems);
projectItems.Clear();
}
/// Builds all the content files which have been added to the project,
/// dynamically creating .xnb files in the OutputDirectory.
/// Returns an error message if the build fails.
public string Build()
{
// Create and submit a new asynchronous build request.
BuildManager.DefaultBuildManager.BeginBuild(buildParameters);
BuildRequestData request = new BuildRequestData(buildProject.CreateProjectInstance(), new string[0]);
BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(request);
submission.ExecuteAsync(null, null);
// Wait for the build to finish.
submission.WaitHandle.WaitOne();
BuildManager.DefaultBuildManager.EndBuild();
// If the build failed, return an error string.
if (submission.BuildResult.OverallResult == BuildResultCode.Failure)
{
//return string.Join("\n", errorLogger.Errors.ToArray());
}
return null;
}
#endregion
#region Temp Directories
/// Creates a temporary directory in which to build content.
void CreateTempDirectory()
{
// Start with a standard base name:
//
// %temp%\WinFormsContentLoading.ContentBuilder
baseDirectory = Path.Combine(Path.GetTempPath(), GetType().FullName);
// Include our process ID, in case there is more than
// one copy of the program running at the same time:
//
// %temp%\WinFormsContentLoading.ContentBuilder\<ProcessId>
int processId = Process.GetCurrentProcess().Id;
processDirectory = Path.Combine(baseDirectory, processId.ToString());
// Include a salt value, in case the program
// creates more than one ContentBuilder instance:
//
// %temp%\WinFormsContentLoading.ContentBuilder\<ProcessId>\<Salt>
directorySalt++;
buildDirectory = Path.Combine(processDirectory, directorySalt.ToString());
// Create our temporary directory.
Directory.CreateDirectory(buildDirectory);
PurgeStaleTempDirectories();
}
/// <summary>
/// Deletes our temporary directory when we are finished with it.
/// </summary>
void DeleteTempDirectory()
{
Directory.Delete(buildDirectory, true);
// If there are no other instances of ContentBuilder still using their
// own temp directories, we can delete the process directory as well.
if (Directory.GetDirectories(processDirectory).Length == 0)
{
Directory.Delete(processDirectory);
// If there are no other copies of the program still using their
// own temp directories, we can delete the base directory as well.
if (Directory.GetDirectories(baseDirectory).Length == 0)
{
Directory.Delete(baseDirectory);
}
}
}
/// <summary>
/// Ideally, we want to delete our temp directory when we are finished using
/// it. The DeleteTempDirectory method (called by whichever happens first out
/// of Dispose or our finalizer) does exactly that. Trouble is, sometimes
/// these cleanup methods may never execute. For instance if the program
/// crashes, or is halted using the debugger, we never get a chance to do
/// our deleting. The next time we start up, this method checks for any temp
/// directories that were left over by previous runs which failed to shut
/// down cleanly. This makes sure these orphaned directories will not just
/// be left lying around forever.
/// </summary>
void PurgeStaleTempDirectories()
{
// Check all subdirectories of our base location.
foreach (string directory in Directory.GetDirectories(baseDirectory))
{
// The subdirectory name is the ID of the process which created it.
int processId;
if (int.TryParse(Path.GetFileName(directory), out processId))
{
try
{
// Is the creator process still running?
Process.GetProcessById(processId);
}
catch (ArgumentException)
{
// If the process is gone, we can delete its temp directory.
Directory.Delete(directory, true);
}
}
}
}
#endregion
}
}
The font which you are loading is an xml file: any_name.spritefont
Example:
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains an xml description of a font, and will be read by the XNA
Framework Content Pipeline. Follow the comments to customize the appearance
of the font in your game, and to change the characters which are available to draw
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>Segoe UI Mono</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>14</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<!-- <DefaultCharacter>*</DefaultCharacter> -->
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start> </Start>
<End>~</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>
If you want the form to be click-through then you need to add a random
transparency key color to form and:
[DllImport("user32.dll", EntryPoint="GetWindowLong")]
static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
In public Form1()
IntPtr initialStyle = GetWindowLongPtr(this.Handle, -20);
SetWindowLong(this.Handle, -20, (IntPtr)((int)initialStyle | 0x20));
Feel free to correct any mistakes I have made.
I'm using OpenTK to make a C# OpenGL application.
I'm trying not to use the GameWindow class to open a context - here is my class managing a window + context :
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Platform;
using OpenTK.Input;
class Window
{
private INativeWindow window_;
private IGraphicsContext context_;
private bool isExiting_;
public Window(int width, int height, bool fullscreen, string title = "StormEngine application")
{
window_ = new NativeWindow(width, height, title, fullscreen ? GameWindowFlags.Fullscreen : GameWindowFlags.FixedWindow, GraphicsMode.Default, DisplayDevice.Default);
context_ = new GraphicsContext(GraphicsMode.Default, window_.WindowInfo, 4, 4, GraphicsContextFlags.Default);
context_.MakeCurrent(window_.WindowInfo);
window_.Visible = true;
window_.KeyDown += (sender, e) =>
{
if (e.Keyboard[Key.Escape])
window_.Close();
};
isExiting_ = false;
}
public Window(int width, int height) : this(width, height, false) { }
public bool EndFrame()
{
context_.SwapBuffers();
window_.ProcessEvents();
return (window_.Exists && !isExiting_);
}
}
I'm then calling Window w = new Window(800, 480); to open a window.
But as soon as I make a call to an OpenGL method, in my case int programId = GL.CreateProgram();, I am given a AccessViolationException.
My guess is that OpenGL function pointers are not loaded correctly, but I cannot find where the problem happens (I get no information about what happens inside GL.CreateProgam()).
Any idea of what might be happening, why, and how to resolve that ? :)
Forget it, I found my mistake :)
In OpenTK, a GraphicsContext needs, once created and made current, to be loaded.
To do so, just after context_.MakeCurrent(window_.WindowInfo);, I added :
(context_ as IGraphicsContextInternal).LoadAll()
I had to figure it out from the GameWindow class source on Github, tho, because this way to create a window isn't documented at all ^^
And it now works like a charm. :)
Can anybody explain me this Java code in C#, since I use Mono for Android? For example I can't find OnGlobalLayoutListener in Mono for Android.
On Android it looks like this:
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int newWidth, newHeight, oldHeight, oldWidth;
//the new width will fit the screen
newWidth = metrics.widthPixels;
//so we can scale proportionally
oldHeight = iv.getDrawable().getIntrinsicHeight();
oldWidth = iv.getDrawable().getIntrinsicWidth();
newHeight = Math.floor((oldHeight * newWidth) / oldWidth);
iv.setLayoutParams(new LinearLayout.LayoutParams(newWidth, newHeight));
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
//so this only happens once
iv.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
What is the Mono for Android equivalent?
OnGlobalLayoutListener is an interface, so in C# it is exposed as ViewTreeObserver.IOnGlobalLayoutListener. Since C# doesn't support anonymous classes as seen here in Java, you would need to provide an implementation of that interface and pass that into AddOnGlobalLayoutListener():
public class MyLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
public void OnGlobalLayout()
{
// do stuff here
}
}
vto.AddOnGlobalLayoutListener(new MyLayoutListener());
You can do this if you want, but the preferred way in Mono for Android is to use events in place of listener interfaces. In this case, it is exposed as the GlobalLayout event:
vto.GlobalLayout += (sender, args) =>
{
// do stuff here
};
You can get an instance of the ViewTreeObserver like this:
var contentView = activity.Window.DecorView.FindViewById(Android.Resource.Id.Content);
contentView.ViewTreeObserver.GlobalLayout += ViewTreeObserverOnGlobalLayout;
Here is an information from Android Developers website :
addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener
listener)
Register a callback to be invoked when the global layout state or the visibility of views within the view tree changes
Here is the link you can take a look : addOnGlobalLayoutListener. and here onGlobalLayoutListener