遊戲架構其一:常用工具集合

<span style="font-size:18px;">2D遊戲中寫出3D效果沒什麼神祕的,我們都會有的~~~</span>
<span style="font-size:18px;">常用工具集合:</span>
<span style="font-size:18px;">I. 遊戲中的幾何圖形:點和矩形相關操作</span>
<span style="font-size:18px;">#pragma once
/* 
 #pragma once
        DO YOUR FAVOURITE
 等價於下面
 #ifndef XXX
 #define XXX
         do something
 #endif

 
 #define XXX
        DO SOMETHING
 #undef XXX //取消宏定義

 
 #ifdef XXX
  do something
 #endif
 
 
 當然了我們還可以對struct使用自定義的內存數據對齊方式
 如:
 情況一:
 struct test {
    char a;
    int  b;
 };
 情況二:
 #pragma pack(push,1) 1的這個參數位置必須是2的冪 1 2 4 8 16 .....  as so on
 struct test {
    char a;
    int  b;
 };
 #pragma pack(pop)

 struct test t;
 情況一:
 sizeof(t) = 8;
 情況二:
 sizeof(t) = 5;
 
 以上有時可以幫助我們節省寶貴的內存資源

 
 另外在class聲名的內部
 函數的聲名+實現=內斂函數 不需要添加inline關鍵字機器自動優化
 當然有些函數的實現較複雜時即使使用inline也會被編譯器“優化”爲不是內斂的
*/


#include <math.h>

//---------------------------------------------------------------------------------------------------------
// This class represents a single point in 2D space
//---------------------------------------------------------------------------------------------------------
class Point
{
public:
	long x, y;
	
	// 構造函數 class聲名內部實現的函數全是inline funcs
	Point(void) { x = y = 0; }
	Point(const long newX, const long newY) { x = newX; y = newY; }
	Point(const Point& newPoint) { x = newPoint.x; y = newPoint.y; }
	Point(const Point* pNewPoint) { x = pNewPoint->x; y = pNewPoint->y; }

	// 賦值函數
	Point& operator=(const Point& newPoint) { x = newPoint.x; y = newPoint.y; return (*this); }
	Point& operator=(const Point* pNewPoint) { x = pNewPoint->x; y = pNewPoint->y; return (*this); }

	// 加減等函數 必須返回這個數的引用 否則+= -=無效
	Point& operator+=(const Point& newPoint) { x += newPoint.x; y += newPoint.y; return (*this); }
	Point& operator-=(const Point& newPoint) { x -= newPoint.x; y -= newPoint.y; return (*this); }
	Point& operator+=(const Point* pNewPoint) { x += pNewPoint->x; y += pNewPoint->y; return (*this); }
	Point& operator-=(const Point* pNewPoint) { x -= pNewPoint->x; y -= pNewPoint->y; return (*this); }
    // 使用上面的拷貝構造函數
	Point operator+(const Point& other) { Point temp(this); temp += other; return temp; }
    // operator overloading(操作符重載)
	Point operator-(const Point& other) { Point temp(this); temp -= other; return temp; }
    // operator casting(操作隱式轉換)***** inline 有無都一樣 *****
    inline Point operator-(const Point& left, const Point& right) { Point temp(left); temp -= right; return temp; }
	// 比較函數
	bool operator==(const Point& other) const { return ((x == other.x) && (y == other.y)); }
	bool operator!=(const Point& other) const { return ((x != other.x) || (y != other.y)); }

	// 訪問內部成員變量 可用於Lua
	long GetX() const { return x; }
	long GetY() const { return y; }
	void SetX(const long newX) { x = newX; }
	void SetY(const long newY) { y = newY; }
	void Set(const long newX, const long newY) { x = newX; y = newY; }
	
    // 距離原點的長度
	float Length() const { return sqrt((float)(x*x+y*y)); }
};</span>

Note:以上是一個點的相關操作,我想在遊戲中沒有比這個更基礎的基礎了。你認爲呢?

接着點的相關操作,自然會想到矩形的相關操作,畢竟這方面需求也是剛性的,下面是一個矩形的相關操作:

<span style="font-size:18px;">//-------------------------------------------------------------------------------------------------------
// This class represents a rectangle
//-------------------------------------------------------------------------------------------------------
class Rect
{
public:
    // 代表一個矩形的最上面 最左邊 最右面 和最下面的 相當於 Ymax , Xmin , Xmax, Ymin
    long top,left,right,bottom;
    

	enum RectCorner { INVALID_CORNER = 0, TOPLEFT_CORNER, TOPRIGHT_CORNER, BOTTOMLEFT_CORNER, BOTTOMRIGHT_CORNER };
	
	// 構造矩形 包括默認的構造,直接的構造,引用拷貝構造,指針拷貝構造和利用點來構造
	Rect(void) { left = top = right = bottom = 0; }
	Rect(long newLeft, long newTop, long newRight, long newBottom) { Set(newLeft,newTop,newRight,newBottom); }
	Rect(const Rect& newRect) { left = newRect.left; top = newRect.top; right = newRect.right; bottom = newRect.bottom; }
	Rect(Rect* pNewRect) { left = pNewRect->left; top = pNewRect->top; right = pNewRect->right; bottom = pNewRect->bottom; }
	Rect(const Point& topLeft, const Point& bottomRight) { top = topLeft.y; left = topLeft.x; right = bottomRight.x; bottom = bottomRight.y; }

	// 操作符重載
	Rect& operator=(const Rect& newRect) { left = newRect.left; top = newRect.top; right = newRect.right; bottom = newRect.bottom; return (*this); }
	Rect& operator=(const Rect* pNewRect) { left = pNewRect->left; top = pNewRect->top; right = pNewRect->right; bottom = pNewRect->bottom; return (*this); }
	Rect& operator+=(const Rect& newRect) { left += newRect.left; top += newRect.top; right += newRect.right; bottom += newRect.bottom; return (*this); }
	Rect& operator-=(const Rect& newRect) { left -= newRect.left; top -= newRect.top; right -= newRect.right; bottom -= newRect.bottom; return (*this); }
	Rect& operator+=(const Rect* pNewRect) { left += pNewRect->left; top += pNewRect->top; right += pNewRect->right; bottom += pNewRect->bottom; return (*this); }
	Rect& operator-=(const Rect* pNewRect) { left -= pNewRect->left; top -= pNewRect->top; right -= pNewRect->right; bottom -= pNewRect->bottom; return (*this); }
	Rect operator+(Rect& other) { Rect temp(this); temp += other; return temp; }
	Rect operator-(Rect& other) { Rect temp(this); temp -= other; return temp; }

	// 矩形對一個點做運算
	Rect& operator+=(const Point& delta) { left += delta.x; top += delta.y; right += delta.x; bottom += delta.y; return (*this); }
	Rect& operator-=(const Point& delta) { left -= delta.x; top -= delta.y; right -= delta.x; bottom -= delta.y; return (*this); }
	Rect& operator+=(const Point* pDelta) { left += pDelta->x; top += pDelta->y; right += pDelta->x; bottom += pDelta->y; return (*this); }
	Rect& operator-=(const Point* pDelta) { left -= pDelta->x; top -= pDelta->y; right -= pDelta->x; bottom -= pDelta->y; return (*this); }
	Rect operator+(Point& delta) { Rect temp(this); temp += delta; return temp; }
	Rect operator-(Point& delta) { Rect temp(this); temp -= delta; return temp; }

	// 矩形是否相等
	bool operator==(const Rect& other) const { return ((left == other.left) && (top == other.top) && (right == other.right) && (bottom == other.bottom)); }
	bool operator!=(const Rect& other) const { return (!((left == other.left) && (top == other.top) && (right == other.right) && (bottom == other.bottom))); }

	// collision 碰撞檢測
    // 矩形和矩形是否有重合 即判斷是否Collide
	bool Collide(const Rect& other) const { if (other.bottom < top || other.right < left || other.left > right || other.top > bottom) {return false;} else {return true;} }
    // 矩形和一個點 判斷點與矩形的關係
	bool Collide(const Point& p) const { if (p.x > left && p.x < right && p.y < bottom && p.y > top) {return true;} else {return false;} }
    // 矩形是否包含關係
	bool IsWithin(const Rect& other) const { return ( (left >= other.left && top >= other.top && right <= other.right && bottom <= other.bottom) || (other.left >= left && other.top >= top && other.right <= right && other.bottom <= bottom) ); }
    // 矩形是否包含點
	bool IsWithin(const Point& other) const { return (other.x >= left && other.x <= right && other.y >= top && other.y <= bottom); }
    // 創建的矩形是否是合法的   判斷矩形是否爲空矩形(姑且把原點作爲空矩形)
	bool IsValid(void) const { return (left <= right && top <= bottom); }
	bool IsNull(void) const { return (left == 0 && right == 0 && top == 0 && bottom == 0); }
	
	// convenience functions
    // 相對與原來的X方向移動dx 相對與原來的Y方向移動dy 相對值
	void ShiftX(int dx) { left += dx; right += dx; }
	void ShiftY(int dy) { top += dy; bottom += dy; }
    // 移動到x, 移動到y 絕對值
	void SetX(int x) { int dx = x - left; ShiftX(dx); }
	void SetY(int y) { int dy = y - top; ShiftY(dy); }

	// accessors for Lua
	long GetTop(void) const { return top; }
	long GetLeft(void) const { return left; }
	long GetRight(void) const { return right; }
	long GetBottom(void) const { return bottom; }
	Point GetCenter(void) const
	{
		if (IsValid())
			return (Point(left + ((right - left) / 2), top + ((bottom - top) / 2)));
		GCC_ERROR("Attempting to get the center of an invalid Rect");
		return Point();
	}
    // 矩形四個角的點
    Point TopLeft(void) const { return Point(left, top); }
    Point TopRight(void) const { return Point(right, top); }
    Point BottomLeft(void) const { return Point(left, bottom); }
    Point BottomRight(void) const { return Point(right, bottom); }
    long GetWidth(void) const { return right - left; }
    long GetHeight(void) const { return bottom - top; }
	void Set(long newLeft, long newTop, long newRight, long newBottom) { left = newLeft; top = newTop; right = newRight; bottom = newBottom; }
    // 相對與ShiftX(int dx)他是全部移動
	void MoveDelta(long dx, long dy) { left += dx; top += dy; right += dx; bottom += dy; }
	void MoveDelta(const Point deltaPoint) { MoveDelta(deltaPoint.x, deltaPoint.y); }
	void MoveTo(long x, long y)
	{
		long width = right - left;
		long height = bottom - top;
		left = x;
		right = left + width;
		top = y;
		bottom = top + height;
	}
	void MoveTo(const Point& point) { MoveTo(point.x, point.y); }
};</span>
<span style="font-size:18px;"><span style="font-size: 18px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">以上就是一個點和矩形的相關操作了。</span>
</span>


II. 一些有用的模版:Some useful templates

C++的信徒對模版估計是愛恨交加了,以下是一些常見模版的實現:

<span style="font-size:18px;">#pragma once
//------------------------------------------------------------------------------
// singleton template manages setting/resettting global variables.
//------------------------------------------------------------------------------</span>
<span style="font-size:18px;">template <class T>
</span>
<span style="font-size:18px;">class singleton
{
	T m_OldValue;
	T* m_pGlobalValue;

public:
	singleton(T newValue, T* globalValue)
	{ 
		m_pGlobalValue = globalValue;
		m_OldValue = *globalValue; 
		*m_pGlobalValue = newValue;
	}

	virtual ~singleton() { *m_pGlobalValue = m_OldValue; }
};</span>
以上是一個單體模版,負責設置或者重新設置全局變量。

<span style="font-size:18px;">template <class Type>
shared_ptr<Type> MakeStrongPtr(weak_ptr<Type> pWeakPtr)
{
    if (!pWeakPtr.expired()) //判斷這個若引用的指針是否已經被delete
        return shared_ptr<Type>(pWeakPtr);
    else
        return shared_ptr<Type>();
}</span>

以上是將一個弱引用指針轉化爲強引用,若引用和強引用的區別是:( 沒有翻譯,仔細看還是沒問題的)

Weak pointers just "observe" the managed object; they don't "keep it alive" or affect its lifetime. Unlike shared_ptrs, when the last weak_ptr goes out of scope or disappears, the pointed-to object can still exist because the weak_ptrs do not affect the lifetime of the object - they have no ownership rights. But the weak_ptr can be used to determine whether the object exists, and to provide a shared_ptr that can be used to refer to it. The definition of weak_ptr is designed to make it relatively foolproof, so as a result there is very little you can do directly with a weak_ptr. For example, you can't dereference it; neither operator* nor operator-> is defined for a weak_ptr. You can't access the pointer to the object with it - there is no get() function. There is a comparison function defined so that you can store weak_ptrs in an ordered container; but that's all.

以下是實現了一個可選初始化的模版:

<span style="font-size:18px;">#include <new>
class optional_empty { };

template <unsigned long size> // size表示存儲數據的長度
class optional_base
{
public:
    // Default - invalid.
    optional_base() : m_bValid(false) { }

    optional_base & operator = (optional_base const & t)
    {
		m_bValid = t.m_bValid;
		return * this;
    }
	//Copy constructor
    optional_base(optional_base const & other) : m_bValid(other.m_bValid)  { }

	//utility functions
    bool const valid() const		{ return m_bValid; }
    bool const invalid() const		{ return !m_bValid; }

protected:
    bool m_bValid;
    char m_data[size];  // storage space for T
};

</span>
<span style="font-size:18px;">/* 使用情況:</span>
<span style="font-size:18px;">函數返回無效對象 有時根據某個條件去查找對象時,如果查找不到對象時就會返回一個無效值,這不表明函數執行失敗,而是表明函數正確執行了,但是結果卻不是有用的值,這時就可以返回一個未初始化的optional對象出去,在外面判斷這個optional對象是否有效對象是否被初始化,如果沒有被初始化就表明這個值是無效的</span>
<span style="font-size:18px;">template <class T>
class optional : public optional_base<sizeof(T)>
{</span>
<span style="font-size:18px;">private:
    T const * const GetT() const { return reinterpret_cast<T const * const>(m_data); }
    T * const GetT()	         { return reinterpret_cast<T * const>(m_data);}
    void construct(T const & t)  { new (GetT()) T(t); }//使用GetT()返回的指針數據來初始化T(t)
    void destroy()               { GetT()->~T(); }</span>
<span style="font-size:18px;">public: </span>
<span style="font-size:18px;">// Default - invalid.</span>
<span style="font-size:18px;">   optional() { } </span>
<span style="font-size:18px;">   optional(T const & t) { construct(t); m_bValid = (true); } </span>
<span style="font-size:18px;">    optional(optional_empty const &) { }</span>

          // 運算符函數


<span style="font-size:18px;">optional & operator = (T const & t)
    {
        if (m_bValid)
        {
            * GetT() = t;
        }
        else
        {
            construct(t);
	    m_bValid = true;	// order important for exception safety.
        }

        return * this;
    }

	//Copy constructor
    optional(optional const & other)
    {
		if (other.m_bValid)
		{
			construct(* other);
            m_bValid = true;	// order important for exception safety.
		}
    }

    optional & operator = (optional const & other)
    {
		GCC_ASSERT(! (this == & other));	// don't copy over self!
		if (m_bValid)
		{						// first, have to destroy our original.
			m_bValid = false;	// for exception safety if destroy() throws.
								// (big trouble if destroy() throws, though)
			destroy();
		}

		if (other.m_bValid)
		{
			construct(* other);
			m_bValid = true;	// order vital.

		}
		return * this;
    }


    bool const operator == (optional const & other) const
    {
	if ((! valid()) && (! other.valid())) { return true; }
	if (valid() ^ other.valid()) { return false; }
        	return ((* * this) == (* other));
    }

    bool const operator < (optional const & other) const
    {
	// equally invalid - not smaller.
	if ((! valid()) && (! other.valid()))   { return false; }

	// I'm not valid, other must be, smaller.
	if (! valid())	{ return true; }

	// I'm valid, other is not valid, I'm larger
	if (! other.valid()) { return false; }

</span>
<span style="font-size:18px;"><span style="white-space:pre">	</span>return ((* * this) < (* other));
    }

    ~optional() { if (m_bValid) destroy(); }

    T const & operator * () const			{ GCC_ASSERT(m_bValid); return * GetT(); }
    T & operator * ()	  					{ GCC_ASSERT(m_bValid); return * GetT(); }
    T const * const operator -> () const	{ GCC_ASSERT(m_bValid); return GetT(); }
    T	* const operator -> ()			{ GCC_ASSERT(m_bValid); return GetT(); }

    //This clears the value of this optional variable and makes it invalid once again.
    void clear()
    {</span>
<span style="font-size:18px;">	if (m_bValid)</span>
<span style="font-size:18px;">	{
		m_bValid = false;
		destroy();
	}
    }

    //utility functions
    bool const valid() const		{ return m_bValid; }
    bool const invalid() const		{ return !m_bValid; }
};
</span>

以下是實現了範型對象工廠,這在其他場合非常有用比如MMORPG中:

<span style="font-size:18px;"><span style="font-size:18px;">template <class BaseType, class SubType>
BaseType* GenericObjectCreationFunction(void) { return new SubType; }

template <class BaseClass, class IdType>
class GenericObjectFactory
{
    typedef BaseClass* (*ObjectCreationFunction)(void);
    std::map<IdType, ObjectCreationFunction> m_creationFunctions;

public:
    template <class SubClass>
    bool Register(IdType id)
    { 
        auto findIt = m_creationFunctions.find(id);
        if (findIt == m_creationFunctions.end())
        {
            m_creationFunctions[id] = &GenericObjectCreationFunction<BaseClass, SubClass>;  // insert() is giving me compiler errors
            return true;
        }

        return false;
    }

    BaseClass* Create(IdType id)
    {
        auto findIt = m_creationFunctions.find(id);
        if (findIt != m_creationFunctions.end())
        {
            ObjectCreationFunction pFunc = findIt->second;
            return pFunc();
        }

        return NULL;
    }
};</span></span>
第一個模版根據基類和子類來創建一個對象,下面的模版是一個生產範型對象的工廠。在其二(事件管理)中將看到範型對象工廠的威力。

III. 自定義字符串操作:

<span style="font-size:18px;">/*
 String.h 頭文件定義
 */

#pragma once

/* int最大數的十進制的數字個數  轉化爲String時添加一個位存儲'\0'
 max number of digits in an int (-2147483647 = 11 digits, +1 for the '\0') */
#define MAX_DIGITS_IN_INT 12  

typedef std::vector<std::string> StringVec;

#ifdef   UNICODE
typedef   wchar_t   TCHAR;
#else
typedef   unsigned   char   TCHAR;
#endif
typedef   unsigned   char   CHAR;
typedef   unsigned   wchar_t   WCHAR;

enum HRESULT {
    E_INVALIDARG,
    E_FAIL,
    S_OK,
};


/* Removes characters up to the first '\n'
 刪除字符,直到達到第一個'\ n' 即將源字符分解爲去掉第一個\n的兩個字符
 
 string? wstring?
 std::string is a basic_string templated on a char, and std::wstring on a wchar_t.
 
 char vs. wchar_t
 char is supposed to hold a character, usually a 1-byte character. wchar_t is supposed to hold a wide character, and then, things get tricky: On Linux, a wchar_t is 4-bytes, while on Windows, it's 2-bytes */
extern void RemoveFirstLine(std::wstring &src, std::wstring &result);

// Removes leading white space 刪除前面的空格直到非空格
extern void TrimLeft(std::wstring &s);

// Counts the number of lines in a block of text
extern int CountLines(const std::wstring &s);

// Does a classic * & ? pattern match on a file name - this is case sensitive!
extern BOOL WildcardMatch(const char *pattern, const char *filename);

// converts a regular string to a wide string
extern void StringToWideString(const std::string& source, std::wstring& outDest);

extern HRESULT AnsiToWideCch( WCHAR* dest, const CHAR* src, int charCount);  
extern HRESULT WideToAnsiCch( CHAR* dest, const WCHAR* src, int charCount);  
extern HRESULT GenericToAnsiCch( CHAR* dest, const TCHAR* src, int charCount); 
extern HRESULT GenericToWideCch( WCHAR* dest, const TCHAR* src, int charCount); 
extern HRESULT AnsiToGenericCch( TCHAR* dest, const CHAR* src, int charCount); 
extern HRESULT WideToGenericCch( TCHAR* dest, const WCHAR* src, int charCount);

extern std::string ToStr(int num, int base = 10);
extern std::string ToStr(unsigned int num, int base = 10);
extern std::string ToStr(unsigned long num, int base = 10);
extern std::string ToStr(float num);
extern std::string ToStr(double num);
extern std::string ToStr(bool val);
extern std::string ToStr(const Vec3& vec);

extern std::string ws2s(const std::wstring& s);
extern std::wstring s2ws(const std::string &s);

// Splits a string by the delimeter into a vector of strings.  For example, say you have the following string:
// std::string test("one,two,three");
// You could call Split() like this:
// Split(test, outVec, ',');
// outVec will have the following values:
// "one", "two", "three"
void Split(const std::string& str, StringVec& vec, char delimiter);


#pragma warning(push)
#pragma warning(disable : 4311)//省略4311類型的警告

// A hashed string.  It retains the initial (ANSI) string in addition to the hash value for easy reference.
class HashedString
{
public:
	explicit HashedString( char const * const pIdentString )
		: m_ident( hash_name( pIdentString ) )
	, m_identStr( pIdentString )
	{
	}

	unsigned long getHashValue( void ) const
	{

		return reinterpret_cast<unsigned long>( m_ident );
	}

	const std::string & getStr() const
	{
		return m_identStr;
	}

	static
	void * hash_name( char const *  pIdentStr );

	bool operator< ( HashedString const & o ) const
	{
		bool r = ( getHashValue() < o.getHashValue() );
		return r;
	}

	bool operator== ( HashedString const & o ) const
	{
		bool r = ( getHashValue() == o.getHashValue() );
		return r;
	}

private:

	// note: m_ident is stored as a void* not an int, so that in
	// the debugger it will show up as hex-values instead of
	// integer values. This is a bit more representative of what
	// we're doing here and makes it easy to allow external code
	// to assign event types as desired.

	void *             m_ident;
	std::string		   m_identStr;
};
//Remove the warning for warning #4311...
#pragma warning(pop)<span style="color:#ff0000;">
</span></span></span>

<span style="font-size:18px;"><span style="font-size:18px;">#include "GameCodeStd.h"
#include "string.h"

using std::string;

// Remove all leading whitespace
void TrimLeft(std::wstring &s)
{
	// find first non-space character
	int i = 0;
	int len = (int)s.length();
	while( i <  len )
	{
		TCHAR ch = s[i];			
		int white = 
		#ifdef UNICODE
				iswspace( ch );
		#else
				isspace( ch );
		#endif
		if (!white)
			break;
		++i;
	}

	if (i<len)
		s = s.substr(i);
}


typedef std::wstring& _T;
void RemoveFirstLine(std::wstring &src, std::wstring &result)
{
	int breakPosition = (int)src.find('\n');
	result = _T("");
	if(breakPosition != string::npos)	//if found...
	{
		int len = (int)src.length();
		result = src.substr(0, breakPosition);
		src = src.substr(breakPosition+1, (len-breakPosition)-1);		// skip the '/n'
	}
	else
	{
		result = src;
		src = _T("");
	}
}



int CountLines(const std::wstring &s)
{
	int lines = 0;
	int breakPos = 0;

	do 
	{
		++lines;
		breakPos = (int)s.find('\n', breakPos+1);
	} while (breakPos != string::npos);

	return lines;
}	

typedef bool BOOL;

BOOL WildcardMatch(const char *pattern, const char *filename)
{
   int i, star;

new_segment:

   star = 0;
   if (*pattern == '*') {
      star = 1;
      do { pattern++; } while (*pattern == '*'); /* enddo */
   } /* endif */

test_match:

   for (i = 0; pattern[i] && (pattern[i] != '*'); i++) {
      //if (mapCaseTable[str[i]] != mapCaseTable[pat[i]]) {
	  if (filename[i] != pattern[i]) {
         if (!filename[i]) return 0;
         if ((pattern[i] == '?') && (filename[i] != '.')) continue;
         if (!star) return 0;
         filename++;
         goto test_match;
      }
   }
   if (pattern[i] == '*') {
      filename += i;
      pattern += i;
      goto new_segment;
   }
   if (!filename[i]) return 1;
   if (i && pattern[i - 1] == '*') return 1;
   if (!star) return 0;
   filename++;
   goto test_match;
}


//-----------------------------------------------------------------------------
// Name: AnsiToWideCch()
// Desc: This is a UNICODE conversion utility to convert a CHAR string into a
//       WCHAR string. 
//       cchDestChar is the size in TCHARs of wstrDestination.  Be careful not to 
//       pass in sizeof(strDest)
//       如果 Win32 ANSI Api 用於 Windows NT 系統中獲取文件名,使用 CP_ACP 將轉換字符串時。Windows NT 檢索        從物理設備的名稱,並將 OEM 名稱轉換爲 Unicode。Unicode 名稱被轉換成 ANSI 如果 ANSI API 調用,然後可將它翻  譯回使用 MultiByteToWideChar() 的 Unicode。
// http://support.microsoft.com/kb/108450
//
//-----------------------------------------------------------------------------
int CP_ACP;
HRESULT AnsiToWideCch( WCHAR* wstrDestination, const CHAR* strSource, 
                                     int cchDestChar )
{
    if( wstrDestination==NULL || strSource==NULL || cchDestChar < 1 )
        return E_INVALIDARG;

    int nResult = MultiByteToWideChar( CP_ACP, 0, strSource, -1, 
                                       wstrDestination, cchDestChar );
    wstrDestination[cchDestChar-1] = 0;
    
    if( nResult == 0 )
        return E_FAIL;
    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: WideToAnsi()
// Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
//       CHAR string. 
//       cchDestChar is the size in TCHARs of strDestination
//-----------------------------------------------------------------------------
HRESULT WideToAnsiCch( CHAR* strDestination, const WCHAR* wstrSource, 
                                     int cchDestChar )
{
    if( strDestination==NULL || wstrSource==NULL || cchDestChar < 1 )
        return E_INVALIDARG;

    int nResult = WideCharToMultiByte( CP_ACP, 0, wstrSource, -1, strDestination, 
                                       cchDestChar*sizeof(CHAR), NULL, NULL );
    strDestination[cchDestChar-1] = 0;
    
    if( nResult == 0 )
        return E_FAIL;
    return S_OK;
}





//-----------------------------------------------------------------------------
// Name: GenericToAnsi()
// Desc: This is a UNICODE conversion utility to convert a TCHAR string into a
//       CHAR string. 
//       cchDestChar is the size in TCHARs of strDestination
//-----------------------------------------------------------------------------
HRESULT GenericToAnsiCch( CHAR* strDestination, const TCHAR* tstrSource, 
                                           int cchDestChar )
{
    if( strDestination==NULL || tstrSource==NULL || cchDestChar < 1 )
        return E_INVALIDARG;

#ifdef _UNICODE
    return WideToAnsiCch( strDestination, tstrSource, cchDestChar );
#else
    strncpy( strDestination, tstrSource, cchDestChar );
    strDestination[cchDestChar-1] = '\0';
    return S_OK;
#endif   
}




//-----------------------------------------------------------------------------
// Name: GenericToWide()
// Desc: This is a UNICODE conversion utility to convert a TCHAR string into a
//       WCHAR string. 
//       cchDestChar is the size in TCHARs of wstrDestination.  Be careful not to 
//       pass in sizeof(strDest) 
//-----------------------------------------------------------------------------
HRESULT GenericToWideCch( WCHAR* wstrDestination, const TCHAR* tstrSource, 
                                           int cchDestChar )
{
    if( wstrDestination==NULL || tstrSource==NULL || cchDestChar < 1 )
        return E_INVALIDARG;

#ifdef _UNICODE
    wcsncpy( wstrDestination, tstrSource, cchDestChar );
    wstrDestination[cchDestChar-1] = L'\0';
    return S_OK;
#else
    return AnsiToWideCch( wstrDestination, tstrSource, cchDestChar );
#endif    
}



//-----------------------------------------------------------------------------
// Name: AnsiToGeneric()
// Desc: This is a UNICODE conversion utility to convert a CHAR string into a
//       TCHAR string. 
//       cchDestChar is the size in TCHARs of tstrDestination.  Be careful not to 
//       pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT AnsiToGenericCch( TCHAR* tstrDestination, const CHAR* strSource, 
                                           int cchDestChar )
{
    if( tstrDestination==NULL || strSource==NULL || cchDestChar < 1 )
        return E_INVALIDARG;
        
#ifdef _UNICODE
    return AnsiToWideCch( tstrDestination, strSource, cchDestChar );
#else
    strncpy( tstrDestination, strSource, cchDestChar );
    tstrDestination[cchDestChar-1] = '\0';
    return S_OK;
#endif    
}





//-----------------------------------------------------------------------------
// Name: AnsiToGeneric()
// Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
//       TCHAR string. 
//       cchDestChar is the size in TCHARs of tstrDestination.  Be careful not to 
//       pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT WideToGenericCch( TCHAR* tstrDestination, const WCHAR* wstrSource, 
                                           int cchDestChar )
{
    if( tstrDestination==NULL || wstrSource==NULL || cchDestChar < 1 )
        return E_INVALIDARG;

#ifdef _UNICODE
    wcsncpy( tstrDestination, wstrSource, cchDestChar );
    tstrDestination[cchDestChar-1] = L'\0';    
    return S_OK;
#else
    return WideToAnsiCch( tstrDestination, wstrSource, cchDestChar );
#endif
}



std::string ws2s(const std::wstring& s)
{
    int slength = (int)s.length() + 1;
    int len = WideCharToMultiByte(CP_ACP, 0, s.c_str(), slength, 0, 0, 0, 0)-1; 
    std::string r(len, '\0');
    WideCharToMultiByte(CP_ACP, 0, s.c_str(), slength, &r[0], len, 0, 0); 
    return r;
}


std::wstring s2ws(const std::string &s)
{
    int slength = (int)s.length() + 1;
	int len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0)-1;
    std::wstring r(len, '\0');
	MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, &r[0], len);
    return r;
}





string ToStr(int num, int base)
{
    char str[MAX_DIGITS_IN_INT];
    memset(str,0,MAX_DIGITS_IN_INT);
    _itoa_s(num,str,MAX_DIGITS_IN_INT,base);
    return (string(str));
}  // end ToStr()

string ToStr(unsigned int num, int base)
{
    char str[MAX_DIGITS_IN_INT];
    memset(str, 0, MAX_DIGITS_IN_INT);
    _ultoa_s((unsigned long)num, str, MAX_DIGITS_IN_INT, base);
    return (string(str));
}

string ToStr(unsigned long num, int base)
{
    char str[MAX_DIGITS_IN_INT];
    memset(str,0,MAX_DIGITS_IN_INT);
    _ultoa_s(num,str,MAX_DIGITS_IN_INT,base);
    return (string(str));
}

string ToStr(float num)
{
    char str[64];  // I'm sure this is overkill
    memset(str,0,64);
    _sprintf_p(str,64,"%f",num);
    return (string(str));
}

string ToStr(double num)
{
    char str[64];  // I'm sure this is overkill
    memset(str,0,64);
    _sprintf_p(str,64,"%fL",num);
    return (string(str));
}

string ToStr(bool val)
{
    return (string( (val == true ? "true" : "false") ));
}

string ToStr(const Vec3& vec)
{
    return string("(" + ToStr(vec.x) + "," + ToStr(vec.y) + "," + ToStr(vec.z) + ")");
}


<span style="color:#ff0000;">
//-------------------------------------------------------------------------------------
// This is basically like the Perl split() function.  It splits str into substrings by cutting it at each delimiter.  
// The result is stored in vec.
//--------------------------------------------------------------------------------------
void Split(const string& str, StringVec& vec, char delimiter)
{
	vec.clear();
	size_t strLen = str.size();
	if (strLen == 0)
		return;

	size_t startIndex = 0;
	size_t indexOfDel = str.find_first_of(delimiter, startIndex);
	while (indexOfDel != string::npos)
	{
		vec.push_back(str.substr(startIndex, indexOfDel-startIndex));
		startIndex=indexOfDel + 1;
		if (startIndex >= strLen)
			break;
		indexOfDel = str.find_first_of(delimiter, startIndex);
	}
	if(startIndex < strLen)
		vec.push_back(str.substr(startIndex));
}</span>



void *
HashedString::hash_name( char const * pIdentStr )
{
	// Relatively simple hash of arbitrary text string into a
	// 32-bit identifier Output value is
	// input-valid-deterministic, but no guarantees are made
	// about the uniqueness of the output per-input
	//
	// Input value is treated as lower-case to cut down on false
	// separations cause by human mistypes. Sure, it could be
	// construed as a programming error to mix up your cases, and
	// it cuts down on permutations, but in Real World Usage
	// making this text case-sensitive will likely just lead to
	// Pain and Suffering.
	//
	// This code lossely based upon the adler32 checksum by Mark
	// Adler and published as part of the zlib compression
	// library sources.

	// largest prime smaller than 65536
	unsigned long BASE = 65521L;

	// NMAX is the largest n such that 255n(n+1)/2 +
	// (n+1)(BASE-1) <= 2^32-1
	unsigned long NMAX = 5552;

#define DO1(buf,i)  {s1 += tolower(buf[i]); s2 += s1;}
#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
#define DO16(buf)   DO8(buf,0); DO8(buf,8);

    if (pIdentStr == NULL)
		return NULL;

    unsigned long s1 = 0;
    unsigned long s2 = 0;

	for ( size_t len = strlen( pIdentStr ); len > 0 ; )
	{
        unsigned long k = len < NMAX ? len : NMAX;

		len -= k;

		while (k >= 16)
		{
            DO16(pIdentStr);
			pIdentStr += 16;
            k -= 16;
        }
		
        if (k != 0) do
		{
			s1 += tolower( *pIdentStr++ );
			s2 += s1;
        } while (--k);
		
        s1 %= BASE;
        s2 %= BASE;
    }

#pragma warning(push)
#pragma warning(disable : 4312)

    return reinterpret_cast<void *>( (s2 << 16) | s1 );

#pragma warning(pop)
#undef DO1
#undef DO2
#undef DO4
#undef DO8
#undef DO16
}
</span></span>
 以上是一個自負串的相關操作,包括分割字符串這個非常好用的工具。


IV. 這部分主要是實現數學常用的函數

<span style="font-size:18px;">#pragma once

#include <functional>
#include <vector>
#include "types.h"
#include "Geometry.h"


/* Period parameters */  
#define CMATH_N 624
#define CMATH_M 397
#define CMATH_MATRIX_A 0x9908b0df   /* constant vector a */
#define CMATH_UPPER_MASK 0x80000000 /* most significant w-r bits */
#define CMATH_LOWER_MASK 0x7fffffff /* least significant r bits */

/* Tempering parameters */   
#define CMATH_TEMPERING_MASK_B 0x9d2c5680
#define CMATH_TEMPERING_MASK_C 0xefc60000
#define CMATH_TEMPERING_SHIFT_U(y)  (y >> 11)
#define CMATH_TEMPERING_SHIFT_S(y)  (y << 7)
#define CMATH_TEMPERING_SHIFT_T(y)  (y << 15)
#define CMATH_TEMPERING_SHIFT_L(y)  (y >> 18)

#define RADIANS_TO_DEGREES(x) ((x) * 180.0f / GCC_PI)
#define DEGREES_TO_RADIANS(x) ((x) * GCC_PI / 180.0f)


class GCCRandom
{
private:
	// DATA
	unsigned int		rseed;
	unsigned int		rseed_sp;
	unsigned long mt[CMATH_N]; /* the array for the state vector  */
	int mti; /* mti==N+1 means mt[N] is not initialized */

	// FUNCTIONS
public:
	GCCRandom(void);	

	unsigned int	Random( unsigned int n );
	float			Random( );
	void			SetRandomSeed(unsigned int n);
	unsigned int	GetRandomSeed(void);
	void			Randomize(void);
};

typedef Point POINT;
typedef Rect  RECT;
typedef std::vector<Point> Poly;


class Math
{
	// DATA
private:
	static const unsigned short angle_to_sin[90];

public:
	static GCCRandom		random;

	// FUNCTIONS
public:
	static int				Cos(short angle, int length);
	static int				Sin(short angle, int length);
	static unsigned int		Sqrt( unsigned int n );
	static void				InterpolateLine(int *x, int *y, int end_x, int end_y, int step_size);
	static unsigned short	GetAngle(int x, int y);
	static bool PointInPoly( Point const &test, const Poly & polygon);
    // 判斷一個點是否在一個多邊形內
	static bool				PointInPoly
							(
								int const			x,
								int const			y,
								int const * const	vertex,
								int const			nvertex
							);
	static RECT				BoundingBox
							( 
								const POINT &pt1,
								const POINT &pt2,
								const POINT &pt3,
								const POINT &pt4
							);
	static RECT				BoundingBox
							(
								const POINT *verts,
								const unsigned int nverts
							);
	static float const		GetDistanceBetween(POINT const & pt1, POINT const & pt2);

	// Used to determine the bounding box for a range of point-like objects.
	// This includes POINTS, CPoints, and VertexUV to name a few.
	// This works on any range which includes all STL containers as well as C style arrays.
	// See BoundingBox(const POINT*, const unsigned int) in cpp for example usage.
	template <typename PointType>
	class BoundingBoxFinder : std::unary_function<PointType, void>
	{
	public:
		void operator()(PointType const & item)
		{
			if (mBoundingBox.invalid())
			{
				RECT initialValue = { item.x, item.y, item.x, item.y };
				mBoundingBox = initialValue;
			}
			else
			{
				mBoundingBox->left = std::min(mBoundingBox->left, item.x);
				mBoundingBox->top = std::min(mBoundingBox->top, item.y);
				mBoundingBox->right = std::max(mBoundingBox->right, item.x);
				mBoundingBox->bottom = std::max(mBoundingBox->bottom, item.y);
			}
		}

		RECT const & BoundingBox() { return *mBoundingBox; }

	private:
		optional<RECT> mBoundingBox;
	};


};

#define	DONT_INTERSECT    0
#define	DO_INTERSECT      1
#define COLLINEAR         2

// 線段定義
struct LineSegment
{
	Point m_begin, m_end;
	LineSegment(const Point &begin, const Point &end) { m_begin=begin; m_end=end; }
	LineSegment() { m_begin = m_end = Point(0,0); }
};


int lines_intersect( Point one,   /* First line segment */
					 Point two,

					Point three,   /* Second line segment */
					Point four,

					Point &result
               );

bool Intersect(const Rect &rect, const Point ¢er, double const radius);

float WrapPi(float wrapMe);  // wraps angle so it's between -PI and PI
float Wrap2Pi(float wrapMe);  // wraps angle so it's between 0 and 2PI
float AngleDiff( float lhs, float rhs );
// vec3 Graphics3D中
Vec3 GetVectorFromYRotation(float angleRadians);
float GetYRotationFromVector(const Vec3& lookAt);

//--------------------------------------------------------------------------------</span>
<span style="font-size:18px;">#include "GameCodeStd.h"
#include <math.h>
#include <vector>
#include <algorithm>
#include "Math.h"


//--------------------------------------------------------------------------------
// static members must be initialized before use

const unsigned short Math::angle_to_sin[90] = 
{
       0, 1144, 2287, 3430, 4572, 5712, 6850, 7987, 9121,10252,
   11380,12505,13626,14742,15855,16962,18064,19161,20252,21336,
   22415,23486,24550,25607,26656,27697,28729,29753,30767,31772,
   32768,33754,34729,35693,36647,37590,38521,39441,40348,41243,
   42126,42995,43852,44695,45525,46341,47143,47930,48703,49461,
   50203,50931,51643,52339,53020,53684,54332,54963,55578,56175,
   56756,57319,57865,58393,58903,59396,59870,60326,60764,61183,
   61584,61966,62328,62672,62997,63303,63589,63856,64104,64332,
   64540,64729,64898,65048,65177,65287,65376,65446,65496,65526,
};

GCCRandom Math::random; 

//--------------------------------------------------------------------------------
// NOTE: % 的運算速度竟然比 -= 的速度慢了許多  如下:
int Math::Sin(short angle, int length)
{
   int result;

   // normalize to 0..359
   if (length < 0) {
      length *= -1;
      angle += 180;
   }
   if (360 <= angle) {
      do {
         angle -= 360;
      } while (360 <= angle);
      //angle = angle % 360; // correct, but slower on average
   } else if (angle < 0) {
      do {
         angle += 360;
      } while (angle < 0);
      //angle = (u16)((angle + 33120) % 360); // correct, but slower on average
   }

   if (angle < 180) {
      if (angle < 90) {
         result = (int)(((unsigned int)length * angle_to_sin[angle] + 32768UL) / 65536UL);
      } else if (angle == 90) {
         result = length;
      } else {
         result = (int)(((unsigned int)length * angle_to_sin[180 - angle] + 32768UL) / 65536UL);
      }
   } else {
      if (angle < 270) {
         result = -(int)(((unsigned int)length * angle_to_sin[angle - 180] + 32768UL) / 65536UL);
      } else if (angle == 270) {
         result = -length;
      } else {
         result = -(int)(((unsigned int)length * angle_to_sin[360 - angle] + 32768UL) / 65536UL);
      }
   }
   return result;
}

int Math::Cos(short angle, int length)
{
   return Sin(angle + 90, length);
}

//--------------------------------------------------------------------------------
// 二分法查找
unsigned int Math::Sqrt( unsigned int n )
{
   unsigned int lo;
   unsigned int hi;

   if (65535U * 65535U <= n)
      return 65535;

   if( n <= 65535 )
   {
      lo = 0;
      hi = 256;
   }
   else
   {
      lo = 256;
      hi = 65535;
   }

   do
   {
      const unsigned int mid = (lo + hi) / 2;

      if( mid * mid <= n )
         lo = mid;
      else
         hi = mid;
   }
   while( lo + 1 < hi );

   return lo;
}


void Math::InterpolateLine(int *x, int *y, int end_x, int end_y, int step_size)
{
   short delta_x, delta_y, angle;

   if ( step_size <= 0 || ( *x == end_x && *y == end_y ) ) return;

   delta_x = short(end_x - *x);
   delta_y = short(end_y - *y);

   if ( abs(delta_x) > 255 || abs(delta_y) > 255 )
      angle    = GetAngle( delta_x/2, delta_y/2 );
   else
      angle    = GetAngle( delta_x, delta_y );

   if ( *x < end_x )
   {
      *x += Cos( angle, step_size );
      if ( *x > end_x ) *x = end_x;
   }
   else
   { 
      *x += Cos( angle, step_size );
      if ( *x < end_x ) *x = end_x;
   }

   if ( *y < end_y )
   {
      *y += Sin( angle, step_size );
      if ( *y > end_y ) *y = end_y;
   }
   else
   { 
      *y += Sin( angle, step_size );
      if ( *y < end_y ) *y = end_y;
   }
   return;
}

/*  給出隨意的兩點 在X-Y座標系中獲取一個角度  藉助X軸正方向實現
	Math::Get_Angle()

	Given arbitrary x, y, this returns angle in degrees 0..359
	(Not 0..360 as old asm version did.)

	E.g. Get_Angle(3,3) returns 45; Get_Angle(-4,0) returns 180

	If passed in (0,0), returns 0; could easily be modified to crash.
	(And perhaps should.)

	This has been tested vs the old asm Get_Angle;
	it agrees, differing at most by 1 from the old result, and
	in all such cases I checked, this one was the more accurate
	result (this version rounds rather than truncates).
	It was also tested for all x,y from -1000 to 1000 versus atan2 function
	and never differed by more than 2 from "true" result (which often
	varied a little from my HP calculator...).

	This C actually runs a little faster than the old asm code
	(without even turning on compiler optimizations).

	It also accepts ints, not just short ints, to plan for possible future use.
	(If very large ints are ever used, some additional checking must be added.)

	Written in standard vanilla C, compiles under Watcom or Borland.

	Most importantly, it gives correct results on inputs where
	the old version crashes (e.g. -1, 376), which prompted Russ to
	rewrite this in clear C to begin with.
*/

unsigned short Math::GetAngle(int x, int y)
{
   short result = -1;

   if (x == 0) {
      if (y < 0) {
         result = 270;
      } else if (0 < y) {
         result = 90;
      } else {
         result = 0;
      }
   } else if (y == 0) {
      if (x < 0) {
         result = 180;
      } else {
         result = 0;
      }
   } else {
      int correction;

      if (x < 0) {
         if (y < 0) {
            x = -x;
            y = -y;
            correction = 180;
         } else {
            const int old_x = x;
            x = y;
            y = -old_x;
            correction = 90;
         }
      } else {
         if (y < 0) {
            const int old_x = x;
            x = -y;
            y = old_x;
            correction = 270;
         } else {
            correction = 0;
         }
      }

      //GCC_ASSERT(0 < x);
      //GCC_ASSERT(0 < y);
      if (x == y) {
         result = 45;
      } else {
         /*
            For y < x, this table takes quotient y * 128 / x
            (which will be 0..127)
            and tells corresponding angle 0..45
         */
         static const unsigned char quotient_to_angle[128] = {
             0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
             7, 8, 8, 8, 9, 9,10,10,11,11,11,12,12,13,13,14,
            14,14,15,15,16,16,16,17,17,18,18,18,19,19,20,20,
            20,21,21,22,22,22,23,23,24,24,24,25,25,25,26,26,
            26,27,27,27,28,28,29,29,29,30,30,30,31,31,31,32,
            32,32,32,33,33,33,34,34,34,35,35,35,36,36,36,36,
            37,37,37,38,38,38,38,39,39,39,39,40,40,40,40,41,
            41,41,41,42,42,42,42,43,43,43,43,44,44,44,44,45,
         };

         if (x < y) {
            const unsigned int quotient = ((unsigned int)x * 128) / (unsigned int)y;
            result = 90 - quotient_to_angle[quotient];
         } else {
            const unsigned int quotient = ((unsigned int)y * 128) / (unsigned int)x;
            result = quotient_to_angle[quotient];
         }
      }
	  result = short(result + correction);
      if (result == 360) {
         result = 0;
      }
   }
   return result;
}

//
// Math::PointInPoly					
//
bool Math::PointInPoly
(
	int const			xt,
	int const			yt,
	int const * const	vertex,
	int const			nvertex
)
{
	int xnew,ynew;     
	int xold,yold;     
	int x1,y1;
    int x2,y2;     
	int i;     
	bool inside=false;     
	
	if (nvertex < 3) 
	{
          return(0);     
	}     
	xold=vertex[(nvertex-1)*2];
    yold=vertex[(nvertex-1)*2+1];     
	for (i=0 ; i < nvertex ; i++) 
	{
		xnew = vertex[i*2];
		ynew = vertex[i*2+1];
		if (xnew > xold) 
		{
			x1=xold;               
			x2=xnew;               
			y1=yold;
            y2=ynew;          
		}          
		else 
		{               
			x1=xnew;
            x2=xold;               
			y1=ynew;               
			y2=yold;          
		}
        if ((xnew < xt) == (xt <= xold)         /* edge "open" at left end */
           && ((long)yt-(long)y1)*(long)(x2-x1)
            < ((long)y2-(long)y1)*(long)(xt-x1)) 
		{               
			inside=!inside;
		}          
		xold=xnew;          
		yold=ynew;     
	}     
	return(inside);
}

RECT Math::BoundingBox
( 
 const POINT &pt1,
 const POINT &pt2,
 const POINT &pt3,
 const POINT &pt4
 )
{
	RECT bounding;
	bounding.top = std::min( pt1.y, std::min( pt2.y, std::min( pt3.y, pt4.y ) ) );
	bounding.bottom = std::max( pt1.y, std::max( pt2.y, std::max( pt3.y, pt4.y ) ) );
	bounding.left = std::min( pt1.x, std::min( pt2.x, std::min( pt3.x, pt4.x ) ) );
	bounding.right = std::max( pt1.x, std::max( pt2.x, std::max( pt3.x, pt4.x ) ) );
	return bounding;
}

RECT Math::BoundingBox
(
 const POINT *verts,
 const unsigned int nverts
)
{
	GCC_ASSERT (nverts > 0);
	return std::for_each(verts, verts + nverts, BoundingBoxFinder<POINT>()).BoundingBox();
}

float const Math::GetDistanceBetween(POINT const & pt1, POINT const & pt2)
{
	float const xDiff = fabsf(float(pt2.x) - float(pt1.x));
	float const yDiff = fabsf(float(pt2.y) - float(pt1.y));
	return sqrtf(xDiff * xDiff + yDiff * yDiff);
}

//
// Math::PointInPoly
//
bool Math::PointInPoly( Point const &test, const Poly & polygon)
{
	Point newPoint, oldPoint;
	Point left, right;

	bool inside=false;     

	size_t points = polygon.size();

	// The polygon must at least be a triangle
	if (points < 3) 
          return false;     

	oldPoint = polygon[points-1];

	for (unsigned int i=0 ; i < points; i++) 
	{
		newPoint = polygon[i];
		if (newPoint.x > oldPoint.x) 
		{
			left = oldPoint;               
			right = newPoint;
		}          
		else 
		{               
			left = newPoint;               
			right = oldPoint;
		}

		// A point exactly on the left side of the polygon
		// will not intersect - as if it were "open"
        if ((newPoint.x < test.x) == (test.x <= oldPoint.x)         
           && (test.y-left.y) * (right.x-left.x)
            < (right.y-left.y) * (test.x-left.x) ) 
		{               
			inside=!inside;
		}

		oldPoint = newPoint;
	}     
	return(inside);
}



/* lines_intersect:  AUTHOR: Mukesh Prasad
 *
 *   This function computes whether two line segments,
 *   respectively joining the input points (x1,y1) -- (x2,y2)
 *   and the input points (x3,y3) -- (x4,y4) intersect.
 *   If the lines intersect, the output variables x, y are
 *   set to coordinates of the point of intersection.
 *
 *   All values are in integers.  The returned value is rounded
 *   to the nearest integer point.
 *
 *   If non-integral grid points are relevant, the function
 *   can easily be transformed by substituting floating point
 *   calculations instead of integer calculations.
 *
 *   Entry
 *        x1, y1,  x2, y2   Coordinates of endpoints of one segment.
 *        x3, y3,  x4, y4   Coordinates of endpoints of other segment.
 *
 *   Exit
 *        x, y              Coordinates of intersection point.
 *
 *   The value returned by the function is one of:
 *
 *        DONT_INTERSECT    0
 *        DO_INTERSECT      1
 *        COLLINEAR         2
 *
 * Error condititions:
 *
 *     Depending upon the possible ranges, and particularly on 16-bit
 *     computers, care should be taken to protect from overflow.
 *
 *     In the following code, 'long' values have been used for this
 *     purpose, instead of 'int'.
 *
 * Changes from the original code:
 *     
 *     Used ATL Point classes instead of straight integers (MLM)
 */


/****************************************************************
 *                                                              *
 *    NOTE:  The following macro to determine if two numbers    *
 *    have the same sign(有相同的符號), is for 2's complement     *
 *    number representation.  It will need to be modified for   *
 *    other number systems.                                     *
 *                                                              *
 ****************************************************************/

#define SAME_SIGNS( a, b )	\
		(((long) ((unsigned long) a ^ (unsigned long) b)) >= 0 )

int lines_intersect( 
	Point one, Point two,				  /* First line segment */
	Point three, Point four,			  /* Second line segment */
	Point &result)
{
    long a1, a2, b1, b2, c1, c2; /* Coefficients of line eqns. */
    long r1, r2, r3, r4;         /* 'Sign' values */
    long denom, offset, num;     /* Intermediate values */

    /* Compute a1, b1, c1, where line joining points 1 and 2
     * is "a1 x  +  b1 y  +  c1  =  0".
     */

    a1 = two.y - one.y; //y2 - y1;
    b1 = one.x - two.x; //x1 - x2;
    c1 = two.x * one.y - one.x * two.y; //x2 * y1 - x1 * y2;

    /* Compute r3 and r4.
     */


    r3 = a1 * three.x + b1 * three.y + c1;  //a1 * x3 + b1 * y3 + c1;
    r4 = a1 * four.x + b1 * four.y + c1;			//a1 * x4 + b1 * y4 + c1;

    /* Check signs of r3 and r4.  If both point 3 and point 4 lie on
     * same side of line 1, the line segments do not intersect.
     */

    if ( r3 != 0 &&
         r4 != 0 &&
         SAME_SIGNS( r3, r4 ))
        return ( DONT_INTERSECT );

    /* Compute a2, b2, c2 */

    a2 = four.y - three.y; //y4 - y3;
    b2 = three.x - four.x; //x3 - x4;
    c2 = four.x * three.y - three.x * four.y; //x4 * y3 - x3 * y4;

    /* Compute r1 and r2 */

    r1 = a2 * one.x + b2 * one.y + c2; //a2 * x1 + b2 * y1 + c2;
    r2 = a2 * two.x + b2 * two.y + c2; //a2 * x2 + b2 * y2 + c2;

    /* Check signs of r1 and r2.  If both point 1 and point 2 lie
     * on same side of second line segment, the line segments do
     * not intersect.
     */

    if ( r1 != 0 &&
         r2 != 0 &&
         SAME_SIGNS( r1, r2 ))
        return ( DONT_INTERSECT );

    /* Line segments intersect: compute intersection point. 
     */

    denom = a1 * b2 - a2 * b1;
    if ( denom == 0 )
        return ( COLLINEAR );
    offset = denom < 0 ? - denom / 2 : denom / 2;

    /* The denom/2 is to get rounding instead of truncating.  It
     * is added or subtracted to the numerator, depending upon the
     * sign of the numerator.
     */

    num = b1 * c2 - b2 * c1;
    result.x = ( num < 0 ? num - offset : num + offset ) / denom;

    num = a2 * c1 - a1 * c2;
    result.y = ( num < 0 ? num - offset : num + offset ) / denom;

    return ( DO_INTERSECT );
    } /* lines_intersect */


//
// bool Intersect - (not in the book) Returns true if the supplied rect is inside a circular radius.
//
bool Intersect(const Rect &rect, const Point ¢er, double const radius)
{
	Rect r = rect;
	double radiusiusSquared = radius * radius;

	/* Translate coordinates, placing C at the origin. */
	r.left -= center.x;  r.bottom -= center.y;
	r.right -= center.x;  r.top -= center.y;

	if (r.right < 0) 			/* R to left of circle center */
	{
   		if (r.bottom < 0) 		/* R in lower left corner */
     		return ((r.right * r.right + r.bottom * r.bottom) < radiusiusSquared);
   		else if (r.top > 0) 	/* R in upper left corner */
   			return ((r.right * r.right + r.top * r.top) < radiusiusSquared);
   		else 					/* R due West of circle */
   			return(abs(r.right) < radius);
	}
	else if (r.left > 0)  	/* R to right of circle center */
	{
 		if (r.bottom < 0) 	/* R in lower right corner */
     				return ((r.left * r.left) < radiusiusSquared);
   		else if (r.top > 0)  	/* R in upper right corner */
     			return ((r.left * r.left + r.top + r.top) < radiusiusSquared);
   		else 				/* R due East of circle */
     			return (r.left < radius);
	}
	else				/* R on circle vertical centerline */
	{
   		if (r.bottom < 0) 	/* R due South of circle */
     			return (abs(r.bottom) < radius);
   		else if (r.top > 0)  	/* R due North of circle */
     			return (r.top < radius);
   		else 				/* R contains circle centerpoint */
     			return(true);
	}
}

//
// void Interpolate								
//
float Interpolate(float normalizedValue, float begin, float end)
{
	// first check input values
	GCC_ASSERT(normalizedValue>=0.0f);
	GCC_ASSERT(normalizedValue<=1.0f);
	GCC_ASSERT(end>begin);

	return ( normalizedValue * (end - begin) ) + begin;
}


//
// void MapYDeadZone			
//
void MapYDeadZone(Vec3 &input, float deadZone)
{
	if (deadZone>=1.0f)
		return;

	// The dead zone is assumed to be zero close to the origin
	// so we have to interpolate to find the right dead zone for
	// our current value of X.
	float actualDeadZone = Interpolate(fabs(input.x), 0.0f, deadZone);

	if (fabs(input.y) < actualDeadZone)
	{	
		input.y = 0.0f;
		return;
	}

	// Y is outside of the dead zone, but we still need to 
	// interpolate it so we don't see any popping.

	// Map Y values [actualDeadZone, 1.0f] to [0.0f, 1.0f]
	float normalizedY = (input.y - actualDeadZone) / (1.0f - actualDeadZone);
	input.y = normalizedY;
}


//
// WrapPi, Wrap2Pi, AngleDiff - returns the smallest angle - useful for knowing which way around the circle to turn
//
float WrapPi(float wrapMe)
{
    float result = Wrap2Pi(wrapMe + GCC_PI);
    return (result - GCC_PI);
}

float Wrap2Pi(float wrapMe)
{
    if (wrapMe > GCC_2PI)
        wrapMe = fmod(wrapMe, GCC_2PI);
    else if (wrapMe < 0)
        wrapMe = GCC_2PI - fmod(fabs(wrapMe), GCC_2PI);
    return wrapMe;
}

float AngleDiff( float lhs, float rhs )
{
	lhs = WrapPi( lhs );
	rhs = WrapPi( rhs );

	return WrapPi( lhs - rhs );
}

//----------------------------------------------------------------------------------------------------------------
// This function returns the look-at vector for a given orientation, which is assumed to be on the Y axis.  Thus, 
// the Y component of the returned vector will always be 0.  This function is used by the AI system which doesn't
// care about orientation along any other axis.
//----------------------------------------------------------------------------------------------------------------
Vec3 GetVectorFromYRotation(float angleRadians)
{
	Vec3 lookAt;
	WrapPi(angleRadians);
	lookAt.x = cos(angleRadians);
	lookAt.y = 0;
	lookAt.z = sin(angleRadians);
	lookAt.Normalize();  // just in case
	return lookAt;
}

//---------------------------------------------------------------------------------------------------------------
// This function returns the target orientation for a given look-at vector.  The orientation will be along the Y
// axis so the Y component of the look-at vector is ignored.  This function is used by the AI system which doesn't
// care about orientation along any other axis.
//----------------------------------------------------------------------------------------------------------------
float GetYRotationFromVector(const Vec3& lookAt)
{
	Vec3 zUnit(0.f,0.f,1.f);  // 0 orientation means staring down the positive Z axis
    float angle = (atan2(lookAt.z,-lookAt.x) - atan2(zUnit.z,zUnit.x));
	return Wrap2Pi(angle);
}
</span>

V. 這部分主要是實現一個隨機數

<span style="font-size:14px;">#include "GameCodeStd.h"
#include <time.h>
#include "Math.h"

/////////////////////////////////////////////////////////////////////////////
// DEBUG info
/////////////////////////////////////////////////////////////////////////////

//--------------------------------------------------------------------------------

GCCRandom::GCCRandom(void)
{
	rseed = 1;
	// safe0 start
	rseed_sp = 0;
	mti=CMATH_N+1;
	// safe0 end
}	
	
// Returns a number from 0 to n (excluding n)
unsigned int GCCRandom::Random( unsigned int n )
{
    unsigned long y;
    static unsigned long mag01[2]={0x0, CMATH_MATRIX_A};

	if(n==0)
		return(0);

    /* mag01[x] = x * MATRIX_A  for x=0,1 */

    if (mti >= CMATH_N) { /* generate N words at one time */
        int kk;

        if (mti == CMATH_N+1)   /* if sgenrand() has not been called, */
            SetRandomSeed(4357); /* a default initial seed is used   */

        for (kk=0;kk<CMATH_N-CMATH_M;kk++) {
            y = (mt[kk]&CMATH_UPPER_MASK)|(mt[kk+1]&CMATH_LOWER_MASK);
            mt[kk] = mt[kk+CMATH_M] ^ (y >> 1) ^ mag01[y & 0x1];
        }
        for (;kk<CMATH_N-1;kk++) {
            y = (mt[kk]&CMATH_UPPER_MASK)|(mt[kk+1]&CMATH_LOWER_MASK);
            mt[kk] = mt[kk+(CMATH_M-CMATH_N)] ^ (y >> 1) ^ mag01[y & 0x1];
        }
        y = (mt[CMATH_N-1]&CMATH_UPPER_MASK)|(mt[0]&CMATH_LOWER_MASK);
        mt[CMATH_N-1] = mt[CMATH_M-1] ^ (y >> 1) ^ mag01[y & 0x1];

        mti = 0;
    }
  
    y = mt[mti++];
    y ^= CMATH_TEMPERING_SHIFT_U(y);
    y ^= CMATH_TEMPERING_SHIFT_S(y) & CMATH_TEMPERING_MASK_B;
    y ^= CMATH_TEMPERING_SHIFT_T(y) & CMATH_TEMPERING_MASK_C;
    y ^= CMATH_TEMPERING_SHIFT_L(y);

	// ET - old engine added one to the result.
	// We almost NEVER wanted to use this function
	// like this.  So, removed the +1 to return a 
	// range from 0 to n (not including n).
    return (y%n);
}

const unsigned int MAXINT = ((1 << sizeof(int)) - 1);
float GCCRandom::Random( )
{
	float r = (float)Random(MAXINT);
	float divisor = (float)MAXINT;
	return (r / divisor);
}



void GCCRandom::SetRandomSeed(unsigned int n)
{
	/* setting initial seeds to mt[N] using         */
	/* the generator Line 25 of Table 1 in          */
	/* [KNUTH 1981, The Art of Computer Programming */
	/*    Vol. 2 (2nd Ed.), pp102]                  */
	mt[0]= n & 0xffffffff;
	for (mti=1; mti<CMATH_N; mti++)
		mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;

	rseed = n;
}

unsigned int GCCRandom::GetRandomSeed(void)
{
	return(rseed);
}

void GCCRandom::Randomize(void)
{
	SetRandomSeed((unsigned int)time(NULL));
}

//-------------------------------------------------------------------------------</span>
以上就是遊戲中常用的工具集合。

下一篇是遊戲通信 ~~








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章