Jump to content

  • Log In with Google      Sign In   
  • Create Account

We need your help!

We need 7 developers from Canada and 18 more from Australia to help us complete a research survey.

Support our site by taking a quick sponsored survey and win a chance at a $50 Amazon gift card. Click here to get started!


Vincent_M

Member Since 16 Jan 2007
Offline Last Active Aug 29 2015 12:34 AM

Posts I've Made

In Topic: Different Resolutions

23 August 2015 - 05:08 PM


In other games, like the example in the picture, more screen real estate means a big advantage in what you can select or activate. The player with the smallest screen (red) can only see a few units and will be surprised as more come on the screen.

That's a pretty good point. I remember some one mentioning in a thread a while back where he'd play Age of Kings on his 4K display. The largest map sizes could fit entirely onscreen. This could be an advantage or disadvantage to the users depending on how physically large the display is (32-inch vs 60-inch). The was we addressed the varying aspect ratio issue in one of our portrait mobile games was similar to how the Warcraft games did it. We'd use our skinniest aspect ratio (9:16) as our basis aspect ratio to draw all UI elements within. The game would be in full-view, while our widest aspect ratio (3:4) would have decorative borders on the sides. We used a fixed-height Cartesian coordinate system where the origin was at the center of the screen, and the y-axis was facing up rather than down. We'd place all of our UI elements within the bounds of the 9:16 aspect ratio, and add those borders just outside of the 9:16 view. Then, wider aspect ratios would show the border. How much of the border it showed depended on how close to 3:4 we got.

 

I believe we used a fixed height of 720, or maybe we used 960. In any case, this was only for placement and size (including 9-slicing). We were targeting the iPad retina as our highest possible resolution, so our fullscreen backgrounds were 1536x2048 (3:4). In a 720 fixed-height coordinate space, we get a viewport range of +/-360, which covers 2048 pixels down on a retina iPad.

 

This can all be accomplished with the projection matrix, as mentioned by IceCave.


In Topic: Career Advice

20 August 2015 - 04:48 PM

I've been really tired today, so I'm struggling to conveying my thoughts.

 

1. Of course, it'll take more time. I've spent a long time trying to get experience on my own, but it appears that I'm only working harder, not smarter. I can't seem to focus on a single thing.

 

2. I'm picky because I'm so new to the software engineering industry in general. I don't know what most job roles really are, or even what I want. Not only do I need experience, but I need to figure out what I want that's obtainable right now. Then, I actually know what to work toward.

 

3. I meant seniors. Most companies are looking for senior developers, not junior developers.

 

4. I mean not knowledgable in as "uncomfortable." If I see something I might be interested in, should I just go for it? Getting a job in that field could land me a reliable mentor.

 

5. This warrants a quote:


5. Great. You can. You'll just need to get some experience in the industry, and good contacts, a plan, and some money. My article 29 might shed more light on that part. http://www.sloperama.com/advice/lesson29.htm

I completely agree. I need more experience in the industry in general, not just programming. I try to balance my programming, gym and family time with a social life too. I'm always meeting people who want to do projects with me. Again, lack of experience and direction on my part's destroyed every personal project I've done to date. I'll post back when I'm done reading your article.


In Topic: Matching Parameter Names to Class Fields

19 August 2015 - 02:41 PM


It's fine. Just use "this->name = name" so you don't try to assign the parameter to itself:

I use "this->" when I have identical names. When assigning variables in the constructor's initializer list, it seems to not make any different that my constructor parameters have identical names to my

 

 

 


The only problem here is your passing of POD types by const reference.

Do class fields still get assigned by value when passing by reference, right? One thing that confuses me about references is that I can still pass literals in as parameters. I've just recently started using references, so it's practice at this point. If I wanted to pass objects by value into my constructor (aka, make a copy, just like push_back() does in std::vector), would I not use a reference? If I wanted to pass by reference, would it be wise to use references over pointers, or does it depend on the situation?

 

 

 


I like to prefix all class member variables with "m" which happens to avoid this issue and make it clearer which variables are member variables. I believe the "m_" prefix is the most common (based on what ive seen), Im just too lazy to type the underscore. Like how some people start all their class names with "C" (I just use capital first letter for classes and nothing else).

I've seen these cases a lot myself in the past. I think the book, Clean Code, suggests not doing this though. I can't remember if I really read that a year ago when I read the book, or even why that'd be the case. Personally, a prefix sounds like a good way to go as, you like said, explicitly establishes scope for that variable throughout the class. I could even have local variables with the same name, but no prefix, and the code wouldn't look as confusing, provided the situation would require a variable named like that. I'm a big fan of just the preceding underscore, which appears to be a common convention in Obj-C, C#, etc. I've been told by a lead developer in the past that a preceding underscore isn't a good practice in C++ while it is in C#.

 

@Gooey: The code compiled fine, but it does look misleading. I didn't even think about this as I was still thinking in terms of passing-by-value. Trying to go with references over value when I can do that. but you also bring up a good point. A const reference makes it misleading.

@Strewya: Good point on the code convention. Removing the const reference sounds like the better way to go.

 

EDIT: I found this post on why a we shouldn't start variable names with an underscore. Bottom line: variables starting with an underscore is reserved for C++ implementors. Sounds like this goes for variables starting with both upper and lower-case letters after the underscore. Josh Petrie also pointed out in a previous post I started 2 days ago when I was doing that with my header guards. I thought he only meant that for header guards though. After reading that post, it makes all sense now. I guess m_* might be the way to go. This posts suggests using trailing underscores for naming. I kind of like that naming convention.


In Topic: [Qt 5] Moving Rows For Scene Tree (QAbstractItemModel)

18 August 2015 - 04:55 PM


Actually that does not seem to be entirely accurate, I think removeRows should remove (delete scene nodes), such that you can use it for that specific purpose, say in a context menu or when pressing the delete key while a node is selected. What I found is that you should enable the DragDropOverrideMode on the view for it not to call remove on the original indices, DragDrop vs InternalMove seems not to make a difference to me.

I override removeRows() to specifically remove scene nodes from my own hierarchy's structure between beginRemoveRows() and endRemoveRows(). It's been a few weeks since I worked on this, but I think removeRows() was being called internally by the base class, QAbstractItemModel, whenever I moved something in my hierarchy. Changing drag and drop operations from InternalMove to DragDrop seemed to stop calling removeRows() internally.

 

I agree with trying to avoid having to subclass QTreeView for simple operations. You'd think it'd provide more intuitive signals for when selections change and occur, but hey, there are reimplementation methods for that (ugh)! Again, I'm still pretty new to Qt, and it looks like it doesn't directly handle selections, but rather the QSelectionModel field it has does this.

 

Either way, my hierarchy system appears to work like a charm now. There are only minor changes here, but here's the working source code I've got:

#include "SceneModel.h"
#include "Scene.h"
#include "Node.h"

#include <stdio.h>
#include <stdlib.h>

#include <QKeyEvent>
#include <QMimeData>
#include <QByteArray>
#include <QColor>
#include <QDebug>


Qt::ItemFlags SceneModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
	if(index.isValid())
		return defaultFlags | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsEditable;
	else
		return defaultFlags | Qt::ItemIsDropEnabled;

	return QAbstractItemModel::flags(index);
}

Qt::DropActions SceneModel::supportedDropActions() const
{
	return QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
}

int SceneModel::rowCount(const QModelIndex &parent) const
{
	// make sure the scene's root node is valid and the parent's column is zero
	if(!scene || !scene->GetRoot() || parent.column() > 0)
		return 0;

	// return the parent's children, if valid
	if(parent.isValid())
		return static_cast<Node*>(parent.internalPointer())->GetNumChildren();

	// otherwise, return the children
	return scene->GetRoot()->GetNumChildren();
}

int SceneModel::columnCount(const QModelIndex&) const
{
	return 1;
}

QModelIndex SceneModel::index(int row, int column, const QModelIndex &parent) const
{
	// make sure the root, row and column data is valid
	if(!scene || !scene->GetRoot() || !hasIndex(row, column, parent))
		return QModelIndex();

	// get the parent Node from the index
	Node *parentItem = scene->GetRoot();
	if(parent.isValid())
		parentItem = static_cast<Node*>(parent.internalPointer());

	Node *childItem = parentItem->GetChild(row);
	if(childItem)
		return createIndex(row, column, childItem);

	return QModelIndex();
}

QModelIndex SceneModel::parent(const QModelIndex &index) const
{
	// make sure the index is valid
	if(!scene || !index.isValid())
		return QModelIndex();

	// get the child item to get the parent item
	Node *childItem = static_cast<Node*>(index.internalPointer());
	if(!childItem) return QModelIndex();
	Node *parentItem = childItem->GetParent();

	// return nothing if the parent item is the root
	if(!parentItem || parentItem == scene->GetRoot())
		return QModelIndex();

	return createIndex(parentItem->GetChildIndex(), 0, parentItem);
}

QVariant SceneModel::data(const QModelIndex &index, int role) const
{
	if(!index.isValid())
		return QVariant();

	if(role == Qt::ForegroundRole)
	{
		Node *node = static_cast<Node*>(index.internalPointer());
		if(node && !node->IsActive())
			return QColor(Qt::gray);
		return QVariant();
	}

	// handle displaying the data
	if(role == Qt::DisplayRole || role == Qt::EditRole)
	{
		// get the child and parent Node pointers
		Node *node = static_cast<Node*>(index.internalPointer());
		return QString(node->GetName().c_str());
	}

	return QVariant();
}

bool SceneModel::submit()
{
	// make sure the scene is available
	if(scene)
	{
		// check if the selected node is valid
		if(selectedNode)
		{
			// get the selected node's index, and update its display data
			QModelIndex selectedIndex = createIndex(selectedNode->GetChildIndex(), 0, selectedNode);
			setData(selectedIndex, QString(selectedNode->GetName().c_str()), Qt::DisplayRole);
		}
	}
	return QAbstractItemModel::submit();
}

bool SceneModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
	// check if the index is valid, and either in EditRole or DisplayRole mode
	if(index.isValid() && (role == Qt::EditRole || role == Qt::DisplayRole))
	{
		Node *node = static_cast<Node*>(index.internalPointer());
		if(!node) return false;

		// set the node's new name, and signal that data has changed
		node->SetName(std::string(value.toString().toUtf8()));
		emit dataChanged(index, index);

		// emit signal that specifically signifies that the name has changed
		if(role == Qt::EditRole)
			emit SelectedNodeNameUpdated(node->GetName());
		return true;
	}
	return QAbstractItemModel::setData(index, value, role);
}

bool SceneModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int, const QModelIndex &parent)
{
	// make sure this is aciton shouldn't be ignored
	if(!scene || !data || action == Qt::IgnoreAction)
		return false;

	// get the encoded data of our Node pointer
	QByteArray encodedData = data->data(SceneModelMIMEType);
	Node *node = (Node*)encodedData.toULongLong();
	if(!node) return false;

	// get the parent node
	QModelIndex destinationParentIndex;
	Node *parentNode = static_cast<Node*>(parent.internalPointer());
	if(parentNode)
	{
		destinationParentIndex = parent;
	} else {
		parentNode = scene->GetRoot();
		destinationParentIndex = createIndex(0, 1, scene->GetRoot());
	}

	// get rowCount if row is invalid (this might be an old Qt bug)
	if(row == -1)
		row = parentNode->GetNumChildren();

	// move the row
	Node *sourceParentNode = node->GetParent();
	QModelIndex sourceParent = createIndex(sourceParentNode->GetChildIndex(), 0, sourceParentNode);
	moveRow(sourceParent, node->GetChildIndex(), destinationParentIndex, row);
	return true;
}

QStringList SceneModel::mimeTypes() const
{
	QStringList types;
	types << SceneModelMIMEType;
	return types;
}

QMimeData *SceneModel::mimeData(const QModelIndexList &indexes) const
{
	// make sure there's exactly 1 index and it's valid
	if(indexes.size() != 1 || !indexes[0].isValid())
		return nullptr;

	// get the Node pointer from the index's internal pointer, convert it to quintptr, then to a UTF string
	quintptr nodeAddress = (quintptr)indexes[0].internalPointer();
	QByteArray encodedData(QString::number(nodeAddress).toUtf8());

	// allocate mimeData, provide it with encodedData, and return it
	QMimeData *mimeData = new QMimeData();
	mimeData->setData(SceneModelMIMEType, encodedData);
	return mimeData;
}

bool SceneModel::insertRows(int row, int count, const QModelIndex &parent)
{
	if(!scene)
		return false;

	Node *parentNode = static_cast<Node*>(parent.internalPointer());
	if(!parentNode)
		parentNode = scene->GetRoot();

	// make sure a parent node was found before continuing
	if(parentNode)
	{
		// begin inserting rows, and add begin adding uniquely-named fields
		beginInsertRows(parent, row, row+count-1);

		// insert nodes
		for(int i=0;i<count;++i)
			parentNode->AddChild("New Node");
		endInsertRows();
		return true;
	}
	return QAbstractItemModel::insertRows(row, count, parent);
}

bool SceneModel::removeRows(int row, int count, const QModelIndex &parent)
{
	if(!scene)
		return false;

	Node *parentItem = static_cast<Node*>(parent.internalPointer());
	if(!parentItem)
		parentItem = scene->GetRoot();

	// make sure a parent node was found before continuing
	if(parentItem)
	{
		beginRemoveRows(parent, row, row+count-1);
		parentItem->RemoveChildren(row, count);
		endRemoveRows();
		return true;
	}
	return QAbstractItemModel::removeRows(row, count, parent);
}

bool SceneModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
{
	Node *sourceParentNode = static_cast<Node*>(sourceParent.internalPointer());
	Node *destinationParentNode = static_cast<Node*>(destinationParent.internalPointer());
	Node *childNode = sourceParentNode->GetChild(sourceRow);

	// make sure the child isn't one of the parent nodes
	if(childNode == sourceParentNode || childNode == destinationParentNode)
		return false;

	// if source and destination parents are the same, move elements locally
	if(sourceParentNode == destinationParentNode)
	{
		// only process if a local move is possible
		if(sourceParentNode->IsMovePossible(sourceRow, count, destinationChild))
		{
			beginMoveRows(sourceParent, sourceRow, sourceRow+count-1, destinationParent, destinationChild);
			sourceParentNode->MoveChildren(sourceRow, count, destinationChild);
			endMoveRows();
			return true;
		}
	} else {
		// otherwise, move the node under the parent
		beginMoveRows(sourceParent, sourceRow, sourceRow+count-1, destinationParent, destinationChild);
		childNode->SetParent(destinationParentNode, destinationChild);
		endMoveRows();
		return true;
	}
	return false;
}

Are you trying to achieve a typical hierarchy-based scene graph with a tree view as well? Also, were you able to get it to work without the mandatory MIME data nonsense? I can see where MIME data is necessary when dragging files from say, Explorer or Finder into your view or items between separate views, but not from within the same view.


In Topic: Code Review

18 August 2015 - 04:19 PM


I think it's a pretty good idea to read Scott Meyers's Effective C++.

I've looked at other C++ books recently. I wanted to get one on STL and templates in general. I've heard Effective C++ was a really good one, but wasn't sure which version to get.

 


Performance in this regards will be increased when you pass stuff like std::vector, std::string which can take a large amount of time to copy. I wouldn't say they exactly increase security, since you can do stuff like this unfortunately:
void takeString(std::string& string)
{
string.clear();
}

std::string* pString = nullptr;

takeString(*pString); // crash inside the function as if you were passing nullptr
but they document intent by telling the user of your function "the value of this parameter is required", while using only pointers technically make it unclear whether or not they have to take a value or can be nullptr.

This is a really good point you've brought up. After reading how references are like pointers that can't be NULL, it looks like I could still pass NULL/invalid pointers into the reference parameter parameter by pointed value, which will result in a segment fault.

 


f you want to go for portable serialization you should either be using a library(e.g. Boost::Serialization) or manually write the serialization code, because even for POD structures, the memory layout may differ due to alignment and padding and then there's also the issue of endianness.

This is another thing I keep going back and forth on. When I started learning C/C++, there were cases where the PSP homebrew community would store their data as POD if they could afford to in an effort to perform less calls to fread() and fwrite(). Again, padding and endianess between environments is a caveat.

 

As far as this convention goes:

if(rotated) { }

I actually prefer it that way for bools, pointers and integer values where zero acts as a special use-case versus a non-zero value. The only reason I wrote that as if(rotated > 0) { } is because I thought that'd be a more accepted convention. I'll post more often when I have questions regarding code conventions.


PARTNERS