Quantcast
Channel: GameDev.net
Viewing all 17825 articles
Browse latest View live

Preview: Reliable UDP implementation, lockstep, LAN, and parity bit checking

$
0
0

Introduction


Whether you're interested in making an FPS or RTS, you've probably heard that you should use UDP. It's probably because of its speed. Using TCP with the TCP_NODELAY option (which means it doesn't wait for enough data to be buffered before sending) may not be enough as TCP also does congestion control and you may want to do voice chat, which is better done using a lossy protocol like UDP. TCP doesn't allow you to adjust the “sliding window”, which means you might not reach the full speed of the communication channel if it has a high delay (search for “bandwidth-delay factor” for more information). If one packet doesn't arrive in TCP and is lost, TCP stops all traffic flow until it arrives, resulting in pauses. The packet header in TCP is also 20 bytes, as opposed to 6 bytes in UDP plus a few for reliability. Combining TCP and UDP is not an option as one induces packet loss in the other. So you should use UDP, but how do you guarantee packets are delivered and in the order they were sent in? Also, if you're making an RTS, how do you make sure that clients are running exactly the same simulation? A small change can cause a butterfly effect and be the difference between one player winning and losing. For this you need reliable UDP (RUDP) and lockstep. In addition, you probably want to use the latest client-server methodology, which allows the game to not be held back by the slowest player, as is done in the older peer-to-peer model. Along the way we'll cover parity bit checking to ensure packet correctness and integrity. We'll also cover LAN networking and setting up a matchmaking server.

Note:  This free article version covers only the UDP implementation. If you are interested in the full-breadth of the topic discussed above, you can purchase the complete 70-page PDF document on the GDNet Marketplace.


Library


The library that I use for networking is SDL2_net. This is just abstraction of networking to allow us to deploy to different platforms like Windows, Linux, iPhone, and Android. If you want, you can use WinSock or the native networking functions of Linux. They're all pretty much the same, with a few differences in initialization and function names. But I would recommand SDL2_net as that is cross-platform. Watch for a possible article on setting up and compiling libraries for details on how to set up SDL2_net for use in your projects if you aren't able to do this yourself. By the way, if you're doing voice chat, try PortAudio for getting microphone input on Windows, Mac, and Linux. For transmitting you'd need Speex or the newer Opus speech codec for encoding the data.

Packet Header


So let's jump in. The way you will keep track of the order packets are sent in and also guarantee delivery is by using an ack or sequence number. We will make a header that will be at the beginning of each packet we send. We also need to know what kind of packet this is.

struct PacketHeader
{
	unsigned short type;
	unsigned short ack;
};

To make sure the compiler packs the packet data as tightly as possible, to reduce network data usage, we can remove byte padding by putting this around all of our packet definitions.

// byte-align structures
#pragma pack(push, 1)

//.. packets go here ÃÃÃâÃÃÃæ

// default alignment
#pragma pack(pop)

By the way, they're not really packets; packets are what we use in TCP, but the subset in UDP is called a datagram. UDP stands for “user datagram protocol”. But I call them packets.

Control/Protocol Packets


Let's define some control/protocol packets that will be part of our reliable UDP protocol.

#define PACKET_NULL						0
#define PACKET_DISCONNECT				1
#define PACKET_CONNECT					2
#define PACKET_ACKNOWLEDGMENT			3
#define PACKET_NOCONN					4
#define PACKET_KEEPALIVE					5
#define PACKET_NACK						6

struct BasePacket
{
	PacketHeader header;
};

typedef BasePacket NoConnectionPacket;
typedef BasePacket AckPacket;
typedef BasePacket KeepAlivePacket;

struct ConnectPacket
{
	PacketHeader header;
	bool reconnect;
	unsigned short yourlastrecvack;
	unsigned short yournextrecvack;
	unsigned short yourlastsendack;
};

struct DisconnectPacket
{
	PacketHeader header;
};

We will send a ConnectPacket to establish a new connection to a computer that is listening on a certain port. If you're behind a router and have several computers behind it, your router doesn't know which computer to forward an incoming connection to unless that computer has already sent data from that port outside. This is why a matchmaking server is needed, unless playing on LAN. More will be covered later.

We will send a DisconnectPacket to end a connection. We need to send an AckPacket (acknowledgment) whenever we receive a packet type that is supposed to be reliable (that needs to arrive and be processed in order and cannot be lost). If the other side doesn't receive the ack, it will keep resending until it gets one or times out and assumes the connection to be lost. This is called “selective repeat” reliable UDP.

Occasionally, we might not need to send anything for a long time but tell the other side to keep our connection alive, for example if we've connected to a matchmaking server and want to tell it to keep our game in the list. When this happens, we need to send a KeepAlivePacket.

Almost all of these packets are reliable, as defined in the second paragraph on this page, except for the AckPacket and NoConnectionPacket. When we're first establishing a connection, we will use the ack number to set as the start of our sequence. When there's an interruption and the other side has dropped us due to timeout, but we still have a connection to them, they will send a NoConnectionPacket, which is not reliable, but is sent every time we receive a packet from an unknown source (whose address we don't recognize as having established a connection to). Whenever this happens, we have to do a full reconnect as the sequence/ack numbers can't be recovered. This is important because we will have a list of packets that we didn't get a reply to that we will resend that need to be acknowledged. You will understand more as we talk about how ack/sequence numbers work.

Lastly, the AckPacket's ack member is used to tell the other side which packet we're acknowledging. So an ack packet is not reliable, and is sent on an as-needed basis, when we receive a packet from the other side.

User Control/Protocol Packets


The rest of the packet types are user-defined (that's you) and depend on the type of application needed. If you're making a strategy game, you'll have a packet type to place a building, or to order units around. But there are also some control/protocol packets needed that are not part of the core protocol. These are packets for joining game sessions, getting game host information, getting a list of game hosts, informing a game client that the game room is full or that his version is older. Here are some suggested packet types for a multiplayer RTS or any kind of strategy game, but may also apply to FPS. These are used after a connection has been established.

#define PACKET_JOIN						7
#define PACKET_ADDSV					8
#define PACKET_ADDEDSV					9
#define PACKET_GETSVLIST					10
#define PACKET_SVADDR					11
#define PACKET_SVINFO					12
#define PACKET_GETSVINFO					13
#define PACKET_SENDNEXTHOST				14
#define PACKET_NOMOREHOSTS				15
#define PACKET_ADDCLIENT					16
#define PACKET_SELFCLIENT				17
#define PACKET_SETCLNAME				18
#define PACKET_CLIENTLEFT				19
#define PACKET_CLIENTROLE				20
#define PACKET_DONEJOIN					21
#define PACKET_TOOMANYCL				22
#define PACKET_MAPCHANGE				23
#define PACKET_CLDISCONNECTED			24
#define PACKET_CLSTATE					25
#define PACKET_CHAT						26
#define PACKET_MAPSTART					27
#define PACKET_GAMESTARTED				28
#define PACKET_WRONGVERSION				29
#define PACKET_LANCALL					30
#define PACKET_LANANSWER				31

Lockstep Control Packets


Once a game has been joined and started, RTS games also need to control the simulation to keep it in sync on all sides. A class of packet types that control the lockstep protocol we will call the lockstep control packets.

#define PACKET_NETTURN					29
#define PACKET_DONETURN					30

User Command / In-Game Packets


Any user command or input that effects the simulation needs to be bundled up inside a NetTurnPacket by the host and sent to all the clients as part of lockstep. But first it is sent to the host on its own. More on this later. Here are some example packets I use.

#define PACKET_PLACEBL					32
#define PACKET_CHVAL					33
#define PACKET_ORDERMAN					34
#define PACKET_MOVEORDER				35
#define PACKET_PLACECD					36

Initialization


You should really read some C/C++ UDP or SDL2_net UDP example code before you try to proceed with this, and the basics of that are not what I'm covering here. But nevertheless, I will mention that you need to initialize before you use SDL2_net or WinSock.

	if(SDLNet_Init() == -1) 
	{ 
		char msg[1280]; 
		sprintf(msg, "SDLNet_Init: %s\n", SDLNet_GetError());
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", msg, NULL);
	}


Here are some examples for low-level (not using SDL) UDP networking:

https://www.cs.rutgers.edu/~pxk/417/notes/sockets/udp.html
http://www.binarytides.com/programming-udp-sockets-c-linux/
http://www.codeproject.com/Articles/11740/A-simple-UDP-time-server-and-client-for-beginners

They all cover the same material. You just need to know the basics of how to intialize, send data, and receive data using UDP (not TCP).

Net Update Loop


So somewhere in your frame loop, where you update your game state and render everything, you will add a call to UpdNet, which will process received packets. The skeleton of it will look roughly like this.

//Net input 
void UpdNet() 
{ 
	int bytes; 

	UDPpacket *in; 

	UDPsocket* sock = &g_sock; 

	if(!sock) 
		return; 

	in = SDLNet_AllocPacket(65535); 

	do 
	{ 
		in->data[0] = 0; 

		bytes = SDLNet_UDP_Recv(*sock, in); 

		IPaddress ip; 

		memcpy(&ip, &in->address, sizeof(IPaddress)); 

		if(bytes > 0) 
			TranslatePacket((char*)in->data, bytes, true, &g_sock, &ip); 
	} while(bytes > 0); 

	SDLNet_FreePacket(in); 
}

Following, inside the do-while loop, is the equivalent code in regular Berkeley sockets using recvfrom, in case you're not using SDL2_net:

//Net input 
void UpdNet() 
{ 
	int bytes; 

	int* sock = &g_sock; 

	if(!sock) 
		return; 

	do 
	{
		struct sockaddr_in from; 
		socklen_t fromlen = sizeof(struct sockaddr_in); 
		char buffer[65535]; 
		bytes = recvfrom(g_socket, buffer, 65535, 0, (struct addr *)&from, &fromlen); 

		if(bytes > 0) 
			TranslatePacket(buffer, bytes, true, &g_sock, &ip); 
	} while(bytes > 0); 
}

This basically loops while we still have data to process in the buffer. If we do, we send it to the TranslatePacket function, which takes as parameters:

  • the buffer of data (“buffer”)
  • the number of bytes received (“bytes”)
  • whether we want to check the acknowledgement/sequence number and process it in order (“checkprev”), as we might choose to send a dummy packet that we stored to reuse our command switch functionality for lockstep batch packets, or whether we want to process it right away
  • the socket (“sock”)
  • and the IP address and port it came from (“from”).

void TranslatePacket(char* buffer, int bytes, bool checkprev, UDPsocket* sock, IPaddress* from)

We will get to TranslatePacket next, but first take a look at these function calls we will also have at the end of the UpdNet function.

	KeepAlive(); 
	CheckConns(); 
	ResendPacks(); 

#ifndef MATCHMAKER 
	CheckAddSv(); 
	CheckGetSvs(); 
#else 
	SendSvs(); 
#endif 

KeepAlive() tries to keep connections alive that are about to time out. CheckConns() checks for connections that we've closed or that have timed out from unresponsiveness and recycles them. ResendPacks() tries to resend packets that we haven't received an acknowledgement for, once they've waited long enough.

Then we have a preprocessor check to see whether we're compiling for the matchmaking server or the client program/app. If we're a game client or host, we only care about CheckAddSv() and CheckGetSv().

CheckAddSv() checks whether our game host address is up on the matchmaker list and getting the information on each one of the received hosts. It also removes hosts from our list that we have lost a connection too (due to time out).

CheckGetSv() makes sure the matchmaker will send us the next host from its list if we've requested to get a host list.

If we're the matchmaker however, we only care about SendSvs(), which sends the next host in the list for each requesting client when the last one has been ack'd.

Connection Class


Because there's no concept of a connection in UDP, we need to make it ourselves.

class NetConn
{
public:
	unsigned short nextsendack;
	unsigned short lastrecvack;
	bool handshook;
	IPaddress addr;
	//TODO change these to flags
	bool isclient;	//is this a hosted game's client? or for MATCHMAKER, is this somebody requesting sv list?
	bool isourhost;	//is this the currently joined game's host? cannot be a host from a server list or something. for MATCHMAKER, it can be a host getting added to sv list.
	bool ismatch;	//matchmaker?
	bool ishostinfo;	//is this a host we're just getting info from for our sv list?
	//bool isunresponsive;
	unsigned long long lastsent;
	unsigned long long lastrecv;
	short client;
	float ping;
	bool closed;
	bool disconnecting;

	void expirein(int millis);

#ifdef MATCHMAKER
	int svlistoff;	//offset in server list, sending a few at a time
	SendSvInfo svinfo;
#endif
	//void (*chcallback)(NetConn* nc, bool success);	//connection state change callback - did we connect successfully or time out?

	NetConn()
	{
		client = -1;
		handshook = false;
		nextsendack = 0;
		//important - reply ConnectPacket with ack=0 will be
		//ignored as copy (even though it is original) if new NetConn's lastrecvack=0.
		lastrecvack = USHRT_MAX;
		isclient = false;
		isourhost = false;
		ismatch = false;
		ishostinfo = false;
		//isunresponsive = false;
		lastrecv = GetTicks();
		lastsent = GetTicks();
		//chcallback = NULL;
#ifdef MATCHMAKER
		svlistoff = -1;
#endif
		ping = 1;
		closed = false;
	}
};

“nextsendack” is the outgoing sequence number that the next reliable packet will have. We increment it by one each time and it wraps around from 0 when it maxes out.

“lastrecvack” is the inbound sequence number of the last received reliable packet (the next one will be greater until it wraps around). Because we can send and receive independently, we keep two acks/sequences.

When we start a connection, “nextsendack” is 0 (for the first packet sent, the ConnectPacket), and “lastrecvack” is 65535, which is the maximum unsigned short value, before it wraps around to 0. Although nextsendack can be set to anything (and should) as long as we set the ack of the first ConnectPacket to that, and that is more secure, as it is harder to predict or crack and probably protects data better (but I haven't tried it).

“handshook” tells us whether the other side has acknowledged the ConnectPacket (and therefore created an accompanying NetConn connection instance for us). When we receive a ConnectPacket, we set “handshook” to true and acknowledge them, recording the inbound ack and setting “nextsendack” to 0. If “handshook” is true, it tells us that we can now send reliable packets on the connection to this address, as the sequence numbers are in place.

IPaddress is the SDL2_net IP address and port structure, the equivalent of sockaddr_in in regular Berkeley sockets.

Translating Packets


This is what TranslatePacket does. When we “translate” or process the packet we want to know if it's an old packet that we've already processed once or if it's ahead of the next expected packet, if it's meant to be reliable as defined previously. We also acknowledge any packets that are reliable. And then finally we execute them. The game host does an extra part in beginning of the function, checking if any client connections are unresponsive or have become responsive again and relays that information to other clients, so the blame can be pinned on the lagger.

The TranslatePacket() function has this basic outline:

1. Match address to a connection
2. Update last received timestamp if match found
3. Check packet type if we need to check sequence number or if we process it right away
4. Check sequence number for one of three cases (behind, current, or future)
5. Acknowledge packet if needed
6. If we don't recognize the connection and it's supposed to be reliable, tell the other side that we don't have a connection with them
7. Execute the packet
8. Execute any buffered packets after the current one (in order)
9. And update the last received sequence number to the last packet executed

Step by step, the function is:

void TranslatePacket(char* buffer, int bytes, bool checkprev, UDPsocket* sock, IPaddress* from)
{
	//1. Match address to a connection
	PacketHeader* header = (PacketHeader*)buffer;

	NetConn* nc = Match(from);

We pass an IPaddress struct pointer to Match which returns the matching connection or NULL on failure.

Then, if we got a match, we update the last received time for the connection. If the connection is associated with client in the game room, and that client was previously unresponsive, we can mark it as responsive again and tell the other clients.

	//If we recognize this connection...
	if(nc)
	{
		//2. Update the timestamp of the last received packet
		nc->lastrecv = GetTicks();

#ifndef MATCHMAKER
		//check if was previously unresponsive
		//and if (s)he was, tell others that (s)he
		//is now responsive.
		if(nc->client >= 0)
		{
			Client* c = &g_client[nc->client];

			//was this client unresponsive?
			if(c->unresp)
			{
				//it's now responsive again.
				c->unresp = false;

				//if we're the game host
				if(g_netmode == NETM_HOST)
				{
					//inform others
					ClStatePacket csp;
					csp.header.type = PACKET_CLSTATE;
					csp.chtype = CLCH_RESP;
					csp.client = nc->client;

					//send to all except the original client (nc->addr)
					SendAll((char*)&csp, sizeof(ClStatePacket), true, false, &nc->addr);
				}
			}
		}
#endif
	}

We know that certain packets are meant to be processed right away, without checking for them to be processed in sequence. For example, acknowedgement packets are non-reliable and don't need to be processed in a specific order. Connect packets are to be executed as soon as they are received, because no other packet is supposed to be sent with them. Same for disconnect packets, “no connection” packets, LAN call, and LAN answer.

	//3. Check packet type if we need to check sequence number or if we process it right away
	//control packets
	//don't check sequence for these ones and process them straight away
	//but acknowledge CONNECT and DISCONNECT
	switch(header->type)
	{
	case PACKET_ACKNOWLEDGMENT:
	case PACKET_CONNECT:	//need to send back ack
	case PACKET_DISCONNECT:	//need to send back ack
	case PACKET_NOCONN:
	case PACKET_NACK:
	case PACKET_LANCALL:
	case PACKET_LANANSWER:
		checkprev = false;
		break;
	default:
		break;
	}

If it's not one of those packet types, checkprev=true, and we check the sequence number. These are reliable packets that must be processed in the order they were sent in. If we're missing a packet in the sequence, we will buffer the packets after it while we wait for the missing packet to arrive.

“next” will be the next expected sequence number (the one after lastrecvack in NetConn). “last” will be updated each time we execute a packet, to update lastrecvack with the last one.

	unsigned short next;	//next expected packet ack
	unsigned short last = PrevAck(header->ack);	//last packet ack to be executed

	//4. Check sequence number for one of three cases (behind, current, or future)

	//If checkprev was set (directly above), we need to check the sequence.
	//It must be a recognized NetConn; otherwise we don't have any sequence numbers.
	if(checkprev && nc != NULL)
	{
		// ÃÃÃâÃÃÃæ check sequence number (check snippet further down) ...
	}

Then we acknowledge the packet if it's meant to be reliable. Acknowledgement packets don't need acknowledgements themselves. A “no connection” packet tells us the other side doesn't even have sequence numbers for us, so there's no point acknowledging it. Usually, if checkprev=false, we don't check the packet sequence so we don't care about acknowledging it, but for connect and disconnect packets we must acknowledge because the other side expects a success signal back.

	//5. Acknowledge packet if needed

procpack:

	//We might disconnect further down in PacketSwitch()
	//So acknowledge packets while we still have the sequence numbers
	nc = Match(from);
	//Don't acknowledge NoConn packets as they are non-reliable,
	//and ack'ing them would cause a non-ending ack loop.
	if(header->type != PACKET_ACKNOWLEDGMENT &&
		header->type != PACKET_NOCONN &&
		sock && nc)
	{
		Acknowledge(header->ack, nc, from, sock, buffer, bytes);
	}
	//Always acknowledge ConnectPacket's
	else if( header->type == PACKET_CONNECT &&
		sock )
	{
		Acknowledge(header->ack, NULL, from, sock, buffer, bytes);
 	}
	//And acknowledge DisconnectPacket's
	else if(header->type == PACKET_DISCONNECT && sock)
	{
		Acknowledge(header->ack, NULL, from, sock, buffer, bytes);
	}

If we got disconnected from the other side and for some reason they retained the connection, we'll get packets that we have to tell the other side we can't process. They can then show an error to the user or try to reconnect.

	//6. If we don't recognize the connection and it's supposed to be reliable, tell the other side that we don't have a connection with them

	//We're getting an anonymous packet.
	//Maybe we've timed out and they still have a connection.
	//Tell them we don't have a connection.
	//We check if sock is set to make sure this isn't a local
	//command packet being executed.
	if(!nc &&
	header->type != PACKET_CONNECT &&
	header->type != PACKET_NOCONN &&
	header->type != PACKET_LANCALL &&
	header->type != PACKET_LANANSWER &&
	sock)
	{
		NoConnectionPacket ncp;
		ncp.header.type = PACKET_NOCONN;
		SendData((char*)&ncp, sizeof(NoConnectionPacket), from, false, true, NULL, &g_sock, 0, NULL);
		return;
	}

Then we execute packets. First, any packets before the current received one. Then the one we just received. And then any that we buffered that come after it.

The reason we execute packets that came BEFORE is because we may have a case like this:

packet 1 received
packet 2 received
packet 5 received

We'll be able to execute packets 1 and 2 even though the current is 5.

updinack:

	//7. Execute the packet
	//8. Execute any buffered packets after the current one (in order)

	// Translate in order
	if(checkprev && nc)
	{
		last = PrevAck(header->ack);
		last = ParseRecieved(next, last, nc);
	}

	// Translate in order
	if(NextAck(last) == header->ack ||
		!checkprev)
	{
		PacketSwitch(header->type, buffer, bytes, nc, from, sock);
		last = header->ack;
	}

	// Translate in order
	if(checkprev && nc)
	{
		while(true)
		{
			if(!Recieved(last+1, last+1, nc))
				break;

			last++;
			ParseRecieved(last, last, nc);
		}
	}

Finally, we update the received sequence number. We have to match up the connection pointer with the address again, because the instance it was pointing to might have been erased, or it might have appeared when it wasn't previously, as a connection is erased or created respectively.

For non-reliable packets we don't update the sequence number. For connect or disconnect packets, we only set the sequence number inside the PacketSwitch read function call when we create a connection.

	//9. And update the last received sequence number to the last packet executed

	//have to do this again because PacketSwitch might
	//read a ConnectPacket, which adds new connections.
	//also connection might have
	//been Disconnected(); and erased.
	nc = Match(from);

	//ack Connect packets after new NetConn added...
	//Don't acknowledge NoConn packets as they are non-reliable
	if(header->type != PACKET_ACKNOWLEDGMENT &&
		header->type != PACKET_NOCONN &&
		sock && nc && checkprev)
	{
		if(header->type != PACKET_CONNECT &&
			header->type != PACKET_DISCONNECT)
			nc->lastrecvack = last;
	}
}

The PacketSwitch() at the end is what executes the packet. It might better be called ExecPacket().

The Match() function at the top compares the “addr” port and IP address integers to every known connection and returns the match, or NULL on failure.

NetConn* Match(IPaddress* addr)
{
	if(!addr)
		return NULL;

	for(auto ci=g_conn.begin(); ci!=g_conn.end(); ci++)
		if(Same(&ci->addr, addr))
			return &*ci;

	return NULL;
}

bool Same(IPaddress* a, IPaddress* b)
{
	if(a->host != b->host)
		return false;

	if(a->port != b->port)
		return false;

	return true;
}

The packet is “old” if we've already buffered it (but it's ahead of the next expected ack/sequence number that we processed), or if its ack/sequence is behind our connection class's “lastrecvack”. We use an unsigned short for the sequence number, which holds a maximum value of 65535. Because we might exceed this value after 36 minutes if we send 30 packets a second, we wrap around and thus, there's a “sliding window” of values that are considered to be in the past (don't confuse this with the “sliding window” packet range that might be being reliably resent at any given moment). We can check if an ack is in the past (behind what is already executed) using PastAck():

bool PastAck(unsigned short test, unsigned short current)
{
	return ((current >= test) && (current - test <= USHRT_MAX/2))
	       || ((test > current) && (test - current > USHRT_MAX/2));
}

Where PastAck tests whether “test” is behind or at “current”.

Let's look in more detail at the part in the middle of TranslatePacket that checks the sequence number.

We define some variables. “next” will hold the current expected ack (lastrecvack+1) inside the following code block. “last” will hold the last packet to have been executed. For now, it's set to something, but it doesn't matter, as we update it at the end.

	unsigned short next;	//next expected packet ack
	unsigned short last = PrevAck(header->ack);	//last packet ack to be executed

We only check the sequence numbers if it's a packet that makes checkprev=true and if it's from a recognized connection.

	if(checkprev && nc != NULL)
	{

We set the “next” expected packet number.

		next = NextAck(nc->lastrecvack);	//next expected packet ack
		last = next;	//last packet ack to be executed

Next, we check how the received packet's sequence number compares to the next expected one.

		//CASE #1: ÃÃÃâoldÃÃÃâ packet
		if(PastAck(header->ack, nc->lastrecvack) || Recieved(header->ack, header->ack, nc))
		{
			Acknowledge(header->ack, nc, from, sock, buffer, bytes);
			return;
		}

		//CASE #2: current packet (the next expected packet)
		if(header->ack == next) 
		{
			// Translate packet
			last = next;
		} 

		//CASE #3: an unbuffered, future packet
		else  // More than +1 after lastrecvack?
		{
			/*
			last will be updated to the last executed packet at the end.
			for now it will hold the last buffered packet to be executed.
			*/
			unsigned short checklast = PrevAck(header->ack);

			if(Recieved(next, checklast, nc))
			{
				// Translate in order
				last = checklast;
				goto procpack;
			}
			else
			{
				AddRecieved(buffer, bytes, nc);

				if(Recieved(next, checklast, nc))
				{
					// Translate in order
					last = checklast;
					goto procpack;
				}
				else
				{
					//TODO
					//how to find which ack was missed, have to go through all buffered
					//this is something somebody smart can do in the future
					//NAckPacket nap;
					//nap.header.type = PACKET_NACK;
					//nap.header.ack =
				}
			}
		}
	}

As can be seen, there are three possible cases for the inbound packet's sequence number: it is either, 1.) behind or buffered, 2.) current expected, or 3.) future unbuffered.

Case 1: behind and buffered received packets


If we've already dealt with (executed) the packet, we simply acknowledge it again and return from TranslatePacket() with no further action.

		if(PastAck(header->ack, nc->lastrecvack) || Recieved(header->ack, header->ack, nc))
		{
			Acknowledge(header->ack, nc, from, sock, buffer, bytes);
			return;
		}

In the the second testcase of the if statement (packet is buffered received), we check if we've already buffered it, using Recieved():

//check when we've recieved a packet range [first,last] inclusive
bool Recieved(unsigned short first, unsigned short last, NetConn* nc)
{
	OldPacket* p;
	PacketHeader* header;
	unsigned short current = first;
	unsigned short afterlast = NextAck(last);
	bool missed;

	//go through all the received packets and check if we have the complete range [first,last]
	do
	{
		//for each number in the sequence...
		missed = true;

		//look through each packet from that address
		for(auto i=g_recv.begin(); i!=g_recv.end(); i++)
		{
			p = &*i;
			header = (PacketHeader*)&p->buffer;

			//is this the sequence number we're looking for?
			if(header->ack != current)
				continue;

			//is this the correct address?
			if(!Same(&p->addr, &nc->addr))
				continue;

			//go to next number in the sequence now that we know we have the previous one
			current = NextAck(current);
			missed = false;
			break;
		}

		//if we finished the inner loop and ÃÃÃâmissedÃÃÃâ is still false, we missed a number in the sequence, so return false
		if(missed)
			return false;

	//continue looping until we've arrived at the number after the ÃÃÃâlastÃÃÃâ number
	} while(current != afterlast);

	//if we got here, we got all the numbers
	return true;
}

“g_recv” is a linked list of OldPacket's. We go through each sequence number between “first” and “last” and check if we have each and every one. Because we use the received packet's ack number for both parameters in Case 1, we only check if we've buffered that one packet. Because g_recv holds inbound packets from every address we're connected to, we have to check to match the address when comparing ack numbers. You can store g_recv in the NetConn's and this might be more efficient.

Buffered Packets


The OldPacket class holds the byte array for the packet and the address and port of the sender (or the outbound port and address for outgoing buffered packets).


class OldPacket
{
public:
	char* buffer;
	int len;
	unsigned long long last;	//last time resent
	unsigned long long first;	//first time sent
	bool expires;
	bool acked;	//used for outgoing packets

	//sender/reciever
	IPaddress addr;
	void (*onackfunc)(OldPacket* op, NetConn* nc);

	void freemem()
	{
		if(len <= 0)
			return;

		if(buffer != NULL)
			delete [] buffer;
		buffer = NULL;
	}

	OldPacket()
	{
		len = 0;
		buffer = NULL;
		onackfunc = NULL;
		acked = false;
	}
	~OldPacket()
	{
		freemem();
	}

	OldPacket(const OldPacket& original)
	{
		len = 0;
		buffer = NULL;
		*this = original;
	}

	OldPacket& operator=(const OldPacket &original)
	{
		freemem();

		if(original.buffer && original.len > 0)
		{
			len = original.len;
			if(len > 0)
			{
				buffer = new char[len];
				memcpy((void*)buffer, (void*)original.buffer, len);
			}
			last = original.last;
			first = original.first;
			expires = original.expires;
			acked = original.acked;
			addr = original.addr;
			onackfunc = original.onackfunc;
		}
		else
		{
			buffer = NULL;
			len = 0;
			onackfunc = NULL;
		}

		return *this;
	}
};

It has some extra fields for outbound packets.

Case 2: current expected received packets


The second case is when the received packet is the next expected one, which means we received it in the correct order without repeats. The next expected (current) packet is the one after the “last received” one (lastrecvack). The variable “next” here will hold that ack. It is equal to nc->lastrecvack + 1, so you can use that instead of the function “NextAck”.

		next = NextAck(nc->lastrecvack);	//next expected packet ack
		last = next;	//last packet ack to be executed
	
		//CASE #2: current packet (the next expected packet)
		if(header->ack == next) 
		{
			// Translate packet
			last = next;
		} 

If it matches “next” we will process the packet and acknowledge it further down. We record the “last” packet executed, to update the sequence number.

Case #3: future, unbuffered received packets


If we reach “else” it means we have an unbuffered, future packet.

		//CASE #3: an unbuffered, future packet
		else  // More than +1 after lastrecvack?
		{
			/*
			last will be updated to the last executed packet at the end.
			for now it will hold the last buffered packet to be executed.
			*/
			unsigned short checklast = PrevAck(header->ack);

			if(Recieved(next, checklast, nc))
			{
				// Translate in order
				last = checklast;
				goto procpack;
			}
			else
			{
				AddRecieved(buffer, bytes, nc);

				if(Recieved(next, checklast, nc))
				{
					// Translate in order
					last = checklast;
					goto procpack;
				}
				else
				{
					//TODO
					//how to find which ack was missed, have to go through all buffered
					//this is something somebody smart can do in the future
					//NAckPacket nap;
					//nap.header.type = PACKET_NACK;
					//nap.header.ack =
				}
			}
		}

We check if we have a range of buffered packets up to this one. If we have a complete range, starting from the current (expected next) packet, we can execute them (because we only run them in the order they're sent in) and increase lastrecvack to equal “last”. We move up lastrecvack at the end of TranslatePacket. We might have more buffered packets after the received one. That is why we check for any extra packets and store the last executed one's ack number in “last”.

If we don't have a complete set of packets up to the received one, we call AddRecieved (buffer it).

void AddRecieved(char* buffer, int len, NetConn* nc)
{
	OldPacket p;
	p.addr = nc->addr;
	p.buffer = new char[ len ];
	p.len = len;
	memcpy((void*)p.buffer, (void*)buffer, len);
	memcpy((void*)&p.addr, (void*)&nc->addr, sizeof(IPaddress));

	g_recv.push_back(p);
}

If we have to buffer it, it means it's ahead of the last executed packet, and there's one missing before it.

If we wanted to only send selective repeats every second or so (if that was the delay on the channel and we didn't want to send some three copies of it before we received back an ack, and we're sure that loss of packets is minimal, and we'd rather leave the “sliding window” huge), we could use NAck's (negative ack's) to tell us when we've missed a packet. But selective repeat works pretty well. (Using nacks is a different kind of reliable UDP implementation.)

Acknowledgements


Further on we send ack's.

void Acknowledge(unsigned short ack, NetConn* nc, IPaddress* addr, UDPsocket* sock, char* buffer, int bytes)
{
	AckPacket p;
	p.header.type = PACKET_ACKNOWLEDGMENT;
	p.header.ack = ack;

	SendData((char*)&p, sizeof(AckPacket), addr, false, true, nc, sock, 0, NULL);
}

We use a SendData function for our RUDP implementation, shown and explained further down.

Whenever we send data, we have to fill out a packet struct for that type of packet. At minimum, we have to set header.type so that the received end can know what packet type it is from reading the first 2 bytes of the packet.

Executing Packet and Updating Sequence Number


If we get to this point in TranslatePacket, we'll execute the packets in order. If we checked sequence numbers, and we have a connection, we'll execute the buffered previous packets, then the current received packets, then check for any future buffered packets. If we don't check the sequence, or don't have a connection, we just execute the one packet we received.

updinack:
	
	// Translate in order
	if(checkprev && nc)
	{
		last = header->ack;
		last = ParseRecieved(next, last, nc);
	}
	
	// Translate in order
	if(NextAck(last) == header->ack ||
		!checkprev)
	{
		PacketSwitch(header->type, buffer, bytes, nc, from, sock);
		last = header->ack;
	}
	
	// Translate in order
	if(checkprev && nc)
	{
		last = header->ack;

		while(true)
		{
			if(!Recieved(last+1, last+1, nc))
				break;

			last++;
			ParseRecieved(last, last, nc);
		}
	}

	//have to do this again because PacketSwitch might
	//read a ConnectPacket, which adds new connections.
	//but also the connection might have
	//been Disconnected(); and erased.
	nc = Match(from);

	//ack Connect packets after new NetConn added...
	//Don't acknowledge NoConn packets as they are non-reliable
	if(header->type != PACKET_ACKNOWLEDGMENT &&
		header->type != PACKET_NOCONN &&
		sock && nc && checkprev)
	{
		if(header->type != PACKET_CONNECT &&
			header->type != PACKET_DISCONNECT)
			nc->lastrecvack = last;
	}

At the end we update the connection's “lastrecvack” to “last” one executed. If it's a ConnectPacket, we set the lastrecvack when reading the packet.

Executing a buffered packet range


We need to execute a packet range when we know we've got a complete sequence up to a certain ack. We return the last executed packet number here, in case it's behind “last”.

unsigned short ParseRecieved(unsigned short first, unsigned short last, NetConn* nc)
{
	OldPacket* p;
	PacketHeader* header;
	unsigned short current = first;
	unsigned short afterlast = NextAck(last);

	do
	{
		bool execd = false;

		for(auto i=g_recv.begin(); i!=g_recv.end(); i++)
		{
			p = &*i;
			header = (PacketHeader*)&p->buffer;

			if(header->ack != current)
				continue;

			if(!Same(&p->addr, &nc->addr))
				continue;

			PacketSwitch(header->type, p->buffer, p->len, nc, &p->addr, &g_sock);
			execd = true;
			current = NextAck(current);

			i = g_recv.erase(i);
			break;
		}

		if(execd)
			continue;

		break;
	} while(current != afterlast);

	return PrevAck(current);
}

SendData


We send data like so, passing the data bytes, size, address, whether it is meant to be reliable, whether we want it to expire after a certain time of resending (like a ConnectPacket that needs to fail sooner than the default timeout), the NetConn connection (which musn't be NULL if we're sending a reliable packet), the socket, the millisecond delay if we want to queue it to send a few moments from now, and a callback function to be called when it's acknowledged so we can take further action (like setting “handshook” to true for ConnectPacket's, or destroying the NetConn when a DisconnectPacket is acknowledged).

void SendData(char* data, int size, IPaddress * paddr, bool reliable, bool expires, NetConn* nc, UDPsocket* sock, int msdelay, void (*onackfunc)(OldPacket* p, NetConn* nc))
{
	//is this packet supposed to be reliable?
	if(reliable)
	{
		//if so, set the ack number
		((PacketHeader*)data)->ack = nc->nextsendack;

		//and add an OldPacket to the g_outgo list
		OldPacket p;
		p.buffer = new char[ size ];
		p.len = size;
		memcpy(p.buffer, data, size);
		memcpy((void*)&p.addr, (void*)paddr, sizeof(IPaddress));
		//in msdelay milliseconds, p.last will be RESEND_DELAY millisecs behind GetTicks()

		//set last sent time
		p.last = GetTicks() + msdelay - RESEND_DELAY;
		p.first = p.last;
		p.expires = expires;
		p.onackfunc = onackfunc;
		g_outgo.push_back(p);

		//update outbound ack for this connection
		nc->nextsendack = NextAck(nc->nextsendack);
	}

	if(reliable && msdelay > 0)
		return;

	PacketHeader* ph = (PacketHeader*)data;

	if(reliable && 
		(!nc || !nc->handshook) && 
		(ph->type != PACKET_CONNECT && ph->type != PACKET_DISCONNECT && ph->type != PACKET_ACKNOWLEDGMENT && ph->type != PACKET_NOCONN) )
	{
		Connect(paddr, false, false, false, false);
		return;
	}

	memcpy(out->data, data, size);
	out->len = size;
	out->data[size] = 0;

	SDLNet_UDP_Unbind(*sock, 0);
	if(SDLNet_UDP_Bind(*sock, 0, (const IPaddress*)paddr) == -1)
	{
		char msg[1280];
		sprintf(msg, "SDLNet_UDP_Bind: %s\n",SDLNet_GetError());
		ErrMess("Error", msg);
		//printf("SDLNet_UDP_Bind: %s\n",SDLNet_GetError());
		//exit(7);
	}

	//sendto(g_socket, data, size, 0, (struct addr *)paddr, sizeof(struct sockaddr_in));
	SDLNet_UDP_Send(*sock, 0, out);

	g_transmitted += size;

	SDLNet_FreePacket(out);
}

If it's reliable, we add an entry to the outbound OldPacket list. We set the “last” member variable of the OldPacket entry such that it is resent in a certain amount of time depending on when we delayed it to and the usual resend delay.

If it's reliable and the delay is greater than 0, we don't take any action in this function after buffering it in the outbound list because we will send it after ResendPacks() is called.

If it's reliable and we don't have a connection specified, we call Connect() to connect first, and return. It is also called if the connection hasn't finished the handshake (in which case Connect() will check to make sure that we have an outgoing ConnectPacket). The only case in which we don't need a handshook connection and send reliably is if we're sending a ConnectPacket or DisconnectPacket.

The SendData function is called itself with “reliable” set to false when resending a reliable packet from a buffered outbound OldPacket container.

The SendData function automatically sets the outbound ack for the reliable packets.

Keeping Connections Alive


As mentioned, there are three more functions in the UpdNet loop function:

KeepAlive();
CheckConns();
ResendPacks();

The KeepAlive() function sends KeepAlive packets to connections that are expiring. It prevents the other side from closing the connection, and also triggers an ack packet back, preventing from the connection being closed locally. The default is to keep connections alive until the user decides to Disconnect them.

//keep expiring connections alive (try to)
void KeepAlive()
{
	unsigned long long nowt = GetTicks();
	auto ci = g_conn.begin();

	//loop while we still have more connections to process...
	while(g_conn.size() > 0 && ci != g_conn.end())
	{
		//if we haven't received a handshake back, or if it's closed, we don't need to be keep it alive
		if(!ci->handshook || ci->closed)
		{
			ci++;
			continue;
		}

		//otherwise, if it's reached a certain percent of the timeout period, send a KeepAlivePacket...
		if(nowt - ci->lastrecv > NETCONN_TIMEOUT/4)
		{
			//check if we're already trying to send a packet to get a reply
			bool outgoing = false;

			//check all outgoing packets for a packet to this address
			for(auto pi=g_outgo.begin(); pi!=g_outgo.end(); pi++)
			{
				//if(memcmp(&pi->addr, &ci->addr, sizeof(IPaddress)) != 0)
				if(!Same(&pi->addr, &ci->addr))
				{
					continue;
				}

				outgoing = true;
				break;
			}

			//if we have an outgoing packet, we don't have to send a KeepAlivePacket
			if(outgoing)
			{
				ci++;
				continue;
			}

			//otherwise, send a KeepAlivePacket...
			KeepAlivePacket kap;
			kap.header.type = PACKET_KEEPALIVE;
			SendData((char*)&kap, sizeof(KeepAlivePacket), &ci->addr, true, false, &*ci, &g_sock, 0, NULL);
		}

		//check next connection next
		ci++;
	}
}

GetTicks() is our 64-bit timestamp function in milliseconds:

unsigned long long GetTicks()
{
#ifdef PLATFORM_WIN
	SYSTEMTIME st;
	GetSystemTime (&st);
	_FILETIME ft;
	SystemTimeToFileTime(&st, &ft);
	//convert from 100-nanosecond intervals to milliseconds
	return (*(unsigned long long*)&ft)/(10*1000);
#else
	struct timeval tv;

	gettimeofday(&tv, NULL);

	return
    (unsigned long long)(tv.tv_sec) * 1000 +
    (unsigned long long)(tv.tv_usec) / 1000;
#endif
}

Checking and Pruning Connections


Two more functions in UpdNet:

CheckConns();
ResendPacks();

In CheckConns we do several things:

1. Send out periodic pings for all the players in the room for all the clients using Cl(ient)StatePacket's
2. Handle and close any connections that are not yet closed but have timed out because the last received message has been longer than NETCONN_TIMEOUT milliseconds ago
3. For closed connections, flush any buffered inbound or outbound OldPacket's, and erase the NetConn from the list
4. For unresponsive clients, inform other players of the lagger


void CheckConns()
{
	unsigned long long now = GetTicks();

	// If we're not compiling for the matchmaker (the game app itself)
#ifndef MATCHMAKER

	static unsigned long long pingsend = GetTicks();

	//send out client pings
	if(g_netmode == NETM_HOST &&
		now - pingsend > (NETCONN_UNRESP/2)
		)
	{
		pingsend = now;

		for(int i=0; i<CLIENTS; i++)
		{
			Client* c = &g_client[i];

			if(!c->on)
				continue;

			if(i == g_localC)
				continue;	//clients will have their own ping for the host

			NetConn* nc = c->nc;

			if(!nc)
				continue;

			ClStatePacket csp;
			csp.header.type = PACKET_CLSTATE;
			csp.chtype = CLCH_PING;
			csp.ping = nc->ping;
			csp.client = i;
			SendAll((char*)&csp, sizeof(ClStatePacket), true, false, NULL);
		}
	}
#endif
	
	auto ci = g_conn.begin();

	while(g_conn.size() > 0 && ci != g_conn.end())
	{
		//get rid of timed out connections
		if(!ci->closed && now - ci->lastrecv > NETCONN_TIMEOUT)
		{
			//TO DO any special condition handling, inform user about sv timeout, etc.

#ifndef MATCHMAKER
			if(ci->ismatch)
			{
				g_sentsvinfo = false;
			}
			else if(ci->isourhost)
			{
				EndSess();
				RichText mess = RichText("ERROR: Connection to host timed out.");
				Mess(&mess);
			}
			else if(ci->ishostinfo)
				;	//ErrMess("Error", "Connection to prospective game host timed out.");
			else if(ci->isclient)
			{
				//ErrMess("Error", "Connection to client timed out.");

				/*
				TODO
				combine ClDisconnectedPacket and ClientLeftPacket.
				use params to specify conditions of leaving:
				- of own accord
				- timed out
				- kicked by host
				*/

				//TODO inform other clients?
				ClDisconnectedPacket cdp;
				cdp.header.type = PACKET_CLDISCONNECTED;
				cdp.client = ci->client;
				cdp.timeout = true;
				SendAll((char*)&cdp, sizeof(ClDisconnectedPacket), true, false, &ci->addr);
				
				Client* c = &g_client[ci->client];
				RichText msg = c->name + RichText(" timed out.");
				AddChat(&msg);
			}
#else
			g_log<<DateTime()<<" timed out"<<std::endl;
			g_log.flush();
#endif

			ci->closed = true;	//Close it using code below
		}

		//get rid of closed connections
		if(ci->closed)
		{
			if(&*ci == g_mmconn)
			{
				g_sentsvinfo = false;
				g_mmconn = NULL;
			}
			if(&*ci == g_svconn)
				g_svconn = NULL;
#ifndef MATCHMAKER
			for(int cli=0; cli<CLIENTS; cli++)
			{
				Client* c = &g_client[cli];

				if(!c->on)
					continue;

				if(c->nc == &*ci)
				{
					if(g_netmode == NETM_HOST)
					{
					}

					if(c->player >= 0)
					{
						Player* py = &g_player[c->player];
						py->on = false;
						py->client = -1;
					}

					c->player = -1;
					c->on = false;
				}
			}
#endif

			//necessary to flush? already done in ReadDisconnectPacket();
			//might be needed if connection can become ->closed another way.
			FlushPrev(&ci->addr);
			ci = g_conn.erase(ci);
			continue;
		}

		//inform other clients of unresponsive clients
		//or inform local player or unresponsive host
		if(now - ci->lastrecv > NETCONN_UNRESP &&
			ci->isclient)	//make sure this is not us or a matchmaker
		{
#ifndef MATCHMAKER
			NetConn* nc = &*ci;

			Client* c = NULL;

			if(nc->client >= 0)
				c = &g_client[nc->client];

			if(g_netmode == NETM_CLIENT &&
				nc->isourhost)
			{
				//inform local player TODO
				c->unresp = true;
			}
			else if(g_netmode == NETM_HOST &&
				nc->isclient &&
				c)
			{
				//inform others
				if(c->unresp)
				{
					ci++;	
					continue; //already informed
				}

				c->unresp = true;

				ClStatePacket csp;
				csp.header.type = PACKET_CLSTATE;
				csp.chtype = CLCH_UNRESP;
				csp.client = c - g_client;
				SendAll((char*)&csp, sizeof(ClStatePacket), true, false, &nc->addr);
			}
#endif
		}

		ci++;
	}
}

Resending Packets


Finally, ResendPacks():

void ResendPacks()
{
	OldPacket* p;
	unsigned long long now = GetTicks();

	//remove expired ack'd packets
	auto i=g_outgo.begin();
	while(i!=g_outgo.end())
	{
		p = &*i;

		if(!p->acked)
		{
			i++;
			continue;
		}

        //p->last and first might be in the future due to delayed sends,
        //which would cause an overflow for unsigned long long.
        unsigned long long safelast = enmin(p->last, now);
        unsigned long long passed = now - safelast;
        unsigned long long safefirst = enmin(p->first, now);

		if(passed < RESEND_EXPIRE)
		{
			i++;
			continue;
		}

		i = g_outgo.erase(i);
	}

	//resend due packets within sliding window
	i=g_outgo.begin();
	while(i!=g_outgo.end())
	{
		p = &*i;

		//kept just in case it needs to be recalled by other side
		if(p->acked)
		{
			i++;
			continue;
		}
        
        unsigned long long safelast = enmin(p->last, now);
        unsigned long long passed = now - safelast;
        unsigned long long safefirst = enmin(p->first, now);

		NetConn* nc = Match(&p->addr);

		//increasing resend delay for the same outgoing packet

		unsigned int nextdelay = RESEND_DELAY;
        unsigned long long firstpassed = now - safefirst;

		if(nc && firstpassed >= RESEND_DELAY)
		{
			unsigned long long sincelast = safelast - safefirst;
			//30, 60, 90, 120, 150, 180, 210, 240, 270
			nextdelay = ((sincelast / RESEND_DELAY) + 1) * RESEND_DELAY;
		}

		if(passed < nextdelay)
		{
			i++;
			continue;
		}

		PacketHeader* ph = (PacketHeader*)p->buffer;

		/*
		If we don't have a connection to them
		and it's not a control packet, we
		need to connect to them to send reliably.
		Send it when we get a handshake back.
		*/
		if((!nc || !nc->handshook) &&
			ph->type != PACKET_CONNECT &&
			ph->type != PACKET_DISCONNECT &&
			ph->type != PACKET_ACKNOWLEDGMENT &&
			ph->type != PACKET_NOCONN)
		{
			Connect(&p->addr, false, false, false, false);
			i++;
			continue;
		}

//do we want a sliding window?
#if 1
		if(nc)
		{
			unsigned short lastack = nc->nextsendack + SLIDING_WIN - 1;

			if(PastAck(lastack, ph->ack) && ph->ack != lastack)
			{
				i++;
				continue;
				//don't resend more than SLIDING_WIN packets ahead
			}
		}
#endif

		if(p->expires && now - safefirst > RESEND_EXPIRE)
		{
			i = g_outgo.erase(i);
			continue;
		}

		SendData(p->buffer, p->len, &p->addr, false, p->expires, nc, &g_sock, 0, NULL);

		p->last = now;

		i++;
	}
}

We

1.) erase OldPacket's that have been acknowledged (acked = true),
2.) check if the OldPacket in question is within the sliding window, and if it is,
2.) resend those OldPacket's that have reached a certain delay,
3.) and erase OldPacket's that are set to expire.

“enmin” and “enmax” are just the min max macros:

#define enmax(a,b) (((a)>(b))?(a):(b))
#define enmin(a,b) (((a)<(b))?(a):(b))

We don't want the “firstpassed” value (the amount of time that has passed since the OldPacket was first sent) to be negative (which would be a giant positive number for an unsigned 64-bit long long), so we set “safefirst” used in its calculation to be no more than the time “now”, from which it is subtracted. If we didn't do this, we would get undefined behaviour, with some packets getting resent and some getting erased.

        unsigned long long safelast = enmin(p->last, now);
        unsigned long long passed = now - safelast;
        unsigned long long safefirst = enmin(p->first, now);

		NetConn* nc = Match(&p->addr);

		//increasing resend delay for the same outgoing packet

		unsigned int nextdelay = RESEND_DELAY;
        unsigned long long firstpassed = now - safefirst;

Reading Acknowledgements


Whenever we receive an AckPacket, we call ReadAckPacket on it in PacketSwitch:

void ReadAckPacket(AckPacket* ap, NetConn* nc, IPaddress* from, UDPsocket* sock)
{
	OldPacket* p;
	PacketHeader* header;

	for(auto i=g_outgo.begin(); i!=g_outgo.end(); i++)
	{
		p = &*i;
		header = (PacketHeader*)p->buffer;
		if(header->ack == ap->header.ack &&
				Same(&p->addr, from))
		{
			if(!nc)
				nc = Match(from);

			if(nc)
			{
				nc->ping = (float)(GetTicks() - i->first);
			}

			if(p->onackfunc)
				p->onackfunc(p, nc);

			i = g_outgo.erase(i);

			return;
		}
	}
}

In it, we will check for the matching buffered inbound OldPacket, and erase it from the list if found. But before that, we call a registered callback method that was set up when the packet was sent.

Using the “first” time the packet was sent, subtracting it from the current time, gives the round-trip latency for that connection, which we can record in the NetConn class.

Callbacks on Acknowledgement


Whenever we send a DisconnectPacket, we set the callback function to:

void OnAck_Disconnect(OldPacket* p, NetConn* nc)
{
	if(!nc)
		return;

	nc->closed = true;	//to be cleaned up this or next frame
}

Which will clean up the connection and stop resending the DisconnectPacket once it's acknowledged. It's best to encapsulate the needed functionality so we can safely Disconnect.

void Disconnect(NetConn* nc)
{
	nc->disconnecting = true;

	//check if we already called Disconnect on this connection
	//and have an outgoing DisconnectPacket
	bool out = false;

	for(auto pit=g_outgo.begin(); pit!=g_outgo.end(); pit++)
	{
		if(!Same(&pit->addr, &nc->addr))
			continue;

		PacketHeader* ph = (PacketHeader*)pit->buffer;

		if(ph->type != PACKET_DISCONNECT)
			continue;

		out = true;
		break;
	}

	if(!out)
	{
		DisconnectPacket dp;
		dp.header.type = PACKET_DISCONNECT;
		SendData((char*)&dp, sizeof(DisconnectPacket), &nc->addr, true, false, nc, &g_sock, 0, OnAck_Disconnect);
	}
}

When we receive an acknowledgement of a ConnectPacket that we sent out, we also need to set “handshook” to true. You can set user callbacks for certain special connections, like matchmakers or game hosts, to carry out certain functions, like immediately polling for servers, or getting server info, or joining the game room.


//on connect packed ack'd
void OnAck_Connect(OldPacket* p, NetConn* nc)
{
	if(!nc)
		nc = Match(&p->addr);

	if(!nc)
		return;

	nc->handshook = true;

	ConnectPacket* scp = (ConnectPacket*)p->buffer;

	//if(!scp->reconnect)
	{
#ifndef MATCHMAKER
		GUI* gui = &g_gui;

		if(nc->isourhost)
		{
			g_svconn = nc;

			//TO DO request data, get ping, whatever, server info

			JoinPacket jp;
			jp.header.type = PACKET_JOIN;
			std::string name = g_name.rawstr();
			if(name.length() >= PYNAME_LEN)
				name[PYNAME_LEN] = 0;
			strcpy(jp.name, name.c_str());
			jp.version = VERSION;
			SendData((char*)&jp, sizeof(JoinPacket), &nc->addr, true, false, nc, &g_sock, 0, NULL);
		}
#endif

		if(nc->ishostinfo)
		{
			//TO DO request data, get ping, whatever, server info
			GetSvInfoPacket gsip;
			gsip.header.type = PACKET_GETSVINFO;
			SendData((char*)&gsip, sizeof(GetSvInfoPacket), &nc->addr, true, false, nc, &g_sock, 0, NULL);
		}

#ifndef MATCHMAKER
		if(nc->ismatch)
		{
			g_mmconn = nc;
			g_sentsvinfo = false;

			if(g_reqsvlist && !g_reqdnexthost)
			{
				g_reqdnexthost = true;

				GetSvListPacket gslp;
				gslp.header.type = PACKET_GETSVLIST;
				SendData((char*)&gslp, sizeof(GetSvListPacket), &nc->addr, true, false, nc, &g_sock, 0, NULL);
			}
		}
#endif
	}
}

You can see there's a commented out function pointer called “chcallback” in the NetConn class, which might be given a function to call when the connection is handshook, instead of hard-coding several cases for the connection type (“ismatch”, “isourhost”, etc.)

Connecting


Before we host a server or connect to the matchmaker, we must open a socket.

void OpenSock()
{
	unsigned short startport = PORT;

	if(g_sock)
	{
		IPaddress* ip = SDLNet_UDP_GetPeerAddress(g_sock, -1);

		if(!ip)
			g_log<<"SDLNet_UDP_GetPeerAddress: "<<SDLNet_GetError()<<std::endl;
		else
			startport = SDL_SwapBE16(ip->port);

		SDLNet_UDP_Close(g_sock);
		g_sock = NULL;
	}

	if(g_sock = SDLNet_UDP_Open(startport))
		return;

	//try 10 ports
#ifndef MATCHMAKER
	for(int i=0; i<10; i++)
	{
		if(!(g_sock = SDLNet_UDP_Open(PORT+i)))
			continue;

		return;
	}
#endif

	char msg[1280];
	sprintf(msg, "SDLNet_UDP_Open: %s\n", SDLNet_GetError());
	g_log<<msg<<std::endl;
	ErrMess("Error", msg);
}

This OpenSock method will try 10 different port numbers if the first one fails. If it still doesn't work, it will log a message from SDLNet. After we open a port, we can send packets. The OpenSock method is encapsulated in the Connect() function. We can call the first Connect method, which takes an IP string or domain address, or the second, which accepts an IPaddress struct. They also accept some parameters to describe their use, like whether the connection is the matchmaker, the host being joined, a client of our room, or a random server we're getting info on.

NetConn* Connect(const char* addrstr, unsigned short port, bool ismatch, bool isourhost, bool isclient, bool ishostinfo)
{
	IPaddress ip;

	//translate the web address string to an IP and port number
	if(SDLNet_ResolveHost(&ip, addrstr, port) == -1)
	{
		return NULL;
	}

	//call the following function...
	return Connect(&ip, ismatch, isourhost, isclient, ishostinfo);
}

//Safe to call more than once, if connection already established, this will just
//update NetConn booleans.
NetConn* Connect(IPaddress* ip, bool ismatch, bool isourhost, bool isclient, bool ishostinfo)
{
	if(!g_sock)
		OpenSock();


	NetConn* nc = Match(ip);

	NetConn newnc;
	bool isnew = false;

	//if we don't recognize this address as having a connection to, make a new NetConn instance for the list
	if(!nc)
	{
		isnew = true;
		newnc.addr = *ip;
		newnc.handshook = false;
		newnc.lastrecv = GetTicks();
		newnc.lastsent = newnc.lastrecv;
		//important - reply ConnectPacket with ack=0 will be
		//ignored as copy (even though it is original) if new NetConn's lastrecvack=0.
		newnc.lastrecvack = USHRT_MAX;
		newnc.nextsendack = 0;
		newnc.closed = false;
		g_conn.push_back(newnc);

		nc = &*g_conn.rbegin();
	}
	else
	{
		//force reconnect (sending ConnectPacket).
		//also important for Click_SL_Join to know that we
		//can't send a JoinPacket immediately after this function,
		//but must wait for a reply ConnectPacket.
		if(nc->closed)
			nc->handshook = false;
	}

	bool disconnecting = false;

	//if we have an outgoing DisconnectPacket, set disconnecting=true
	for(auto pit=g_outgo.begin(); pit!=g_outgo.end(); pit++)
	{
		OldPacket* op = &*pit;

		if(!Same(&op->addr, &nc->addr))
			continue;

		PacketHeader* ph = (PacketHeader*)op->buffer;

		if(ph->type != PACKET_DISCONNECT)
			continue;

		disconnecting = true;
		break;
	}

	//if we're closing this connection, don't send any other reliable packets on it except DisconnectPacket and clear any outbound or inbound OldPacket's
	if(disconnecting)
	{
		nc->handshook = false;
		FlushPrev(&nc->addr);
	}

	//different connection purposes
	//only "true" it, or retain current state of nc->...
	nc->isclient = isclient ? true : nc->isclient;
	nc->isourhost = isourhost ? true : nc->isourhost;
	nc->ismatch = ismatch ? true : nc->ismatch;
	nc->ishostinfo = ishostinfo ? true : nc->ishostinfo;

	if(isourhost)
		g_svconn = nc;
	if(ismatch)
		g_mmconn = nc;

	//see if we need to connect for realsies (send a ConnectPacket).
	//i.e., send a connect packet and clean previous packets (OldPacket's list).
	if(!nc->handshook)
	{
		bool sending = false;	//sending ConnectPacket?
		unsigned short yourlastrecvack = PrevAck(nc->nextsendack);

		//check if we have an outgoing ConnectPacket
		for(auto pi=g_outgo.begin(); pi!=g_outgo.end(); pi++)
		{
			if(!Same(&pi->addr, &nc->addr))
				continue;

			PacketHeader* ph = (PacketHeader*)pi->buffer;

			if(PastAck(PrevAck(ph->ack), yourlastrecvack))
				yourlastrecvack = PrevAck(ph->ack);

			if(ph->type != PACKET_CONNECT)
				continue;

			sending = true;
			break;
		}

		if(!sending)
		{
			ConnectPacket cp;
			cp.header.type = PACKET_CONNECT;
			cp.reconnect = false;
			cp.yourlastrecvack = yourlastrecvack;
			cp.yournextrecvack = nc->nextsendack;
			cp.yourlastsendack = nc->lastrecvack;
			SendData((char*)&cp, sizeof(ConnectPacket), ip, isnew, false, nc, &g_sock, 0, OnAck_Connect);
		}
	}

	nc->closed = false;

	return nc;
}

When closing a connection, or connecting again after a connection had been disconnected, we flush any buffered in- or out-bound OldPacket's.

//flush all previous incoming and outgoing packets from this addr
void FlushPrev(IPaddress* from)
{
	auto it = g_outgo.begin();

	while(it!=g_outgo.end())
	{
		if(!Same(&it->addr, from))
		{
			it++;
			continue;
		}

		it = g_outgo.erase(it);
	}

	it = g_recv.begin();

	while(it!=g_recv.end())
	{
		if(!Same(&it->addr, from))
		{
			it++;
			continue;
		}

		it = g_recv.erase(it);
	}
}

Conclusion


That is all for this article. If you want to see the rest of the article covering parity bit checking, lockstep, and LAN networking, purchase the full article here: http://www.gamedev.net/files/file/223-reliable-udp-implementation-lockstep-lan-and-parity-bit-checking/



Article Update Log


6 Oct 2015: Initial release

2D Transforms 101

$
0
0
A presentation/tutorial on 2D transforms for programmers and practitioners favouring intuition over mathematical rigour; animations are used to illustrate the effect of every transform explained.

Click Here to view presentation.

Firefox, Chrome or Opera recommended as they support animations; see Requirements below for details. Press ? while in the presentation for controls and Esc for an overview and quick navigation. Comments and suggestions are welcome.

Overview


Transformations are a general mathematical concept that is applicable to anyone working in:

  • Computer Graphics
    • Animation
    • Game Programming (2D and 3D)
    • Image Processing
    • Motion Capture
    • UI Design
  • Computer Vision
  • Robotics
  • Aeronautics
  • Parallel Computing

Concepts discussed are dimension-indepedant; it's just easier to explain and visualize things in 2D but they're applicable to higher dimensions without loss of generality.


Instead of dealing with only elementary transforms (rotate, scale, translate) on points, which most resources do, it also covers:

  • Basic math behind transforms (without matrices)
    • Matrix introduced past the basics since it's just a tool
  • Composite transforms — concatenation of multiple transforms
    • Anti-commutativity
    • Transforms about an arbitrary origin
  • Active and passive viewpoints of transforms
  • Transformation of coordinate systems
  • Mapping between multiple coordinate systems
  • Hierarchical Transforms

Requirements


The presentation was hand-crafted using HTML5, CSS3, JavaScript and SVG; so nothing more than a browser is required to view the presentation. Firefox, Chrome or Opera, even if you've an older version, is highly recommended since support for SVG animations isn't that great in other browsers; with browsers like Safari, Edge or IE you will not be able to see these animations — caveat lector.


You can also view the presentation on a mobile or a tablet; the content, without any loss of fidelity, should get resized and fit to given form factor, thanks to vector graphics and CSS3. Touch (tap and swipe left/right) is supported both for navigation and animation control.



Animations


Transformations are better understood visually than with just dry theory. Hence every transformation workout is accompanied, along with the math, by an animation. Images with a graph sheet background contain animations. Simply click on such an image to cycle through the animation. Every click changes the image's state, animating it into a new figure. When you see the original figure you started with, it denotes the end of all the animation the image contains.


If you're using a keyboard to navigate the presentation, clicking the image steals focus from the presentation and sets in on to the image; simply click anywhere outside the image to give the focus back to the presentation. Now press space / ? to continue with the presentation.



Solution


The solution to problem of mapping the boy space to street space that is posed at the end of the presentation is in map_boy_to_street.md.



SVG


The demonstrations/interactive animations embedded within the presentation was done in SVG, the XML-based, open standard file format for vector graphics with support for animation. In English, all it has are just instructions like move to, line to, rect, circle, ellipse, etc. in a very readable, XML format and no unintelligible binary data. So a reader (yes, a human one too) can easily read and understand it; a renderer can render it at any resolution without any loss in fidelity. The presentation's first slide has a 3D-transformed background which too is an SVG — it should show up as something similar to this; a simple check to see how well your browser supports SVGs and CSS3 transforms.


It's highly recommended that you fiddle with the SVGs under images directory. SVG format is very similar to PostScript (which also has commands like move to, line to, etc.) and is an excellent Hello World test bed (Short, Self Contained, Correct, Example) for learning transformations or 2D graphics in general. Oh they also have a tag for groupings <g> which may be used to learn hierarchical transformations. An SVG is only more readable than a PS, PDF or XAML. Just open it in a (modern) browser to view it (no, not Edge, it doesn't do 3D CSS3 transforms in SVGs yet :), open it in your favourite text editor, muck around, save and refresh your browser to see the changes immediately; rinse and repeat.



Credits

Jump Point Search: Fast A* Pathfinding for Uniform Cost Grids

$
0
0
In 2011, at the 25th National Conference on Artificial Intelligence. AAAI, Daniel Harabor and Alban Grastien presented their paper "Online Graph Pruning for Pathfinding on Grid Maps".

This article explains the Jump Point Search algorithm they presented, a pathfinding algorithm that is faster than A* for uniform cost grids that occur often in games.

What to know before reading


This article assumes you know what pathfinding is. As the article builds on A* knowledge, you should also know the A* algorithm, including its details around traveled and estimated distances, and open and closed lists. The References section lists a few resources you could study.

The A* algorithm


The A* algorithm aims to find a path from a single start to a single destination node. The algorithm cleverly exploits the single destination by computing an estimate how far you still have to go. By adding the already traveled and the estimated distances together, it expands the most promising paths first. If your estimate is never smaller than the real length of the remaining path, the algorithm guarantees that the returned path is optimal.


Attached Image: a_star.png
The figure shows a path computed with A* with its typical area of explored squares around the optimal path.


This grid is an example of a uniform cost grid. Traveling a rectangle horizontally or vertically has a distance of 1, traveling diagonally to a neighbour has length sqrt(2). (The code uses 10/2 and 14/2 as relative approximation.) The distance between two neighbouring nodes in the same direction is the same everywhere.

A* performs quite badly with uniform cost grids. Every node has eight neighbours. All those neighbours are tested against the open and closed lists. The algorithm behaves as if each node is in a completely separate world, expanding in all directions, and storing every node in the open or closed list. In other words, every explored rectangle in the picture above has been added to the closed list. Many of them also have been added to the open list at some point in time.

While A* 'walks' towards the end node, the traveled path gets longer and the estimated path gets shorter. There are however in general a lot of feasible parallel paths, and every combination is examined and stored.


Attached Image: grid_path.png


In the figure, the shortest path between the left starting point and the right destination point is any sequence of right-up diagonal or right steps, within the area of the two yellow lines, for example the green path.

As a result, A* spends most of its time in handling updates to the open and closed lists, and is very slow on big open fields where all paths are equal.

Jump point search algorithm


The JPS algorithm improves on the A* algorithm by exploiting the regularity of the grid. You don't need to search every possible path, since all paths are known to have equal costs. Similarly, most nodes in the grid are not interesting enough to store in an open or closed list. As a result, the algorithm spends much less time on updating the open and closed lists. It potentially searches larger areas, but the authors claim the overall gain is still much better better due to spending less time updating the open and closed lists.


Attached Image: jps.png


This is the same search as before with the A* algorithm. As you can see you get horizontal and vertical searched areas, which extend to the next obstacle. The light-blue points are a bit misleading though, as JPS often stacks several of them at the same location.

The algorithm


The JPS algorithm builds on the A* algorithm, which means you still have an estimate function, and open and closed lists. You also get the same optimality properties of the result under the same conditions. It differs in the data in the open and closed lists, and how a node gets expanded.

The paper discussed here finds paths in 2D grids with grid cells that are either passable or non-passable. Since this is a common and easy to explain setup, this article limits itself to that as well. The authors have published other work since 2011 with extensions which may be interesting to study if your problem is different from the setup used here.

Having a regular grid means you don't need to track precise costs every step of the way. It is easy enough to compute it when needed afterwards. Also, by exploiting the regularity, there is no need to expand in every direction from every cell, and have expensive lookups and updates in the open and closed lists with every cell like A* does. It is sufficient to only scan the cells to check if there is anything 'interesting' nearby (a so-called jump point).

Below, a more detailed explanation is given of the scanning process, starting with the horizontal and vertical scan. The diagonal scan is built on top of the former scans.

Horizontal and vertical scan


Horizontal (and vertical) scanning is the simplest to explain. The discussion below only covers horizontal scanning from left to right, but the other three directions are easy to derive by changing scanning direction, and/or substituting left/right for up/down.


Attached Image: horizontal.png


The (A) picture shows the global idea. The algorithms scans a single row from left to right. Each horizontal scan handles a different row. In the section about diagonal scan below, it will be explained how all rows are searched. At this time, assume the goal is to only scan the b row, rows a and c are done at some other time.

The scan starts from a position that has already been done, in this case b1. Such a position is called a parent. The scan goes to the right, as indicated by the green arrow leaving from the b1 position. The (position, direction) pair is also the element stored in open and closed lists. It is possible to have several pairs at the same position but with a different direction in a list.

The goal of each step in the scan is to decide whether the next point (b2 in the picture) is interesting enough to create a new entry in the open list. If it is not, you continue scanning (from b2 to b3, and further). If a position is interesting enough, new entries (new jump points) are made in the list, and the current scan ends.

Positions above and below the parent (a1 and c1) are covered already due to having a parent at the b1 position, these can be ignored.

In the [A] picture, position b2 is in open space, the a and c rows are handled by other scans, nothing to see here, we can move on [to b3 and further]. The [B] picture is the same. The a row is non-passable, the scan at the a row has stopped before, but that is not relevant while scanning the b row.

The [C] picture shows an 'interesting' situation. The scan at the a row has stopped already due to the presence of the non-passable cell at a2 [or earlier]. If we just continue moving to the right without doing anything, position a3 would not be searched. Therefore, the right action here is to stop at position b2, and add two new pairs to the open list, namely (b2, right) and (b2, right-down) as shown in picture [D]. The former makes sure the horizontal scan is continued if useful, the latter starts a search at the a3 position (diagonally down). After adding both new points, this scan is over and a new point and direction is selected from the open list.

The row below is not the only row to check. The row above is treated similarly, except 'down' becomes 'up'. Two new points are created when c2 is non-passable and c3 is passable. (This may happen at the same time as a2 being non-passable and a3 being passable. In that case, three jump points will be created at b2, for directions right-up, right, and right-down.) Last but not least, the horizontal scan is terminated when the scan runs into a non-passable cell, or reaches the end of the map. In both cases, nothing special is done, besides terminating the horizontal scan at the row.

Code of the horizontal scan

def search_hor(self, pos, hor_dir, dist):
    """
    Search in horizontal direction, return the newly added open nodes

    @param pos: Start position of the horizontal scan.
    @param hor_dir: Horizontal direction (+1 or -1).
    @param dist: Distance traveled so far.
    @return: New jump point nodes (which need a parent).
    """
    x0, y0 = pos
    while True:
        x1 = x0 + hor_dir
        if not self.on_map(x1, y0):
            return [] # Off-map, done.

        g = grid[x1][y0]
        if g == OBSTACLE:
            return [] # Done.

        if (x1, y0) == self.dest:
            return [self.add_node(x1, y0, None, dist + HORVERT_COST)]

        # Open space at (x1, y0).
        dist = dist + HORVERT_COST
        x2 = x1 + hor_dir

        nodes = []
        if self.obstacle(x1, y0 - 1) and not self.obstacle(x2, y0 - 1):
            nodes.append(self.add_node(x1, y0, (hor_dir, -1), dist))

        if self.obstacle(x1, y0 + 1) and not self.obstacle(x2, y0 + 1):
            nodes.append(self.add_node(x1, y0, (hor_dir, 1), dist))

        if len(nodes) > 0:
            nodes.append(self.add_node(x1, y0, (hor_dir, 0), dist))
            return nodes

        # Process next tile.
        x0 = x1

Coordinate (x0, y0) is at the parent position, x1 is next to the parent, and x2 is two tiles from the parent in the scan direction.

The code is quite straightforward. After checking for the off-map and obstacle cases at x1, the non-passable and passable checks are done, first above the y0 row, then below it. If either case adds a node to the nodes result, the continuing horizontal scan is also added, all nodes are returned.

The code of the vertical scan works similarly.

Diagonal scan


The diagonal scan uses the horizontal and vertical scan as building blocks, otherwise, the basic idea is the same. Scan the area in the given direction from an already covered starting point, until the entire area is done or until new jump points are found.


Attached Image: diagonal.png


The scan direction explained here is diagonally to the right and up. Other scan directions are easily derived by changing 'right' with 'left', and/or 'up' with 'down'.

Picture [E] shows the general idea. Starting from position a1, the goal is to decide if position b2 is a jump point. There are two ways how that can happen. The first way is if a2 (or b1) itself is an 'interesting' position. The second way is if up or to the right new jump points are found.

The first way is shown in picture [F]. When position b1 is non-passable, and c1 is passable, a new diagonal search from position b2 up and to the left must be started. In addition, all scans that would be otherwise performed in the diagonal scan from position a1 must be added. This leads to four new jump points, as shown in picture [G]. Note that due to symmetry, similar reasoning causes new jump points for searching to the right and down, if a2 is non-passable and a3 is passable. (As with the horizontal scan, both c1 and a3 can be new directions to search at the same time as well.)

The second way of getting a jump point at position b2 is if there are interesting points further up or to the right. To find these, a horizontal scan to the right is performed starting from b2, followed by a vertical scan up from the same position.

If both scans do not result in new jump points, position b2 is considered done, and the diagonal scan moves to examining the next cell at c3 and so on, until a non-passable cell or the end of the map.

Code of the diagonal scan

def search_diagonal(self, pos, hor_dir, vert_dir, dist):
    """
    Search diagonally, spawning horizontal and vertical searches.
    Returns newly added open nodes.

    @param pos: Start position.
    @param hor_dir: Horizontal search direction (+1 or -1).
    @param vert_dir: Vertical search direction (+1 or -1).
    @param dist: Distance traveled so far.
    @return: Jump points created during this scan (which need to get a parent jump point).
    """
    x0, y0 = pos
    while True:
        x1, y1 = x0 + hor_dir, y0 + vert_dir
        if not self.on_map(x1, y1):
            return [] # Off-map, done.

        g = grid[x1][y1]
        if g == OBSTACLE:
            return []

        if (x1, y1) == self.dest:
            return [self.add_node(x1, y1, None, dist + DIAGONAL_COST)]

        # Open space at (x1, y1)
        dist = dist + DIAGONAL_COST
        x2, y2 = x1 + hor_dir, y1 + vert_dir

        nodes = []
        if self.obstacle(x0, y1) and not self.obstacle(x0, y2):
            nodes.append(self.add_node(x1, y1, (-hor_dir, vert_dir), dist))

        if self.obstacle(x1, y0) and not self.obstacle(x2, y0):
            nodes.append(self.add_node(x1, y1, (hor_dir, -vert_dir), dist))

        hor_done, vert_done = False, False
        if len(nodes) == 0:
            sub_nodes = self.search_hor((x1, y1), hor_dir, dist)
            hor_done = True

            if len(sub_nodes) > 0:
                # Horizontal search ended with a jump point.
                pd = self.get_closed_node(x1, y1, (hor_dir, 0), dist)
                for sub in sub_nodes:
                    sub.set_parent(pd)

                nodes.append(pd)

        if len(nodes) == 0:
            sub_nodes = self.search_vert((x1, y1), vert_dir, dist)
            vert_done = True

            if len(sub_nodes) > 0:
                # Vertical search ended with a jump point.
                pd = self.get_closed_node(x1, y1, (0, vert_dir), dist)
                for sub in sub_nodes:
                    sub.set_parent(pd)

                nodes.append(pd)

        if len(nodes) > 0:
            if not hor_done:
                nodes.append(self.add_node(x1, y1, (hor_dir, 0), dist))

            if not vert_done:
                nodes.append(self.add_node(x1, y1, (0, vert_dir), dist))

            nodes.append(self.add_node(x1, y1, (hor_dir, vert_dir), dist))
            return nodes

        # Tile done, move to next tile.
        x0, y0 = x1, y1

The same coordinate system as with the horizontal scan is used here as well. (x0, y0) is the parent position, (x1, y1) is one diagonal step further, and (x2, y2) is two diagonal steps.

After map boundaries, obstacle, and destination-reached checking, first checks are done if (x1, y1) itself should be a jump point due to obstacles. Then it performs a horizontal scan, followed by a vertical scan.

Most of the code is detection that a new point was created, skipping the remaining actions, and then creating new jump points for the skipped actions. Also, if jump points got added in the horizontal or vertical search, their parent reference is set to the intermediate point. This is discussed further in the next section.

Creating jump points


Creating jump points at an intermediate position, such as at b2 when the horizontal or vertical scan results in new points has a second use. It's a record of how you get back to the starting point. Consider the following situation


Attached Image: path_storage.png


Here, a diagonal scan started at a1. At b2 nothing was found. At c3, the horizontal scan resulted in new jump points at position c5. By adding a jump point at position c3 as well, it is easy to store the path back from position c5, as you can see with the yellow line. The simplest way is to store a pointer to the previous (parent) jump point.

In the code, I use special jump points for this, which are only stored in the closed list (if no suitable node could be found instead) by means of the get_closed_node method.

Starting point


Finally, a small note about the starting point. In all the discussion before, the parent was at a position which was already done. In addition, scan directions make assumptions about other scans covering the other parts. To handle all these requirements, you first need to check the starting point is not the destination. Secondly, make eight new jump points, all starting from the starting position but in a different direction.

Finally pick the first point from the open list to kick off the search.

Performance


I haven't done real performance tests. There is however an elaborate discussion about it in the original paper. However, the test program prints some statistics about the lists

Dijkstra: open queue = 147
Dijkstra: all_length = 449

A*: open queue = 91
A*: all_length = 129

JPS: open queue = 18
JPS: all_length = 55

The open/closed list implementation is a little different. Rather than moving an entry from the open to the closed list when picking it from the open list, it gets added to an overall list immediately. This list also knows the best found distance for each point, which is used in the decision whether a new point should also be added to the open list as well.

The all_length list is thus open + closed together. To get the length of the closed list, subtract the length of the open list.

For the JPS search, a path back to the originating node is stored in the all_length list as well (by means of the get_closed_node). This costs 7 nodes.

As you can see, the Dijkstra algorithm uses a lot of nodes in the lists. Keep in mind however that it determines the distance from the starting point to each node it vists. It thus generates a lot more information than either A* or JPS.

Comparing A* and JPS, even in the twisty small area of the example search, JPS uses less than half as many nodes in total. This difference increases if the open space gets bigger, as A* adds a node for each explored point while JPS only add new nodes if it finds new areas behind a corner.

References


The Python3 code is attached to the article. Its aim is to show all the missing pieces of support code from the examples I gave here. It does not produce nifty looking pictures.

Dijkstra algorithm

Not discussed but a worthy read.
A* algorithm
JPS algorithm

Versions

  • 20151024 First release

Leading the Target

$
0
0
Where should we aim if we want to hit a moving target with a finite-speed projectile? This is one of the recurrent questions from beginner game developers. If we naively aim at the target's current position, by the time our projectile gets there the target will have moved, so we need to aim ahead of the target's current position. The technical name for the technique is "deflection", but most people use "leading the target". Wikipedia page here.

There are several variations of the problem, where perhaps the projectile is a missile that needs to accelerate, or where the shooter is a turret that is currently aiming in some direction and needs time to aim somewhere else... We'll cover the simple case first, and then we'll present a general template for solving variations of the problem.

Plain-vanilla deflection


Let's assume the shooter can aim and shoot anywhere instantly, the target is moving at a constant velocity and the projectile will travel at a constant velocity too. We are given as inputs the target's current position, its velocity and the speed of our projectile. We'll use coordinates where the shooter is at the origin and has zero velocity.

First-order correction


As mentioned before, if we naively aim at the target's current position, by the time the projectile gets there, the target will have moved. We can compute how long it will take for the projectile to get to the target's current position, compute where the target will be then and aim there instead.

Position compute_first_order_correction(Position target_position, Vector target_velocity, float projectile_speed) {
    float t = distance(Origin, target_position) / projectile_speed;
    return target_position + t * target_velocity;
}

This simple piece of code is probably good enough in many cases (if the target is moving slowly compared to the projectile speed, if the target is moving perpendicularly to the shooter-to-target vector, or if we want to sometimes miss because a more precise solution would be detrimental to the fun of the game).

Iterative approximation


For a more precise solution, you could iterate this first-order correction until it converges.

Position iterative_approximation(Position target_position, Vector target_velocity, float projectile_speed) {
    float t = 0.0f;
    for (int iteration = 0; iteration < MAX_ITERATIONS; ++iteration) {
		float old_t = t;
        t = distance(Origin, target_position + t * target_velocity) / projectile_speed;
        if (t - old_t < EPSILON)
            break;
    }
	
    return target_position + t * target_velocity;
}

Computing the answer directly


In the iterative approximation, we would stop if we found a place where old_t and t match. This gives us an equation to solve:

t = distance(Origin, target_position + t * target_velocity) / projectile_speed

Let's do some computations to try to solve it.

t = sqrt(dot_product(target_position + t * target_velocity, target_position + t * target_velocity)) / projectile_speed

t^2 * projectile_speed^2 = dot_product(target_position + t * target_velocity, target_position + t * target_velocity)

t^2 * projectile_speed^2 = dot_product(target_position, target_position) + 2 * t * dot_product(target_position, target_velocity) + t^2 * dot_product(target_velocity, target_velocity)


This is a second-degree equation in t^2 which we can easily solve, leading to the following code:

// a*x^2 + b*x + c = 0
float first_positive_solution_of_quadratic_equation(float a, float b, float c) {
  float discriminant = b*b - 4.0f*a*c;
  if (discriminant < 0.0f)
    return -1.0f; // Indicate there is no solution                                                                      
  float s = std::sqrt(discriminant);
  float x1 = (-b-s) / (2.0f*a);
  if (x1 > 0.0f)
    return x1;
  float x2 = (-b+s) / (2.0f*a);
  if (x2 > 0.0f)
    return x2;
  return -1.0f; // Indicate there is no positive solution                                                               
}

Position direct_solution(Position target_position, Vector target_velocity, float projectile_speed) {
    float a = dot_product(target_velocity, target_velocity) - projectile_speed * projectile_speed;
    float b = 2.0f * dot_product(target_position, target_velocity);
    float c = dot_product(target_position, target_position);
    
    float t = first_positive_solution_to_quadratic_equation(a, b, c);
    if (t <= 0.0f)
        return Origin; // Indicate we failed to find a solution
	
    return target_position + t * target_velocity;
}

The general case


There are many variations of the problem we could consider: Accelerating targets, accelerating projectiles, situations where it takes time to aim at a new direction... All of them can be solved following the same template.

The things that could change can be encoded in two functions:
  • position_of_target_at(time)
  • time_to_hit(position)
All we are really doing is finding a time t at which the following equation holds:

t = time_to_hit(position_of_target_at(t))

We then compute where the target will be at time t and aim there.

Just as before, we could do a first-order correction, use iterative approximation or solve the problem directly. It might be the case that an analytical solution can be found, like we did in the previous section, but things can get messy quickly and you may have to resort to a numerical solution.

Conclusion


This article covered three methods to implement deflection in your games: First-order correction, iterative approximation and directly finding the solution. You'll need to use some judgement to decide which one to use. Hopefully this article gives you enough to make an informed decision.

Article Update Log


30 Oct 2015: Initial release

Code for Game Developers: Optimization

$
0
0
Code for Game Developers is another take on Math for Game Developers - a weekly instructional YouTube series starting from the basics of a concept and working up towards more complex topics. In the case of this video series, after laying out the foundation of optimization you will learn about:
  • Amdahl's Law
  • Big O notation
  • Cache Levels
  • Binary Search
  • Hash Tables
  • CPU optimizations
If you have questions about the topics covered or requests for future topics, I would love to hear them! Leave a comment, or ask me on my Twitter, @VinoBS

Note:  
The video below contains the playlist for all the videos in this series, which can be accessed via the playlist icon at the top of the embedded video frame. The first video in the series is loaded automatically




Combining Material Friction and Restitution Values

$
0
0


Physics simulations commonly use the Coulomb friction model, which requires a coefficent of friction between the materials of two interacting objects to calculate the friction force. Similarly, a coefficient of restitution is required to calculate the collision response force. But how do you determine these coefficients for each pair of materials?


A common approach is to define values of friction and restitution for each individual material and then combine them to get the material-material values. Given individual material friction values \(x\) and \(y\), we need to define a combination function \(f(x,y)\). Similarly, \(r(x,y)\) for restitution (using \(x\) and \(y\) to mean the material restitution values this time).


Function requirements


The value of \(f(x,y)\) and \(r(x,y)\) should lie between \(x\) and \(y\). So \(f(x,x) = x\) and \(r(x,x) = x\).


For any constant \(c\), \(f(x,c)\) and \(r(x,c)\) should be monotonically increasing (it shouldn't ever decrease as \(x\) increases). It should also avoid long flat sections, so as to be able to discriminate bewteen materials. For instance, putting boxes of ice and rubber on an plane and increasing the slope, the ice should slip first, whether the plane is made of ice or rubber.


\(f(x,y)\) should favour slippy over grippy, i.e. \(f(0,1) \lt 0.5\). This corresponds to the intuitive notion that ice should have a greater effect on the combined restitution value than rubber, e.g. vehicle tyres on ice.


I decided that I wanted \(r(x,y)\) to favour the extremes, in a similar way that \(f(x,y)\) should favour values towards \(0\). So very bouncy or very energy-absorbing materials predominate over average ones. In a game world, you then have the option of making the world less or more bouncy for all objects within it by changing the world material, while maintaining a reasonably wide range of restitution values when the world is set to an averagely bouncy material.


Friction


Candidates for \(f\) that are often used are the minimum, the arithmetic average, and the geometric average.


Minimum


\(f_{min}(x,y) = min(x,y)\)

Attached Image: min.gif


This has too many flat sections and doesn't adequately discrimiate between materials, e.g. \(f(0.1,0.1) = f(0.1,1)\).


Arithmetic average


\(f_{aa}(x,y) = \frac{x + y}{2}\)

Attached Image: x+y_2.gif


This doesn't favour slippy over grippy enough.


Geometric average


\(f_{ga}(x,y) = \sqrt{{x}{y}}\)

Attached Image: sqrt(xy).gif


This is good, but the values aren't equally spaced. For this reason, I have found an alternative function - a weighted sum of \(x\) and \(y\).


Weighted sum


\(f_{ws}(x,y) = \frac{{x}{w_{x}} + {y}{w_{y}}}{w_{x} + w_{y}}\) where \(w_{x} = \sqrt{2}(1 - x) + 1\)

Attached Image: f_wc_c=sqrt(2).gif


\(f_{ws}\) has a more regular spacing between values than \(f_{ga}\). The trade-off is that it doesn't cover the full range of values for \(f(x,1)\) (\(f(0,1) \approx 0.3\)). However, they are approximately equal for \(0.2 \le x \le 1\).


Restitution


As with friction, the minimum, the arithmetic average, and the geometric average are often used.


Minimum

Attached Image: min.gif


\(r_{min}\) has the same objections as \(f_{min}\).


Geometric average

Attached Image: sqrt(xy).gif


\(r_{ga}\) is not suitable because it would require the world material to have a restiution near \(1\) to provide a wide range of combined values.


Arithmetic average

Attached Image: x+y_2.gif


\(r_{aa}\) is better, but it doesn't give a very wide range of values for \(r(x,0.5)\). We can improve on this range by allowing some flatness and defining \(r\) as a piecewise min/max/sum function.


Piecewise min/max/sum


\(r_{pmms} = \begin{cases}min(x,y) & \text{if }x \lt 0.5 \land y \lt 0.5\\max(x,y) & \text{if }x \gt 0.5 \land y \gt 0.5\\x + y - 0.5 & \text{otherwise}\end{cases}\)

Attached Image: r_pmms.gif


This has the same shortcomings as \(r_{min}\) at the corners where the min and max functions are used. But you can't avoid that if you want to have the full range of values for \(r(x,0.5)\) and still satisfy \(r(x,x) = x\). Similar to the friction, I have created a weighted sum function that I think is better.


Weighted sum


\(r_{ws}(x,y) = \frac{{x}{w_{x}} + {y}{w_{y}}}{w_{x} + w_{y}}\) where \(w_{x} = \sqrt{2}\left\vert{2x - 1}\right\vert + 1\)

Attached Image: r_wc_c=sqrt(2).gif


As with \(f_{ws}\), \(r_{ws}\) sacrifices some of the range for \(r(x,0.5)\) to provide better continuity in the corners.


Why \(\sqrt{2}\)?


It's the choice that maximizes the function range, while maintaining monotonicity. These graphs show what \(f_{ws}\) looks like with \(w_{x} = c(1 - x) + 1\), for \(c = 0.5\), \(c = 1\), \(c = \sqrt{2}\), \(c = 2\), and \(c = 4\). For \(c \lt \sqrt{2}\), \(f_{ws}(x,1)\) has less range. For \(c \gt \sqrt{2}\), \(f_{ws}\) is no longer monotonic - near \(f_{ws}(0.1,0.9)\) it starts to curve back, making a more grippy material have a lower combined friction! You can see this more clearly for higher values of \(c\).


\(c = 0.5\)

Attached Image: f_wc_c=0.5.gif

\(c = 1\)

Attached Image: f_wc_c=1.gif

\(c = \sqrt{2}\)

Attached Image: f_wc_c=sqrt(2).gif

\(c = 2\)

Attached Image: f_wc_c=2.gif

\(c = 4\)

Attached Image: f_wc_c=4.gif

Conclusion


The method to combine material friction and restitution values may seem like a minor detail, but sometimes the devil's in the details.


Since the whole concept of defining values for friction and restitution for a single material and then combining them isn't physically accurate, this is obviously just a case of finding a suitable function rather than attempting to model reality. You may have different requirements for your functions, but I hope this discussion of the alternatives is useful.


Links


I made the graphs at the WolframAlpha web site. I find it's a pretty useful tool. Here's an example plot.



Article Update Log


12 Nov 2015: Initial release

Making Your C++ Namespace Solid and Future-Proof

$
0
0
This article provides a possible solution to a real problem, nothing more, nothing less. It is up to developers evaluating pros and cons to decide if this approach is worthwhile for their framework/library.

The problem


In C++ you do not import stuff, you include files which means "text replacement" and some preprocessor magic. When you include a file you are indirectly including many other files, relying on this behaviour is bad and can cause harm in the long run:
  • What you expect "by design" is that you have at your disposal only what you "imported/included"
  • Side-included headers are actually only a implementation detail: it may change!

Two real examples of breaking code


Example #1

This GCC distribution at least have a STL library that indirectly include

<functional>

from other headers, when you accidentally use stuff from such a header then the code will just compile fine, but when you try to compile the code from elsewhere the compilers will complain that there is no such thing called "std::function" and (maybe) you are missing some include (and you are truly missing an include).

Example #2

Your class is using another class as a private member:

#include "Foo.h" // a implementation detail
    
class MyClass{
    Foo _foo;
public:
    //...
};

Later you decide to refactor the code and use internally another class:

#include "Bar.h" //ops.. any client used "foo" but not included it? => dang compile error for him
    
class MyClass{
    Bar _bar;
public:
    //...
};

The Solution


The solution to the problem is actually very simple: Put everything in another namespace, and "import" it only if client is actually including it from the right header.

Your library BEFORE


Directory structure:
mylib/
    +MyClass.h
    +Foo.h
    +MyClass.cpp
    +Foo.cpp

MyClass.h: including this file actually cause the inclusion of "Foo.h".
    #pragma once
    #include "Foo.h" // a implementation detail
    
    namespace mylib{
        class MyClass{
            Foo _foo;
        public:
            //...
        };
    }

MyClass.cpp
    #include "MyClass.h" // a implementation detail
    
    namespace mylib{
        //...
    }

Foo.h
    #pragma once
    
    namespace mylib{
        class Foo{
            //...
        };
    }

Your library AFTER


Directory structure:
mylib/
    
    +MyClass.h
    +Foo.h
    
    priv/
        +MyClass.h
        +Foo.h
        +MyClass.cpp
        +Foo.cpp

You move all old files to a private folder, then you just import stuff into your namespace from public headers

Forwarding headers

mylib/MyClass.h
#include "priv/MyClass.h"

namespace PUBLIC_NAMESPACE{
    using MyClass = PRIVATE_NAMESPACE::MyClass; //requires C++11
}

mylib/Foo.h
#include "priv/Foo.h"

namespace PUBLIC_NAMESPACE{
    using Foo = PRIVATE_NAMESPACE::Foo; //requires C++11
}

Internally you keep everything in a private namespace, so the user is forced to include correct headers immediatly:

Now entering the "priv" folder

mylib/ priv/ MyClass.h
    #pragma once
    #include "Foo.h"
    
    namespace PRIVATE_NAMESPACE{
        class MyClass{
            Foo _foo;
        public:
            //...
        };
    }

Note how important is the usage of "relative path" inclusion

mylib/ priv/ MyClass.cpp
    #include "MyClass.h" // a implementation detail
    
    namespace PRIVATE_NAMESPACE{
        //...
    }

mylib/ priv/ Foo.h
    #pragma once
    
    namespace PRIVATE_NAMESPACE{
        class Foo{
            //...
        };
    }

Apart from renaming namespaces, there are no major changes in the pre-existing code nor pre-processor magic, the whole task could be automated so that you get C#-style headers almost for free. Basically you can continue to develop as always because it is always possible to re-import stuff in a different namespace (even third party libraries).

Effects on client code:


Without forwarding:
#include <MyClass.h>
using namespace PUBLIC_NAMESPACE;
    
int main(){
    MyClass a;
    Foo b;     //allowed (public namespace polluted)
}

With forwarding:
#include <MyClass.h>
using namespace PUBLIC_NAMESPACE;
    
int main(){
    MyClass a;
    Foo b;     //NOT ALLOWED Compile error (need to include Foo)
}

Pros
  • Less pollution in public namespace
  • Users are forced to not rely on implementation details
  • Less chance to break code after library refactoring
Cons
  • Increased compile time
  • More maintenance cost for library developers

Article updates


14/11/2015 17:10 added usage example

Maintenance-free Enum to String in Pure C++ with "Better Enums"

$
0
0

Background


Enums are used in game programming to represent many different things – for example the states of a character, or the possible directions of motion:

enum State {Idle, Fidget, Walk, Scan, Attack};
enum Direction {North, South, East, West};

During debugging, it would be useful to see "State: Fidget" printed in the debug console instead of a number, as in "State: 1". You might also need to serialize enums to JSON, YAML, or another format, and might prefer strings to numbers. Besides making the output more readable to humans, using strings in the serialization format makes it resistant to changes in the numeric values of the enum constants. Ideally, "Fidget" should still map to Fidget, even if new constants are declared and Fidget ends up having a different value than 1.

Unfortunately, C++ enums don't provide an easy way of converting their values to (and from) string. So, developers have had to resort to solutions that are either difficult to maintain, such as hard-coded conversions, or that have restrictive and unappealing syntax, such as X macros. Sometimes, developers have also chosen to use additional build tools to generate the necessary conversions automatically. Of course, this complicates the build process. Enums meant for input to these build tools usually have their own syntax and live in their own input files. The build tools require special handling in the Makefile or project files.

Pure C++ solution


It turns out to be possible to avoid all the above complications and generate fully reflective enums in pure C++. The declarations look like this:

BETTER_ENUM(State, int, Idle, Fidget, Walk, Scan, Attack)
BETTER_ENUM(Direction, int, North, South, East, West)

And can be used as:

State   state = State::Fidget;

state._to_string();                     // "Fidget"
std::cout << "state: " << state;        // Writes "state: Fidget"

state = State::_from_string("Scan");    // State::Scan (3)

// Usable in switch like a normal enum.
switch (state) {
    case State::Idle:
        // ...
        break;

    // ...
}

This is done using a few preprocessor and template tricks, which will be sketched out in the last part of the article.

Besides string conversions and stream I/O, it is also possible to iterate over the generated enums:

for (Direction direction : Direction._values())
    character.try_moving_in_direction(direction);

You can generate enums with sparse ranges and then easily count them:

BETTER_ENUM(Flags, char, Allocated = 1, InUse = 2, Visited = 4, Unreachable = 8)

Flags::_size();     // 4

If you are using C++11, you can even generate code based on the enums, because all the conversions and loops can be run at compile time using constexpr functions. It is easy, for example, to write a constexpr function that will compute the maximum value of an enum and make it available at compile time – even if the constants have arbitrary values and are not declared in increasing order.

I have packed the implementation of the macro into a library called Better Enums, which is available on GitHub. It is distributed under the BSD license, so you can do pretty much anything you want with it for free. The implementation consists of a single header file, so using it is as simple as adding enum.h to your project directory. Try it out and see if it solves your enum needs.

How it works


To convert between enum values and strings, it is necessary to generate a mapping between them. Better Enums does this by generating two arrays at compile time. For example, if you have this declaration:

BETTER_ENUM(Direction, int, North = 1, South = 2, East = 4, West = 8)

The macro will expand to something like this:

struct Direction {
    enum _Enum {North = 1, South = 2, East = 4, West = 8};

    static const int _values[] = {1, 2, 4, 8};
    static const char * const _names[] = {"North", "South", "East", "West"};

    // ...functions using the above declarations...
};

Then, it's straightforward to do the conversions: look up the index of the value or string in _values or _names, and return the corresponding value or string in the other array. So, the question is how to generate the arrays.

The values array


The _values array is generated by referring to the constants of the internal enum _Enum. That part of the macro looks like this:

    static const int _values[] = {__VA_ARGS__};

which expands to

    static const int _values[] = {North = 1, South = 2, East = 4, West = 8};

This is almost a valid array declaration. The problem is the extra initializers such as "= 1". To deal with these, Better Enums defines a helper type whose purpose is to have an assignment operator, but ignore the value being assigned:

template <typename T>
struct _eat {
    T   _value;

    template <typename Any>
    _eat& operator =(Any value) { return *this; }   // Ignores its argument.

    explicit _eat(T value) : _value(value) { }      // Convert from T.
    operator T() const { return _value; }           // Convert to T.
}

It is then possible to turn the initializers "= 1" into assignment expressions that have no effect:

    static const int _values[] =
        {(_eat<_Enum>)North = 1,
         (_eat<_Enum>)South = 2,
         (_eat<_Enum>)East = 4,
         (_eat<_Enum>)West = 8};

The strings array


For the strings array, Better Enums uses the preprocessor stringization operator (#), which expands __VA_ARGS__ to something like this:

    static const char * const _names[] =
        {"North = 1", "South = 2", "East = 4", "West = 8"};

We almost have the constant names as strings – we just need to trim off the initializers. Better Enums doesn't actually do that, however. It simply treats the whitespace characters and the equals sign as additional string terminators when doing comparisons against strings in the _names array. So, when looking at "North = 1", Better Enums sees only "North".

Is it possible to do without a macro?


I don't believe so, for the reason that stringization (#) is the only way to convert a source code token to a string in pure C++. One top-level macro is therefore the minimum amount of macro overhead for any reflective enum library that generates conversions automatically.

Other considerations


The full macro implementation is, of course, somewhat more tedious and complicated than what is sketched out in this article. The complications arise mostly from supporting constexpr usage, dealing with static arrays, accounting for the quirks of various compilers, and factoring as much of the macro as possible out into a template for better compilation speed (templates don't need to be re-parsed when instantiated, but macro expansions do).

Billboarded Foliage in Unreal Engine 4

$
0
0
This article will guide you through how to create billboarded folage in Unreal Engine 4. That is, foliage which is a simple quad repeated many times, all of which always face the camera. I will also discuss why this is a good idea (along with some of the potential downsides, too) and the performance implications.

Foliage in Unreal Engine 4


By default, Unreal Engine 4 supports foliage via its foliage editing tool. This tool allows placement if instanced static meshes (static meshes meaning non-skeletal models, in more generic terms). Each of these foliage assets can be drawn many thousands of times onto the map, each with instance-specific parameters, e.g. rotation, location, etc.

For relatively small (e.g. under 10,000) assets, which are simple (e.g. less than 100 polygons) this generally performs OK with shadows and good quality lighting on the foliage, so long as you set a reasonable culling distance so that the graphics hardware doesn't try to draw all 10,000 instances at once.

For relatively large amounts of foliage (e.g. entire forests) this can pose problems.

Let's discuss a scenario which happened in my game to clarify. To the north of the map is a large forest, and the player may enter the southern edge of that forest. Everything beyond the southern edge of that forest is outside the playable area, but still needs to be rendered and needs to give the impression of a huge, impassible forested area.

If we were to render the entire forest using even low-poly versions of the trees, this can and does cause a performance issue for players with lower end hardware.

The solution is to create a billboard.

A billboard? Isn't that something to do with advertising?


Out of the box, Unreal Engine 4 supports a component known as a Billboard Material Component. These are great for one-off or small numbers of static meshes placed into the level, as shown in the example above. Unfortunately they cannot be used for the foliage tool, as the foliage tool only accepts placement of static meshes and to make use of the Billboard Component, it must be contained within an Actor which is not a supported type for this tool.

So, we must make our own Billboard Material. The base instructions on this to get you started can be found on Epic's documentation in a section that discussed stylized rendering (this is obscure enough that many may not find it).

I will not reproduce Epic's content here, however I will provide a screenshot of my own material, based upon theirs, and discuss its creation and how to use it in your game.

Start out by importing some simple 2D textures which represent your billboarded sprite. For this, I took screenshots of my actual tree model, and made the backgrounds transparent. This makes them look identical to the actual high poly models, at any reasonable distance.

It is important to have some variety here, for my game I chose four different views of the same tree, so that they look different when displayed:



billboard tree 1
billboard tree 2
billboard tree 3
billboard tree 4


Preparing the material


The next step in creation of the billboards is to create a material which will ensure that the verticies of your static mesh face the camera. This works because the static mesh we will use will be a simple quad built from two triangles (more on this later).

Go into Unreal Engine's Content Browser, right click and choose to create a new Material.

You should create a material which has a blend mode of Masked, Shading model of Unlit and is Two Sided:

material properties for billboaded materials

You may then place your nodes in the material's graph pane. These are similar to the one on Epic's documentation, however the parts missing from their documentation are shown here in my implementation (click the image to enlarge):

billboardmaterial

There are some important things to note about this material. As discussed already the material adjusts the position of its verticies to face the camera. It does this via the nodes connected to the World Position Offset.

The 'Custom' node, within the billboard effect section, contains some custom HLSL code which is used to calculate the correct rotation of the quad. The content should read as follows, with the 'Inputs' set to one value named 'In' and the output type as "CMOT Float 2" (note, this differs slightly from Epic's version, which had broken parameter names):

float2 output; 
output = atan2 (In.y,In.x); 
return (output);

The nodes attached to the PivotPosition ensure that the quad rotates around its origin, and is translated to world space so that the coordinates make some sense.

Note that this material is not intended to be used directly. The Texture has been promoted to a Material Parameter, which can then be used by material instances.

You should right click on this material in the content browser once you have saved it, and make multiple instances of it, each of which uses the correct texture that you want to use as a billboard. This will prevent code duplication and is slightly more efficient on resources.

instances

Now we have defined the material, we can move on to loading a simple quad into Unreal Engine 4, which can be used as the static mesh for the foliage tool.

Creating and loading the mesh


All we need in terms of a model for the billboard is a simple quad, built from two triangles. You can create such an asset in Blender, or alternatively I have attached mine to this article, it may be downloaded below and used freely in any project.

Attached File  squareplane.zip   6.06KB   45 downloads

When you import the quad, be aware you may need to adjust the rotation and translation of the quad so that it sits squarely initially facing the camera, and on the surface of your terrain. If you do not do this, then your material will face sideways, or upside down (or both!) and the material will translate its position to always face in the wrong direction!

For example, to import my quad, you could use:
  • Roll: 270 degrees
  • Pitch: 180 degrees
  • Yaw: 0 degrees
  • Import Uniform Scale: Adjust this to suit your map, the quad is quite large.
  • Import translation, Z axis: Adjust this to suit your terrain. I used 190 units.
Once you have imported the quad, name it something sensible (I named mine TreeBillboard_Quad_1) and assign it your billboard material.

You could now place this into your map, and observe how as you move the camera, the quad will always face your viewpoint. Alternately, and more sensibly, you can now drag this static mesh into the foliage tool, and start placing it:

foliage edit

When you add the mesh to the foliage tool you should be sure to disable the Align to Normal and Random Yaw options, as both of these rotate your mesh and will break the effect put in place by the material.

Be aware that you should always place this mesh outside the playable area, where the player cannot reach it and attempt to walk behind or at the side of it. If they do so, the illusion will be broken and it won't look good at all.

Once you have placed many thousands of these trees they will start to look like a forest. The quality of your result is directly related to the quality of the initial textures you used:

trees


Performance


As we are only drawing two triangles for each tree, the performance of this solution is more than acceptable on modern hardware. I was able to place enough trees to fill my map without noticable slowdown on my mid-range graphics card (AMD R9 270X). Although I used this for trees, the same approach could be used for grass, reeds, or any other smaller plants that the player might be close to, but wouldn't notice the flatness.

It is also difficult to properly light and display shadows for billboards. In my game, I have turned off shadows for the billboard meshes. As they are usually placed very far from the camera, the player should not notice this. If they get too close, all bets are off.

It is worth noting however that the performance of this solution is not perfect. By using the 'Custom' node in our material we prevent Unreal Engine 4 from making various optimisations to our shader. This has not been noticable for me, but your mileage may vary. If you are unsure, always profile your game.

Conclusion


I hope this has been a useful article on how to render many pieces of foliage without breaking the bank in terms of your GPU budget. There are many ways of rendering foliage of which this is just one. If you have any questions, improvements or other feedback as always please feel free to comment below.

Article Update Log


26 Nov 2015: Initial release

Favor Small Components in Unity

$
0
0

This article is reposted from here Platform RPG

A common mistake many developers make when making their first game is trying to cram too much functionality into a single class. This violates the single responsibility principle. When designing your classes they should only do one thing and do it well. A good rule is if you need to use the word ‘and’ to describe what a class does, it should probably be broken up into multiple classes.

There are a couple of good reasons why want to break up your classes

Small Classes Make Your Code More Reusable


By breaking down your code into smaller pieces you make it easier to reuse code. As an example, suppose you have a player class for any playable character in your game. The player can take damage so you add a damage method to the player and keep track of it health in that same class. Any attacks that hit the player you apply to the player class. Now suppose you want to make enemies able to damage each other or even have destructible environment elements. To reuse the damage code on the player you would have to bring along the rest of the player code, even if it has no place on an enemy. The other option would be to have the attacks check what type of object is being attacked and handle them separately but this makes for some messy code that is more brittle and has more bugs. A better solution would be to have a single damageable component that only handles damage as a separate class from the player.

public class Damageable : MonoBehavior {
  public float maxHealth = 100.0f;
  private float currentHealth;

  void Start() {
    currentHealth = maxHealth;
  }

  public void ApplyDamage(float amount) {
    currentHealth -= amount;
  }

  public bool IsDead {
    get {
      return currentHealth <= 0.0f;
    }
  }
}

Now you can attach this damageable component to anything you want to be able to take damage. Whenever a projectile hits an object or there is an explosion you simply apply damage to any game objects with a Damageable on it. The player, enemy, or destructible object class can simply check its own damageable to see if it is dead or not.

Small Classes Make Your Code Easier to Understand


Another benefit of making small modular pieces is they are easier to manage. You can fit the functionality of small classes in your mind all at once. This makes it easier to find bugs and to verify that the code does what you want it to. Large classes usually have lots of complicated interactions and makes it much more likely for bugs to emerge.

Small modular design has been key in the development of our game, the platform rpg. We want to have many characters with unique abilities. Each ability is composed of many small and simple pieces, such as a timer class, an apply damage event class, or fire projectile class. We then compose these small pieces into more complicated abilities. A projectile doesn’t have hard coded behavior when it makes contact with anything. Instead it simply fires a collided event and that event can be connected to a damage event, or any other event you want to happen when the projectile hits something.


projectile.jpg?w=1000


Since one of the main mechanics of the platform rpg is to rewind time after each player on a team it is key that the entire state of a scene can be restored to a previous time. Instead of writing code to save and restore the state of an entire ability, each small and simple piece of the ability manages saving and restoring its own state. We wrote a spell editor for the platform rpg to allow us to program visually and take a data based approach to game dev, but that is a whole other post.

So if you ever find your projects falling apart because they become too hard to manage, take a close look at the design of your code. By breaking your large over-encumbered classes into smaller manageable classes you may find your entire project is easier to manage and the complexity of the project can be manageable again.

My Year as a Mobile Gamedev

$
0
0

Over a year ago I released my first Android game after a few months of working and posting my progress on wykop.pl with #odzeradogierdevelopera hashtag. with literally no prior knowledge in gamemaking, I've managed to create a simple game which has been positively acclaimed.

Now, having gained a lot of experience and with a slightly new vision and approach to mobile gamedev, I’m publishing my fourth game – MiniCab: Animal Express. I’d like to briefly outline the differences I notice regarding my actions and choices.


OU01Ohy.png
reVoid


When I was releasing my first game, I had no marketing knowledge whatsoever. I was driven by the presumption that a good game will sell itself. But nothing could be further from the truth. The truth is, even the best, the most innovative game with poor advertising won’t be a success or, what’s worse, a well marketed crappy game may be a hit! That’s the problem with the mobile games market. Flooding the market with garbage which cunningly imitates popular games or films by using strikingly similar graphics, using keywords (so-called ASO) and so on. A large part of such games are completely unplayable. What’s more – they shouldn’t even be called games at all, although they hold higher ranking positions than true games simply by misleading those less-aware gamers who are oblivious to such trickeries. Of course, everything is done in accordance with law, because there is no direct copyright infringement.

Back to reVoid marketing, which was pretty much non-existent, the only publicity and marketing was achieved through my reports in a form of posts tagged #odzeradogierdevelopera on wykop.pl. The summary post has been upvoted by over 1000 users. That contributed to a high ranking position on the first day after publication in the Play Store and a high download rate, although it has dropped and now the game is downloaded by a few users a week.

In total, the game has been downloaded 34,878 times, 20% on the first week, and 60% of the downloads were from Poland.


8OY2kgs.png
reVoid – total downloads

THGC8nV.png
Fly! Fly!


A few months after reVoid release I started thinking about a new game. I thought that it’d be cool to make something quickly, try to meet a certain short deadline. I settled on 14 days and after those two weeks under low workload I released a simple game without any marketing coverage. I’ve only made one post on wykop.pl/mikroblog about the release. How many downloads? A little over 900. The game didn’t really catch on. Yes, maybe it wasn’t the most remarkable game, but first of all it was pretty much unheard of :) It’s important to note that every day app stores are flooded with hundreds of new products; over 500 new apps are added to Play Store each day! That’s why it’s impossible for a potential gamer to find a desirable game on their own – they need help and that’s why marketing is so important.


fPaxB16.png
Fly! Fly – total downloads

OMao38K.png
Hools Run


After Fly! Fly! it took me quite a long time to bring myself to start another project. I made a few prototypes but none of them were suitable to become a legitimate game. Then wykop.pl user @Oskarek89 suggested that we cooperate on production of a football-themed game. The cooperation was based on the premise that I manage the development and he makes sure that the game sells out, using his fanpage and connections. It was a good decision, because I was able to focus on game making without bothering with the marketing.

On the first week, the game has been downloaded 14,000 times and after that it skyrocketed to a high position in the ranking. Though it was downloaded mostly in Poland (86%), it was virtually non-existent in other countries. And that’s the next issue: when a game gains popularity on the biggest market – USA – its popularity rapidly spreads throughout other countries through the media. In the case of Poland, everything stays here and, for all intents and purposes, restricting the marketing only to Polish market makes our game unavailable to other markets.

In total, Hools Run has been downloaded 25,337 times, 51% of the downloads happened on the first week.


rRmJwdY.png
Hools Run – total downloads

6ouKDD3.png
Minicab Animal Express


At the beginning it was supposed to be a project similar to Fly! Fly! – a simple pixel-art game about driving a taxi created in no more than 3 weeks. I ended up making it for nearly three months :) It was all because in the course of its production I found that it’s not a good idea to release the next game as soon as possible, since no one is chasing me. I realized it’ll be better to apply myself, work a little longer and release a game which is much better than every next pixel-art shitty game. I also implemented some changes in terms of marketing. I started to show the game in its various stages of production, not just on wykop.pl, but also on foreign sites, i.e. on reddit or Twitter. The game hasn’t gained much popularity on those sites, but with each new post it piqued the interest of several people who praised some of its aspects, asked about the release date and so on.

I prepared a presskit with the most important information, screenshots and gameplay, and I sent it to Polish and foreign gaming and Android websites, hoping that they will publish some information about my game in the form of news/reviews. This kind of marketing costs nothing, and can really give a lot. Most of these sites warmly welcome any information about the games directly from their developers – this way presskit news are almost ready, and the journalists who are reportedly always very busy, are very eager to indulge in the opportunity to use any free stuff. Of course, provided that the game is good enough and worthy of publication :)

With that in mind, it’s important not to be overeager, no one likes spam in their mailbox. The message must be sent once, to a proper address. Some newsrooms have a separate box/forms to contact the developers or specific individuals on tech websites who specialize in gaming.

Summary


Mobile gamedev it is not, as I discovered, a simple matter, especially for someone who works alone, dealing with all the aspects of production: starting with graphics, through gameplay, to the marketing. During this year of game dev, I’ve learned many new things, but still I barely even touched on the subject. It is essential for every developer who works alone (and who has no intention of going to any publisher and would rather release their own game by themselves) to start showing it to the world as soon as possible, gather opinions, comments and ideas. Reddit, twitter, gamedev forums, groups on Facebook, itch.io. It’s also a very good idea to start a devlog e.g. on Tumblr (example: http://jettoast.tumblr.com/). If someone starts to show their game two weeks before the release, they can be sure that it won’t succeed. Building up the position on the market starts long before the release of the game, especially if you don’t have millions for big advertising campaigns.

The second issue is building a community. This is important, because faithful and devoted community surrounding a particular game can, in addition to support during various stages of production, yield a nice profit and provide free advertising. The third issue is the search for new opportunities. Instead of doing everything by yourself, you can hire a person or company that specializes in independent game marketing ($$$ needed) or you can contact a publisher. Nowadays, more and more publishers are running special programs for independent developers where they offer technical, financial and marketing support for the game in exchange for share in profits (examples: http://www.11bitstudios.com/pl/launchpad/ or http://www.publishing.vividgames.com/). Then there is the question: will I earn more by publishing the game on my own or with the help of a publisher who will want a large portion of the profits in return? Everyone has to answer that by themselves. I am guided by a simple principle: it is better to get 10% of something than 100% of nothing :)

What about my finances? How much money have I managed to earn in this multi-billion-dollar mobile gaming market so far? A little less than 250$. All of my games are 100% free and the only way of monetizing is through the ads. My games are, what’s very important, released only on one platform – Android, which is mainly due to the lack of funds for investment in iOS platform on which the entry threshold is much higher than in the Play Store. I’d rather not talk about Windows Phone platform.

And this way I’ve described my first year of amateur gamedev. In this short text I failed to cover all the aspects and details, so if you feel unsatisfied or have any questions, feel free to discuss, at the same time I encourage you to download my youngest child: MiniCab Animal Express :)

Regards.

https://twitter.com/ZarzyckiAdrian

Creating your first game with Game Maker: Studio

$
0
0

Introduction


So you want to start creating your own games? Then you've come at the right place. This article will get you started on creating simple 2D games.

Game Maker: Studio


What is that?


Game Maker: Studio is a popular game development software used by many Indie Game Developers all over the world. It's easy; yet powerful.

It has a free version which is capable of making good games for Windows. Further, the professional version can be bought for more features and can be extended by buying packages to be able to make games for different platforms such as Android, Mac, iOS, etc.

There are mainly two ways of creating your game: Using the Drag&Drop actions, or by coding. In this article, we'll make our game using the coding language of Game Maker: Studio, which is called Game Maker Language and often abbreviated as GML. Don't worry, it's very easy. Let's proceed.

Understanding the basics


Before starting, understanding the basics is a must. Let's see how a game is created in GM:S.

Sprites


Sprites are the images created/imported in the software to be used for different things such as the character, a block, the wall, or the boss. A sprite can be a single image or a series of images (or sub-images) which results in an animated sprite.

Sprites can be created using the sprite-editor in GM:S or can be imported from any file.

Objects


Objects signify different elements of a game, such as a character, a block, a power-up, an enemy, or anything. So every different element, or simply object of a game needs a different object.

Sprites can be assigned to objects. Further, you can add events, actions and code in the object which define its actions, and then can be placed in a room, which will be shown when you play your game.

Note: If you understand this completely, then, good for you. If not, or if you're confused, don't worry - just keep reading. You'll get it eventually.

Rooms


Rooms can be defined as levels in your game. A room is a rectangular space - its size is defined by its width and height in number of pixels. (Example: A width of 1024 and a height of 768 pixels will result in a room of size 1024x768)

After a room is created, objects can be put in the space of the room, and a level can be designed in this way. This way, many rooms, or levels, can be created. When you start your game, the first room is shown first.

Our first game!


So now that we're done with the basics, we'll start by creating a simple game. We'll be making a simple character who needs to collect coins to win, avoiding the enemies.

Start Game Maker: Studio. You'll see a small window with many tabs. Open the New Tab, enter your project name and click on Create. You'll see an empty window with many folders in the left pane. That's where all of the Sprites, Objects, Rooms, Sounds and everything is sorted. Quite neat, isn't it? ;)

Sprites


In that left pane, the first folder will be Sprites. Right-click on it and select Create Sprite. You'll see sprite0 under the folder Sprites - that's your first sprite! A small window will open - that's the sprite manager. It shows your sprite with many options. It's empty for now, because you haven't created or imported any sprite.

Name it spr_player, because it will be our player's sprite.

Click on Load Sprite to load any image file for the player, or click on Edit Sprite to create your own.

Creating your sprite


Now that you've clicked on Edit Sprite, another window will open: This is where all of the subimages of your sprite are created. From the menus on the top, open the File menu and select New.... Enter the dimensions of the sprite in the window that opens. In our case, we'll use width: 32, height: 32. Hit enter, and in that window, a small sprite will be created, with the name image 0.

Wow. You just created the first subimage of your sprite! Double-click on the sub-image you just created. Another window will open, and this one is the sprite editor. You'll see it's mostly like Paint. Now - use your creativity! Create your player, as you like. Remember: We're creating a Top-Down game, which means the player must be created as seen from the top. And, it must face right: or else it'll cause problems. Now, create! :D

...

Done with the sprite? Click on the Green Tick on the top-left in the window. It'll close, and in the sub-image editor you'll see your player image. Again, click on the Green Tick. There's your sprite! Now click OK to save the sprite.

Now, in the same way, create these sprites: a wall block, a coin, and an enemy - and remember, they too must be from the top and the enemy also should be facing right. Don't forget to name them after "spr_"(Like spr_player, spr_coin). Use the size 32x32 for all of these sprites.

Objects


Done with creating the sprites? Let's move on to creating our objects. Find the objects folder from the left pane, right-click on it and choose Create Object. Your object (object0) will be created and the object manager window will open.

First of all, change the name from object0 to obj_player. Yes, obj_ will be used as the object name prefix.

Prefixes: Name prefixes such as spr_ (for sprite names), obj_ (for object names), room_ (for room names) aren't compulsory but they're used so that it's easier to reference them in code. For example: A coin is a coin but while coding, you'll know what you want to reference and it will be easier: spr_coin for the sprite and obj_coin for the object.


Now, under the place where you typed the name of the object will be a menu where you can select the sprite you want to use with the object. Click on it and select spr_player. Click on OK to save the object.

Now, in the same way, create objects for the coin, the wall block and the enemy.

Wow! Do you realise that you're creating your own game? You're not far away from getting it running. Just keep reading!

Done with creating the objects, and assigning sprites to them? Good. Now let's start the next step.

Coding


Double-click on obj_player. In object manager, you'll see two empty panes: the one on the left is the event pane, and the one on the left is the action pane.

Events are the specific events which trigger the actions inside them. For example, actions inside the 'left arrow key' event will be executed when the left arrow key on the keyboard is pressed. The 'Create' event works when the object is created first, and that's the only time the actions inside the create event are executed. Actions inside the 'Step' event will be executed every step: or every frame. By default, there are 30 steps in a game, so it means actions inside the Step event will be executed 30 times a second. Woah! Similarly, there are many events.

Room Speed, which tells how many steps there will be in the room, can be changed from the room settings. Default is 30.

Now, right-click in the event pane and select "Add" or just click on "Add Event" below the pane. A small window will open, which contains all of the existing events. Click on Keyboard, and select Left Arrow Key. Similarly, add events for Right Arrow Key, Up Arrow Key and Down Arrow Key. Now we'll add the code for these events.

Click on the Left Arrow Key Event. Now look at the action pane - in the rightmost pane, there are many events which can be dragged into the action pane. They're called Drag & Drop actions. We'll not use them; we'll use an action which is used to add code. See the many tabs on the right side? Open the control tab. From the actions there, choose the first action under the name "Code". (There will be total 3 actions under 'Code') Drag it into the action pane. A window will open - it's the text editor for entering the code. Enter this code in the window:

x-=3

Let me explain what this does.

x is the horizontal property, so it defines the horizontal position of the object in the room in the number of pixels. Similarly, y is the vertical property - it defines the vertical position of the object. So, x=254 will change the horizontal position of the object to 254 pixels in the room.

If x increases, the object will move right. If it decreases, it'll go left.
If y increases, the object will go down, and if it decreases, it'll move up.

What we're doing is telling the object to move left when Left Arrow Key is pressed - so we're decreasing its x property, by using x-=3 - which means subtract 3 from x.

Now click on the Green Tick at the Top-Left. You'll see that your code action has been added in the action pane. You can open and edit the code any time just by double-clicking on the action.

Now, in the same way, add codes for the other Arrow Key actions. Open the next event. Drag the Code action from the control tab of D&D (Drag and Drop) menu. Here are the codes for the arrow keys: (I suggest you first yourself guess what the code will be based on what I told you in the previous paragraph about reducing and increasing x and y to move)

Right Arrow Key:

x+=3

Up Arrow Key:

y-=3

Down Arrow Key:

y+=3

Added the code? Press OK to save your object. Let's move on to creating the room.

Rooms


Find the Rooms folder in the left pane and... I think you know what to do. Right Click > Create Room. Your room, namely room0, will be created. We'll let this be the name. Another window opens - this one will be the room manager. You'll see an empty space along with a left pane and some options on the top - that empty space is your room, basically what you'll see when you start your game. In the left pane, there will be many tabs. Open the Settings tab, and change the room width and height to 800 and 600, respectively.

Now, open the Objects tab. Before adding objects, change both x snap and y snap to 32, which are found on the top in the room manager window. Now, in the left pane, click on the empty pane under the tabs. A menu will open with all of your objects. Click on the wall object (obj_wall). You'll see its sprite there - it means that the object has been selected. Now, use your mouse to add the wall blocks in the room (that empty space on the right). Left-click to add one or hold Ctrl+Shift while Left-clicking to continuously add them. What you want to do here is create your level - design it. Add the wall blocks so that the player has to navigate through the level to find the coins.

If you misplace any object, they can just be dragged and re-placed, or deleted using Right Click > Delete.

Done with adding the wall blocks? Now select the coin object (obj_coin) from the left pane and add 10 coins in your level. After adding the coins, add a few enemies. Add them at a place where they can move up - where a wall block is not blocking their way. After adding the enemies, select obj_player and add it into the room. That's where our player will start, so choose a nice place.

Now save the room using the same green tick you see in every window.

Now let's add more code. We've just made the player move. Double-click open obj_coin. Click on Add Event and choose Collision and select obj_player. This is the event that triggers when the object (obj_coin) collides with the object you just selected (obj_player). Let's add code in it. Select the event, open the control tab, and drag the Code action into the Action Pane.

Add this code:

score+=1
instance_destroy()

score+=1 will increase the score by one everytime the player gets the coin.
instance_destroy() will delete the object instance of the coin. That's to show that the player had taken the coin.

Click on the Green Tick. Press OK to save the object.

Now, open obj_player. Add a collision event with obj_wall.
Add Event > Collision > obj_wall
Add this code in it:

x=xprevious
y=yprevious

This code restricts the player from walking through the wall block.
xprevious is the previous x property of the object.
yprevious is the previous y property of the object.
When the player collides with the wall, its position is immediately changed to its previous position (before the collision), stopping it there.

Click on the green tick.

Add another collision event, this one with obj_enemy. Add this code:

room_restart()

This will restart the room whenever the player collides with the enemy. Click on the green tick.

Add Step event. It executes 30 times a second. In its code, add:

if x>xprevious image_angle=0
if x<xprevious image_angle=180
if y>yprevious image_angle=270
if y<yprevious image_angle=90

This code rotates the player based on where it's going. Copy this code because we'll need it again. Click on OK.

Now.. It's time to open obj_enemy. Add create event. In its code, add:

vspeed=-1

This code will set its vertical speed to -1, which means it'll go up. Now add step event, and add the code I told you to copy.(the code of obj_player's step event). Now add a collision event with obj_wall and add this code:

direction-=180

It'll reverse its direction and the object (enemy) will move down instead of up. Now if a collision happens again, it'll go up.

Click on OK to save the object.

Now let's test the game!


Press F5 to start the game. Test it. Move your player using the Arrow Keys. Collect the coins. Run into enemies.
Isn't it cool? Your own game!
Now close the game window.

Let's make winning possible.

Open obj_player and in the Step event, add this code.

if score=10 room_goto_next()

This one is simple: it checks if the score is 10, and then opens the next room. This is how it works: after if, comes the condition (score=10, here) and then the action which must be performed if the condition (score=10) is true. Now green tick, OK.

Now, create a new sprite. Name it spr_play. Click on Edit Sprite. Open File menu, select New. Enter the size as width = 600, height = 400 and press OK. Double-click on the sub-image that is created. From the left pane, use the Text tool to write this there:

"You Win!" or "You completed the game!" or whatever you like - it's your game.

There's an option to change the font and size in the left pane, and to change the color in the right pane. Click in the empty area to add the text. After adding it, add another text:

"Click here to play again"
The text must be big enough for the player to click on it.

Now save the sprite. Create an object for it, namely obj_play. Click on Add Event. There'll be a Mouse event. Click on it, and select Left Button. Open the event and add this code:

room_goto(room0)

This will take the user to the first room (room0) when they click on the sprite so that they can play the game again.

So let's create a room for it!

Right-click on the Rooms folder and select Create Room. When the Room Manager for the new room opens, open the settings tab and rename it room_play and change its size to 800 and 600 (like the previous room). Add obj_play in the room, wherever you like.

Testing time!


Press F5. Play the game, collect all of the 10 coins and win. It'll take you to the next room (room_play), and when you click on the text, it'll take you back to your game.

Fun, isn't it?

Adding more levels


You can create as many levels as you want. Just do this: Create a new room, change its size (800,600), add the wall blocks, coins, enemies, and the player, as you did in the first room, but differently: because this is a new level. Make sure the snap x and snap y are 32. After clicking on the green tick, take a look at the Rooms folder in the left pane. It'll be like this:

room0
room_play
room2


This means that you can't access room2, because it comes after room_play. So, what you need to do here is drag room2 where room_play is, so that after the first level, the next level comes, and in the end, room_play.

This way, you can create as many levels as you want.

Press Ctrl+S to save your project.

Creating an executable


Open the File Menu, and select Create Executable. A window will open. Select the folder where you want to save your game as an *.exe. Enter the name at the text field at the bottom. Select Executable from the drop-down list below it, and hit enter. You can now share the exe file you just created to share your game!

Conclusion


So creating a game wasn't that hard after all? Game Maker: Studio really does a great job at helping you create your game. There are many things to learn about this great software - so never stop learning!


If you found this article helpful, please consider sharing it so that it can help other people also. Have some suggestions, need help or did I make a mistake? Comment below or mail me at gurpreetsingh793@gmail.com. Have a great day!


Article Update Log


16 Dec 2015: Updated about the price.
12 Dec 2015: Initial release

A List of HTML5 Game Publishers

$
0
0

(This article is a cross-post of the original article published at WiMi5.com)

This is a list of the main HTML5 games publishers and distributors. If you are developing HTML5 games you should know how to distribute or publish your game and which business models you can find in the arena.

A list of HTML5 game publishers


In this article, I’d like to make a list of the most important publishers of HTML5 games on the international stage. I think that this list of HTML5 distributors, publishers, and portals can be of great use as a starting point for those who are just discovering this world and don’t know very well where to begin.

The first thing you need to understand is the different types of agents. You have to distinguish between HTML5 game distributors and publishers. Distributors aggregate HTML5 games to offer them to publishers or create whitelabel portals that other websites with high traffic can monetize. Publishers normally have several portals or game sites that generate millions of monthly visitors and that monetize directly, either with ad networks (like AdSense) or dealing with advertisers directly.

Normally, these publishers and distributors usually provide an SDK of their own to incorporate your HTML5 game into their system. Generally, they are easy to integrate and usually offer features aimed at analytics, cross-marketing, and monetization via advertising.

As for the business model, each publisher’s can vary. The community of HTML5 game developers seems to agree that the most used model is the non-exclusive license. This means that you may sell user licenses of your HTML5 games to as many publishers or distributors as you can. In contrast, with an exclusive license, you give the exploitation rights to your game to only one publisher. Choosing one model or another naturally depends on the sale figure. As we mentioned earlier, it is much less common to sell exclusive licenses than it is to sell non-exclusive ones. Finally, some publishers of HTML5 games offer a model based on the distribution of the income produced by the game (revenue share). Depending on the type of game, the publisher, and the traffic the game may generate, this is an option to bear in mind.

Main Publishers of HTML5 Games


SOFTGAMES
This German publisher, headquartered in Berlin, is specialized in HTML5 games and claims to be the largest network of HTML5 games in the world. They work with more than 400 HTML5 game developers, and they have a catalogue of over 350 games which provides them with a total of 132 million game sessions. One of their founders, and CEO, Alex Krug, is an HTML5 evangelist, who usually gives talks at events around the world.


SPIL GAMES
This Dutch publisher is one of the most veteran ones in HTML5 games. With domains such as game.com, gamesgames.com, or girlsgogames.com, they receive more than 100 million unique users every month. Their game catalogue totals more than 10,000, but most of them are Flash. It’s one of the big HTML5 game publishers and one of the first options to go to.


BOOSTER MEDIA
With 12 million unique monthly users, this is another Dutch publisher to remember. They are completely specialized in HTML5 games, and are present in several countries, with offices in Tokyo, Singapore, and Sao Paolo.


GAMEPIX
This is a platform for publishing, distributing, and aggregating HTML5 games, based in Rome. They in turn distribute to other publishers in order to get the greatest amount of traffic possible for their games.


FAMOBI
This distributor, based in Colombia, also has portals such as HTML5games.com. They also offer whitelabel HTML5 game portals to be monetized by websites with a lot of traffic. They are specialized in distributing HTML5 games and have a catalogue of over 300 games.


BONGIORNO
This Italian company is part of the Japanese giant NTT Docomo. They have several game portals, such as B!Games, Giochissimo, or GamiFive, most of which work on a subscription model. They also have a large network of collaborators, including many mobile operator companies, which eases the distribution of games and the charging of subscriptions.


MINIJUEGOS
This is a publisher with 4.5 million monthly users, of which 1.4 are in Spain, and the rest in Spanish-speaking countries. They also have other portals, such as miniplay, minigiochi, or minijogos.


PLAY.IM
This publisher (some games are provided to it by Gamepix) has portals such as yepi, Bgames, or Huz. Many of their games are Flash, but there are more and more HTML5 games. For example, they’ve launched a new portal called Yepi Mobile which is totally for HTML5 games. In total, they have 25 million unique users every month.


Apart from our list of publishers of HTML5 games, there are other ones already out there which are very interesting and which provide different types of information. For example, the forum called Sponsors and Portals (which is only open to registered and active members of this forum of developers of HTML5 games) provides first-hand information by many developers, and is constantly being updated. In general, the forums at HTML5 Game Devs are one of the best ways to get information and contact with interesting agents in the sector, and of course with HTML5 game publishers.

Another good reference is the list at TrueValhalla, which has a variety of information, for example, about the type of licenses each portal offers or the traffic each HTML5 game publisher generates. However, this list does not include distributors such as Famobi or Gamepix.

Conclusion


With this list you can know who are the main players in HTML5 game publishing and what business models are the most used by developers and publishers.

Top 10 Best 2D Game Asset Sites

$
0
0

2D games are making a big come back with the proliferation of mobile games, HTML5 and major support of big publishers. Producing them now have been much easier than before with tools like Unity and help of online 2D Game Asset stores. Below is a list of top 10 2D games asset stores you can find online, some commercial and some free.

1. Unity Asset Store (commercial)


Biggest commercial game assets site. No you don’t have to use the Unity engine to use games assets offered on the Unity asset store, you can simply download a free copy of Unity and download assets through the Unity asset store online. Game assets will be imported into Unity, and the files will be saved into your Unity project folder. You can use the saved game assets in any 2D game engine.

Many people may not know Unity is a very successful 2D game engine, in the hands of professional game developers. Amazing variety of highly successful 2D games have been created such as Rovio’s smash hit, Bad Piggies. The integrated asset store offers a large range of 2D assets designed to make well use of Unity’s 2D physics engine.

https://www.assetstore.unity3d.com/


unity_zpshz4jdyln.jpg


2. Super Game Asset (commercial)


Best for RPG Games. If you are creating your own RPG or isometric games, Super Games Asset store has the highest quality 2D games assets you can buy online. They offer awesome RPG game icons (these are probably the best you can purchase directly online), 2D sprites, character animation sprites, and huge hand painted RPG game maps in isometric view. Majority of game assets here are visually consistent.

http://www.supergameasset.com


sga_zps61xiezkr.jpg


3. GameDev Market (commercial)


GameDev Market is quite a new entry, like Super Game Asset it is very well organised into different categories. The offer both 2D and 3D including Game UI, character sprites, icons and environments. It is a market so all assets are artist submissions and it is growing very quickly.

https://www.gamedevmarket.net/


gamedevmarket_zpsjvjiw7nb.jpg


4. Graphicriver - Game Assets (commercial)


Graphicriver is the world's most popular stock image resource, with the proliferation of casual games on mobile they have recently started a specific category just for game assets, and it is all 2D. This is a trustable source for quality game resources as all assets will be reviewed by envato internally before getting listed on the site. Plus you have community review on the sources as well. It is a constantly-growing source with great support from envato.

http://graphicriver.net/category/game-assets


graphicriver_gameasset_zpscosamg4p.jpg


5. Scirra Store (commercial)


Scirra is the developer of Construct 2, a popular HTML5 2D game editor and they now have their own game asset store. You don’t have to use them in Construct 2 it is perfectly okay to use in any 2D editor really. The game assets are categorized in graphics, sound, music and even game templates which can help you start a new game quickly in Construct 2.

https://www.scirra.com/store


scirra_store_zpsxodppspe.jpg


6. Game Art Partners (commercial)


Plenty for 2D platform games. This site has plenty of 2D game assets for you. All assets are very cartoon styled and include large collections of characters with animations, monsters, weapons, effects and user interface kits well suited for platform games.

http://gameartpartners.com/


gameartpartners_zpsljixkeov.jpg


7. Open Game Art (free)


The definitive place for open source games. This place is most likely the biggest online community for free licenced game assets. There is a large selection of game assets all submitted under GNU or Creative Commons licences, you can find anything from sprites to icons. It is a great place to start if you are a beginner or just require prototyping game assets. Not all visual styles are consistent though, so you’ll need to find and match.

http://opengameart.org/


opengameart_zps4h33aiea.jpg


8. Kenny Game Assets (free)


This is another good source for prototyping a game, there are over 20,000+ game assets on this site. It includes various user interface assets, common 2D platform game assets and sprites. Best of all most assets are vector graphics, so it is resolution independent and can be used on any device’s screen. Most assets are free to download separately or you can give a small $9 donation to download the whole 20,000 asset pack.

http://kenney.nl/assets


kenny_zpsazsfz7n6.jpg


9. Game-Icons.net (free)


best free icon only site, game-icons.net offer a really comprehensive set of icons, in fact over 2000 different variations of monochromatic icons. I say monochromatic because all of the icons here are in black and white but still very generic enough to be used in just about any game. It's a great source to search everything from actions icons, health, potion, character skills, weapons, items. Best of all it’s all in vector format.

http://game-icons.net/


gameicons_net_zpsc0hkcywv.jpg


10. Reiner’s Tilesets (free)


Best free tileset site. The game assets on this site are well suited for games with isometric view, Reiner have sprites for animals, plants, buildings, weapons, visual effects and just about every general object to populate an RPG world. The style are more realistic, along the lines of Diablo 2. It is a great place to start game development and test out RPG game engines as all assets are free.

http://www.reinerstilesets.de/en/


reiners_zpsyzjrdw6h.jpg

Not All is Fine in the Morrowind Universe

$
0
0

I have checked the OpenMW project by PVS-Studio and written this tiny article. Very few bugs were found, so OpenMW team can be proud of their code.

OpenMW


OpenMW is an attempt to reconstruct the popular RPG Morrowind, a full-blown implementation of all of the game's specifics with open source code. To run OpenMW, you will need an original Morrowind disk.

The source code can be downloaded from https://code.google.com/p/openmw/

Suspicious fragments found


Fragment No. 1

std::string getUtf8(unsigned char c,
  ToUTF8::Utf8Encoder& encoder, ToUTF8::FromType encoding)
{
  ....
  conv[0xa2] = 0xf3;
  conv[0xa3] = 0xbf;
  conv[0xa4] = 0x0;
  conv[0xe1] = 0x8c;
  conv[0xe1] = 0x8c;   <<<<====
  conv[0xe3] = 0x0;
  ....
}

PVS-Studio diagnostic message: V519 The 'conv[0xe1]' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 103, 104. openmw fontloader.cpp 104

I guess it is a typo. It is the 0xe2 index that should be probably used in the marked line.

Fragment No. 2

enum Flags
{
  ....
  NoDuration = 0x4,
  ....
}

bool CastSpell::cast (const ESM::Ingredient* ingredient)
{
  ....
  if (!magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)
  ....
}

PVS-Studio diagnostic message: V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' operator. openmw spellcasting.cpp 717

Here we deal with a mistake related to operation precedence. At first, the (!magicEffect->mData.mFlags) statement is executed which evaluates either to 0 or 1. Then the statement 0 & 4 or 1 & 4 is executed. But it doesn't make any sense, and the code should most likely look as follows:

if ( ! (magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) )

Fragment No. 3

void Clothing::blank()
{
  mData.mType = 0;
  mData.mWeight = 0;
  mData.mValue = 0;
  mData.mEnchant = 0;
  mParts.mParts.clear();
  mName.clear();
  mModel.clear();
  mIcon.clear();
  mIcon.clear();
  mEnchant.clear();
  mScript.clear();
}

PVS-Studio diagnostic message: V586 The 'clear' function is called twice for deallocation of the same resource. Check lines: 48, 49. components loadclot.cpp 49

The mIcon object is cleared twice. The second clearing is redundant or something else should have been cleared instead.

Fragment No. 4

void Storage::loadDataFromStream(
  ContainerType& container, std::istream& stream)
{
  std::string line;
  while (!stream.eof())
  {
    std::getline( stream, line );
    ....
  }
  ....
}

PVS-Studio diagnostic message: V663 Infinite loop is possible. The 'cin.eof()' condition is insufficient to break from the loop. Consider adding the 'cin.fail()' function call to the conditional expression. components translation.cpp 45

When working with the std::istream class, calling the eof() function to terminate the loop is not enough. If a failure occurs when reading data, the call of the eof() function will return false all the time. To terminate the loop in this case, you need an additional check of the value returned by fail().

Fragment No. 5

class Factory
{
  ....
  bool getReadSourceCache() { return mReadSourceCache; }
  bool getWriteSourceCache() { return mReadSourceCache; }
  ....
  bool mReadSourceCache;
  bool mWriteSourceCache;
  ....
};

PVS-Studio diagnostic message: V524 It is odd that the body of 'getWriteSourceCache' function is fully equivalent to the body of 'getReadSourceCache' function. components factory.hpp 209

I guess the getWriteSourceCache() function should look like this:

bool getWriteSourceCache() { return mWriteSourceCache; }

Fragments No. 6, 7, 8

std::string rangeTypeLabel(int idx)
{
  const char* rangeTypeLabels [] = {
    "Self",
    "Touch",
    "Target"
  };
  if (idx >= 0 && idx <= 3)
    return rangeTypeLabels[idx];
  else
    return "Invalid";
}

PVS-Studio diagnostic message: V557 Array overrun is possible. The value of 'idx' index could reach 3. esmtool labels.cpp 502

Here we see an incorrect check of an array index. If the idx variable equals 3, an array overrun will occur.

The correct code:

if (idx >= 0 && idx < 3)

A similar defect was found in two other fragments:
  • V557 Array overrun is possible. The value of 'idx' index could reach 143. esmtool labels.cpp 391
  • V557 Array overrun is possible. The value of 'idx' index could reach 27. esmtool labels.cpp 475
Fragment No. 9

enum UpperBodyCharacterState
{
  UpperCharState_Nothing,
  UpperCharState_EquipingWeap,
  UpperCharState_UnEquipingWeap,
  ....
};

bool CharacterController::updateWeaponState()
{
  ....
  if((weaptype != WeapType_None ||
      UpperCharState_UnEquipingWeap) && animPlaying)
  ....
}

PVS-Studio diagnostic message: V560 A part of conditional expression is always true: UpperCharState_UnEquipingWeap. openmw character.cpp 949

This condition is very strange. In its current form, it can be reduced to if (animPlaying). Something is obviously wrong with it.

Fragments No. 10, 11

void World::clear()
{
  mLocalScripts.clear();
  mPlayer->clear();
  ....
  if (mPlayer)
  ....
}

PVS-Studio diagnostic message: V595 The 'mPlayer' pointer was utilized before it was verified against nullptr. Check lines: 234, 245. openmw worldimp.cpp 234

Similar defect: V595 The mBody pointer was utilized before it was verified against nullptr. Check lines: 95, 99. openmw physic.cpp 95

Fragment No. 12

void ExprParser::replaceBinaryOperands()
{
  ....
  if (t1==t2)
    mOperands.push_back (t1);
  else if (t1=='f' || t2=='f')
    mOperands.push_back ('f');
  else
    std::logic_error ("failed to determine result operand type");
}

PVS-Studio diagnostic message: V596 The object was created but it is not being used. The 'throw' keyword could be missing: throw logic_error(FOO); components exprparser.cpp 101

The keyword throw is missing. The fixed code should look like this:

throw std::logic_error ("failed to determine result operand type");

Conclusion


Purchase a PVS-Studio for in your team, and you will save huge amount of time usually spent on eliminating typos and diverse bugs.

Brain Dead Simple Game States

$
0
0

Lately, I've realized that game state management is always vastly overcomplicated. Here's a brain dead simple system that does everything you probably need in a straightforward way.

Just what the heck is a game state?


Well, what happens when you boot up a game? You probably see some credit to the engine, get shown "the way it's meant to be played", and maybe watch a sweet FMV cutscene. Then you get launched into the menu, where you can tighten up the graphics on level 3 and switch the controls to accommodate your DVORAK keyboard. Then you pick your favourite level, and start playing. A half an hour later, you've had too much Mountain Dew, so you have to pause the game for a few minutes to stop the action to be resumed later.

That's about 4 game states right there: introduction, menu, gameplay, pause screen.

Alright, how do we start coding?


The job of a state is pretty simple. Generally, it needs to update something, and then draw something. Sounds like an interface to me.

public interface State {
  public void update(float dt);
  public void draw();
}

You'd then have concrete states like Menu or Play that implement this interface. Now, I'm going to put a little spin on it, by changing the type of the update method.

public interface State {
  public State update(float dt);
  public void draw();
}

Why did I do that? Well, one of the important parts about game states is the ability to change between them. A game wouldn't be very fun if all you could do was watch the intro FMV over and over again. So the update method now returns whichever state should be used next. If there's no change, it should just return itself.

public class Menu implements State {
  public State update(float dt) {
    if(newGameButton.clicked()) {
      return new Play("Level 1");
    }
      
    return this;
  }
    
  public void draw() {
    drawSomeButtons();
  }
}

Now, the state management code becomes extremely simple, and doesn't require any separate manager class or anything. Just stick it in your main method or whatever holds the game loop.

State current = new Intro();

while(isRunning) {
  handleInput();
  current = current.update(calculateDeltaTime());
  current.draw();
  presentAndClear();
}

Wait, that's it?


Yup.

For real?


Nah, just kidding. Here's something really cool about this method.

Take the pause state. You have to be able to unpause and return to what you were doing, unchanged, right? Usually, a stack is advocated. You push the pause state on to the stack, and pop it off when you're done to get back to the play state. You would then only update and draw the topmost state.

I say, screw the stack. Have the pause state take a State in its constructor, which is stored, and then returned instead of the pause state itself when the update method detects that the game should be unpaused. If the pause screen needs to be an overlay over whatever was going on before the game was paused, that's really easy, too!

public class Pause implements State {
  private State previous;

  public Pause(State previous) {
    this.previous = previous;
  }

  public State update(float dt) {
    if(resumeButton.clicked()) {
      return previous;
    }

    return this;
  }

  public State draw() {
    previous.draw();
    applyFancyBlurEffect();
    drawThePauseMenu();
  }
}

Closing Remarks


Although it may seem like this method requires garbage collection, it really doesn't. You might have to slightly complicate the barely logical "management logic" to accomodate languages without automatic destruction, but in general, the idea of handling transitions by returning different states will work just fine.

I'm sure there are many more ways to use/abuse this system that I can't think of right now, and I appreciate all feedback! Thanks for reading, and I hope it helped!

Action Lists: Simple, Flexible, Extendable AI

$
0
0

As humans, we like to implement solutions which are familiar to us. We get caught up doing things the way we know how to do them, rather than the “best” way to do them. It’s easy to get caught up in thinking like this and as a result we end up using outdated technologies and implement features in ways that our modern contemporaries don’t understand, or are simply less effective or efficient. My purpose with this and future papers will be to expose readers to a broad spectrum of solutions that will hopefully help them in their own coding. Today I’ll be covering Action Lists!

Action Lists are a simple yet powerful type of AI that all game developers should know about. While ultimately not scalable for large AI networks, they allow for relatively complex emergent behavior and are easy to implement. Whether you are just getting into AI programming or are an experienced industry veteran looking to expand your toolkit, this presentation will introduce you to Action Lists and provide concrete examples to help you implement your own solutions. Let’s begin the story.

Once Upon A Time…


Several years ago I was starting the development of King Randall’s Party, a game in which the player builds a castle and tries to defend it against the King who is trying to knock it down. I needed to create a reasonably smart AI that could look at the player’s castle and figure out how to circumvent or destroy it in order to reach its objective – the gold pile the player is defending. This was a big challenge, almost too big to consider all at once. So like any good programmer I broke it down into a more manageable problem set. The first challenges: I needed to get the units to have a specific set of behaviors that they would act according to.


Attached Image: Wallpaper1.jpg


A quick internet search caused my mind to implode, of course. Searching for “Game AI” brought up results on planners, finite state machines, steering behaviors, flocking, A*, pathfinding, etc. I had no clue where to start, so I did what any reasonable, rational person would do. I asked my dog. This isn’t anything new, of course. Whenever I have a tech problem, I ask my dog. Now I know what you’re thinking “Jesse, that’s dumb, and you’re crazy… What do dogs know about computers?” Well, let’s just gloss over that. It may or may not have involved some illegal technology and or college class auditing.

Anyway, I said “Ok Frankie – there is so much going on with AI, I really don’t have a clue where to start. How should I go about creating an AI framework for my game units?” Frankie gave me this pretty withering look that basically informed me that I was an idiot and pretty dumb, despite what my high school teacher Mr. Francis may have told me about there never being any stupid questions. I’m pretty sure Frankie wouldn’t have had nice thoughts about Mr. Francis either. “Jesse”, she asked, “how do you typically start your day?”

Well, I write everything that I need to do that day down in a list and then prioritize it according to how important the task is and how soon it needs to be done. I explained this to Frankie and she responded “And that, is an Action list.” She told me that an Action List is a list of tasks or behaviors that your game units work their way through one at a time. It is a form of finite state system, and could be described as a simple behavior tree with a single branch level. Here is how they work. According to my dog.

How Action Lists Work


First you write down all the behaviors you want your AI to have.


Attached Image: List Behaviors.png


Then you order them according to priority. Lowest to highest.


Attached Image: Ordered List.png


Now we iterate over the list checking each action to see if it can execute and if it can, executing it. We then check the actions Blocking property, and if the item is Blocking further actions we exit the list. We will get into why Blocking is important later.

In our example here, our first action Attack Player will only execute if the AI is close to the player. Let’s say it is not, so it checks if it can build a ladder at this location (and should it), and so on until it finds an action item it can execute such as Break Door. It then executes the Break Door code.


Attached Image: Executing.png


Here is where Blocking comes into play. If Break Door occurs instantly, then it will not block any further actions and the rest of the list can execute. This is typically not the case – actions usually take up more than one frame. So in this case the Break Door Action Item calls unit.Attack(door), which will change the unit’s CurrentState from Waiting to BreakDoor and will return true until the door is broken.

A Simple Finite State Machine


Well ok. That sounds workable. Frankie had made a good suggestion and Action Lists seem like a great fit for my project. But I’d never heard of them before so I had my doubts – most of what I hear floating around about AI has to do with transition-based finite state machines, similar to what Unity3D uses for animations in Mechanim. You define a bunch of states, and identify when and how they transition between each other. In exchange for some belly scratches, Frankie explained to me when you make a transition based finite state machine, you need to define all the states you want to have, and then also define all of the transitions to and from each of those individual states. This can get complicated really, really fast. If you have a Hurt state, you need to identify every other state that can transition to this state, and when. Walking to hurt; jumping to hurt, crouching to hurt, attacking to hurt. This can be useful, but can also get complicated really fast. If your AI requirements are fairly simple, that’s a lot of potentially unnecessary overhead.

Another difficulty with transition-based state machines is that it is difficult to debug. If you set a break point and look at what the AI’s current state is, it is impossible without additional debug code to know how the AI got into its current state, what the previous states were and what transition was used to get to the current state.

Drawbacks of Action Lists


As I dug into action lists some more, I realized that they were perfect for my implementation, but I also realized they had some drawbacks. The biggest flaw is simply the result of its greatest strength – its simplicity. Because it is a single ordered list, I couldn’t have any sort of complex hierarchy of priorities. So if I wanted Attack Player to be a higher priority than Break Door, but lower than Move To Objective, while also having Move To Objective being lower priority than Break Door… that’s not a simple problem to solve with action lists, but trivial with finite state machines.

In summary, action lists are really useful for reasonably simple AI systems, but the more complex the AI you want to model, the more difficult it will be to implement action lists. That being said, there are a few ways you can extend the concept of Action Lists to make them more powerful.

Ways to Extend This Concept


So some of my AI units are multitaskers – they can move and attack at the same time. I could create multiple action lists – one to handle movement and one to handle attacking, but that is can be problematic – what if some types of movement preclude attacking, and some attacks require the unit to stand still? This is where Action Lanes come in.

Action Lanes are an extension to the concept of Blocking. With Action Lanes, Action Items can now identify specific types of Action Items that it blocks from executing while allowing others to execute without problem. Let’s show this in action.

An Action Lane is just an additional way to determine what action. Each Action Item belongs to one or more lanes, and when its Blocking property returns true, it will add the lanes it belongs to Each action has a lane or multiple lanes they are identified with, and they will only block other actions which belong to those lanes. As an example, Attack Player belongs in the Action Lane, Move to Goal belongs in the Movement lane, and Build Ladder belongs in both since the unit must stand still and cannot attack while building. Then we order these items, and if they execute they will block subsequent actions appropriately.


Attached Image: Action Lanes.png


Example Implementation


Now that we’ve gone over the theory, it is useful to step through a practical implementation. First let’s setup our Action List and Action Items. For Action Items I like to decouple implementation, so let’s make an interface.

Attached Image: Action Item Interface.png

Here is an example implementation of the IActionItem for the BreakDoor Action Item.

Attached Image: Break Door Action Item.png

For the Action List itself we can use a simple List. Then we load it up with the IActionItems.

Attached Image: Action List Constructor.png

After that, we setup a method that iterates over the list every frame. Remember we also have to handle blocking.

Attached Image: Iteration Method.png

Things get a bit more complicated if you want to use action lanes. In that case we define Action Lanes as a bitfield and then modify the IActionItem interface.

Attached Image: Action Item Interface With Action Lanes.png

Then we modify the iterator to take these lanes into account. Action Items will be skipped over if their lane is blocked, but will otherwise check as normal. If all lanes are blocked then we break out of the loop.

Attached Image: Action Item Iterator With Lanes.png

Conclusion


So that’s a lot to take in. While coding the other day Frankie asked me to summarize my learnings. Thinking about it, there were a few key takeaways for me.
  • Action lists are easier to setup and maintain then small transition-based state systems.
  • They model a recognizable priority system.
  • There are a few ways they can be extended to handle expanded functionality.
As with anything in coding, there is often no best solution. It is important to keep a large variety of coding tools in our toolbox so that we can pick the right one for the problem at hand. Action Lists turned out to be perfect for King Randall’s Party. Perhaps they will be the right solution for your project?


Originally posted on gorillatactics.com

How to Create a Mobile Game on the Cheap

$
0
0

This article originally appeared on medium.com

This is a guide that helps anyone, with software development experience, get started creating their first mobile game.

This year, I made it one of my goals to create a mobile video game. I have been developing software for over a decade, making business applications and platforms. All the while, I have been jotting down ideas for games. I recently released Clear the Zombies! on iOS and Android.

The first choice I had to make was what tools, frameworks and platforms I would use. To help with that choice I created requirements for what I needed these tools to be and do:

  • Cross platform (so that I am efficient with my time and don’t have to learn multiple languages and build the game more than once.)
  • Free to use
  • Easy to test and build
  • Great documentation and community support

The tools I chose and why


More details on how to get started with these platforms are given later in this article.

Game Framework: Phaser
Phaser is a great javascript/canvas based 2D game framework. It is free to use, has excellent documentation, and tons of community-driven examples.

Cross Platform Mobile Framework: Cordova
Cordova is the gold standard for cross platform mobile apps. I had previously used it with Ionic Framework when i was building a business app. It is free and managed by the Apache Software Foundation.

Emulation, Testing, Build, Configuration: Intel XDK
I wish I had discovered this tool when I was working with Ionic. It has a great emulator that displays many devices (iPhones, iPads, Android Devices, etc.) and emulates the core phone capabilities such as geolocation and network status. It also has a preview app that you can install on your phone. The preview app allows you to upload your game to your device without having to build and install your game every time. Best of all, Intel XDK takes care of managing your build configurations, certificates, and your build processes.

Source Control: Bitbucket and SourceTree
Bitbucket has free to use private hosted repos. SourceTree is the best git GUI I have used so far. It takes a bit of time to get used to this tool because there are many bells and whistles, but it is worth the time investment. Also, my friend Jon Gregorowicz got me hooked on the Gitflow Workflow and SourceTree has gitflow management built in.

Image Asset Creation: Inkscape and Piskel
Creating my own sprites and images was by far the most intimidating part of this project. I considered using free sprites and images, but realized very quickly that there would be large inconsistencies with the style, dimensions and scaling that would make my game look like a Frankenstein's monster. I resigned to the fact that I would be making all my own images and sprites and found that I not only really enjoyed it, but it was not as hard as I thought it would be.

Inkscape and Piskel make the process easy. Anil Beniwal introduced me to Inkscape, which is a free svg tool. I mainly used it for scaling and effects. It has great features that make converting a png into an svg (vector graphics) easy and adding masks and filters a cinch. Piskel is a simple but effective tool for making spritesheets. I used this tool to create everything from backgrounds to sprites to buttons.

Analytics: Google Analytics
You will inevitably want to know how users are using your game. Google analytics is an easy and free way to start tracking how many users you have, what screens they are using, and what levels they have achieved.

What type of game should I make?


Since this was my first game, I knew I would spend a considerable amount of time ramping up on the technologies. I wanted to make sure that the game I would be making would not be too ambitious so that I could finish building it in a reasonable amount of time. I wrote down some requirements for myself when designing the game. I would suggest you do the same.
  • A simple core game mechanic that can be developed in a few days and continue to be refined to make the game more fun and challenging.
  • Simple controls.
  • The game screen is static and confined to a set space. This allowed me to avoid camera complexities and simplified my scaling logic for different devices.
Where do I start?

These are high level steps to start you on your way. I do not go into the nitty gritty details because there are many options and most of the features of these tools are described in detail in the documentation for the given tool. What I aim to accomplish with this list is helping you to shortcircuit a lot of the research that I have already done concerning what tools work best for the different parts of game development.

  1. Start by downloading and installing Intel XDK. From here you will create a new project and select XDK’s Phaser template from the templates->games section.
  2. To start understanding phaser and Cordova run through this tutorial by Pablo Farias Navarro. It is great for beginners
  3. Create your core game engine using either mock images or free sprites. This should be the minimum amount of code that can be written to have a working version of your game.
  4. Iterate, iterate, iterate. Create a task list for yourself and knock off items one at a time to build up your game.
  5. Once you have something working that is fun, start to think about replacing the mock images with real ones.
  6. Once you have a working version with good images, sign yourself up to become an Apple and Android developer. There is a cost associated with each of these. Unfortunately, this is the one part that you will have to pay actual money for.
  7. Create your icon and splash screen.
  8. Get your Apple certificates following the documentation from Intel XDK.
  9. Generate marketing materials for the Play Store and App Store. This includes creating your own icons and banners.
  10. Build your apk and ipa files and submit them to the Play Store and iTunes Connect respectively.
  11. Market, market, market.

The devil is in the details


In this section I will go over things I had to figure out on my own about each tool and each step in the process.

Using Intel XDK


As I said earlier, Intel XDK has become my favorite new tool. You can use it for writing code, emulating devices, testing, and building. Personally, I continued to use Sublime for code editing because I am used to it.

XDK’s emulator is great, because unlike developing using a chrome browser, it allows you to test Cordova plugins and the code around them without having to install on a device.

The test tab in combination with Intel’s App Preview app works great for early stages of the game. It will help you to see what your game looks like on a real app without having to build and install. However, it only works with a subset of Cordova plugins. As you start to add more, you will need to do full builds.

The build features are the best I have seen so far for free. It makes the process super easy and delivers a fully signed app that is ready for each of the stores. For iOS, you will need to download a certificate from the Apple Developer Console. You only need to do this once. The step by step guide can be found here.

When building for Android, always use Crosswalk! It is a browser that Intel XDK can build into your apk for consistency. I tried using a straight android build for a while. However, my game was laggy and inconsistent.

Starting to develop with Phaser


Phaser is one of the best application frameworks I have ever used. I have done a lot of web development in the past few years and have had to rely on many different third party packages. Usually, there are at least one or two headaches involved in that process. Phaser, however, is very straight forward, it does exactly what you need it to do, and works exactly as the documentation says.

Scaling

The one big gotcha is scaling. Mobile devices have many different screen sizes and pixel density ratios. This can be a nightmare to code against and test. Here are two resources that helped me understand how to get around this:
Here is my adapted code that I use to set the game size:

var w = window.innerWidth * window.devicePixelRatio,
    h = window.innerHeight * window.devicePixelRatio;
if(h > 800) {
    var div = h / 800; //800 is target
    w = w / div;
    h = 800;
}
var game = new Phaser.Game(w, h, Phaser.AUTO, "game");

Even with this code, you will still have different screen sizes. This could cause you to place a sprite too close to the edge of a screen, putting it in danger of being cut off on some devices. To counteract this, I created a standard “Safe Zone” on the screen that I could guarantee would not be cut off. I would then use that zone as a reference when placing all content. The code for that safe zone looks like this:

this.scaleFactor = window.devicePixelRatio / 3;
//determine the safe zone
this.safeZone = {
    width: this.game.width,
    height: this.game.height,
    x: 0,
    y: 0,
};
var heightWidthRatio = 1.9;
var currentHeightWidthRatio = this.game.height / this.game.width;
if (currentHeightWidthRatio > heightWidthRatio) {
    this.safeZone.height = this.game.width * heightWidthRatio;
    this.safeZone.y = (this.game.height - this.safeZone.height) / 2;
}
else {
    this.safeZone.width = this.game.height / heightWidthRatio;
    this.safeZone.x = (this.game.width - this.safeZone.width) / 2;
}

When placing items (sprites, buttons, text, etc.) onto the screen I always do 3 things. I place them at x: 0, y: 0 to get their natural size. Then I scale them to a percent size of the safe zone using either width or height (By using % sizes you make sure that the screen will look the same on all devices). Finally, I relocate the item to where it should go.

Example adding a sprite:

//Step 1 - place
var sprite = this.game.add.sprite(0, 0, "ZombieSprite");
//Step 2 - scale
sprite.height = this.safeZone.height * .10 //10% of safeZone
sprite.scale.x = sprite.scale.y; //scale width same as height
//Step 3 - relocate
//In this example I will place the sprite in the center of the safeZone
sprite.x = (this.safeZone.width - sprite.width) / 2;
sprite.y = (this.safeZone.height- sprite.height) / 2;

In the below images of my game, you can see the effect the safe zone has on the left and right sides of the screen. The gutter on the iphone is smaller than on the Nexus 6P. I always emulate using the iPhone 4 because it has the smallest screen. If it works on the iPhone 4 it will work anywhere.


Attached Image: screenshot-android-noglare.png Attached Image: screenshot-iphone2073x3701.png
Different safe zone sizes and scaling


States

If you end up having more than one screen in your project, I suggest using states. When you switch to a new state, the old state and all of its sprites will automatically be destroyed. It is an easy way of managing different screens and their setup.

Creating Sprites


I had never created sprites before I created this game. The first thing I did was look for articles on best practices. I found this article, by Derek Yu, simple enough and effective. Don’t worry if your sprites look crappy at first. Applying Derek’s shading techniques will go a long way to making them look great.

I used Piskel to create all my spritesheets. It is great because you can create the sprites in a browser without having to download any software. It allows you to easily make pixel by pixel changes. It also allows you to create each frame of your sprite sheet so you can animate your characters and buttons. In order to standardize I made all of my characters on a 64 x 64 canvas.

Inkscape came in handy whenever I needed to vectorize a png, scale it, add effects or combine images. It was invaluable when I was creating my marketing materials. The most important feature I learned to use was the Path-> Trace Bitmap feature, which allowed me to convert a png to an svg.


Attached Image: piskel.png
One of my zombies for “Clear the Zombies!”. I later vectorized the head in inkscape for my icon.

Attached Image: ic_launcher.png
The vectorized version of the image above.


Splash Screens


Splash screens are great for showing off your brand as the game loads. However, they are not so straightforward because, as I discussed earlier, devices have many different screen sizes.

For iOS the number of screen sizes is limited. You can use inkscape to crop and resize your splash screen and load it into XDK under the projects tab.

Android is a little more difficult, because there are so many devices. To get around this, Google recommends you create a 9-patch image. 9-patch images are images that have stretch zones, telling the device what is ok to stretch and what is not. I tried to find a site to generate these for me automatically, but got fed up and used the Google recommended tool to manually adjust them. The documentation for that tool is here.

Marketing


Your game will not sell itself. You will have to get out there and market it. Two of the most important free tools for marketing are Facebook and Twitter. Create a specific Facebook page and Twitter page and start posting. This article from Soomla had some great ideas.

Conclusion


There is a lot that goes into making a game. With this article, I tried to highlight all of the issues that I thought would hang up a first time game developer. If you want to know more about a specific subject, please reply to this post and I will do my best to respond and update the information herein.

Decoding Audio for XAudio2 with Microsoft Media Foundation

$
0
0

As I was learning XAudio2 I came across countless tutorials showing how to read in uncompressed .wav files and feed them into an XAudio2 source voice. What was even worse was most of these tutorials reinvented the wheel on parsing and validating a .wav file (even the sample on MSDN “How to: Load Audio Data Files in XAudio2” performs such manual parsing). While reinventing the wheel is never a good thing you also might not want to utilize uncompressed audio files in your game because, well... they are just to big! The .mp3 compression format reduces audio file size by about 10x and provides no inherently noticeable degradation in sound quality. This would certainly be great for the music your games play!

Microsoft Media Foundation


Microsoft Media Foundation, as described by Microsoft, is the next generation multimedia platform for Windows. It was introduced as a replacement for DirectShow and offers capabilities such as the following

  1. Playing Media
  2. Transcoding Media
  3. Decoding Media
  4. Encoding Media

NOTE: I use Media to represent audio, video, or a combination of both

The Pipeline Architecture


Media Foundation is well architectured and consists of many various components. These components are designed to connect together like Lego pieces to produce what is known as a Media Foundation Pipeline. A full Media Foundation pipeline consists of reading a media file from some location, such as the file system, to sending the it to one or more optional components that can transform the audio in someway and then finally sending it to a renderer that forwards the media to some output device.

The Media Foundation Source Reader


The Source reader was introduced to allow applications to utilize features of Media Foundation without having to build a full MF Pipeline. For Example, you might want to read and possibly decode an audio file and then pass it to the XAudio2 engine for playback.

Source Readers can be thought of as a component that can read an audio file and produce media samples to be consumed by your application in any way you see fit.

Media Types


Media Types are used in MF to describe the format of a particular media stream that came from possibly a file system. Your applications generally use media types to determine the format and the type of media in the stream. Objects within Media Foundation, such as the source reader, use these as well such as for loading the correct decoder for the media type output you are wanting.

Parts of a Media Type


Media Types consist of 2 parts that provide information about the type of media in a data stream. The 2 parts are described below:

  1. A Major Type
    1. The Major Type indicates the type of data (audio or video)

  2. A Sub Type
    1. The Sub Type indicates the format of the data (compressed mp3, uncompressed wav, etc)


Getting our hands dirty


With the basics out of the way, let’s now see how we can utilize Media Foundation’s Source Reader to read in any type of audio file (compressed or uncompressed) and extract the bytes to be sent to XAudio2 for playback.

First Things First, before we can begin using Media Foundation we must load and initialize the framework within our application. This is done with a call to MSStartup(MF_VERSION). We should also be good citizens and be sure to unload it once we are done using it with MSShutdown(). This seems like a great opportunity to use the RAII idiom to create a class that handles all of this for us.

struct MediaFoundationInitialize
{
  MediaFoundationInitialize()
  {
    HR(MFStartup(MF_VERSION));
  }
  ~MediaFoundationInitialize()
  {
    HR(MFShutdown());
  }
};

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
 MediaFoundationInitialize mf{};
 return 0;
}

Once Media Foundation has been initialized the next thing we need to do is create the source reader. This is done using the MFCreateSourceReaderFromURL() factory method that accepts the following 3 arguments.

  1. Location to the media file on disk
  2. Optional list of attributes that will configure settings that affect how the source reader operates
  3. The output parameter of the newly allocated source reader

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
 MediaFoundationInitialize mf{};
 // Create Attribute Store
 ComPtr<IMFAttributes> sourceReaderConfiguration;
 HR(MFCreateAttributes(sourceReaderConfiguration.GetAddressOf(), 1));
 HR(sourceReaderConfiguration->SetUINT32(MF_LOW_LATENCY, true));
 // Create Source Reader
 ComPtr<IMFSourceReader> sourceReader;
 HR(MFCreateSourceReaderFromURL(L"C:\\Users\\TraGicCode\\Desktop\\394506-n-a--1450673416.mp3", sourceReaderConfiguration.Get(), sourceReader.GetAddressOf()));
 return 0;
}

Notice we set 1 attribute for our source reader

  1. MF_LOW_LATENCY – This attribute informs the source reader we want data as quick as possible for in near real time operations

With the source reader created and attached to our media file we can query the source reader for the native media type of the file. This will allow us to do some validation such as verifying that the file is indeed an audio file and also if its compressed so that we can branch off and perform extra work needed by MF to uncompress it.

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
  MediaFoundationInitialize mf{};
  // Create Attribute Store
  ComPtr<IMFAttributes> sourceReaderConfiguration;
  HR(MFCreateAttributes(sourceReaderConfiguration.GetAddressOf(), 1));
  HR(sourceReaderConfiguration->SetUINT32(MF_LOW_LATENCY, true));
  // Create Source Reader
  ComPtr<IMFSourceReader> sourceReader;
  HR(MFCreateSourceReaderFromURL(L"C:\\Users\\TraGicCode\\Desktop\\394506-n-a--1450673416.mp3", sourceReaderConfiguration.Get(), sourceReader.GetAddressOf()));
  // Query information about the media file
  ComPtr<IMFMediaType> nativeMediaType;
  HR(sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nativeMediaType.GetAddressOf()));
  
  // Check if media file is indeed an audio file
  GUID majorType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType));
  if (MFMediaType_Audio != majorType)
  {
    throw NotAudioFileException{};
  }
  // Check if media file is compressed or uncompressed
  GUID subType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &subType));
  if (MFAudioFormat_Float == subType || MFAudioFormat_PCM == subType)
  {
    // Audio File is uncompressed
  }
  else
  {
    // Audio file is compressed
  }
  return 0;
}

If the audio file happens to be compressed (such as if we were reading in an .mp3 file) then we need to inform the source reader we would like it to decode the audio file so that it can be sent to our audio device. This is done by creating a Partial Media Type object and setting the MAJOR and SUBTYPE options for the type of output we would like. When passed to the source reader it will look throughout the system for registered decoders that can perform such requested conversion. Calling IMFSourceReader::SetCurrentMediaType() will pass if a decoder exists or fail otherwise

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
  MediaFoundationInitialize mf{};
  // Create Attribute Store
  ComPtr<IMFAttributes> sourceReaderConfiguration;
  HR(MFCreateAttributes(sourceReaderConfiguration.GetAddressOf(), 1));
  HR(sourceReaderConfiguration->SetUINT32(MF_LOW_LATENCY, true));
  // Create Source Reader
  ComPtr<IMFSourceReader> sourceReader;
  HR(MFCreateSourceReaderFromURL(L"C:\\Users\\TraGicCode\\Desktop\\394506-n-a--1450673416.mp3", sourceReaderConfiguration.Get(), sourceReader.GetAddressOf()));
  // Query information about the media file
  ComPtr<IMFMediaType> nativeMediaType;
  HR(sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nativeMediaType.GetAddressOf()));
  
  // Check if media file is indeed an audio file
  GUID majorType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType));
  if (MFMediaType_Audio != majorType)
  {
    throw NotAudioFileException{};
  }
  // Check if media file is compressed or uncompressed
  GUID subType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &subType));
  if (MFAudioFormat_Float == subType || MFAudioFormat_PCM == subType)
  {
    // Audio File is uncompressed
  }
  else
  {
    // Audio file is compressed
    // Inform the SourceReader we want uncompressed data
    // This causes it to look for decoders to perform the request we are making
    ComPtr<IMFMediaType> partialType = nullptr;
    HR(MFCreateMediaType(partialType.GetAddressOf()));
    // We want Audio
    HR(partialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
    // We want uncompressed data
    HR(partialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
    HR(sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, partialType.Get()));
  }
  return 0;
}

Now that we have the source reader configured we must next create a WAVEFORMATEX object from the source reader. This data structure essentially represent the fmt chunk in a RIFF file. This is needed so that XAudio2 or more generally anything that wants to play the audio knows the speed at which playback should happen. This is done by Calling IMFSourceReader::MFCreateWaveFormatExFromMFMediaType(). This function takes the following 3 parameters

  1. The Current Media Type of the Source Reader
  2. The address to a WAVEFORMATEX struct that will be filled in by the function
  3. The address of an unsigned int that will be filled in with the size of the above struct

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
  MediaFoundationInitialize mf{};
  // Create Attribute Store
  ComPtr<IMFAttributes> sourceReaderConfiguration;
  HR(MFCreateAttributes(sourceReaderConfiguration.GetAddressOf(), 1));
  HR(sourceReaderConfiguration->SetUINT32(MF_LOW_LATENCY, true));
  // Create Source Reader
  ComPtr<IMFSourceReader> sourceReader;
  HR(MFCreateSourceReaderFromURL(L"C:\\Users\\TraGicCode\\Desktop\\394506-n-a--1450673416.mp3", sourceReaderConfiguration.Get(), sourceReader.GetAddressOf()));
  // Query information about the media file
  ComPtr<IMFMediaType> nativeMediaType;
  HR(sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nativeMediaType.GetAddressOf()));
  
  // Check if media file is indeed an audio file
  GUID majorType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType));
  if (MFMediaType_Audio != majorType)
  {
    throw NotAudioFileException{};
  }
  // Check if media file is compressed or uncompressed
  GUID subType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &subType));
  if (MFAudioFormat_Float == subType || MFAudioFormat_PCM == subType)
  {
    // Audio File is uncompressed
  }
  else
  {
    // Audio file is compressed
    // Inform the SourceReader we want uncompressed data
    // This causes it to look for decoders to perform the request we are making
    ComPtr<IMFMediaType> partialType = nullptr;
    HR(MFCreateMediaType(partialType.GetAddressOf()));
    // We want Audio
    HR(partialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
    // We want uncompressed data
    HR(partialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
    HR(sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, partialType.Get()));
  }
  ComPtr<IMFMediaType> uncompressedAudioType = nullptr;
  HR(sourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, uncompressedAudioType.GetAddressOf()));
  WAVEFORMATEXTENSIBLE d;
  WAVEFORMATEX * waveformatex;
  unsigned int waveformatlength;
  HR(MFCreateWaveFormatExFromMFMediaType(uncompressedAudioType.Get(), &waveformatex, &waveformatlength));
  return 0;
}

lastly we synchronously read all the audio from the file and store them in a vector<byte>.

NOTE: In production software you would definitely not want to synchronously read bytes into memory. This is only meant for this example

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
  MediaFoundationInitialize mf{};
  // Create Attribute Store
  ComPtr<IMFAttributes> sourceReaderConfiguration;
  HR(MFCreateAttributes(sourceReaderConfiguration.GetAddressOf(), 1));
  HR(sourceReaderConfiguration->SetUINT32(MF_LOW_LATENCY, true));
  // Create Source Reader
  ComPtr<IMFSourceReader> sourceReader;
  HR(MFCreateSourceReaderFromURL(L"C:\\Users\\TraGicCode\\Desktop\\394506-n-a--1450673416.mp3", sourceReaderConfiguration.Get(), sourceReader.GetAddressOf()));
  // Query information about the media file
  ComPtr<IMFMediaType> nativeMediaType;
  HR(sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nativeMediaType.GetAddressOf()));
  
  // Check if media file is indeed an audio file
  GUID majorType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType));
  if (MFMediaType_Audio != majorType)
  {
    throw NotAudioFileException{};
  }
  // Check if media file is compressed or uncompressed
  GUID subType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &subType));
  if (MFAudioFormat_Float == subType || MFAudioFormat_PCM == subType)
  {
    // Audio File is uncompressed
  }
  else
  {
    // Audio file is compressed
    // Inform the SourceReader we want uncompressed data
    // This causes it to look for decoders to perform the request we are making
    ComPtr<IMFMediaType> partialType = nullptr;
    HR(MFCreateMediaType(partialType.GetAddressOf()));
    // We want Audio
    HR(partialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
    // We want uncompressed data
    HR(partialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
    HR(sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, partialType.Get()));
  }
  ComPtr<IMFMediaType> uncompressedAudioType = nullptr;
  HR(sourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, uncompressedAudioType.GetAddressOf()));
  WAVEFORMATEXTENSIBLE d;
  WAVEFORMATEX * waveformatex;
  unsigned int waveformatlength;
  HR(MFCreateWaveFormatExFromMFMediaType(uncompressedAudioType.Get(), &waveformatex, &waveformatlength));
  std::vector<BYTE> bytes;
  // Get Sample
  ComPtr<IMFSample> sample;
  while (true)
  {
    DWORD flags{};
    HR(sourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &flags, nullptr, sample.GetAddressOf()));
    // Check for eof
    if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
    {
      break;
    }
    // Convert data to contiguous buffer
    ComPtr<IMFMediaBuffer> buffer;
    HR(sample->ConvertToContiguousBuffer(buffer.GetAddressOf()));
    // Lock Buffer & copy to local memory
    BYTE* audioData = nullptr;
    DWORD audioDataLength{};
    HR(buffer->Lock(&audioData, nullptr, &audioDataLength));
    for (size_t i = 0; i < audioDataLength; i++)
    {
      bytes.push_back(*(audioData + i));
    }
    // Unlock Buffer
    HR(buffer->Unlock());
  }
  return 0;
}

Now that we have the WAVEFORMATEX object and vector<byte> of our audio file we are reading to send it to XAudio2 for playback!

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
  MediaFoundationInitialize mf{};
  // Create Attribute Store
  ComPtr<IMFAttributes> sourceReaderConfiguration;
  HR(MFCreateAttributes(sourceReaderConfiguration.GetAddressOf(), 1));
  HR(sourceReaderConfiguration->SetUINT32(MF_LOW_LATENCY, true));
  // Create Source Reader
  ComPtr<IMFSourceReader> sourceReader;
  HR(MFCreateSourceReaderFromURL(L"C:\\Users\\TraGicCode\\Desktop\\394506-n-a--1450673416.mp3", sourceReaderConfiguration.Get(), sourceReader.GetAddressOf()));
  // Query information about the media file
  ComPtr<IMFMediaType> nativeMediaType;
  HR(sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nativeMediaType.GetAddressOf()));
  
  // Check if media file is indeed an audio file
  GUID majorType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType));
  if (MFMediaType_Audio != majorType)
  {
    throw NotAudioFileException{};
  }
  // Check if media file is compressed or uncompressed
  GUID subType{};
  HR(nativeMediaType->GetGUID(MF_MT_MAJOR_TYPE, &subType));
  if (MFAudioFormat_Float == subType || MFAudioFormat_PCM == subType)
  {
    // Audio File is uncompressed
  }
  else
  {
    // Audio file is compressed
    // Inform the SourceReader we want uncompressed data
    // This causes it to look for decoders to perform the request we are making
    ComPtr<IMFMediaType> partialType = nullptr;
    HR(MFCreateMediaType(partialType.GetAddressOf()));
    // We want Audio
    HR(partialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
    // We want uncompressed data
    HR(partialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
    HR(sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, partialType.Get()));
  }
  ComPtr<IMFMediaType> uncompressedAudioType = nullptr;
  HR(sourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, uncompressedAudioType.GetAddressOf()));
  WAVEFORMATEXTENSIBLE d;
  WAVEFORMATEX * waveformatex;
  unsigned int waveformatlength;
  HR(MFCreateWaveFormatExFromMFMediaType(uncompressedAudioType.Get(), &waveformatex, &waveformatlength));
  std::vector<BYTE> bytes;
  // Get Sample
  ComPtr<IMFSample> sample;
  while (true)
  {
    DWORD flags{};
    HR(sourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &flags, nullptr, sample.GetAddressOf()));
    // Check for eof
    if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
    {
      break;
    }
    // Convert data to contiguous buffer
    ComPtr<IMFMediaBuffer> buffer;
    HR(sample->ConvertToContiguousBuffer(buffer.GetAddressOf()));
    // Lock Buffer & copy to local memory
    BYTE* audioData = nullptr;
    DWORD audioDataLength{};
    HR(buffer->Lock(&audioData, nullptr, &audioDataLength));
    for (size_t i = 0; i < audioDataLength; i++)
    {
      bytes.push_back(*(audioData + i));
    }
    // Unlock Buffer
    HR(buffer->Unlock());
  }
  // Create XAudio2 stuff
  auto xAudioEngine = CreateXAudioEngine();
  auto masteringVoice = CreateMasteringVoice(xAudioEngine);
  auto sourceVoice = CreateSourceVoice(xAudioEngine, *waveformatex);
  XAUDIO2_BUFFER xAudioBuffer{};
  xAudioBuffer.AudioBytes = bytes.size();
  xAudioBuffer.pAudioData = (BYTE* const)&bytes[0];
  xAudioBuffer.pContext = nullptr;
  sourceVoice->Start();
  HR(sourceVoice->SubmitSourceBuffer(&xAudioBuffer));
  // Sleep for some time to hear to song by preventing the main thread from sleep
  // XAudio2 plays the sound on a seperate audio thread <img src="http://tragiccode.com/wp-includes/images/smilies/simple-smile.png" alt=":)" class="wp-smiley" style="height: 1em; max-height: 1em;" />
  Sleep(1000000);
  return 0;
}

And There you have it. Not too bad if you ask me!

Sony C#/.NET Component Set Analysis

$
0
0

Some of you may know that we have recently released version 6.00 of our analyzer, that now has C# support. The ability to scan C# projects increases the number of open-source projects we can analyze. This article is about one such check. This time it is a project, developed by Sony Computer Entertainment (SCEI).

What have we checked?


Sony Computer Entertainment is a video game company. Being a branch of Sony Corporation, it specializes in video games and game consoles. This company develops video games, hardware and software for PlayStation consoles.


image2.png


Authoring Tools Framework (ATF) is a set of C#/.NET components for making tools on Windows®. ATF has been used by most Sony Computer Entertainment first party game studios to make custom tools. This component set is used by such studios as Naughty Dog, Guerrilla Games and Quantic Dream. Tools developed with these program components were used during the creation of such well know games as 'The Last of Us' and 'Killzone'. ATF is an open-source project which is available at this GitHub repository.

Analysis tool


To perform the source code analysis, we used PVS-Studio static code analyzer. This tool scans projects written in C/C++/C#. Every diagnostic message has a detailed description in the documentation with examples of incorrect code and possible ways to fix the bugs. Quite a number of the diagnostic descriptions have a link to corresponding sections of error base, where you can see information about the bugs that were found in real projects with the help of these diagnostics.


image4.png


You can download the analyzer here and run it on your (or somebody's) code.

Examples of errors


public static void DefaultGiveFeedback(IComDataObject data, 
                                       GiveFeedbackEventArgs e)
{
  ....
  if (setDefaultDropDesc && (DropImageType)e.Effect != currentType)
  {
    if (e.Effect != DragDropEffects.None)
    {
      SetDropDescription(data, 
        (DropImageType)e.Effect, e.Effect.ToString(), null);
    }
    else
    {
      SetDropDescription(data, 
        (DropImageType)e.Effect, e.Effect.ToString(), null);
    }
    ....
  }
}

Analyzer warning: V3004 The 'then' statement is equivalent to the 'else' statement. Atf.Gui.WinForms.vs2010 DropDescriptionHelper.cs 199

As you see in the code, the same method with similar arguments will be called, in spite of the e.Effect != DragDropEffects.None being true or not. It's hard to suggest any ways to fix this code fragment, without being a developer of this code, but I think it is clear that this fragment needs a more thorough revision. What exactly should be fixed, is a question which should be directed to the author of this code.

Let's look at the following code fragment:

public ProgressCompleteEventArgs(Exception progressError, 
            object progressResult, 
            bool cancelled)
{
  ProgressError = ProgressError;
  ProgressResult = progressResult;
  Cancelled = cancelled;
}

Analyzer warning: V3005 The 'ProgressError' variable is assigned to itself. Atf.Gui.Wpf.vs2010 StatusService.cs 24

It was supposed that during the method call, the properties would get values, passed as arguments; at the same time the names of properties and parameters differ only in the first letter case. As a result - ProgressError property is assigned to itself instead of being given the progressError parameter.

Quite interesting here, is the fact that it's not the only case where capital and small letters get confused. Several of the projects we've checked have the same issues. We suspect that we'll find a new error pattern typical for C# programs soon. There is a tendency to initialize properties in a method, where the names of parameters differ from the names of the initialized properties by just one letter. As a result, we have errors like this. The next code fragment is probably not erroneous, but it looks rather strange, to say the least.

public double Left { get; set; }
public double Top  { get; set; }

public void ApplyLayout(XmlReader reader)
{
  ....
  FloatingWindow window = new FloatingWindow(
                                this, reader.ReadSubtree());
  ....
  window.Left = window.Left;
  window.Top = window.Top;
  ....
}

Analyzer warning: V3005 The 'window.Left' variable is assigned to itself. Atf.Gui.Wpf.vs2010 DockPanel.cs 706 | V3005 The 'window.Top' variable is assigned to itself. Atf.Gui.Wpf.vs2010 DockPanel.cs 707

In the analyzer warnings you can see that the window object properties Left and Top are assigned to themselves. In some cases this variant is perfectly appropriate, for example when the property access method has special logic. But there is no additional logic for these properties, and so it is unclear why the code is written this way.

Next example:

private static void OnBoundPasswordChanged(DependencyObject d,
                      DependencyPropertyChangedEventArgs e)
{
    PasswordBox box = d as PasswordBox;

    if (d == null || !GetBindPassword(d))
    {
        return;
    }

    // avoid recursive updating by ignoring the box's changed event
    box.PasswordChanged -= HandlePasswordChanged;
    ....
}

Analyzer warning: V3019 Possibly an incorrect variable is compared to null after type conversion using 'as' keyword. Check variables 'd', 'box'. Atf.Gui.Wpf.vs2010 PasswordBoxBehavior.cs 38

We have already seen quite a number of errors of this type in the C# projects that we checked. By casting an object to a compatible type using as operator the programmer gets a new object, but further in the code the source object is compared to null. This code can work correctly, if you are sure that the d object will always be compatible with the PasswordBox type. But it is not so (for now or if there are more changes in the program); you can easily get NullReferenceException in code that used to work correctly. So in any case this code needs to be reviewed.

In the following example, conversely, the programmer clearly tried to make the code as secure as possible, although it's not quite clear what for.

public Rect Extent
{
    get { return _extent; }
    set
    {
        if (value.Top    < -1.7976931348623157E+308  || 
            value.Top    >  1.7976931348623157E+308  || 
            value.Left   < -1.7976931348623157E+308  ||
            value.Left   >  1.7976931348623157E+308  || 
            value.Width  >  1.7976931348623157E+308  || 
            value.Height >  1.7976931348623157E+308)
        {
            throw new ArgumentOutOfRangeException("value");
        }
        _extent = value;
        ReIndex();
    }
}

Analyzer warning: V3022 Expression is always false. Atf.Gui.Wpf.vs2010 PriorityQuadTree.cs 575

This condition will always be false. Let's look into the code and see why.

This is an implementation of the property that has Rect type, hence value also has Rect type. Top, Left, Width, Height are properties of this type, that have double type. This code checks if these property values exceed the range of values, that the double type takes. We also see that "magic numbers" are used here for comparison, not constants, defined in the double type. That's why this condition will always be false, since the double type values are always within the value range.

Apparently, the programmer wanted to secure the program from a non-standard implementation of double type in the compiler. Nevertheless, it looks rather strange, so it was reasonable for the analyzer to issue a warning, suggesting that the programmer double-check the code.

Let's go on.

public DispatcherOperationStatus Status { get; }
public enum DispatcherOperationStatus
{
  Pending,
  Aborted,
  Completed,
  Executing
}
public object EndInvoke(IAsyncResult result)
{
  DispatcherAsyncResultAdapter res = 
    result as DispatcherAsyncResultAdapter;
  if (res == null)
    throw new InvalidCastException();

  while (res.Operation.Status != DispatcherOperationStatus.Completed
         || res.Operation.Status == DispatcherOperationStatus.Aborted)
  {
    Thread.Sleep(50);
  }

  return res.Operation.Result;
}

Analyzer warning: V3023 Consider inspecting this expression. The expression is excessive or contains a misprint. Atf.Gui.Wpf.vs2010 SynchronizeInvoke.cs 74

The condition of the while loop is redundant, it could be simplified by removing the second subexpression. Then the loop can be simplified in the following way:

while (res.Operation.Status != DispatcherOperationStatus.Completed)
  Thread.Sleep(50);

Next example, quite an interesting one:

private Vec3F ProjectToArcball(Point point)
{
  float x = (float)point.X / (m_width / 2);    // Scale so bounds map
                                               // to [0,0] - [2,2]
  float y = (float)point.Y / (m_height / 2);

  x = x - 1;                           // Translate 0,0 to the center
  y = 1 - y;                           // Flip so +Y is up
  if (x < -1)
    x = -1;
  else if (x > 1)
    x = 1;
  if (y < -1)
    y = -1;
  else if (y > 1)
    y = 1;
  ....
}

Analyzer warning: V3041 The expression was implicitly cast from 'int' type to 'float' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. Atf.Gui.OpenGL.vs2010 ArcBallCameraController.cs 216 | V3041 The expression was implicitly cast from 'int' type to 'float' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. Atf.Gui.OpenGL.vs2010 ArcBallCameraController.cs 217

This is one of those cases where it's very hard for a third-party developer to say for sure if there is an error in this code or not. On the one hand - integer division with implicit casting to a real type looks strange. On the other hand, sometimes it can be done deliberately, regardless of the precision loss.

It's difficult to say what was meant here. Perhaps the programmer didn't want to lose the precision of the code, but it will still occur as a result of m_width / 2 operation. In this case we should rewrite the code in the following way:

float x = point.X / ((float)m_width / 2);

On the other hand, there is a possibility that an integer number was meant to be written to x, as further on we see operations of comparison with integer values. But in this case, there was no need to do explicit casting to float type.

float x = point.X / (m_width / 2);

Our analyzer continues developing and obtaining new diagnostics. The next error was found with the help of our new diagnostic. But as this diagnostic wasn't in the release version of the analyzer, there will be no link to the documentation, but I hope the idea is clear:

public static QuatF Slerp(QuatF q1, QuatF q2, float t)
{
  double dot = q2.X * q1.X + q2.Y * q1.Y + q2.Z * q1.Z + q2.W * q1.W;

  if (dot < 0)
    q1.X = -q1.X; q1.Y = -q1.Y; q1.Z = -q1.Z; q1.W = -q1.W;

  ....
}

Analyzer warning: V3043 The code's operational logic does not correspond with its formatting. The statement is indented to the right, but it is always executed. It is possible that curly brackets are missing. Atf.Core.vs2010 QuatF.cs 282

You can see that a sum of several products is evaluated and the result is written to the dot variable. After that if the dot value is negative, there is inversion of all values of this operation. More precisely, the inversion was meant to be here, judging by the code formatting. In reality, only X property of q1 will be inverted, all other properties will be inverted regardless of the value of dot variable. Solution for this problem is curly brackets:

if (dot < 0)
{
  q1.X = -q1.X; 
  q1.Y = -q1.Y; 
  q1.Z = -q1.Z; 
  q1.W = -q1.W;
}

Let's go on.

public float X;
public float Y;

public float Z;
public void Set(Matrix4F m)
{
  ....
  ww = -0.5 * (m.M22 + m.M33);
  if (ww >= 0)
  {
    if (ww >= EPS2)
    {
      double wwSqrt = Math.Sqrt(ww);
      X = (float)wwSqrt;
      ww = 0.5 / wwSqrt;
      Y = (float)(m.M21 * ww);
      Z = (float)(m.M31 * ww);
      return;
    }
  }
  else
  {
    X = 0;
    Y = 0;
    Z = 1;
    return;
  }

  X = 0;
  ww = 0.5 * (1.0f - m.M33);
  if (ww >= EPS2)
  {
    double wwSqrt = Math.Sqrt(ww);
    Y = (float)wwSqrt;                   //<=
    Z = (float)(m.M32 / (2.0 * wwSqrt)); //<=
  }

  Y = 0; //<=
  Z = 1; //<=
}


Analyzer warning: V3008 The 'Y' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 221, 217. Atf.Core.vs2010 QuatF.cs 221 | V3008 The 'Z' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 222, 218. Atf.Core.vs2010 QuatF.cs 222

We have intentionally provided an additional code fragment so that the error is more evident. Y and Z are instance fields. Depending on the conditions, some values are written to these fields and then method execution is terminated. But in the body of the last if operator, the programmer forgot to write the return operator, so the fields will be assigned not those values, as it was supposed. This being the case, the correct code could look like this:

X = 0;
ww = 0.5 * (1.0f - m.M33);
if (ww >= EPS2)
{
  double wwSqrt = Math.Sqrt(ww);
  Y = (float)wwSqrt;                   
  Z = (float)(m.M32 / (2.0 * wwSqrt)); 
  return;
}

Y = 0; 
Z = 1;

Perhaps this is enough. These fragments seemed the most interesting to us, that's why we've brought them here. There were more bugs found, but we haven't provided low severity level examples here, instead opting to show the mid to high severity level examples.

Conclusion


image6.png


As you see, no one is immune to failure, it's quite easy to assign an object to itself, or miss some operator due to carelessness. At times, such errors are hard to detect visually in big projects, moreover most of them will not immediately show up - some of them will shoot you in the foot half a year later. To avoid such misfortune, it's a good idea to use an analyzer that is able to detect bugs during the early phases of development, decreasing the development costs, keeping you sane, and your legs safe.
Viewing all 17825 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>