Jump to content

  • Log In with Google      Sign In   
  • Create Account

Nypyren

Member Since 19 Aug 2002
Offline Last Active Today, 07:18 PM

#5187535 DLL-Based Plugins

Posted by Nypyren on 16 October 2014 - 06:47 PM

If you're doing native DLLs (C, C++, etc), then it's going to be somewhat of a pain like Apoch says.

If you're using .Net-based DLLs (C#, VB.Net, etc), they are much less restrictive and MUCH more convenient to create and use.


Depending on how much work you want to put into it, it is also possible to have a (primarily) native C++ executable host .Net DLL plugins. Though this is still much more painful than when the EXE and DLLs are all .Net.


#5186802 3D coordinates transformation

Posted by Nypyren on 13 October 2014 - 07:40 PM

You want the camera's world position transformed into the sprite's local space, right?

My wild guess is that you might just not be inverting your sprite's world matrix.

So:

Matrix = Build a matrix representing the sprite's orientation (and position!!)
Matrix = Invert the matrix.
Multiply your camera's position vector by the matrix.


If you're already doing all of that, then it's probably just a typo somewhere.


#5185637 What language to choose?

Posted by Nypyren on 07 October 2014 - 04:29 PM

C#... I never touched. Mostly because it's tied to only Microsoft, which would be incredibly limiting in my opinion.


That was only true 10 years ago. C# runs on: Windows (Desktop), Windows (RT), OSX, Linux, Xbox 360, iOS, Android, WP7, WP8, WP8.1, and probably others that I haven't personally developed for yet. I have personally executed C# code on every single one of those platforms I mentioned.

If you want, you can develop for many of those platforms without ever using a Microsoft product.


#5185190 TGA RLE : Something wrong ?

Posted by Nypyren on 05 October 2014 - 04:18 PM

The pixel data starts at offset 18, which is <bh:ff>, which is after the ** in your hex display (I haven't seen that syntax for hex dumps before, but whatever).

FF 1B 1B FF -> FF (1 = RLE, 7F = 0x7F+1 = 128 count), RLE mode, so read one color block: color(RGB24) = 1B 1B FF -> Blue 1B, Green 1B, Red FF (note, this is the bottom left corner of the image, not the top left).
FF 1D 1D FF -> RLE:128, 1D 1D FF
D0 1D 1D FF -> RLE:81, 1D 1D FF
03 1E 1E ?? -> RUN:4, 1E 1E ??, ?? ?? ??, ?? ?? ??, ?? ?? ??

etc....

RLE/RUN is the highest bit: RLE = ((rleCountByte & 0x80) == 0x80);
Count is the low 7 bits, then add 1: Count = (rleCountByte & 0x7F) + 1;

When in RLE mode, you read 1 color block and repeat it Count times. When in RUN mode, you read Count color blocks. The number of bytes in the color block depends on the color mode of the image. This particular one is 24-bit in BGR byte order.

TGA supports bottom-up and top-down vertical layout, so make sure you read that info from the header. In most cases programs use bottom-up to match the conventions of other image formats.


NOTE: The reason why there is a 4-pixel run between your colors is because the border between each color is smoothed slightly.

Loading TGA RLE is pretty easy. Writing them is a bit more complex but still nothing too difficult.


#5183247 Source control with git hub/lab?

Posted by Nypyren on 27 September 2014 - 12:22 AM

Branches are not a list of commits. Branches are pointers to a single commit. Commits point to their parent(s). There is also a reflog of commits where the branch pointer has been recently, in case you need to examine or return to a point in your sequence of branch operations for some reason.

Making a new branch doesn't change anything until you commit to it. Even then, the commit doesn't care what branch it's in, it cares about it's parent commit. The branch pointer just happens to move with it.

When you understand that branches are just pointers to nodes in a graph of commits, you also realize that you're free to do almost anything you want to. You can move branches anywhere (with the reset command) - backwards, forwards, even to a totally unrelated commit. When you move a branch, you're not moving the commits. You're changing your viewpoint (your "working tree" in git terminology - the files that you can view, edit and then commit), and you're changing the location where new commits will be attached.

When you checkout a remote branch, you aren't editing that branch. You're making a separate branch with the same name that you can edit locally. Even though they have the same name at first glance, they are *different branches*. Your local branch is "tracking" the remote branch. This is just a convenience to let git commands infer the relationship between the two without you having to manually type it in each time. You're ALWAYS automatically working in a separate branch from everyone else, and cannot affect anyone else, until you push.

When you push, your computer copies your commits to the other computer, then asks it "please move your branch pointer to the same commit I'm pointing at...". It might deny this request, usually because someone else pushed a different set of commits without your computer being aware of it. This is a special rule the server uses to prevent people from losing work. When this happens, you need to properly join the two commit sub-graphs together such that the new commit you ask the server to move to is a descendant of the commit it is currently pointing to. You can do this in two ways: You can use fetch-and-rebase or pull. I prefer fetch-and-rebase.

The particular style you use for committing and dealing with branches in git is referred to as your "git workflow". The workflow is just the agreement the particular team has for how they go about coordinating their git repos.


At work, our workflow is:

- Checkout the branch that your sub-team is currently working on.
- Do some work, commit as necessary.
- Test your changes to make sure they work. Where I work, everyone that is able to make changes is also able to build and run the game on their own machine, and is expected to test their stuff before pushing it.
- IF code reviews are currently mandated, make a new branch at your commit, push that branch, and get it reviewed. After it's reviewed and given the thumbs-up, switch back to the local tracking branch and push it. Delete your code-review branch.


On some projects, this workflow can't be used! On some projects, you won't be able to build and test the change on your own machine. You need to have a dedicated build machine compile and test your changes instead. In THAT case, you want to push to a branch with a unique name each time, tell the build machine about your branch (it might automatically do this), and then merge back into the common branch after it tells you everything is OK.


#5182959 Reflection problem in C#

Posted by Nypyren on 25 September 2014 - 01:45 PM

You can implement 'dynamic' using the old ways:
 
if (someObject is float)
{
    float value = (float)someObject;
    // do stuff with it.
}
else if (someObject is YourEnumType)
{
    YourEnumType value = (YourEnumType)someObject;
    // do stuff with it.
}

Or a Dictionary<Type,Action<object>>:

table[typeof(float)] = FunctionFloat;
table[typeof(YourEnumType)] = FunctionYourEnumType;

void FunctionFloat(object o)
{
    var value = (float)o;
    // do stuff
}

void FunctionYourEnumType(object o)
{
   var value = (YourEnumType)o;
   // do stuff
}

And call it with:

table[someObject.GetType()](someObject);

Obviously this gets out of hand quite quickly.


#5182775 Web App Development?

Posted by Nypyren on 24 September 2014 - 07:35 PM

Why would you use an executable you have to download when you can do the same operation on a website?


If the web site supports the same things, then go for it. Otherwise, the reason is because you *can't* do all operations on a website.

The opinionated answer is because web technology is absolutely terrible in comparison to native app technology. As web technology advances, almost consistently poor design decisions are being made from an engineering point of view. Of course, if you're not an engineer, you may not care. From what I've seen, the vast majority of people working on new web technology are not engineers and don't care about designing new systems well.

If you're thinking of learning PHP, STOP. RIGHT. NOW. Pick a language that doesn't suck. Use Java or C#, or even C++. PHP is the language of choice for developing unmaintainable codebases. You don't want to start down that path.


#5181576 .net framework text generator

Posted by Nypyren on 19 September 2014 - 11:28 AM

.Net has a lot nicer printing support than Win32 did.

Here's another example of just printing text from a file:

http://msdn.microsoft.com/en-us/library/system.drawing.printing.printdocument.print(v=vs.110).aspx

You can obviously get the text from anywhere - it doesn't need to be a file. Your program can ask the user to type it in.

If you want to draw other things, like lines, boxes, etc - you can do that as well. Instead of "DrawString" just use the various other "Draw*" methods.


#5181355 The difference between Logic, Reasoning, and Thinking; Data, Information, and...

Posted by Nypyren on 18 September 2014 - 02:23 PM

"It is raining" is missing a lot of key information.


Exactly. Statements are always incomplete information. So is the information that an agent is receiving as input from the (virtual) world.

Learning processes need to take this into account. In humans, sensory data is fed from receptors to the nervous system, triggering reflexes and/or being processed by the brain. At some point, the brain may encode the data in a way that can be reasoned about.

This kind of data is never something you should unconditionally trust. What people see and hear is not a perfect representation of the world. You might be hallucinating. There might be a lot of noise that your brain is having a hard time filtering out (a torrent of raindrops on your windshield). Or you might have mental health issues which can do all kinds of bad things to your thoughts. Data also might be something intentionally misleading (a lie) that someone else has told you.

In a computer simulation, you might not need to take all these things into account - it's likely overcomplicating things. But if you want fault-tolerant AI, you need to treat data critically at all times.


I think an interesting way of doing that might be to reuse the concept of contexts during learning. Let's say you have your knowledge stored in a general purpose graph data structure. Now, your agent receives some new data that you want to try to store. First off, you can just add that data to the graph without connecting it to any existing nodes. Then you can begin searching for ways to connect it. Each time you attempt a new connection, you can search the graph to find out if contradictions were introduced by the new connection. If contradictions are found, you don't need to discard the data; you remove the connection and try somewhere else.

I imagine your knowledge graph would start out as several islands of data, and eventually as you start adding enough data, the islands could finally be connected to each other, loosely at first, perhaps forming stronger connections as your reasoning process has time to analyze and re-form those thoughts.

Perhaps you never find a way to connect the data to the rest of your graph. That's fine, you can leave it isolated. It's always possible that new data might arrive LATER which serves as a way to connect everything up. On the other hand, you don't want to collect data that you store forever if you can't process it. Some data IS going to be useless, noise, or nonsense, and you should discard it eventually. As your system refines its existing knowledge, some data will become redundant or get disconnected from the graph. Some data might stay connected, but will never be accessed by any important functions of the agent. You can use various techniques such as mark-and-sweep garbage collection and last-accessed timestamps to eventually eliminate data that you no longer need.



For a concrete example, let's think of a bot that's playing a first person shooter. One of the things the bot needs to do is learn how the navigate the level, and where powerups/ammo/health are located. If the level doesn't already have a navmesh precomputed by the level creator, the bot has to make this itself.

The data that it "learns" would be the set of 3D positions which represent places where it can move, and 3D positions and types of objects that are important. These points can be connected together in a graph. The bot might spawn into a room and start sampling its immediate vicinity with line probes (collision detection). If there is an unobstructed path from where it is to the other end of the line probe, it can add that link as a walkable path to its knowledge.

However, perhaps the line probe went through a small window that the bot can't actually navigate through. When the bot attempts to follow that edge in its navigation graph, it will get stuck. At this point, if there is code which notices that the bot is stuck, it can "learn" that the edge is a false positive and mark it as untraversable. It might retain the link in case it has a sniping behavior, since the bot can still shoot through that window if it wanted to.

If a player comes in and kills the bot, and the bot respawns somewhere completely different, their new room probably doesn't connect to the one they were just in. But as they move through the level, they may eventually form a complete connection between the two rooms. So the old knowledge and new knowledge doesn't need to be discarded just because it can't be connected for a while. At the same time, perhaps the level is divided into two halves which have no way of getting between. The bot shouldn't discard one half of its map data, since when it respawns, it has a chance of landing on either half of the map, and it will have knowledge about wherever it respawns already prepared as long as it keeps it.


#5181209 The difference between Logic, Reasoning, and Thinking; Data, Information, and...

Posted by Nypyren on 18 September 2014 - 02:05 AM

Also be sure to consider: http://en.wikipedia.org/wiki/Domain_of_discourse

Pretty much any concept can be evaluated to mean different things (and be true or false) depending on what context you evaluate them in. Some examples of statements that you could try evaluating for truth, which are obviously affected by the current context (by no means comprehensive):

Time:
"The president is Abe Lincoln." -> If you said this in the past, it would have been true then, but it's not now. But it's nuanced: If you found a document from back then with this written in it, what the document is saying is true in the context of the document itself, even if you're reading that document today where the statement by itself is false. The same thing can be said of fictional stories - they have internal consistencies which may make no sense when compared against our reality, but which make perfect sense in the story.

Place:
"It's raining." -> True where I am right now, but obviously not true in various other places.

Universe (fictional, mental, real, etc):
"I cast magic missile!" -> You're talking about a game (I hope).


#5181097 reading float from binary file (endian problem?)

Posted by Nypyren on 17 September 2014 - 01:52 PM

The spot that achild pointed out looks like the culprit to me as well. Any time you conditionally write to the file, you need to make sure the reader also follows the exact same condition check.

Sometimes this means writing a "is the next section present ? 1 : 0" byte into the file. More advanced formats include things like "the next section is type X, and it is length Y" so that a reader that doesn't know how to read type X (or perhaps one which doesn't care and just wants to skip to a section that it's currently interested in reading) can just skip over Y bytes and try the next section.

A pattern that comes up a lot is:
 
// Writing
WriteByte(obj != null ? 1 : 0);
if (obj != null)
{
   WriteObjectFields(obj);
}

// Reading
byte objectPresent = ReadByte();
if (objectPresent)
{
    obj = new Object();
    ReadObjectFields(obj);
}
else
{
    obj = null;
}



#5180937 reading float from binary file (endian problem?)

Posted by Nypyren on 17 September 2014 - 01:39 AM

Since the 20 and 42 are in the same order, it's not a byte endian problem. And I doubt you're on a platform that swaps 16-bit words around, either.

The most likely problem is that your reader and writer code don't match the amount of data they're handling. For example, if something in your read code reads an extra two bytes before the float is read, then the file pointer will be in the middle of the float when you try to read it, instead of at the beginning of the float. Ex:

 
  ???            0.0           40.0          1.0
(other stuff?) | 00 00 00 00 | 00 00 20 42 | 00 00 80 3F |
                                     ^ file pointer before you read your float, so [20 42 | 00 00] will be read (0x00004220) => 2.372118E-41
If you're freading/fwriting whole structs in your code somewhere, then you may want to read up on struct packing: http://stackoverflow.com/questions/3318410/pragma-pack-effect


#5180768 ASCII-graphic: using Console vs. API

Posted by Nypyren on 16 September 2014 - 11:58 AM

I use the TextOut function to do this for ASCII rendering in my hexeditor.

Pros:
- You can print all characters, even ones unprintable in the Console.
- You can use any colors you want.
- Ridiculously fast.

Cons:
- Requires a lot of P/Invoke and therefore will likely only run on Windows.
 
public class ASCIIPalette
{
	uint[] fg; // foreground color table
	uint[] bg; // background color table
	byte[] xl; // character translation table
		
	public uint[] FG { get { return fg;} }
	public uint[] BG { get { return bg;} }
	public byte[] XL { get { return xl;} }
		
	public ASCIIPalette()
	{
		fg = new uint[256];
		bg = new uint[256];
		xl = new byte[256];
			
		uint black  = GDI.RGB(0,0,0);
		uint lgrey  = GDI.RGB(160,160,160);
		uint dgrey  = GDI.RGB(112,112,112);
		uint yellow = GDI.RGB(255,255,0);
			
		// Set default colors
		for (int i=0; i<256; ++i)
		{
			bg[i] = black;
				
			switch (i)
			{
				case 0:
					fg[i] = dgrey;
					xl[i] = 177;
					break;
					
				case 255:
					fg[i] = yellow;
					xl[i] = 177;
					break;
				
				default:
					fg[i] = lgrey;
					xl[i] = (byte)i;
					break;
			}
		}
	}
}
	
[SuppressUnmanagedCodeSecurity]
public class ASCIIUtil
{
	static byte[] buffer = new byte[256];
		
	public static unsafe void Paint(IntPtr hdc, ASCIIPalette palette, byte[] data, int x, int y, int count)
	{
		// 1 = some random value that shouldn't be used.
		uint fgOld=1, fgNew=1;
		uint bgOld=1, bgNew=1;
			
		GDI.TextMetric textmetric = new GDI.TextMetric();
			
		GDI.SelectObject(hdc, GDI.GetStockObject(GDI.StockObject.OemFixedFont));
		GDI.GetTextMetrics(hdc, ref textmetric);
		int fontwidth = textmetric.AveCharWidth;
			
		int i=0;  // buffer insertion index
		int bX=x; // batch X coordinate
			
		fixed (byte *fixedData = data)
		fixed (byte *fixedBuffer = buffer)
		fixed (uint *paletteFG = palette.FG)
		fixed (uint *paletteBG = palette.BG)
		fixed (byte *paletteXL = palette.XL)
		{
			for (int a=0; a<count; ++a)
			{
				bool flush = false;
				byte current = fixedData[a];
					
				fgNew = paletteFG[current];
				bgNew = paletteBG[current];
					
				// Flush conditions
                if (i > 255)
                {
                    GDI.SetTextColor(hdc, fgNew);
                    GDI.SetBkColor  (hdc, bgNew);
                    flush = true;
                }
                else if (fgNew != fgOld || bgNew != bgOld)
                {
                    GDI.SetTextColor(hdc, fgOld);
                    GDI.SetBkColor  (hdc, bgOld);

                    fgOld = fgNew;
                    bgOld = bgNew;

                    flush = true;
                }
					
				if (flush && i > 0)
				{
					GDI.TextOutA(hdc, bX, y, fixedBuffer, i);
					i=0;
					bX = x + a*fontwidth;
				}
					
				fixedBuffer[i++] = paletteXL[current];
			}
				
			// Write out the final batch
			GDI.SetTextColor(hdc, fgNew);
			GDI.SetBkColor  (hdc, bgNew);

			GDI.TextOutA(hdc, bX, y, fixedBuffer, i);
		}
	}
		
	public static int GetTextWidth(IntPtr hdc)
	{
		GDI.TextMetric tm = new GDI.TextMetric();
		GDI.SelectObject(hdc, GDI.GetStockObject(GDI.StockObject.OemFixedFont));
		GDI.GetTextMetrics(hdc, ref tm);
		return tm.AveCharWidth;
	}
	public static int GetTextHeight(IntPtr hdc)
	{
		GDI.TextMetric tm = new GDI.TextMetric();
		GDI.SelectObject(hdc, GDI.GetStockObject(GDI.StockObject.OemFixedFont));
		GDI.GetTextMetrics(hdc, ref tm);
		return tm.Height;
	}
}

public static class GDI
{
	public enum StockObject : int
	{
		OemFixedFont = 10
	}
	
	public enum BmpUsage : uint
	{
		RGB = 0,
		PAL = 1
	}
		
	public enum RasterOp : uint
	{
			// Binary raster ops
			Black          = 1,  //  0
			NotMergePen    = 2,  // DPon
			MaskNotPen     = 3,  // DPna
			NotCopyPen     = 4,  // PN
			MaskPenNot     = 5,  // PDna
			Not            = 6,  // Dn
			XorPen         = 7,  // DPx
			NotMaskPen     = 8,  // DPan
			MaskPen        = 9,  // DPa
			NotXorPen      = 10, // DPxn
			Nop            = 11, // D
			MergeNotPen    = 12, // DPno
			CopyPen        = 13, // P
			MergePenNot    = 14, // PDno
			MergePen       = 15, // DPo
			White          = 16, //  1
			Last           = 16,
			/// <summary>
			/// Dest = Source
			/// </summary>
			SrcCopy        = 0x00CC0020,
			/// <summary>
			/// Dest = Source | Dest
			/// </summary>
			SrcPaint       = 0x00EE0086,
			/// <summary>
			/// Dest = Source & Dest
			/// </summary>
			SrcAnd         = 0x008800C6,
			/// <summary>
			/// Dest = Source ^ Dest
			/// </summary>
			SrcInvert      = 0x00660046,
			/// <summary>
			/// Dest = Source & ~Dest
			/// </summary>
			SrcErase       = 0x00440328,
			/// <summary>
			/// Dest = ~Source
			/// </summary>
			NotSrcCopy     = 0x00330008,
			/// <summary>
			/// Dest = ~Source & ~Dest
			/// </summary>
			NotSrcErase    = 0x001100A6,
			/// <summary>
			/// Dest = Source & Pattern
			/// </summary>
			MergeCopy      = 0x00C000CA,
			/// <summary>
			/// Dest = ~Source | Dest
			/// </summary>
			MergePaint     = 0x00BB0226,
			/// <summary>
			/// Dest = Pattern
			/// </summary>
			PatCopy        = 0x00F00021,
			/// <summary>
			/// Dest = DPSnoo
			/// </summary>
			PatPaint       = 0x00FB0A09,
			/// <summary>
			/// Dest = Pattern ^ Dest
			/// </summary>
			PatInvert      = 0x005A0049,
			/// <summary>
			/// Dest = ~Dest
			/// </summary>
			DstInvert      = 0x00550009,
			/// <summary>
			/// Dest = Black
			/// </summary>
			Blackness      = 0x00000042,
			/// <summary>
			/// Dest = White
			/// </summary>
			Whiteness      = 0x00FF0062,
			/// <summary>
			/// Do not mirror the bitmap in this call
			/// </summary>
			NoMirrorBitmap = 0x80000000,
			/// <summary>
			/// Include layered windows
			/// </summary>
			CaptureBlt     = 0x40000000
	}
		
	/// <summary>
	/// Edge Types for DrawEdge.
	/// </summary>
	public enum Edge : int
	{
		RaisedOuter = 0x0001,
		SunkenOuter = 0x0002,
		RaisedInner = 0x0004,
		SunkenInner = 0x0008,
		Raised      = RaisedOuter | RaisedInner,
		Sunken      = SunkenOuter | SunkenInner,
		Etched      = SunkenOuter | RaisedInner,
		Bump        = RaisedOuter | SunkenInner,
	}
		
	/// <summary>
	/// Border Flags for DrawEdge.
	/// </summary>
	public enum BorderFlags : int
	{
		Left                   = 0x0001,
		Top                    = 0x0002,
		Right                  = 0x0004,
		Bottom                 = 0x0008,
		TopLeft                = (Top | Left),
		TopRight               = (Top | Right),
		BottomLeft             = (Bottom | Left),
		BottomRight            = (Bottom | Right),
		EntireRect             = (Top | Bottom | Left | Right),
		Diagonal               = 0x0010,
		DiagonalEndTopRight    = (Diagonal | Top | Right),
		DiagonalEndTopLeft     = (Diagonal | Top | Left),
		DiagonalEndBottomLeft  = (Diagonal | Bottom | Left),
		DiagonalEndBottomRight = (Diagonal | Bottom | Right),
		Middle                 = 0x0800,  // Fill in the middle
		Soft                   = 0x1000,  // For softer buttons
		Adjust                 = 0x2000,  // Calculate the space left over
		Flat                   = 0x4000,  // For flat rather than 3D borders
		Mono                   = 0x8000,  // For monochrome borders
	}

		
	[StructLayout(LayoutKind.Sequential)]
	public struct TextMetric
	{
		public int Height;
		public int Ascent;
		public int Descent;
		public int InternalLeading;
		public int ExternalLeading;
		public int AveCharWidth;
		public int MaxCharWidth;
		public int Weight;
		public int Overhang;
		public int DigitizedAspectX;
		public int DigitizedAspectY;
		public char FirstChar;
		public char LastChar;
		public char DefaultChar;
		public char BreakChar;
		public byte Italic;
		public byte Underlined;
		public byte StruckOut;
		public byte PitchAndFamily;
		public byte CharSet;
	}
		
		
	[StructLayout(LayoutKind.Sequential)]
	public struct Rect
	{
		public Rect(int left, int top, int width, int height)
		{
			this.left = left;
			this.top = top;
			this.right = left+width-1;
			this.bottom = top+height-1;
		}
			
		public int left;
		public int top;
		public int right;
		public int bottom;
	}
		
	public enum BitmapInfoCompression : uint
	{
		RGB       = 0,
		RLE8      = 1,
		RLE4      = 2,
		Bitfields = 3,
		JPEG      = 4,
		PNG       = 5
	}
		
	[StructLayout(LayoutKind.Sequential)]
	public struct BitmapInfo
	{
		public BitmapInfo(int width, int height)
		{
			Size=40;
			Width=width;
			Height=height;
			Planes=1;
			BitCount=32;
			Compression=0;
			SizeImage=0;
			XPelsPerMeter=0;
			YPelsPerMeter=0;
			ClrUsed=0;
			ClrImportant=0;
		}
		public uint Size;
		/// <summary>
		/// Width in pixels.
		/// </summary>
		public int Width;
		/// <summary>
		/// Height in pixels.
		/// </summary>
		public int Height;
		/// <summary>
		/// Planes.  Must be set to one.
		/// </summary>
		public ushort Planes;
		/// <summary>
		/// Bits per pixel.
		/// </summary>
		public ushort BitCount;
		/// <summary>
		/// Compression type.
		/// </summary>
		public BitmapInfoCompression Compression;
		/// <summary>
		/// Size, in bytes of the image.  If Compression is set to BitmapInfoCompression.RGB, use 0.
		/// </summary>
		public uint SizeImage;
		/// <summary>
		/// Horizontal pixels-per-meter
		/// </summary>
		public int XPelsPerMeter;
		/// <summary>
		/// Vertical pixels-per-meter
		/// </summary>
		public int YPelsPerMeter;
		/// <summary>
		/// Amount of colors used in the color table (use zero).
		/// </summary>
		public uint ClrUsed;
		/// <summary>
		/// Amount of colors requires (use zero).
		/// </summary>
		public uint ClrImportant;
	}
		
	
	/// <summary>
	/// Select a GDI Object into the specified DC
	/// </summary>
	/// <param name="hdc">Handle to the DC to select into</param>
	/// <param name="obj">Handle to the object to select</param>
	/// <returns>Handle to the old selected object</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);
		
	/// <summary>
	/// Gets an object handle for a stock (system-provided) object.
	/// </summary>
	/// <param name="stockobject">The stock object identifier</param>
	/// <returns>Handle to the specified object</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static extern IntPtr GetStockObject(StockObject stockobject);
		
	/// <summary>
	/// Get the text metrics for the currently selected font object on the specified DC
	/// </summary>
	/// <param name="hdc">Handle to the DC to use</param>
	/// <param name="tm">The TextMetric struct to fill with info</param>
	/// <returns>True if the function succeeds, False if it fails</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static extern bool GetTextMetrics(IntPtr hdc, ref TextMetric tm);
		
	/// <summary>
	/// Writes text to the specified DC using the pre-selected font
	/// </summary>
	/// <param name="hdc">Handle to the DC to write on</param>
	/// <param name="X">Initial X position</param>
	/// <param name="Y">Initial Y Position</param>
	/// <param name="chars">A byte ASCII string pointer</param>
	/// <param name="length">The number of ASCII chars to write</param>
	/// <returns>True on success, False otherwise</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static unsafe extern bool TextOutA(IntPtr hdc, int X, int Y, byte *chars, int length);
		
	/// <summary>
	/// Sets the text foreground color for the specified DC
	/// </summary>
	/// <param name="hdc">Handle to the DC to use</param>
	/// <param name="color">The new color to use</param>
	/// <returns>The previous selected foreground color</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static extern uint SetTextColor(IntPtr hdc, uint color);
		
	/// <summary>
	/// Sets the text background color for the specified DC
	/// </summary>
	/// <param name="hdc">Handle to the DC to use</param>
	/// <param name="color">The new color to use</param>
	/// <returns>The previous selected background color</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static extern uint SetBkColor(IntPtr hdc, uint color);
		
	/// <summary>
	/// Gets the system dialog base units.
	/// </summary>
	/// <returns>Loword: Horizontal, Hiword: Vertical</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("user32.dll")]
	public static extern uint GetDialogBaseUnits();
		
	/// <summary>
	/// Converts dialog units to screen units.
	/// </summary>
	/// <param name="hwnd">Handle to a dialog box (other windows are invalid).</param>
	/// <param name="rect">The Rect to convert.</param>
	/// <returns>True: Success, False: Failure</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("user32.dll")]
	public static extern bool MapDialogRect(IntPtr hwnd, ref Rect rect);
		
	/// <summary>
	/// Creates a DC that is compatible with an existing DC.
	/// </summary>
	/// <param name="hdc">The existing DC to use.</param>
	/// <returns>The compatible DC.</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

	#if UNSAFE
	// TODO: finalize DIB Section help
	/// <summary>
	/// Creates a DeviceIndependateBitmap Section.
	/// </summary>
	/// <param name="hdc">DC to use.</param>
	/// <param name="bitmapinfo">Information used to create the DIB.</param>
	/// <param name="usage">Specifies whether to use RGB or palette info.</param>
	/// <param name="data">Receives a pointer to the actual bitmap data.</param>
	/// <param name="mappingsection">Handle to a mapped file to use to create the DIB (or null).</param>
	/// <param name="offset">Offset in the mapped file to start (ignored if mappingsection is null).</param>
	/// <returns>The DIB Section.</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static unsafe extern IntPtr CreateDIBSection(IntPtr hdc, BitmapInfo bitmapinfo, BmpUsage usage, ref byte* data, IntPtr mappingsection, uint offset);
	#endif
		
	/// <summary>
	/// Copies bitmap data from one DC to another.
	/// </summary>
	/// <param name="hdcdest">Destination DC.</param>
	/// <param name="xdest">X coordinate of the upper left corner of the destination.</param>
	/// <param name="ydest">Y coordinate of the upper left corner of the destination.</param>
	/// <param name="width">Width of data to copy.</param>
	/// <param name="height">Height of data to copy.</param>
	/// <param name="hdcsrc">Source DC.</param>
	/// <param name="xsrc">X coordinate of the upper left corner of the source.</param>
	/// <param name="ysrc">Y coordinate of the upper left cornet of the source.</param>
	/// <param name="ROP">Raster operation used when writing to the destination.</param>
	/// <returns>True: Success, False: Failure</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static extern bool BitBlt(IntPtr hdcdest, int xdest, int ydest, int width, int height, IntPtr hdcsrc, int xsrc, int ysrc, RasterOp ROP);
		
	/// <summary>
	/// Gets the DC for a window.
	/// </summary>
	/// <param name="hwnd">Window handle.</param>
	/// <returns>The DC.</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("user32.dll")]
	public static extern IntPtr GetDC(IntPtr hwnd);
		
	/// <summary>
	/// Releases a DC previously retrieved from GetDC.
	/// </summary>
	/// <param name="hwnd">The Window that owns the DC.</param>
	/// <param name="hdc">The DC to release.</param>
	/// <returns>True: Success, False: Failure</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("user32.dll")]
	public static extern bool ReleaseDC(IntPtr hwnd, IntPtr hdc);
		
	/// <summary>
	/// Deletes a DC.
	/// </summary>
	/// <param name="hdc">The DC to delete.</param>
	/// <returns>True: Success, False: Failure</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static extern bool DeleteDC(IntPtr hdc);
		
	/// <summary>
	/// Deletes a GDI Object.
	/// </summary>
	/// <param name="hobj">The Object to delete.</param>
	/// <returns>True: Success, False: Failure</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("gdi32.dll")]
	public static extern bool DeleteObject(IntPtr hobj);
		
	/// <summary>
	/// Draws one or more edges of a rectangle.
	/// </summary>
	/// <param name="hdc">The DC to draw to.</param>
	/// <param name="qrc">Rectangle defining the box to draw.</param>
	/// <param name="edge">Type of edge.</param>
	/// <param name="grfFlags">Type of border.</param>
	/// <returns>True: Success, False: Failure</returns>
	[SuppressUnmanagedCodeSecurity]
	[DllImport("user32.dll")]
	public static extern bool DrawEdge(IntPtr hdc, ref Rect qrc, Edge edge, BorderFlags grfFlags);
		
	/// <summary>
	/// Create a uint (COLORREF compatible) from red, green, blue components
	/// </summary>
	/// <param name="r">Red amount (0-255)</param>
	/// <param name="g">Green amount (0-255)</param>
	/// <param name="b">Blue amount (0-255)</param>
	/// <returns>The packed color value</returns>
	public static uint RGB(int r, int g, int b)
	{
		return (uint)(r + (g<<8) + (b<<16));
	}
}
IntPtr hdc comes from this in a WinForms-style OnPaint method:
 
protected override void OnPaint(PaintEventArgs e)
{
    IntPtr hdc = e.Graphics.GetHdc();

    // Calls to ASCIIUtil.Paint here
}
In my ASCIIPalette above, characters 0 and 0xFF are altered slightly to make them render differently from 0x20 (the space character). Normally characters 0, 0x20 and 0xFF all render as an empty glyph (in the OEM font anyway), and in my hexeditor it's important to be able to visually differentiate all bytes.

Also keep in mind that my code is not thread-safe due to the use of that static buffer.


#5180375 My teams all hate eachother.

Posted by Nypyren on 14 September 2014 - 10:54 PM

Once upon a time, several coworkers and I talked about making a game in our free time (I work at a large game development studio, but we make games that we don't actually like to play ourselves). I got a basic skeleton of the game code thrown together, one of the artists whipped up some cool art, then everyone else lost motivation and I salvaged it as a simple tech demo.

What went wrong in our case?

- Everyone on the team (except for me) was only motivated by money. The main goal was to quickly throw together a vertical slice (fully functional demonstration of the core feature set) and then pitch it to management (so that we could get paid to work on it - at work), but we didn't even make it that far before everyone fizzled out, because we had...

- No leadership. We had the high-level picture of what we wanted to make, but NOBODY could agree on the details. If you can't agree on details, you can't actually implement them.


#5180289 "Permanent" pointer-like references in C#? Or design that works for t...

Posted by Nypyren on 14 September 2014 - 12:25 PM

The problem with .Net is that the class instances can actually move around in memory at runtime due to garbage collection, so you can't store a simple, long-term pointer to the address of one of their members (at least, not in the normal way you'd expect - you CAN do this with delegates).

You could make a Variable<T> class, using it to define each of the player's parameters. This would let you get a long-term reference, similar to a pointer. The downside is that it's still pretty cumbersome to use. It also incurs a performance penalty for all normal operations, not just pointer-like operations.

You could make a class which uses reflection, storing the Object and FieldInfo you want to affect, but then you lose all type safety. Performance is also not as good as it could be.

The other consideration is that you probably want to be able to create a Potion without binding it to a Player immediately. What if you're in a game with multiple players/characters and want to give the potion to a different character? You don't want to have the potion point to a player directly until you need to use it.


Here's an option that is fairly simple and clean:
 
public class Player
{
    public float HitPoints;
}

public delegate void ApplyEffectToPlayer(Player player);

public class Potion
{
    public ApplyEffectToPlayer ApplyTo;

    public Potion(ApplyEffectToPlayer applyTo)
    {
        ApplyTo = applyTo;
    }

    public void Use(Player player)
    {
        ApplyTo(player);
    }
}

public class PotionFactory
{
    public static Potion CreateHealthPotion(float healingAmount)
    {
        return new Potion(player => player.HitPoints += healingAmount);
    }
}





PARTNERS