Branching depends a little on VCS support for the feature (ie: whether the VCS makes it easy or difficult).
But at a minimum, you want a branch for each independently supported release of your project. That is, if you have "Game 2", and "Game 2 + Expansion" which are separate products built from the same codebase, and which you need to be able to patch and issue updates to, then you want to have each of these exist in their own branch off of the main codebase, so that fixes to the core codebase can be merged into each of these products independently. (Typically, these branches are created when each product is released, or perhaps a few days/weeks before, if you have people working on things in the codebase which you don't wish to go out with the initial release)
When working with a VCS which makes the use of branches tricky or painful (SourceSafe, svn, etc), then you'll probably be happiest maintaining a "release" branch for each released product, and doing your main development in "trunk", merging changes from "trunk" into the "release" branches if and when you need to release hotfixes for those releases.
If, on the other hand, you're working with one of the newer VCS systems which are built around branching and merging (git, Bazaar, Mercurial, etc), then you'll probably be happiest doing your development in a lot of short-lived "feature" branches. For example, if you're working on AI pathfinding, you can make a "pathfinding" branch and implement the code in there. When you finish, you merge that branch back into your main trunk of development, and (optionally) delete the branch you were working in. The benefit of this approach is that it lets you work on multiple tasks simultaneously, instead of needing to complete one task before starting on the next.
In my current home project (using git), I have five different feature branches active right now, working on various different features. Two of them are alternate approaches to doing the same thing (for profiling), two are experimental game mechanic ideas, and one is a big refactor of my AI systems, and is actually broken in such a way that the code won't compile right now. But it's committed in its feature branch for reference and for backup, and it being broken doesn't stop me from working on the other features; Those other feature branches (and the main development trunk as well) still compile and run correctly.
In my experience of big-team professional game development, we're still mostly stuck with older (and commercially supported) version control systems. Perforce is probably the most commonly used, followed by Subversion. Everywhere I've worked, we've had a 'trunk' branch, and then a separate 'release' branch for every deliverable (milestone/demo/release/etc). Occasionally someone will make a personal branch for some huge change they're making or testing, but this is extremely rare, and is usually for things like "converting the game to run with this different physics library" which may not actually go through to the released product.