• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.


  • Content count

  • Joined

  • Last visited

Community Reputation

1410 Excellent

About Enigma

  • Rank
  1. You have an open parenthesis ('(') on line 4 instead of an open brace ('{').
  2. Why is it that you "don't like using reinterpret_cast" but are happy using a union cast? Although both will undoubtably work in any environment you can envisage, the union cast is technically undefined behaviour (by implication) whereas reinterpret_cast is designed for exactly the kind of thing you are doing and (I believe) guaranteed to work (assuming you are casting float to int and back again with no intermediate operations). Σnigma
  3. To try and expand a bit on the tutorial, a visitor gets its value from the variant via the boost::apply_visitor function. boost::apply_visitor takes a visitor and a variant and internally calls the appropriate visitor function depending on the type currently held in the variant. To try and illustrate (note, this is not at all how the actual implementation works, I'm just trying to help you understand the usage): #include <iostream> // A really rubbish int/float variant class variant { public: variant(int value) : int_value_(value), float_value_(0.0f), is_int_(true) { } variant(float value) : int_value_(0), float_value_(value), is_int_(false) { } int int_value() const { return int_value_; } float float_value() const { return float_value_; } bool is_int() const { return is_int_; } private: int int_value_; float float_value_; bool is_int_; }; struct visitor { // this is the function called by apply_visitor when the variant // contains an int. The parameter 'value' is the value of the // variant void operator()(int value) const { std::cout << "int " << value << '\n'; } // this is the function called by apply_visitor when the variant // contains a float. The parameter 'value' is the value of the // variant void operator()(float value) const { std::cout << "float " << value << '\n'; } }; void apply_visitor(visitor const & visitor_to_apply, variant & variant_to_visit) { // call the appropriate member function of visitor depending on // the type in variant_to_visit (in a really rubbish way) if (variant_to_visit.is_int()) { visitor_to_apply(variant_to_visit.int_value()); } else { visitor_to_apply(variant_to_visit.float_value()); } } int main() { variant v1(3); variant v2(7.0f); apply_visitor(visitor(), v1); apply_visitor(visitor(), v2); } Obviously boost::variant is far more sophisticated in its implementation. Σnigma
  4. What, no unordered_set? Dictionary Profiling Test Test: Name of the test used Setup: Time taken to setup the data structure in ms Lookup: Time taken to search all of the test words in ms Fail: A count of the number of words encountered that did not exist in the data structure Success: A count of the number of words encountered that were successfully found in the data structure Bytes used: Bytes allocated for the data structure and its testing class MSVC 2008 Release Testing 82368 words (~1000 misspelled, ~1000 gibberish), 25 times Test Setup Lookup Fail Success Bytes used ------------------------------------------------------------------------------- Set 109 1781 1935 80433 3536312 Vector 62 1500 1935 80433 2580840 Vector/vector 47 1219 1935 80433 2946456 Trie (simple) 94 922 1935 80433 16558952 Trie (map) 156 1859 1935 80433 9812716 UnorderedSet 78 578 1935 80433 2965148
  5. Your Value type is unsafe. It does not implement proper copy semantics and leaks memory. The constructors new[] memory but nothing delete[]s it and if you copy a value both the original and copy will point to the same memory, so changes to one will affect the other. This means it does not meet the requirements for value types in a std::vector. You need to give your Value type a copy constructor (which allocates memory and deep copies the value) and copy-assignment operator (which deletes and allocates memory and deep copies the value) and make the destructor delete[] the memory. In the long term may I suggest you investigate some of the concepts presented here (this might be pretty heavy, so don't worry about digesting it all in one go, and feel free to ask questions): #include <iostream> // C++ comes with a stack implementation #include <stack> // boost (www.boost.org) is a very useful set of libraries // I consider it a must-have for serious C++ coding #include <boost/utility.hpp> #include <boost/variant.hpp> // define your error codes int const ICE_PARSE_STACK_SUCCESS = 0; int const ICE_PARSE_STACK_FAILURE_CORRUPT_STACK = 1; int const ICE_PARSE_STACK_FAILURE_WRONG_FORMAT = 2; // create some types for your memory index and table index struct memory_index { unsigned int value_; }; struct table_index { unsigned int value_; }; // create a typedef to replace your value class // boost::variant is a datatype that holds exactly one of its listed types // at any time. See http://www.boost.org/doc/libs/1_43_0/doc/html/variant.html typedef boost::variant< int, float, memory_index, table_index > value; // to access the value in a variant you apply a visitor // this visitor is for retrieving an int value from the visitor class retrieve_int_stack_value : // we inherit from boost::static_visitor< int > // so that boost's implementation knows that this // visitor returns an int (our error code) public boost::static_visitor< int > { public: // we'll store the value we extract from the stack // into value, so we store a reference to it here retrieve_int_stack_value(int & value) : value_(value) { } // these functions are called by boost's implementation // to retrieve a value from the variant. In this case // the variant stored an int and we want an int, so we // simply perform an assignment int operator()(int value) const { value_ = value; return ICE_PARSE_STACK_SUCCESS; } // the variant contains a float and we want an int int operator()(float value) const { value_ = static_cast< int >(value); return ICE_PARSE_STACK_SUCCESS; } // the variant contains a memory_index and we want an // int int operator()(memory_index) const { return ICE_PARSE_STACK_FAILURE_CORRUPT_STACK; } // the variant contains a table_index and we want an // int int operator()(table_index) const { return ICE_PARSE_STACK_FAILURE_CORRUPT_STACK; } private: int & value_; }; // the version for retrieving a float class retrieve_float_stack_value : public boost::static_visitor< int > { public: retrieve_float_stack_value(float & value) : value_(value) { } int operator()(int value) const { value_ = static_cast< float >(value); return ICE_PARSE_STACK_SUCCESS; } int operator()(float value) const { value_ = value; return ICE_PARSE_STACK_SUCCESS; } int operator()(memory_index) const { return ICE_PARSE_STACK_FAILURE_CORRUPT_STACK; } int operator()(table_index) const { return ICE_PARSE_STACK_FAILURE_CORRUPT_STACK; } private: float & value_; }; // the version for retrieving a memory_index class retrieve_memory_index_stack_value : public boost::static_visitor< int > { public: retrieve_memory_index_stack_value(memory_index & value) : value_(value) { } int operator()(int) const { return ICE_PARSE_STACK_FAILURE_WRONG_FORMAT; } int operator()(float) const { return ICE_PARSE_STACK_FAILURE_WRONG_FORMAT; } int operator()(memory_index) const { return ICE_PARSE_STACK_FAILURE_WRONG_FORMAT; } int operator()(table_index) const { return ICE_PARSE_STACK_FAILURE_WRONG_FORMAT; } private: memory_index & value_; }; // the version for retrieving a table_index class retrieve_table_index_stack_value : public boost::static_visitor< int > { public: retrieve_table_index_stack_value(table_index & value) : value_(value) { } int operator()(int) const { return ICE_PARSE_STACK_FAILURE_CORRUPT_STACK; } int operator()(float) const { return ICE_PARSE_STACK_FAILURE_CORRUPT_STACK; } int operator()(memory_index) const { return ICE_PARSE_STACK_FAILURE_CORRUPT_STACK; } int operator()(table_index value) const { value_ = value; return ICE_PARSE_STACK_SUCCESS; } private: table_index & value_; }; // a common technique - depending on the type we want to read // we need to use different visitors, so define a bunch of overloaded // functions that return visitors of the appropriate types retrieve_int_stack_value retrieve_stack_value(int & value) { return retrieve_int_stack_value(value); } retrieve_float_stack_value retrieve_stack_value(float & value) { return retrieve_float_stack_value(value); } retrieve_memory_index_stack_value retrieve_stack_value(memory_index & value) { return retrieve_memory_index_stack_value(value); } retrieve_table_index_stack_value retrieve_stack_value(table_index & value) { return retrieve_table_index_stack_value(value); } // a class for parsing a stack // we make this non-copyable to avoid accidently storing a copy of the // stack parser and parsing the same stack with multiple parsers class stack_parser : boost::noncopyable { public: stack_parser(std::stack< value > & stack) : stack_(stack), error_(ICE_PARSE_STACK_SUCCESS) { } // a template member function which takes a reference to a // type and, if the stack is not empty and no error has // occured, retrieves the value from the top of the stack // using boost::apply_visitor. It returns a reference // to itself so we can retrieve multiple values quickly and // easily template < typename type > stack_parser & operator()(type & value) { if (!stack_.empty() && error_ == ICE_PARSE_STACK_SUCCESS) { error_ = boost::apply_visitor(retrieve_stack_value(value), stack_.top()); stack_.pop(); } else if (error_ == ICE_PARSE_STACK_SUCCESS) { error_ = ICE_PARSE_STACK_FAILURE_CORRUPT_STACK; } return *this; } int error_code() const { return error_; } private: std::stack< value > & stack_; int error_; }; int main() { std::stack< value > stack; stack.push(value(55)); stack.push(value(1234.567f)); int int_value; float float_value; // rather than using error-prone varargs we create a stack parser // and retrieve values via successive applications of operator() //int error_code = IceExtension::ParseStack("fi", stack, &float_value, &int_value); int error_code = stack_parser(stack)(float_value)(int_value).error_code(); std::cout << int_value << ' ' << float_value << '\n'; }
  6. Inclusion guards will not help in this situation. Inclusion guards only prevent multiple definitions within a single translation unit (cpp file and accompanying includes), they cannot prevent multiple definitions across multiple translation units. Did you read the article Zahlman linked to?
  7. The original version of the C++ standard did not require vector to store objects in contiguous memory. This was fixed in TC1. Here's the defect report. In fact I'm pretty sure that this entire post is (probably indirectly) a rip from something written by Sutter, Meyers or another of that crowd. Also note that you must also guarantee that the vector is not empty before applying this technique.
  8. The primary reason to prefer C++ streams over C file IO is type safety. Using C-style file IO the caller is responsible for ensuring that the number and type of all descriptors and arguments match. Any errors results in undefined behaviour. There are also potential security concerns when dealing with strings passed from external sources. C++ streams have none of these problems - it is impossible to mismatch a type or number of arguments. Unfortunately C++ streams can be unwieldly for output which is why some people prefer boost::format which arguably provides the best of both worlds (albeit at a performance penalty). Σnigma
  9. // a function void func() { } // an object int i; // a pointer to a function void (*fp)() = func; // a pointer to an object int * p = &i; // a reference to a function void (&fr)() = func; // a reference to an object int & r = i; // error - fpr is a reference to pointer, func decays to an rvalue of // type void (*)(), rvalues cannot be bound to references to non-const // void (*&fpr)() = func; // error - pr is a reference to a pointer, &i is an rvalue of type int *, // rvalues cannot be bound to references to non-const // int * & pr = &i; // a reference to a const pointer void (* const & fpcr)() = func; // a reference to a const object int * const & pcr = &i;Hopefully you can now see the solution to your problem. Σnigma
  10. Here's a first pass at clearing it up: // a function for calculating the next highest power of two unsigned int next_power_of_two(unsigned int value) { unsigned int power_of_two = 1; while (power_of_two < value) { power_of_two <<= 1; } return power_of_two; } // prefer to use std::string for text - memory ownership is clearer and there // is no possibility of passing a null pointer // pass by const reference to avoid a copy // I've left the return type as is since I have no context // I would usually prefer to return by value or smart-pointer modelTexture_t * CUtility::LoadPCXTexture(std::string const & filename) { // don't declare variables until just before you need them, // preferably not until you can properly initialise them // using auto_ptr ensures that we can't leak thisTexture // it essentially performs a delete whenever we return from this // function unless we explicitly release it. std::auto_ptr< modelTexture_t > thisTexture(new modelTexture_t()); // as fpsgamer pointed out, it's not possible for thisTexture to be null, // so testing for nullness is redundant // load the PCX file into the texture struct PCXHEADER texInfo; // header of texture thisTexture->data = LoadPCXFile(filename, &texInfo); if (thisTexture->data.empty()) { return NULL; } // store the texture information thisTexture->palette = texInfo.palette; thisTexture->width = texInfo.xMax - texInfo.xMin + 1; thisTexture->height = texInfo.yMax - texInfo.yMin + 1; // allocate memory for the unscaled data // like auto_ptr, vector ensures we can't leak this memory std::vector< unsigned char > unscaledData(thisTexture->width*thisTexture->height*4); // store the unscaled data via the palette // use unsigned types for values that should never be negative for (unsigned int j = 0; j < thisTexture->height; j++) { for (unsigned int i = 0; i < thisTexture->width; i++) { unscaledData[4*(j*thisTexture->width+i)+0] = (unsigned char)thisTexture->palette[3*thisTexture->data[j*thisTexture->width+i]+0]; unscaledData[4*(j*thisTexture->width+i)+1] = (unsigned char)thisTexture->palette[3*thisTexture->data[j*thisTexture->width+i]+1]; unscaledData[4*(j*thisTexture->width+i)+2] = (unsigned char)thisTexture->palette[3*thisTexture->data[j*thisTexture->width+i]+2]; unscaledData[4*(j*thisTexture->width+i)+3] = (unsigned char)255; } } // break repeated functionality out into functions // did you mean to transpose width and height? thisTexture->scaledHeight = next_power_of_two(thisTexture->width); thisTexture->scaledWidth = next_power_of_two(thisTexture->height); // reallocate memory for the texture data // again, use vector thisTexture->data.resize(thisTexture->scaledWidth*thisTexture->scaledHeight*4); // use the GL utility library to scale the texture to the unscaled dimensions gluScaleImage (GL_RGBA, thisTexture->width, thisTexture->height, GL_UNSIGNED_BYTE, &unscaledData[0], thisTexture->scaledWidth, thisTexture->scaledHeight, GL_UNSIGNED_BYTE, &thisTexture->data[0]); // #### OLD MEM LEAK PROBLEM WHEN REFERENCING unscaledData with auto_ptr. return thisTexture.release(); } Your latest code contained an error because you used the scalar form of delete (delete x;) to delete memory allocated with the array form of new (new y[z];). You must always match scalar new with scalar delete (x = new y; delete x;) and array new with array delete (x = new y[z];delete[] x;). Or, even better, avoid explicit invocations of new and delete altogether, as demonstrated above. Σnigma
  11. std::string identifier(std::size_t count) { std::string letters = "abcdefghijklmnopqrstuvwxyz"; std::string result; while (true) { result = letters.substr(count % 26, 1) + result; count /= 26; if (!count) { break; } --count; } return result; } I just so happened to need the same thing myself a week ago. Σnigma
  12. XMLNode *readXML(const char *file) { std::ifstream file_stream(file, std::ios::binary); std::istreambuf_iterator< char > begin(file_stream); std::istreambuf_iterator< char > end; std::vector< char > data(begin, end); std::auto_ptr< XMLNode > root(new XMLNode("root")); populateObj(root,data,nb); return root.release(); } Σnigma
  13. Quite right, I missed that. (I also inadvertantly deleted one too many lines at the top - the declaration of texInfo is still required, albeit not until one line later). Σnigma
  14. Why are you using malloc in what is clearly C++ code? modelTexture_t *CUtility::LoadPCXTexture(char *filename) { // allocate memory for texture struct std::auto_ptr< modelTexture_t > thisTexture(new modelTexture_t()); if (thisTexture == NULL) return NULL; // load the PCX file into the texture struct thisTexture->data = LoadPCXFile(filename, &texInfo); if (thisTexture->data == NULL) { return NULL; } // store the texture information thisTexture->palette = texInfo.palette; thisTexture->width = texInfo.xMax - texInfo.xMin ; thisTexture->height = texInfo.yMax - texInfo.yMin ; // allocate memory for the unscaled data std::vector< unsigned char > unscaledData((thisTexture->width*thisTexture->height)*4); There, all memory leaks eliminated. Σnigma
  15. Quote:Original post by ApochPiQ Only one problem with your rewrite - it doesn't properly handle segments that are oriented in opposite directions. For instance, (0,0)-(1,0) should combine with (2,0)-(1,0) because they are colinear, but since you do a comparison of the sign rather than the absolute value of the sign, these cases will be missed. Once your code is updated to handle that particular edge case, it's effectively isomorphic to mine.(0,0)-(1,0) dx = 1 dy = 0 (2,0)-(1,0) other_dx = -1 other_dy = 0 ((1 == -1 && 0 == 0) || (1 == 1 && 0 == 0)) == true I agree that our code should be exactly equivalent, but given that they were written from scratch by two different people it gives you a point of differentiation. If they both produce the same output it would strongly imply the error lies in the inputs, if they produce different output then at least one algorithm is incorrect and they can be piecewise compared to discover the difference. Σnigma