Skip navigation

Monthly Archives: September 2012

GPU style: do both, then throw away the one you don’t want.

Overarchitecting is overplanning for things you don’t know the system will need or planning for things you don’t understand that you think the system needs.

When will programmers realize that the over-emphasis (Java-style) on the use of classes, with every variable as a class-level variable, is just moar globals!! ?

* A class is just a mini program
* class-members in a large program with many classes are equivalent to globals in a smaller program (with a single class)
* you still have to use locals correctly
* just because it’s a class doesn’t mean overusing class members is ok

Being a programmer today is like being a diamond in a sea of diamonds. There are so many of us out there.
But finding a good programmer is like finding an IF diamond, where most of us are at least SI-2.


Image from here

Don’t forget to key a std::map, you must overload operator< Maps use binary search and insertion defaults to being in-order!

Friend classes in C++: In C++ a friend is allowed to touch your privates.

C++: public inheritance: everybody knows who your parents are. private inheritance: You are ashamed of your parents so nobody knows that you even have a parent class.

Oct 11 2012 12:49pm

Experimenting is the only way you know your assumptions are wrong.

Oct 15 2012 1:47pm

.push_back() into an STL vector is not necessarily slower than .push_back() into an STL list. Even though you would think it should be ;).

Oct 16 2012 7:26pm

C preprocessor is the original duck typing

static is C’s private

Jan 4 2013

NEVER return references to entries (ie &theAddressOf) in a <vector> of value-types. return &vec[2] and storing that value as a pointer is a no no. If you push_back into the vector, all the references you took earlier may become invalidated. Always use simple integer indices into a <vector>, or better yet use a vector of pointer types (where that makes sense to do).

Jan 26 2013

Overloading is overrated. (Not always, but sometimes). Is having 4 methods named intersects in your mesh class easier to read, or is naming them intersectsSphere, intersectsBounds, intersectsTri better?

Jan 26 2013

Marking a member function const is for when that member function call should have no change on the state of the object, neither now nor further down the line. If a const member function wants to return a pointer to a member inside the object, then that member function should not be labelled const, because that pointer is a key to making changes to the object further down the line.

constify member functions only when the member function being called is a prostitute and the code calling the “prostitute” is a married politician. Neither the caller nor the callee want the rest of society to know what went down in that function, so no record is kept. The prostitute agrees that the politician will have no lasting impact on her internal state (ie she will not talk about the affair). const on a member function means “this call never happened, and nothing changed inside the prostitute, nor will anything _ever_ change inside the prostitute anywhere down the line as a result of the politician’s call”

A bit of a long one, but hey, it involved prostitutes.

Feb 7 2013

If you delete the front node of a <list> while reverse iterating, list.rend() changes. list.rend() is not “one past the end” as list.end() would be.

Mar 3 2013

If I had a dollar every time I realized this. There are 6 cases to consider when doing ray sphere intersections: 1) Fall short 2) Entry 3) Impale 4) COMPLETE CONTAINMENT (which usually is mistakenly counted as a miss) 5) Exit wound 6) Past. Usually ppl only check for Entry,Impale and Exit, but CONTAINMENT must also be considered a “hit” (short ray inside large sphere))

Mar 21 2013

Never use M_2_PI from <math.h>. It is 2/PI, NOT 2*PI like you’d expect.

Mar 21 2013 11:28p

Crazy numbers like 8589934592 are almost certainly OOB problems.
Can you spot the potential OOB problem in the line below?

for( int i = 0 ; i < pts.size(); i++ )
  nextPts.push_back( pts[i+i].pos + Matrix3f::rotation( alongLine, angle )*perpLine*r ) ;

The i+i was supposed to be i+1! Oops!

Mar 26 2013 11:58a

The ternary operator precedence level is VERY low!

int a = 5 + 1 ? 2 : 3 ;

The result for a is always 2. Why? because evaluation is happening as:

int a = (5 + 1) ? 2 : 3 ;

5+1=6 turns into TRUE, then the result is 2.

To make it evaluate properly, you would need

int a = 5 + (1 ? 2 : 3) ;

Mar 30 2013 11:32p

The Common Part Last rule

When naming multiple variables that have to do with the same thing, with similar names, such as playerVel for the player’s velocity and enemyVel for the enemy’s velocity, always put the different part first, not last. So never use a naming convention like velPlayer and velEnemy. It makes it far too easy to interchange the variables, especially when the similar string is long (for example, rollAccel and rollAccelRadar. I know of this rule and I just mistakenly used rollAccel where I wanted to use rollAccelRadar).

Another example: you’re more likely not to notice IconDashBaseship should be IconDashFighter than you would notice BaseshipIconDash is wrong and FighterIconDash should be there instead.

Apr 1 2013 3:26p

acos and asin on Mac are different than on PC.
acos( 1.0 + 1e-38 ) is nan on Mac because the arg is OOB strict domain of acos [-1,1]
This is not an April Fool’s joke.

Apr 10 2013 1:19a

Never use Google App Engine. Always use Amazon EC2 + RDS AWS instead.

Apr 24 2013 11:41p

Why encapsulate? It’s IMPOSSIBLE to remember all the implementation details of how a cohesive bunch of code works — even if you wrote it 3 weeks ago. THAT’S the reason for encapsulation. It says “Don’t fuck with this, it’s self managing”.

Apr 25 2013 1:19a

The factory pattern is most useful when an object type cannot and should not be used independently of some master managing class.

Fri May 3 2013 9:27p

Avoid remove_if for std::vector. It is rat poison, not syntactic sugar.

Use the following idiom for removal from a std::vector instead

for( vector<TYPE>::iterator iter = vec.begin() ; iter != vec.end() ; )
if( iter->shouldRemove )
  iter = vec.erase( iter ) ; // advances iter
else
  ++iter ; // don't remove

UI is 90% of a game.

Fri May 24 2013 11:30a

The best way to fast debugging is error checking and log messages for even the most outlandish cases. The condition that “shouldn’t happen” will always happen, at some point or another (usually do to programmer error!)

Great code has just the right amount of debug checks. No debug checks should be logically redundant or impossible (ie don’t set a flag=TRUE then immediately after check if that flag is still TRUE).

Mon May 27 2013 6:13

Exceptions are a retroactive way of handling error conditions. They kind of say “Yeah try this, if that doesn’t work, then do that.” Exceptions are too easily abused.

Sat Jun 1 2013 12:05p

Never use your extern variables inside a function that runs before main.

Tues Jun 4 2013 6:06a

I think I like writing stored procedures because the tables are like working with a couple of GB of global variables. Nothing better.

Fri Jun 7 2013 7:57p

There is a difference between brittle code and fragile code. Robust code survives all kinds of fail cases, at the extra overhead cost it takes to code that in. Brittle code breaks if something changes (for example, code that uses fixed value integer indexing for array access), but it might break in a safe way (check bounds before accessing). Fragile code is brittle code without the error checking.

Wed Jun 12 2013 3:14p

Betas are for suckers.

Thurs June 20 2013 5:31p

In good code, the author’s intentions clear.

If your intentions aren’t clear, use comments.

Sun June 30 2013 5:26p

The key to removing low area triangles (slivers AND small tris) is collapsing edges, not deleting triangles.

Tues July 1 2013 12:43p

Always clean up your mess in the kitchen before going to bed.
There is nothing, nothing worse than having to come back to your own mess.
Clean up your dirty code immediately (and by immediately I usually mean once the primary task the code is for is working correctly, clean it up).

Sun July 7 2013 6:32p

Calls to glVertexAttribPointer() MUST BE AFTER glBindBuffer().

Sun July 7 2013 6:33p

OpenGL is stupid.

Sun July 7 2013 6:33p

OpenGL is like a baby with OCD that cries inconsolably if you do something wrong. The biggest problem with OpenGL (unlike Direct3D’s detailed albeit hidden debug information) is that it doesn’t tell you what exactly you did wrong — just that it was an INVALID OPERATION. The order doesn’t always have logical reasoning — some state changes must be done just that way.

Thurs July 11 2013 3:16p

If you’re going to frustum cull, use SAT, but use a square based pyramid instead of a true frustum.

Sun July 14 2013 11:27a

The best way to develop is to “unit test” your code immediately as you produce it.
A “unit test” can be as simple as some specialized code that allows you to make sure your code is working by testing a wide variety of test cases all at once. For code such as raycasting, a good unit test is just draw a ray from the player’s forward vector and walk around the world, visually displaying the results.

Sun July 14 2013 7:56p

Use (int) for mathematical floor operation only if you’ve never heard of negative numbers.

Otherwise use floorf().

Sat July 27 2013 12:58p

Journals are my answer to dead code.

Mon July 29 2013 11:19p

Don’t consider writing good or efficient code off the bat as just a “premature optimization”. To gracefully birth an efficient, well written program, you have to make good choices about the code you write at every step.

Fri Aug 2 2013 1:28p

Never let your program crash-fail on purpose, even for situations that should “never happen”. Emit an error message and fail gracefully.

Fri Aug 16 2013 12:42p

I hate NaN.
If you change your compiler optimization settings, be prepared for NaN bugs that didn’t appear before, to suddenly fuck everything up now. For this reason, whenever dividing floating point numbers don’t check for NaN; but Prevent NaN from ever occurring instead.

Tues Aug 27 2013 10:47p

Long live Globals.
(Tongue in cheek)

Wed Aug 28 2013 12:56p

If you can’t avoid having hardcoded constants in your program, at least confine them to a single loader or initialization function and put them in an array as soon as possible.

Sat Aug 31 2013 4:05p

Using a boolean variable to a constructor to indicate whether something is switched on or off isn’t very good. Use a 2-number enumeration instead. What I mean is, the code MakeLine( EndCapStyle::Flat ) is much more readable than MakeLine( 0 ).

Sat Aug 31 2013 4:12p

1 is much more readable than true, TRUE, and YES.

Thurs Sept 5 2013 6:15p

Initializer lists: they will bite you in the ass.

Fri Sept 6 2013 7:00p

Every bug is an OPPORTUNITY to uncover a mistake in the engine. A CLUE. If you paste over it and bury it, then that is the opposite of what you should be doing, and you will end up with a mudball.

Tues Sept 10 2013 2:40p

Every concept has a “burn-in” period. That is, anything you work on / paradigm you work in will seem foreign and strange and difficult for the first day, two days, month, whatever the burn in period is. After the burn in period, where you intensively work on a thing, then things start to become familiar things start to make sense. So you can’t get things right the first time for a new concept, because of the burn in period. Plan to iterate, or be happy with whatever the first result looks like.

Tues Sept 10 2013 2:41p

The correct abbreviation for Tuesday is Tues. Not Tue.

Wed Sept 18 2013 4:14p

Floating point subtractions in a squareroot where the values can be close together in value is just asking for the enemy nanemesis to appear. sqrtf( 1.f - valuePossiblyCloseTo_1 ) should have CLAMPED valuePossiblyCloseTo_1 above by 1.f.

Wed Sept 18 2013 4:17p

nan is my enemy.

Mon Sept 23 2013 7:01p

Large amounts of boilerplate code mean something needs a redesign. Often, the API you’re working with is too primitive (doesn’t do enough with little code — e.g. WinAPI via C) or is just old and crufty (browsers ~ early 2000’s).

Large boilerplate means you should refactor the underlying API to do more with less code. Or, if you can’t do that, you should write an API on top of the base API so that the user can do more with less code.

Thurs Oct 3 2013 7:05p

Polling a datastructure for state is usually easier to maintain than changing state.

For example, say you have a UI element that displays the “mode” of a program, call it displayMode. Instead of having code that goes and updates the mode that will be displayed by displayMode, you should have displayMode look up the system mode when it needs to display. The reason this is easier to maintain is, if you don’t poll, then every place the system mode is changed, displayMode must also be updated.

This difficult to follow piece of advice brought to you by bobobobo

Thurs Oct 3 2013 11:31p

I think in computer programming you are continually learning about logic. When debugging, you are often finding a mistake in your logic. A bug is basically: there was a problem in your logic, or you forgot to take care of something.

Sun Oct 6 2013 12:13p

The fastest way to find a class or struct declaration is Xcode is not to cmd+click the class, rather do cmd+shift+F, then type ss classname or ct firstfewlettersofstructname, which will probably only turn up the correct class or struct declaration in search results.

Wed Oct 9 2013 2:55p

Developers or actual active software tech lead not being present at the interview is a red flag.

Thurs Oct 10 2013 8:28a

In software development, paranoia can save you a lot of time*.

* paranoid if statement and error checking means catching bugs early, as soon as they appear. Or, you could use unit tests.

Thurs Oct 10 2013 11:33a

ASSERT is bad. If your assert condition dissolves in release mode, you may have hidden bugs which only appear in release mode (usually uninitialized variable-type bugs).

Tues Oct 15 2013 2:21p

In polled environments (such as games) messages do not have to be passed via function calls. A message can be passed by setting a variable.

As long as the recipient of the message reads the variable frequently (and you have taken care of any threading issues), you can “send a message” by directly setting a variable that the recipient reads every frame.

Tues Oct 15 2013 6:11p

When writing a walkthrough or a tutorial, if you get stuck or end up not liking the progression, starting over, but drawing from the previous attempt, is a powerful way to do it. It clears your mind and relieves you of tension, but you can also draw from your previous work.

Sun Oct 20 2013 1:27p

Clean math==clean code

Tues Oct 22 2013 12:20p

Recruiters are like shoe sales-people, but they try to stuff random customers into random shoes. So you have to tell the recruiter if the shoe doesn’t fit. Otherwise, they’ll keep jamming away.

Tues Oct 22 2013 12:44p

Functors are a great way to customize object behavior per instance, where there isn’t a need to derive a custom class. onMove, onDie are two useful examples.

Fri Oct 25 2013 3:51p

Successful people have a grudge against unsuccessful people, where the successful try to keep the unsuccessful unsuccessful, believing success to only lie with the previously successful. Just a trend.

Fri Oct 25 2013 7:07p

Getting an inheritance hierarchy is a bitch to start out with, but it makes things unbelievably easy to extend and maintain later on if done right.

Sat Oct 26 2013 4:57p

Achievements are kind of like documentation for a game’s special features or hidden capabilities.

I think achievements done right, are like a list of “special” things you might not have known you could do in the gameworld. About discovering hidden things, or doing a task in a special but somehow preferred way (eg 50 x headshots). They shouldn’t be lame like the infamous Avatar 5x achievements you can get in one single place, and they shouldn’t feel like mind numbing work (e.g. “10 million kills”). They should just be like little tasks, akin to Quests from WOW or something similar.

Wed Nov 6 2013 9:09p

Using integer indices into a std::vector over iterators/std::list whenever reasonable (small array, not a lot of insertion into the middle).

Sat Nov 9 2013 12:03p

Meaningless loop counter names such as i, j, k are fine, as long as you are consistent with usage (eg I always use i as the outermost, j for inner loops, and k for triple-nested loops).

Don’t take the “variable names must be meaningful” bit of wisdom overboard. If you have a list of names and use i simply as the loop variable, then i is meaningless and doesn’t need a more meaningful name.

The context of the loop makes it completely clear that i will be the name-number in the list. Don’t use an extra long name like nameNo because you “think” you should.

Sat Nov 23 2013 3:49p

Say you had some branching code in a for loop

for( each checkersquare )
if( isRed ) color=Red ;
else color = Yellow ;

There are a number of ways to do this branchlessly.

The basic way to write branchless code is to write something like:

for( each checkersquare )
color = isRed * Red + (1-isRed) * Yellow

Or, for the specific case of red/yellow alternating patterns,

for( each checkersquare )
color = (1,0,0) + (1,1-isRed,0)

friend functions are just an admission by C++ that public members are sometimes desirable. Java will forever remain in denial.


Bobobobo’s links to non-bobobobo boboboboisms

8 things web designers should keep in mind

Jonathan Blow’s talk on How To Program Independent Games

Advertisements

Wacom replacement nibs are actually hard to find at reasonable prices in Canada.

For one, most of the dealers are in the US, so there’s really high shipping costs.

Anyway, I’ve found one of the best places to buy wacom tablet nibs is DIRECTCANADA. The secret to buying there is BUY OVER $50 OF GOODS AND YOU GET FREE SHIPPING.

So for example, here I’ve filled my cart with $50.09 of nibs. Magically, the shipping cost goes down from $20 to $0.00.

The best nibs for the 12WX and 21UX (the pens that come with both of those devices use the same nibs) are:

I learned this from this guy, but this is just a super short, clean simple bare class that loads a single file (called “explode1.caf”). Adapt it to your needs.

// SoundMan.h
@interface SoundMan : NSObject

- (void) loadSound: (NSString*) name ;
- (void) genTestSound ;
- (void) playSound ;

@end

// SoundMan.mm
@implementation SoundMan

  ALCcontext *alC ;
  ALCdevice *alD ;
  NSUInteger sourceId ;
  NSUInteger soundBufferId ;
  
  - (id) init
  {
    if( self = [super init] )
    {
      // initializes openal
      alD = alcOpenDevice(NULL);
      if( alD )
      {
        alC = alcCreateContext( alD, NULL ) ;
        alcMakeContextCurrent( alC ) ;
        
        // Generate a test sound
        [self genTestSound] ;
      }
    }
    
    return self ;
  }
  
  - (void) genTestSound
  {
    alGenSources( 1, &sourceId );
    
    // Load explode1.caf
    NSString *path = [[NSBundle mainBundle] pathForResource:@"explode1" ofType:@"caf" ];

    AudioFileID fileID ;
    NSURL *afUrl = [NSURL fileURLWithPath:path];
    
    // NON zero means err
    if( AudioFileOpenURL((__bridge CFURLRef)afUrl, kAudioFileReadPermission, 0, &fileID) )
      printf( "cannot open file: %s", [path UTF8String] ) ;
      
    UInt32 fileSize = [self audioDataSize:fileID];
    unsigned char *outData = (unsigned char*)malloc(fileSize);
    if( AudioFileReadBytes(fileID, FALSE, 0, &fileSize, outData) )
    {
      printf( "Cannot load sound %s", [path UTF8String] );
      return;
    }
    AudioFileClose(fileID);
    	
    alGenBuffers(1, &soundBufferId);
    alBufferData( soundBufferId, AL_FORMAT_STEREO16, outData, fileSize, 44100 );
    
    free( outData ) ;
  }
  
  - (void) playSound
  {
    // set source
    alSourcei( sourceId, AL_BUFFER, soundBufferId ) ;
    
    alSourcef( sourceId, AL_PITCH, 1 ) ;
    alSourcef( sourceId, AL_GAIN, 1 ) ;
    alSourcei( sourceId, AL_LOOPING, AL_FALSE ) ;
    
    ALenum err = alGetError();
    if( err ) // 0 means no error
      printf( "ERROR SoundManager: %d", err ) ;
    else
      alSourcePlay( sourceId );
  }
  
  - (UInt32) audioDataSize:(AudioFileID)fileId {
    UInt64 dataSize = 0;  // dataSize
    UInt32 ps = sizeof(UInt64); // property size
    if( AudioFileGetProperty(fileId, 
        kAudioFilePropertyAudioDataByteCount, &ps, &dataSize) )
      puts( "error retriving data chunk size" );
    return dataSize ;
  }
  

@end