• 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.

Hodgman

Moderators
  • Content count

    14284
  • Joined

  • Last visited

  • Days Won

    6

Hodgman last won the day on July 14

Hodgman had the most liked content!

Community Reputation

51165 Excellent

2 Followers

About Hodgman

  • Rank
    Moderator - APIs & Tools

Personal Information

Social

  • Twitter
    @BrookeHodgman
  • Github
    hodgman

Recent Profile Visitors

65541 profile views
  1. Yeah I don't any more either. Interfaces, abstract-classes and concrete-classes all use the same convention.
  2. A $1M budget for an indie game isn't that unusual. Often the founders of new companies will "invest" their wages back into the company, which means they're owed the money on paper, but no money exists yet. e.g. A senior game developer could have a salary of around $120k pa. When they quit to start their own studio, they'll pay themselves that same salary. Let's say we've got two veterans who are "going indie". The company is founded by Bob and Alice, who sign employment contracts for a $120K pa salary. Every month, the company "pays" Bob $12k (the payslip / tax invoices say so), but Bob never receives any cash. At the same time, Bob "invests" $12k of his own cash into the company as a loan (at least the paperwork says so). At the end of the year, because Bob has "earned" $120k, he has to pay $32k to the tax office as income tax. Alice does the same thing as Bob. No money exists, and no money actually changed hands... but after one year, the company has $240K of debt on it's books -- it legally owes that money to Bob and Alice, who have loaned it to the company. The company declares on its tax paperwork that it has spent $240K on R&D activities, to which it is entitled a 45% cash rebate. The tax office pays the company a $108k reimbursement on its losses. The company partially repays Bob and Alice's loans, giving them $54k each (which after paying their own tax bills, leaves them with $22k each actual income, free from the taxpayer...), and the company still owes them $66k each. If they sell their company in the future, the new owners have to pay them $66k each (on top of actually buying the company). That's a simple two person team with no company expenses, and their on-paper budget for one year's work is around a quarter million If it was a four-person team, working for two years or more, and they had some actual expenses (hiring contractors, buying equipment, renting a cheap office space) then their company could easily be in the $1-2M range... And it's not that this is all just made up either -- that's the amount of money they'd have to make off their game to actually make quitting their jobs and "going indie" a worthwhile venture. That's the difference between "indies" who are already far into their careers and treat their independent work as a legitimate small business, and "indies" who are beginning their careers and make games in their spare time as a hobby. AAA games cost more like $100M these days, not $1M. $1M is the new indie.
  3. I don't have much input, sorry... But the human powered helicopter was also sci-fi/fantasy until it was recently built. This sounds like a fun concept.
  4. Here's the documentation: https://partner.steamgames.com/doc/sdk/api
  5. Ah thanks for the correction, I thought it was off the min/max value in the enum, not the min/max bits of the values in the enum. So to correct my example: enum Flags { F_1 = 1, F_2 = 2, F_4 = 4, }; Flags f = ...; switch( f ) { default: return Case_Other();//a valid compiler could assume this is impossible case -1: return Case_1();//a valid compiler could assume this is impossible case 0: return Case0(); case 1: return Case1(); case 2: return Case2(); case 3: return Case3(); case 4: return Case4(); case 5: return Case5(); case 6: return Case6(); case 7: return Case7(); case 8: return Case8();//a valid compiler could assume this is impossible case 257: return Case257();//a valid compiler could assume this is impossible - perhaps the underlying type for Flags is char, not int. } There's a difference between does not currently, and is not allowed to. "enum class" explicitly defaults to using "int" as the underlying type ("For a scoped enumeration type, the underlying type is int if it is not explicitly specified"), but unscoped enums pick an implementation defined underlying type. It's not inconceivable that a compiler could pick an uint8, and then assume that values above 255 are impossible. It's also valid for the compiler to invent a 3-bit underlying type and then optimize based on the assumptions that you can make about 3-bit variables. Unlikely, but allowed. It's not crazy for the compiler to make assumptions based on the strict wording of the spec either. For example, usually this code will print "ok!", but I managed to get it to print "what?" under GCC, which is also correct behavior if the compiler makes certain valid assumptions: int* test = new int(42); *(short*)test = 0; switch( *test ) { case 42: printf("what?"); break; case 0: printf("ok!"); break; }; Interestingly, other times, the compiler will try to be super clever and remove most of that code, leaving a call to global operator new, and a hard-coded call to printf("ok!")...
  6. It doesn't matter that it's silly. That's the rules of the language as laid down by the spec. It's the assumptions that your optimizing compiler holds up as golden. You break the rules of the language and the compiler is allowed to fuck you. e.g. you can play with pointers willy-nilly, but the optimizer is allowed to assume that you haven't broken the aliasing rules. float f = 1.0f; (*(int*)f) = 0; // note that int(0) and float(0) have the same bitpattern, so this should work just fine! Yay, clever! printf( "%f", f );//does this print 1 or 0? In practice, that will often print 0.000... but the rules of the language say that this program is invalid, so you could expect it to print 1.000 as well. The aliasing rule says that on the 3rd line it's accessed as a float type, so only the most recent write to a float type could possibly impact the value. The 2nd line can be optimized out or otherwise ignored. That's the specified rules of the language. Likewise: enum Flags { F_1 = 1, F_2 = 2, F_4 = 4, }; Flags f = (Flags)((int)F_1 | (int)F_2 | (int)F_4); printf( "%d", (int)f );//does this print 7? In practice, this will probably print 7... but the rules of the language say that the variable f is only allowed to hold values from 1 to 4 (inclusive), so you could expect this program to print out something entirely different, such as 4 as well. That might sound silly, but optimizers are built to follow the rules of the language! So, say you've written a switch statement that's designed to handle the 8 possible values that you expect the above 'f' variable to hold, the compiler is well within it's rights to assume that half of these cases are impossible as the spec says that it can only hold values 1-4, and therefore the compiler is safe to go ahead and simply optimize away those function calls completely out of existence. switch( (int)f ) { default: return Case_Other();//impossible case 0: return Case0();//impossible case 1: return Case1(); case 2: return Case2(); case 3: return Case3(); case 4: return Case4(); case 5: return Case5();//impossible case 6: return Case6();//impossible case 7: return Case7();//impossible } It doesn't matter that this is silly. These are the rules, and when you choose to break them, you are choosing to write code that could stop working at any time, and only happens to work right now because the optimizer has not done a good enough job to break your invalid code, yet. Choosing to write time-bombed code is silly. If you continue to use enums as flags after this, you're choosing to play chicken with your compiler. Good luck, and pray that it doesn't optimize as much as the spec says that it's allowed to.
  7. The quake/doom engines are all great. Fabien Sanglard's code reviews are great also - http://fabiensanglard.net - a good way to get up to speed on those code bases without having to decypher the architecture yourself. I learned so much of my own game programming knowledge from reading (and modding) the Half Life 1 code base. The engine is closed source, but the game code is available. The gameplay architecture is bad by modern standards (incorrect use of OOP everywhere) but the separation of engine, network, client and server responsibilities follows the Quake model closely, so is quite clean.
  8. Open Source

    Yeah I define an engine as "after you make a game, all the bits you can reuse to make a second game, are the engine". So the only way to prove an engine is complete is to make a game Or at least plan out a game and work out what kinds of frameworks and tools might be required.
  9. The logical order of the API and the physical order of the hardware doesn't have to match up exactly, as long as it behaves the same way. If a clever GPU decides to do the depth-test before the pixel shader instead of after, there's no way for you to know -- the behaviour is the same... except that performance will improve... so GPU's will do this.
  10. IIRC from the Frostbite papers, they validate against Mitsuba. Another studio that I've worked with uses Marmoset to preview all their assets, so their validation procedure was to make sure that the assets looked the same in-game as they do within Marmoset
  11. I use option #1 and #2 in different situations If re-exporting is a pain, fix your build pipeline I have multiple template types for #2. Storing offsets from the start of the file is actually harder to deal with at runtime, because you can't just use the variable like a pointer, as "dereferencing" requires you to know the address of the beginning of the file. What I usually do is to store offsets from the current file-cursor (offsets from the "fake pointer" field itself), which is simpler to use at runtime. e.g. For offsets-from-cursor, I use this template which has overloaded operators to make it look just like a regular pointer at runtime. template<class T, class Y=s32> struct Offset { const T* NullablePtr() const { return offset ? Ptr() : 0; } T* NullablePtr() { return offset ? Ptr() : 0; } const T* Ptr() const { return (T*)(((u8*)&offset) + offset); } T* Ptr() { return (T*)(((u8*)&offset) + offset); } const T* operator->() const { return Ptr(); } T* operator->() { return Ptr(); } const T& operator *() const { return *Ptr(); } T& operator *() { return *Ptr(); } static uint DataSize() { return sizeof(T); } bool operator!() const { return !offset; } Offset& operator=( void* ptr ) { offset = ptr ? (Y)((u8*)ptr - (u8*)&offset) : 0; return *this; } Y offset; }; Or this one for offsets-from-beginning-of-file, which isn't compatible with operator overloading: template<class T, class Y=u32> struct Address { const T* Ptr(const void* base) const { return (T*)(((u8*)base) + address); } T* Ptr(const void* base) { return (T*)(((u8*)base) + address); } uint DataSize() const { return sizeof(T); } Y address; }; And this is probably over-complicated, but it works for option #1: template<class T> struct Pad32to64 { union{ T data; u64 pad; }; operator T&() { return data; } operator const T&() const { return data; } T operator -> () { return data; } const T operator -> () const { return data; } bool operator!() const { return !data; } operator bool() const { return !!data; } Pad32to64<T>& operator=( const T& o ) { data = o; return *this; } }; template<int size, class T> struct Select64Wrapper {}; template<class T> struct Select64Wrapper<4,T> { typedef Pad32to64<T> Type; eiSTATIC_ASSERT(sizeof(Type)==8); }; template<class T> struct Select64Wrapper<8,T> { typedef T Type; eiSTATIC_ASSERT(sizeof(Type)==8); }; template<class T> struct PadTo64 { typedef typename Select64Wrapper<sizeof(T), T>::Type Type; }; struct Test { PadTo64<int*>::Type myPointer; };
  12. OpenGL

    Lots of games from that era would trace a single ray downwards to find the floor under the player's feet, then find the lightmap-UV coordinates at that point, then sample the lightmap at those coordinates and use it as a constant ambient value for the player.
  13. Ooh good timing for this thread - Albrecht just posted some new slides! The original 2009 talk (still relevant!): http://harmful.cat-v.org/software/OO_programming/_pdf/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf And the 2017 version: https://docs.google.com/presentation/d/1ST3mZgxmxqlpCFkdDhtgw116MQdCr2Fax2yjd8Az6zM/edit#slide=id.g2398a4e2af_0_118 https://docs.google.com/presentation/d/1305-i5JZ98cLqXgVwzTyB1NQUjjBJ0_0diwU2diS_TI/edit#slide=id.p tl;dr:
  14. Ah ok I didn't get the last assumption -- that the big picture work of each buffer is independent, but contain internal dependencies. Yes, in theory multiple queues could be used to allow commands from another queue to be serviced while one queue is blocked doing some kind of barrier work. In practice on current hardware I don't know if this makes any difference though -- the "barrier work" will usually be made up of a command along the lines of "halt the front-end from processing any command from any queue until all write-through traffic has actually been flushed from the L2 cache to RAM"... In the future there may be a use for this though. I don't know if the Vulkan spec allows for it, but another use of multiple queues is prioritization. If a background app is using the GPU at the same time as a game, it would be wise for the driver to set the game's queues as high priority and the background app's queues as low priority. Likewise if your gameplay code itself uses GPU compute, you could issue it's commands via a "highest/realtime" priority queue which is configured to immediately interrupt any graphics work and do the compute work immediately -- which would allow you to perform GPGPU calculations without the typical one-frame delay. Again, I don't know if this is possible (yet) on PC's either. AFAIK, they're similar to "bundles" in D3D12 or display lists in GL, which are meant for saving on the CPU cost of repeatedly re-recording draw commands for a particular model every frame, and instead re-using a micro command buffer over many frames.
  15. If the work is truly independent, you won't have any barriers and could use one queue just fine.