Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!

1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


Member Since 19 Dec 2012
Offline Last Active Apr 29 2015 02:41 AM

Posts I've Made

In Topic: Looking for step my step guide for visual studio

05 April 2015 - 10:09 PM

Hello, I am working on an RTS myself, and I have been working on it for about a year (off and on). You will need to learn the graphics part separately first. I recommend lazyfoo or some other tutorials to learn the API. What you want is the ability to load and draw objects. I recommend having all of this loading and drawing in a 'Mesh' class, so you can just do 'Mesh.Draw'.


Actually your draw function will look like this:


pipeline.translate(xpos, 0, zpos)

pipeline.translate(unit angle);



You will of course have to write these translate/rotate functions yourself. They can be complicated because of matrix math, but google is your friend to find these functions, and you can wrap them up in a Matrix class.




I am by no means a master at this, as I am just following my gut, but I do have a working game with units that run around and kill each other etc. So I will describe how I did it, and you can take what you like


Firstly You have your main "Game" class, taking in whatever parameters you like, which runs everything and contains everything. You effectively run the game at 5 fps or 10 fps. You just have a timer set to 0.1 or 0.2 (for the seconds per frame a turn takes), and every frame you minus the time passed from that number. If it is below zero, you want to run a while loop that adds 0.1 or 0.2, and does the logic.

while(timer < 0.0)
   // Do Logic
   timer += 0.2;

This is super useful if you ever want to implement multiplayer because it makes the game deterministic rather than having the outcome change slightly depending on your frame rate.


During the logic phase you will step forward the movement, missiles, hits etc.minus hp. What is also good at running the game at 5fps is that it's a lot lighter on your CPU.



For Units, you want two classes.

UnitType - This is the specifications, such as maxHP, attack, speed, range, mesh to draw with etc.

UnitInstance - A generic unit that contains an object of UnitType. I have a reference for this in the class, however I'm not sure how references are done in c#. You could actually just copy over the details to the unit instance, its just each unit of the same time will be using memory to store their speed etc.

class UnitType
	string m_name;
	int m_maxHP;
	float m_speed; // I like to make it distance per second, or you could make it per turn to save some calculations
	int m_attack;
	float m_range;
	float LineOfSight;

		// set parameters to create all members.
class UnitInstance
	UnitType& m_unit;
	int m_hp;
	vector3f m_position;
	vector3f m_previousPosition;
	vector3f m_localPosition;

	int state;

	// Movement variables
	vector3f target;
	UnitInstance* unitTarget;

	Move(seconds passed);
	Attack(seconds passed);
	// etc

And you will have an array of UnitInstance's that you add/remove to as they are created/die (just check if hp < 0, then "kill"). You can start off with 1-3 UnitTypes, for example "Frigate" or "Battleship" etc, since you said its a naval battle game, and create your UnitInstance unit with a parameter of the "Battleship" object, and initialize the UnitInstance with the information from the Battleship (such as speed, hp = maxMP etc).


Also the reason I have "Previous position" in the UnitInstance is so that when the gameplay is occurring, units effectively animate at the machines framerate, going as fast as the GPU can render them. Here's how the function could look:

Game::MainLoop(float timePassed)

	// I'd recommend putting all this logic stuff in its own function
	time -= timePassed;

while(timer < 0.0)


   // Do Logic

   timer += 0.2;










The map design can help with further optimization. What I have is a map of tiles 128x128 (or watever it is), each tile has an array of units currently on it. the UnitInstance class has a variable for localPosition (which is its position on the tile). if the x or z goes below 0.0 or above 1.0, I then remove that unit from the array in the tile, and add it to the new til it's on.


Why this is so good, is that whenever you are check a range, you just check certain tiles. I did it like this:

class Tile
 	int* unitsOnTile;	// Since you're using C#, it will have to be int unitsOnTile[MAX_UNITS_PER_TILE];
	int numOfUnitsOnTile;

	void AddUnit(int ID)'
	void RemoveUnit(int ID);

The reason I made the list of units as an 'int' and not 'UnitInstance' is because the list is referring to the Unit's place in the UnitInstance array in the Game Class.




This is so efficient, because you only do certain functions whenever the unit changes tile. Like if the units tile changes, you then update the fogofwar. I am actually planning to add another list to my tileClass eventually, "int* unitsSeenBy" For units that can see the tile. That way I only ever have to do a checkAgro function every time units change tiles, thus saving even more processing power. Might be unnecessary for this amount of optimization.


Do you have questions about anything else? I am still working on this, and maybe other forum members can contribute more than me.

In Topic: 2 strange opengl problems - small window and reverse

25 November 2014 - 09:55 PM

Fixed problem I had posted, but this new one had taken me hours and I found no results on google.


I have 2 shaders

tileShader = new SHADER("Shaders/StandardShader.vs", "Shaders/TileShader.fs");
skinShader = new SHADER("Shaders/SkinningShader.vs", "Shaders/ShadingShader.fs");

and they each have 2 textures like so:

uniform sampler2D texture0;
uniform sampler2D texture1;

and then I do this:

glUniform1i(glGetUniformLocation(tileShader->GetProgramID(), "texture0"), 0);
glUniform1i(glGetUniformLocation(tileShader->GetProgramID(), "texture1"), 1);

glUniform1i(glGetUniformLocation(skinShader->GetProgramID(), "texture0"), 0);
glUniform1i(glGetUniformLocation(skinShader->GetProgramID(), "texture1"), 1);

In the shaders they do some stuff with the two textures mixing them together, however one of these shaders will only render ONE texture in place of both, depending on whichever I do the "new SHADER" on first(last works properly, first just has 1 texture, or is just sometimes completely black)


And I have no idea what the hell is causing this. I know my binding is fine, as swapping their "new SHADER" lines after each other makes them swap, one working and one not.


Any help is greatly appreciated.

In Topic: 2 strange opengl problems - small window and reverse

23 November 2014 - 03:23 AM


What does this call look like "pipeline.Ortho(-1, 1, -1, 1, -1, 1);"?

What does "quad.Render();" look like? Im thinking its a problem with the quads vertices or texture coordinates assigned to them.

For the blending if you textures alpha channel is black (0) or doesnt have an alpha channel and defaults to black it might not show.


Oh man thanks, I had my quad be 0,0 to 1,1, but it should have been -1,-1 to 1,1. That problem is now fixed. By I still have the issue of the model being back to front and rotating the wrong way.

In Topic: Opengl Spazzing out for no reason

02 November 2014 - 02:50 AM

Sorry everyone I'm retarded.


My shader:

void main()
	mat4 BoneTransform;
	BoneTransform += gBones[BoneIDs[0]] * Weights[0];
	BoneTransform += gBones[BoneIDs[1]] * Weights[1];
	BoneTransform += gBones[BoneIDs[2]] * Weights[2];
	BoneTransform += gBones[BoneIDs[3]] * Weights[3];

bonetransform is just full of nothing. Changed to:

void main()
	mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
	BoneTransform += gBones[BoneIDs[1]] * Weights[1];
	BoneTransform += gBones[BoneIDs[2]] * Weights[2];
	BoneTransform += gBones[BoneIDs[3]] * Weights[3];

All better

In Topic: Opengl Spazzing out for no reason

20 October 2014 - 06:18 PM

Get ready for code overload:




bool MESH::Load(const char* filename)

	m_hasTexture = false;
	m_numBones = 0;

	std::vector<VECTOR3f> positions;
	std::vector<VECTOR3f> normals;
	std::vector<VECTOR2f> texCoords;
	std::vector<VertexBoneData> bones;
	std::vector<uint> indices;

	uint numVertices = 0;
	uint numIndices = 0;

	// Use assimp to get data from the file
	Assimp::Importer Importer;
	m_pScene = m_Importer.ReadFile(filename, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs);

	if (!m_pScene)
		printf("Error parsing '%s': '%s'\n", filename, Importer.GetErrorString());
		return false;

	m_globalInverseTransform = m_pScene->mRootNode->mTransformation;

	// Make all vectors the appropriate size for the data

	for (uint i = 0; i < m_objects.size(); i++)
		m_objects[i].numVertices	= m_pScene->mMeshes[i]->mNumVertices;
		m_objects[i].numIndices		= m_pScene->mMeshes[i]->mNumFaces * 3;
		m_objects[i].startVertex	= numVertices;
		m_objects[i].startIndex		= numIndices;

		numVertices += m_objects[i].numVertices;
		numIndices	+= m_objects[i].numIndices;


	// Initialize the meshes
	int fuck = m_objects.size();

	for (uint i = 0; i < m_objects.size(); i++)
		const aiMesh* paiMesh = m_pScene->mMeshes[i];
		const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);

		// Populate Vertex Attributes
		for (uint j = 0; j < paiMesh->mNumVertices; j++)
			const aiVector3D* aiTexCoord	= paiMesh->HasTextureCoords(0) ? &(paiMesh->mTextureCoords[0][j]) : &Zero3D;

			positions.push_back(VECTOR3f(paiMesh->mVertices[j].x, paiMesh->mVertices[j].y, paiMesh->mVertices[j].z));
			normals.push_back(VECTOR3f(paiMesh->mNormals[j].x, paiMesh->mNormals[j].y, paiMesh->mNormals[j].z));
			texCoords.push_back(VECTOR2f(aiTexCoord->x, aiTexCoord->y));

		// Populate Indices
		for (uint j = 0; j < paiMesh->mNumFaces; j++)
			const aiFace& face = paiMesh->mFaces[j];
			assert(face.mNumIndices == 3);


		// Load Bones for current Mesh
		for (uint j = 0; j < paiMesh->mNumBones; j++)
			uint boneIndex = 0;
			std::string boneName(paiMesh->mBones[j]->mName.data);

			if (m_boneMapping.find(boneName) == m_boneMapping.end())
				boneIndex = m_numBones;
				m_boneInfo[boneIndex].BoneOffset = paiMesh->mBones[j]->mOffsetMatrix;
				m_boneMapping[boneName] = boneIndex;
				boneIndex = m_boneMapping[boneName];

			m_boneMapping[boneName] = boneIndex;
			m_boneInfo[boneIndex].BoneOffset = paiMesh->mBones[j]->mOffsetMatrix;

			for (uint k = 0; k < paiMesh->mBones[j]->mNumWeights; k++)
				uint VertexID = m_objects[i].startVertex + paiMesh->mBones[j]->mWeights[k].mVertexId;
				float weight = paiMesh->mBones[j]->mWeights[k].mWeight;
				bones[VertexID].AddBoneData(boneIndex, weight);

	// Load the material (only using one for now)
	// We assume the texture file is in the same directory as the model file
	const aiMaterial* pMaterial = m_pScene->mMaterials[0];
	aiString path;
	if (pMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS)
		std::string::size_type startIndex = std::string(path.C_Str()).find_last_of("\\");
		if (startIndex == std::string::npos)
			startIndex = std::string(path.C_Str()).find_last_of("/");

		std::string texFilename;

		if (startIndex != std::string::npos)
			texFilename = std::string(path.C_Str()).substr(startIndex + 1);
			texFilename = path.C_Str();

		// get texture path, and make it lower case		
		std::string texPath = std::string(filename).substr(0, std::string(filename).find_last_of("/")) + "/";
		for (uint i = 0; i < texFilename.length(); i++)
			texPath += texFilename[i];

		// Load Texture
		if (!m_texture.Load(texPath.c_str()))
			printf("Failed to load texture: \"%s\"", texPath.c_str());
			m_hasTexture = true;

	const aiNodeAnim* pNodeAnim = NULL;
	uint i = 0;

	while (!pNodeAnim)
		pNodeAnim = FindNodeAnim(m_pScene->mAnimations[0], m_pScene->mRootNode->mChildren[i]->mName.data);

		if (i > 500)	// Too many loops
		//FindNodeAnim(pAnimation, nodeName);

	if (pNodeAnim)
		m_animInfo.numKeyframes = pNodeAnim->mNumPositionKeys;
		m_animInfo.frameDuration = m_pScene->mAnimations[0]->mDuration / (pNodeAnim->mNumPositionKeys);

	// Negate the last frame, as it is the same as the first
	m_animInfo.MaxAnimTime = m_animInfo.frameDuration * (m_animInfo.numKeyframes - 1);

	// Initialize buffers with the data
	InitBuffers(positions, normals, texCoords, indices, bones);

	return true;
bool MESH::InitBuffers(const std::vector<VECTOR3f>& positions,
					   const std::vector<VECTOR3f>& normals,
					   const std::vector<VECTOR2f>& texCoords,
					   const std::vector<uint>& indices,
					   const std::vector<VertexBoneData>& bones)
	// Create the VAO
	glGenVertexArrays(1, &m_VAO);

	// Create the buffers for the vertices attributes
	glGenBuffers(ArraySizeInElements(m_buffers), m_buffers);

	// Generate and populate the buffers with vertex attributes and the indices
	glBindBuffer(GL_ARRAY_BUFFER, m_buffers[POS_VB]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(positions[0]) * positions.size(), &positions[0], GL_STATIC_DRAW);
	glVertexAttribPointer(POSITION_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ARRAY_BUFFER, m_buffers[TEXCOORD_VB]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords[0]) * texCoords.size(), &texCoords[0], GL_STATIC_DRAW);
	glVertexAttribPointer(TEX_COORD_LOCATION, 2, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ARRAY_BUFFER, m_buffers[NORMAL_VB]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(normals[0]) * normals.size(), &normals[0], GL_STATIC_DRAW);
	glVertexAttribPointer(NORMAL_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, 0);

	if (m_numBones > 0)
		glBindBuffer(GL_ARRAY_BUFFER, m_buffers[BONE_VB]);
		glBufferData(GL_ARRAY_BUFFER, sizeof(bones[0]) * bones.size(), &bones[0], GL_STATIC_DRAW);
		glVertexAttribIPointer(BONE_ID_LOCATION, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
		glVertexAttribPointer(BONE_WEIGHT_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)16);

	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), &indices[0], GL_STATIC_DRAW);


	return true;
void MESH::Clear()
	if (m_buffers[0] != 0)
		glDeleteBuffers(ArraySizeInElements(m_buffers), m_buffers);

	if (m_VAO != 0)
		glDeleteVertexArrays(1, &m_VAO);
		m_VAO = 0;

	if (m_objects.size() > 0)


void GAME::RenderModels()
	// Draw Geometry / Scene Objects



		//m_pipeline.Translate(0.0f, 0.0f, -6.0f);
		//m_pipeline.Translate(0.0f, 1.0f, 0.0f);

		std::vector<MATRIX4f> transforms;

		float time = m_timer.m_secsPassedSinceStart / ninja.GetAnimTime() * 0.5f;

		ninja.BoneTransform(time, transforms);

		for (uint i = 0; i < transforms.size(); i++)
			glUniformMatrix4fv(m_boneLocations[i], 1, GL_TRUE, (const GLfloat*)transforms[i].m_matrix);

		m_pipeline.Translate(0.0f, 0.0f, 1.0f);


void MESH::Render()

	if (m_hasTexture)

	for (uint i = 0; i < m_objects.size(); i++)
								 (void*)(sizeof(uint) * m_objects[i].startIndex),

	if (m_hasTexture)

// This function simply has the bones be where they're supposed to be depending on the time in seconds passed in. At the moment its used as a loop
void MESH::BoneTransform(float& TimeInSeconds, std::vector<MATRIX4f>& transforms)
	MATRIX4f identity;

	float ticksPerSecond = (float)(m_pScene->mAnimations[0]->mTicksPerSecond != 0 ? m_pScene->mAnimations[0]->mTicksPerSecond : 25.0f);
	float TimeInTicks = TimeInSeconds * ticksPerSecond;
    //float AnimationTime = fmod(TimeInTicks, (float)m_pScene->mAnimations[0]->mDuration);
	float AnimationTime = fmod(TimeInTicks, m_animInfo.MaxAnimTime);

	ReadNodeHeirarchy(AnimationTime, m_pScene->mRootNode, identity);


	for (uint i = 0; i < m_numBones; i++)
		transforms[i] = m_boneInfo[i].FinalTransformation;
void MESH::ReadNodeHeirarchy(float AnimationTime, const aiNode* pNode, const MATRIX4f& ParentTransform)
	std::string nodeName(pNode->mName.data);

	const aiAnimation* pAnimation = m_pScene->mAnimations[0];

	MATRIX4f NodeTransformation(pNode->mTransformation);

	const aiNodeAnim* pNodeAnim = FindNodeAnim(pAnimation, nodeName);

	if (pNodeAnim)
		// Interpolate scaling and generate scaling transformation matrix
        aiVector3D Scaling;
        CalcInterpolatedScaling(Scaling, AnimationTime, pNodeAnim);
        MATRIX4f ScalingM;
        ScalingM.InitScale(Scaling.x, Scaling.y, Scaling.z);
        // Interpolate rotation and generate rotation transformation matrix
        aiQuaternion RotationQ;
        CalcInterpolatedRotation(RotationQ, AnimationTime, pNodeAnim);        
        MATRIX4f RotationM = MATRIX4f(RotationQ.GetMatrix());

        // Interpolate translation and generate translation transformation matrix
        aiVector3D Translation;
        CalcInterpolatedPosition(Translation, AnimationTime, pNodeAnim);
        MATRIX4f TranslationM;
        TranslationM.InitTranslate(Translation.x, Translation.y, Translation.z);
        // Combine the above transformations
        NodeTransformation = TranslationM * RotationM * ScalingM;

	MATRIX4f GlobalTransformation = ParentTransform * NodeTransformation;

	if (m_boneMapping.find(nodeName) != m_boneMapping.end())
		uint boneIndex = m_boneMapping[nodeName];
		m_boneInfo[boneIndex].FinalTransformation = m_globalInverseTransform * GlobalTransformation * m_boneInfo[boneIndex].BoneOffset;;

	for (uint i = 0; i < pNode->mNumChildren; i++)
		ReadNodeHeirarchy(AnimationTime, pNode->mChildren[i], GlobalTransformation);

Now this is where I suspect the problem may lie, however I don't really understand it:

void MESH::CalcInterpolatedPosition(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
    if (pNodeAnim->mNumPositionKeys == 1) {
        Out = pNodeAnim->mPositionKeys[0].mValue;
    uint PositionIndex = FindPosition(AnimationTime, pNodeAnim);
    uint NextPositionIndex = (PositionIndex + 1);
    assert(NextPositionIndex < pNodeAnim->mNumPositionKeys);
    float DeltaTime = (float)(pNodeAnim->mPositionKeys[NextPositionIndex].mTime - pNodeAnim->mPositionKeys[PositionIndex].mTime);
    float Factor = (AnimationTime - (float)pNodeAnim->mPositionKeys[PositionIndex].mTime) / DeltaTime;
	//assert(Factor >= 0.0f && Factor <= 1.0f);
    const aiVector3D& Start = pNodeAnim->mPositionKeys[PositionIndex].mValue;
    const aiVector3D& End = pNodeAnim->mPositionKeys[NextPositionIndex].mValue;
    aiVector3D Delta = End - Start;
    Out = Start + Factor * Delta;
void MESH::CalcInterpolatedRotation(aiQuaternion& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
	// we need at least two values to interpolate...
    if (pNodeAnim->mNumRotationKeys == 1) {
        Out = pNodeAnim->mRotationKeys[0].mValue;
    uint RotationIndex = FindRotation(AnimationTime, pNodeAnim);
    uint NextRotationIndex = (RotationIndex + 1);
    assert(NextRotationIndex < pNodeAnim->mNumRotationKeys);
    float DeltaTime = (float)(pNodeAnim->mRotationKeys[NextRotationIndex].mTime - pNodeAnim->mRotationKeys[RotationIndex].mTime);
    float Factor = (AnimationTime - (float)pNodeAnim->mRotationKeys[RotationIndex].mTime) / DeltaTime;
	//assert(Factor >= 0.0f && Factor <= 1.0f);
    const aiQuaternion& StartRotationQ = pNodeAnim->mRotationKeys[RotationIndex].mValue;
    const aiQuaternion& EndRotationQ   = pNodeAnim->mRotationKeys[NextRotationIndex].mValue;    
    aiQuaternion::Interpolate(Out, StartRotationQ, EndRotationQ, Factor);
    Out = Out.Normalize();
void MESH::CalcInterpolatedScaling(aiVector3D& Out, float AnimationTime, const aiNodeAnim* pNodeAnim)
    if (pNodeAnim->mNumScalingKeys == 1) {
        Out = pNodeAnim->mScalingKeys[0].mValue;

    uint ScalingIndex = FindScaling(AnimationTime, pNodeAnim);
    uint NextScalingIndex = (ScalingIndex + 1);
    assert(NextScalingIndex < pNodeAnim->mNumScalingKeys);
    float DeltaTime = (float)(pNodeAnim->mScalingKeys[NextScalingIndex].mTime - pNodeAnim->mScalingKeys[ScalingIndex].mTime);
    float Factor = (AnimationTime - (float)pNodeAnim->mScalingKeys[ScalingIndex].mTime) / DeltaTime;

	//assert(Factor >= 0.0f && Factor <= 1.0f);
    const aiVector3D& Start = pNodeAnim->mScalingKeys[ScalingIndex].mValue;
    const aiVector3D& End   = pNodeAnim->mScalingKeys[NextScalingIndex].mValue;
    aiVector3D Delta = End - Start;
    Out = Start + Factor * Delta;

You will notice i commented out the assertion because i do actually get values outside 0 and 1. I must confess I don't really understand this function.


Any help is greatly appreciated!