一年内能精通c++做游戏吗?

知乎上回答了一个问题,因为写的挺长的,所以在这里也保存一份。

首先我不是大神所以这个只是我的个人见解,我放到这里只是为了留个档,今后回顾过去的自己的时候,也许会发现“哈哈我以前居然还这么想过!”

原贴:
http://www.zhihu.com/question/23933514

问题:

一年内能精通c++吗?

我是个大二即将升大三的学生,大一大二忙于社团活动,,6月6号社团换届了,又进入了期末复习周。希望能在暑假和大三弄好技术,因为专业为软件工程,大一大二的学习对c语言有一定基础,只是渣渣菜鸟。各位大神一路走来的,我想日后当游戏开发等工作,大三1年能精通c++吗,还需要学习其他什么,望指点迷津!

我的回答:

1年肯定无法“精通”,但是如果认真学能让你基本了解这玩意是个怎么回事,而且最关键的,能够帮助你进入游戏的行业。

这个东西没有什么答案,而更加取决于你想做什么。

你想成为一个程序员,还是更想成为一个游戏开发者,游戏设计师或者,或是只是喜欢游戏而想呆在游戏的圈子里。。。

游戏是一个很大的产业,每个环节都是可以做到最大最强从而改变世界的,而且每个环节都是密不可分没有什么孰优孰劣的。
上面不少回答角度是从资深程序员的角度来说的,但我个人认为开发一个好的游戏编程技术的重要性其实并不是最最重要的。美工,设定,故事背景,营销和项目管理方面都是非常重要的环节,相比之下程序的重要性有的时候并没有想象的那么重要。 这也是为什么很多人认为不懂编程也能做游戏的原因。有的游戏比如卡牌游戏,甚至不需要任何程序。

并且另一方面,对于玩家来说,他们只知道这个游戏是不是好玩,爱不爱玩,对于用的什么引擎,
你c++写的多厉害根本都是没有直接影响的。比如说minecraft这样的游戏也没有用到高大上的引擎,dota这样风靡世界的游戏当初也是用的魔兽争霸编辑器那个自带的脚本编辑器完成的。

另外涉及到游戏引擎的方面,也并不是如某些人所说,直接用别人的引擎的人就没有任何技术含量的。
第一,为自己的游戏开发专用的游戏引擎其实也并没有那么困难,只是涉及到的知识和需要的编程技术基础比较高,而且通常没有绝对的必要性,所以做的人不多,就好像你想盖房子会自己去造吊车吗。所以以上说国内游戏开发不好是因为没有好的吊车其实是不对的。
第二,往往大家把游戏引擎和好的游戏画面联系在一起,但其实好的游戏画面并不由引擎决定,就好像photoshop是一个创建图片的工具,但是有的人能用ps创造出惊人的作品,而有的人只能胡乱的涂鸦,同样的工具而最后产生的效果也可能是完全不同的。现在无论什么游戏引擎大部分都是基于当前已有的dx或者opengl图形渲染技术,就算某个引擎能够同时渲染更多的多边形,也不能代表其画面效果好,因为这些其实是取决于使用引擎的艺术家和技术美工的。当然如果一个游戏引擎的编辑工具非常的aritst friendly,对于画面的提升帮助是很大的。(于是问题便从开发游戏引擎变成了开发游戏引擎编辑工具)
第三,如何巧妙的利用游戏引擎也是一个非常有技术的事情,比如dota用的魔兽地图编辑器,弱到连让一个单位眩晕固定的秒数的功能都没有,所有的眩晕都是要靠创建一个隐藏的单位向敌人丢出一个隐藏的风暴之锤来实现的,即便如此,别人还是做出了一个非常成功的游戏。这样巧妙的利用一个引擎的各种功能,从而达到自己的设计目的,也是非常有技术含量的一件事情。在我眼里他们这些游戏程序员的工作并没有这些引擎程序员的工作低级,歧视他们的技术含量是可笑的。

另一方面,技术大部分的时候是因为需求而生的,比如我的游戏需要一个特殊的效果从而能够过表现某种艺术效果,但目前的市面上的引擎没有这样的功能(比如我想要我的游戏里任何东西都和果冻一样,并且有物理的效果), 那么这个时候熟练C++编程能力就变得非常的重要,因为涉及到修改原引擎功能的二次开发往往是使用c++的,并且对于各种优化的要求相当的高。(当然如果做不到,那么巧妙的修改设计方案避开这是永远存在的解决的办法,当然结果也是这将使得游戏变得平庸)

但是总的来说,花一年时间尽可能深入的学习c++是十分必要的,即使你今后不使用c++作为自己的编程工具。因为c++是目前大部分编程语言的基础,了解这些细节对于今后快速掌握另一种语言也是非常有益的,我c++水平不是大神,但我也能做到在从来没有看过任何java/c#的书籍的情况下看懂大段的java/C#代码并且自己写出一些常用的功能。

如果真的想从事游戏行业,在学习c++之外更重要的是,你需要尽早开始着手做一个你想做的游戏,无论多差先做出原形来,无论用什么样的办法多落后的技术,在中间你还会学到无数编程之外的东西。编程这个东西,一旦编程技术达到一定的水平,剩下的在编程语言之外的思想才是更重要的,就好像有的人写作的词汇特别的美,语法特别的对,但是内容空洞无力的话也是没有任何价值的。

Memory System

Memory System

Preface

I have spent a lot of time on this. I know nothing about this kind of stuff before. Also, I’m not that familiar with the C++ programming pattern. For PA2, the teacher’s code include a “singleton pattern” which confuse me a lot at the beginning. After I read the book and look for the website, I learnt that this is a very common design pattern, which help creating a global single object, I think I should take some time to learn something about that.

I have no idea how to deal with everything at the beginning, but some of my class mates finish their homework very fast, which makes me under pressure. I check my classmate Weixing An’s code, and under how the whole system working. I write my code referenced his code at first, and finally finished non-fixed block part for this assignment.

Two weeks later, after the midterm, I finally got some time to re-work on this Memory System. I read the book Memory Allocation Chapter of “Game Engine Architecture”, which helps me understand why we need to do this and how to adjust alignment. This time, I delete all my previous code, and write it from blank. I finally finished the normal and fixed block, which, I think, should follow the design better than others students. This assignment helps me a lot on the understanding of memory stuff.

Why we need memory system for game engine

malloc & new is slow

It’s a general-purpose facility, need to handle a lots of management.

In most operate system, call malloc and free must context-switch from user mode into kernel mode.

Count the usage of the memory

We need to count the current usage memory, allocation times, and peak memory usage for debugging and improving performance.

Deal with memory fragmentation

We can improve the problem of fragmentation, like design the memory as a stack or pool.

Memory System Design

Untitled-1

Tracking Block class

Store the information for each block. Include 4 pointer for two double links.

Heap class

Store the information for each heap. Include 2 pointer for Heap links.

HeapInfo

The structure in the Heap class storing the heap block information.

Mem class

Store the whole memory system information.

Store the head pointers point to Heap list head and Tracking Block list head.

MemInfo

The structure in the Mem class storing the memory system information.

Overload new & delete

To use our memory system, it means, we create our object into the memory where the tracking block is, and use tracking block to manage them.

So we need to overload original new and delete to offer these function.

Out new function should return the pointer that points to the raw memory in the tracking block.

Alignment

Data should be stored in alignment. The return pointer of the new operator should be aligned.

To adjust the data to be aligned, we should put an extra padding in each block’s tracking block.

And, when delete, we need a return pointer to get the Tracking block. We can store it in the 4 bytes just upon the raw memory.

head

Implement

Singleton Pattern

Only one Mem object is created in the whole system. We use a singleton pattern to design it.

Code Example

// create a singleton
class Mem
{
private:
   // Prevent default constructor, copy constructor, assignment operator from being called externally
      Mem(){};
      Mem( const Mem &in ){};
      Mem &operator = (const Mem &in){};
      ~Mem(){};

   // Magical internal function (singleton Keeper)
      static Mem	*privGetInstance( void );
};

// Magical internal function (singleton Keeper)
Mem* Mem::privGetInstance( void )
{
   static Mem mSingleton;
   return &mSingleton;
}

Windows Heap API

Get the memory from system memory.

HeapCreate

Creates a private heap object, return a win handle

HeapAlloc

Allocates a block of memory from a heap. The allocated memory is not movable.

new(pointer)object

“new” an object at the pointer’s memory location. Help us put the Heap head object in to the heap’s real memory location.

New & delete

There are two way to new and delete the system.

Normal Block

Call new

Get the needed size of allocation

Allocate the size + tracking block + padding

Put the tracking block into the memory

Link the linked list

Fixed Block

Allocate all heap when we create the heap

We should decide how large per block is before new

Create all the tracking block in the heap

Link all free block

When new, get the first node in the free memory linked list, unlink the node.

Return the aligned address.

When delete, relink the block in to the free list.

Linked list

Heap linked list

List links all heaps.

Heap tracking block linked list

Inside heap tracking block list.

Global tracking block linked list

A global linked list to track every allocation.

Notice:

When we destroy a Heap, we need to first remove all the tracking block, so the global list will not mess.

Need to rework

Here are something that I haven’t deal well when I do the assignment.

Need to rework when I have time.

All two kind of heap has a fixed size, how the deal with the issue, if I allocate more memory than it could offer.

The alignment might cost too many memories, need to make it better.

 

Jin Han

February 22, 2013

Dynamic Memory Allocation Summary

Dynamic Memory Allocation Summary

Preface

This is the hard part of the engine, it took me a little bit longer to understand the detail, and make me about 1 week behind the class.

In the assignment PA2, it’s important to understand this. Here are my reading notes for Game Engine Architecture Page 205 – 215.

Why we need to do this?

C++ malloc(), free(), new, delete operator is slow, because they have to meet any different situation and need to context-switch from user mode to kernel mode.

Solution

Implement custom dynamic allocation for games. Pre-allocate the memory at the beginning of the game.

A rule in game development:

Keep heap allocations to a minimum, and never allocate from the heap within a tight loop.

Memory Allocator Implementation

Several ways to implement the custom memory allocators

Stack-Based Allocators

Allocate a large contiguous block by malloc(), new, or declaring a global array of bytes.

A pointer to top of the stack is maintained.

Add/Delete from top, memory cannot be freed in an arbitrary order.

Double-Ended Stack Allocators

A single memory can contain two stack allocators.

One from top, one from down.

More efficient.

Pool Allocators

Perfect for the allocation of lots of small blocks of memory which are the same size.

Pre-allocate a large block of memory.

Pre-allocation size should be exact multiple of the size of the elements that will be allocated.

Each element in within the pool is added to a linked list of free elements.

Whenever an allocation request is made, simply grab the next free element off the free list and return it.

When an element is freed, track back it onto the free list.

Single linked list pointer can stored in the free block.

Aligned Allocators

Every data and variable has an alignment requirement.

All memory allocator must be capable of returning aligned memory blocks.

Implement

Allocate a little bit more memory than was actually requested.

Adjust the address of the memory block upward slightly so that it is aligned properly.

Mostly, additional bytes allocated is equal to the alignment.

Determine the adjustment bytes. (By using a mask calculation)

Add adjustment bytes to the original address.

Need to store original address in the byte immediately preceding the adjusted address for free().

Adjustment will never be more than 256 so only 1 bytes will be used.

Return the adjusted address.

Alignment Graph

ss

Code Example

// Aligned allocation function. IMPORTANT: 'alignment'
// must be a power of 2 (typically 4 or 16
void* allocateAligned(U32 size_bytes, U32 alignment)
{
   // Clients must call allocateUnaligned() and
   // freeUnaligned() if alignment == 1.
   ASSERT(alignment > 1);

   // Determine total amount of memory to allocate.
   U32 expandedSize_bytes = size_bytes + alignment;

   // Allocate an unaligned block & convert address to a
   // U32
   U32 rawAddress
      = (U32)allocateUnaligned(expandedSize_bytes);

   // Calculate the adjustment by masking off the lower
   // bits of the address, to determine how "misaligned"
   // it is.
   U32 mask = (alignment - 1);
   U32 misalignment = (rawAddress & mask);
   U32 adjustment = alignment - misalignment;

   // calculate the adjusted address, and return as a
   // pointer.
   U32 alignedAddress = rawAddress + adjustment;

   // Store the adjustment in the four bytes immediately
   // preceding the adjusted address that we're
   // returning.

   U32* pAdjustment = (U32*)(alignedAddress - 4);
   *pAdjustment = adjustment;
   // adjustment will never be more than 256
   // so only 1 bytes will be used

   return (void*)alignedAddress;
}

// corresponding freeAligned()
void freeAligned(void* p)
{
   U32 alignedAddress = (U32)p;
   U8* pAdjustment = (U8*)(alignedAddress - 4);
   U32 adjustment = (U32)*pAdjustment;

   U32 rawAddress = alignedAddress - adjustment;

   freeUnaligned((void*)rawAddress);
}

 

Single-Frame and Double-Buffered Memory Allocators

For temporary data storage for games.

Single-Frame Allocators

A block of memory can be allocated and used the only in the current frame. Clear every frame.

Benefits

Allocated memory needn’t ever be freed

Negative

Requires a reasonable level of discipline on the part of the programmer.

Only valid on current frame.

Never cache a pointer to a single-frame memory block across the frame boundary.

Double-Buffered Allocators

A block of memory can be allocated on frame i to be used on frame (i+1);

Useful for caching the results of asynchronous processing on a multicore game console.

 

Jin Han

February 18, 2013

Object Layout in Memory

Object Layout in Memory

Preface

Teacher mentioned this in class, but I don’t understand at all about these stuff at that time.

So I read the Game Engine Architecture page 121 – 128, which helps me a lot. Here is the reading notes.

Rules

Put larger data type on the top in struct to fit the alignment. Add explicit padding to the end of the struct manually.

Alignment and Packing

When small data members are interspersed with larger members, it will leave “holes” in the layout.

inse

Many modern CPU can only read and write aligned block of data.Every data type has a natural alignment which must be respected in order to permit the CPU to read and write memory effectively.

Good to do

Re-arrange the members by putting largest member on the top.

Add explicit padding to the end of the struct manually, to make the wasted space visible and explicit.

e

It’s better to make the padding visible by manually add them to the end._pad[0] and _pad[1] will be add automatically by compiler in to struct anyway in order to guarantees all subsequent element in an array will be aligned properly.

Memory Layout of C++ Classes

Inheritance

v

When B inherits from class A, B’s data member simply appear immediately after A’s in memory.

Each new derived class tacks its data member on at the end.

Game programmer usually prefer to avoid multiple inheritance altogether.

Virtual functions

If a class contains or inherits one or more virtual functions, then four additional bytes are added to the class layout, typically at the very beginning of the class’ layout.

These four pointers are called the virtual table pointer or vpointer, because they contain a pointer to a data structure known as the virtual function table or vtable.

The vtable contains pointers to all the virtual functions that it declares or inherits. Every concrete class has its own virtual table, every instance of that class has a pointer to it, stored in its vpointer.

The heart of polymorphism

Example of a Circle class inherits the Shape class

Graph

graph

Code Example

 

class Shape
{
public:
   virtual void SetId(int id) { m_id = id; }
   int          GetId() const { return m_id;}
   virtual void Draw() = 0; //pure virtual - no impl.

privite:
   int m_id;
};

class Circle : public Shape
{
public:
   void     SetCenter (const Vector3& c) { m_center=c; }
   Vector3  GetCenter() const { return m_center; }
   void     SetRadius(float r) { m_radiuis = r; }
   float    GetRadius() const { return m_radius;}

   virtual void Draw()
   {
      // code to draw a circle
   }

private:
   vector3 m_center;
   float m_radius;
};

 

Jin Han

February 17, 2013

PCS Tree Summary

PCS Tree Summary

Preface

For the first assignment, I learn how to deal with the test. Actually I just took Data structure class last quarter, so everything here is not that unfamiliar with me. It just complex a lit bit from the stuff we learnt from data structure class. I haven’t spent much time on the assignment. But this is still a good recall of my understanding of data structure.

What is a PCS Tree?

Definition

Parent – Child –Sibling Tree

PCS Tree is a data structure used to store a hierarchical tree in form of a set of linked nodes. Each node in the tree can be a Parent, a Child or a Sibling of another node. Each parent node has one link to its first child only. The child nodes of a node are connected as a one-way linked list.

P for Parent

A pointer link to the parent

C for Child

A pointer link to the child

S for Sibling

A pointer link to siblings

Application

There are maybe various applications of PCS Tree. You may want to use it to store a directory structure inside an archive. Or it can be used to store data in a GUI control like QTreeView. In games, PCS Tree can be used to store a hierarchy of 3D objects. Each node will represent one object so that transformation applied to an object will affects all its child objects and grandchild objects.

Structures

PCSNode Graph:

1

PCSNode Method:

// constructor
PCSNode();
   
// copy constructor
PCSNode(const PCSNode &in );

// Specialize Constructor
PCSNode( PCSNode * const inParent, PCSNode * const inChild, 
         PCSNode * const inSibling, const char * const inName);
PCSNode( const char * const inName );


// destructor
~PCSNode();

// assignment operator
PCSNode &operator = (const PCSNode &in);

// accessors
void setParent( PCSNode * const in );
void setChild( PCSNode * const in );
void setSibling( PCSNode * const in );
PCSNode *getParent( void ) const;
PCSNode *getChild( void ) const;
PCSNode *getSibling( void ) const;

// name
PCSNodeReturnCode setName(const char * const inName );
PCSNodeReturnCode getName(char * const outBuffer, int sizeOutBuffer ) const;

// dump
void print() const;
void dumpChildren() const;
void dumpSiblings() const;
void dumpTree() const;

// get number of children/siblings
int getNumSiblings() const;
int getNumChildren() const;       
int getNumLevels() const;
int getNumNodes() const;
int getNumNodesRecursion() const;

// get level
int getLevel() const;

// remove
void removeAllNodes();
void removeAllNodesRecursion();

PCSTree Graph:

PCSTree

PCSTree Method:

// constructor
PCSTree();

// destructor
~PCSTree();

// get Root
PCSNode *getRoot( void ) const;

// insert
void insert(PCSNode * const inNode, PCSNode * const parent);

// remove
void remove(PCSNode * const inNode);

// get info
void getInfo( PCSTreeInfo &infoContainer );
void dumpTree( ) const;

 

Features

Every node has hierarchy.

Adding node is very fast.

Easy to use and implement.

Seeking and removing speed is OK.

Can get parents and children fast.

Good for storing objects.

 

Jin Han
February 16, 2013

Game Engine Study – Object System Summary

Object System Summary

Preface

Sorry that I was not able to create the blog in time, because I’m kind of stack on some problems at the beginning of this term. I move very slowly because I was still not that familiar with C++. It becomes much better after several weeks. Now I get some time to make up for my missing Blog. I decided to write a summary for each topic we talked in class as a review for the course. The first is the object system.

What is an Object?

Definition

Object is the container to holds data, it associates like object together.

It can hold different types of data. They generally don’t care what type of data to hold.

Object Services

Provide manipulation to the object system.

Creational: Insertion, Removing.

Searching/Retrieval: Walking the list, finding particular object.

Unique Identifiers

Help to identify which object is what.

String Names, performance is low.

Unique numbers, better choice, can be hash from a string name.

Ownership

Think about who should own the data before declaration.

Embedded system should know:

Where every bytes of data are all times.

Number of objects and types.

Shared memory pools. (What is available, used or free?)

Need to be centralized

Ownership in manager.

Group by functionality / types.

Resource managers.

Resource pools.

Object Relationships

Related by:

List, Arrays, Associations, Hierarchy.

Memory, performance usage needs to be finite and understood.

Serialization of objects

For Networking, Load/Save, Debugging.

Unique the type of data be stored.

Make the specific data types.

Cloning

Deep copies with complete sets
Semi static data sets with instance data that allows the data to be specialized

 

Jin Han
February 15, 2013