• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            [轉(zhuǎn)載]Flash為客戶端的多人網(wǎng)絡(luò)游戲的實現(xiàn)

            來源:http://outspace.spaces.live.com/

            多人網(wǎng)絡(luò)游戲的實現(xiàn)

            項目開發(fā)的基本硬件配置
            一臺普通的pc就可以了,
            安裝好windows 2000和vc6就可以了,
            然后連上網(wǎng),局域網(wǎng)和internet都可以,

            接下去的東西我都簡化,不去用晦澀的術(shù)語,

            既然是網(wǎng)絡(luò),我們就需要網(wǎng)絡(luò)編程接口,
            服務(wù)器端我們用的是winsock 1.1,使用tcp連接方式,

            [tcp和udp]
            tcp可以理解為一條連接兩個端子的隧道,提供可靠的數(shù)據(jù)傳輸服務(wù),
            只要發(fā)送信息的一方成功的調(diào)用了tcp的發(fā)送函數(shù)發(fā)送一段數(shù)據(jù),
            我們可以認為接收方在若干時間以后一定會接收到完整正確的數(shù)據(jù),
            不需要去關(guān)心網(wǎng)絡(luò)傳輸上的細節(jié),
            而udp不保證這一點,
            對于網(wǎng)絡(luò)游戲來說,tcp是普遍的選擇。

            [阻塞和非阻塞]
            在通過socket發(fā)送數(shù)據(jù)時,如果直到數(shù)據(jù)發(fā)送完畢才返回的方式,也就是說如果我們使用send( buffer, 100.....)這樣的函數(shù)發(fā)送100個字節(jié)給別人,我們要等待,直到100個自己發(fā)送完畢,程序才往下走,這樣就是阻塞的,
            而非阻塞的方式,當(dāng)你調(diào)用send(buffer,100....)以后,立即返回,此時send函數(shù)告訴你發(fā)送成功,并不意味著數(shù)據(jù)已經(jīng)向目的地發(fā)送完畢,甚至有可能數(shù)據(jù)還沒有開始發(fā)送,只被保留在系統(tǒng)的緩沖里面,等待被發(fā)送,但是你可以認為數(shù)據(jù)在若干時間后,一定會被目的地完整正確的收到,我們要充分的相信tcp。
            阻塞的方式會引起系統(tǒng)的停頓,一般網(wǎng)絡(luò)游戲里面使用的都是非阻塞的方式,
            ——————————————————————
            注意,僅僅用flash作為客戶端,
            服務(wù)器端,我們使用vc6,
            我將陸續(xù)的公開服務(wù)器端的源代碼和大家共享,
            并且將講解一些網(wǎng)絡(luò)游戲開發(fā)的原理,
            希望對此感興趣的朋友能夠使用今后的資源或者理論開發(fā)出完整的網(wǎng)絡(luò)游戲。
            我們從簡單到復(fù)雜,
            從棋牌類游戲到動作類的游戲,
            從2個人的游戲到10個人的游戲,
            因為工作忙的關(guān)系,我所做的一切僅僅起到拋磚引玉的作用,
            希望大家能夠熱情的討論,為中國的flash事業(yè)墊上一塊磚,添上一片瓦。

            現(xiàn)在的大型網(wǎng)絡(luò)游戲(mmo game)都是基于server/client體系結(jié)構(gòu)的,
            server端用c(windows下我們使用vc.net+winsock)來編寫,
            客戶端就無所謂,
            在這里,我們討論用flash來作為客戶端的實現(xiàn),

            實踐證明,flash的xml socket完全可以勝任網(wǎng)絡(luò)傳輸部分,
            在別的貼子中,我看見有的朋友談?wù)搈sn中的flash game
            他使用msn內(nèi)部的網(wǎng)絡(luò)接口進行傳輸,
            這種做法也是可以的,
            我找很久以前對于2d圖形編程的說法,"給我一個打點函數(shù),我就能創(chuàng)造整個游戲世界",
            而在網(wǎng)絡(luò)游戲開發(fā)過程中,"給我一個發(fā)送函數(shù)和一個接收函數(shù),我就能創(chuàng)造網(wǎng)絡(luò)游戲世界."

            我們抽象一個接口,就是網(wǎng)絡(luò)傳輸?shù)慕涌冢?br />對于使用flash作為客戶端,要進行網(wǎng)絡(luò)連接,
            一個網(wǎng)絡(luò)游戲的客戶端,
            可以簡單的抽象為下面的流程
            1.與遠程服務(wù)器建立一條長連接
            2.用賬號密碼登陸
            3.循環(huán)
            接收消息
            發(fā)送消息
            4.關(guān)閉

            我們可以直接使用flash 的xml socket,也可以使用類似msn的那種方式,
            這些我們先不管,我們先定義接口,
            Connect( "127.0.0.1", 20000 ); 連接遠程服務(wù)器,建立一條長連接
            Send( data, len ); 向服務(wù)器發(fā)送一條消息
            Recv( data, len ); 接收服務(wù)器傳來的消息


            說明一下普通網(wǎng)絡(luò)游戲中windows下面網(wǎng)絡(luò)傳輸?shù)脑恚?br />——————————————————————
            ?
            [有狀態(tài)服務(wù)器和無狀態(tài)服務(wù)器]
            在c/s體系中,如果server不保存客戶端的狀態(tài),稱之為無狀態(tài),反之為有狀態(tài),

            在這里要強調(diào)一點,
            我們所說的服務(wù)器不是一臺具體的機器,
            而是指服務(wù)器應(yīng)用程序,
            一臺具體的機器或者機器群組可以運行一個或者多個服務(wù)器應(yīng)用程序,

            我們的網(wǎng)絡(luò)游戲使用的是有狀態(tài)服務(wù)器,
            保存所有玩家的數(shù)據(jù)和狀態(tài),
            ——————————————————————
            一些有必要了解的理論和開發(fā)工具

            [開發(fā)語言]
            vc6
            我們首先要熟練的掌握一門開發(fā)語言,
            學(xué)習(xí)c++是非常有必要的,
            而vc是windows下面的軟件開發(fā)工具,
            為什么選擇vc,可能與我本身使用vc有關(guān),
            而且網(wǎng)上可以找到許多相關(guān)的資源和源代碼,

            [操作系統(tǒng)]
            我們使用windows2000作為服務(wù)器的運行環(huán)境,
            所以我們有必要去了解windows是如何工作的,
            同時對它的編程原理應(yīng)該熟練的掌握

            [數(shù)據(jù)結(jié)構(gòu)和算法]
            要寫出好的程序要先具有設(shè)計出好的數(shù)據(jù)結(jié)構(gòu)和算法的能力,
            好的算法未必是繁瑣的公式和復(fù)雜的代碼,
            我們要找到又好寫有滿足需求的算法,
            有時候,最笨的方法同時也是很好的方法,
            很多程序員沉迷于追求精妙的算法而忽略了宏觀上的工程,
            花費了大量的精力未必能夠取得好的效果,

            舉個例子,
            我當(dāng)年進入游戲界工作,學(xué)習(xí)老師的代碼,
            發(fā)現(xiàn)有個函數(shù),要對畫面中的npc位置進行排序,
            確定哪個先畫,那個后畫,
            他的方法太“笨”,
            任何人都會想到的冒泡,
            一個一個去比較,沒有任何的優(yōu)化,
            我當(dāng)時想到的算法就有很多,
            而且有一大堆優(yōu)化策略,
            可是,當(dāng)我花了很長時間去實現(xiàn)我的算法時,
            發(fā)現(xiàn)提升的那么一點效率對游戲整個運行效率而言幾乎是沒起到什么作用,
            或者說雖然算法本身快了幾倍,
            可是那是多余的,老師的算法雖然“笨”,
            可是他只花了幾十行代碼就搞定了,
            他的時間花在別的更需要的地方,
            這就是他可以獨自完成一個游戲,
            而我可以把一個函數(shù)優(yōu)化100倍也只能打雜的原因

            [tcp/ip的理論]
            推薦數(shù)據(jù)用tcp/ip進行網(wǎng)際互連,tcp/ip詳解,
            這是兩套書,共有6卷,
            都是國外的大師寫的,
            可以說是必讀的,
            ——————————————————————
            網(wǎng)絡(luò)傳輸中的“消息”

            [消息]
            消息是個很常見的術(shù)語,
            在windows中,消息機制是個十分重要的概念,
            我們在網(wǎng)絡(luò)游戲中,也使用了消息這樣的機制,

            一般我們這么做,
            一個數(shù)據(jù)塊,頭4個字節(jié)是消息名,后面接2個字節(jié)的數(shù)據(jù)長度,
            再后面就是實際的數(shù)據(jù)

            為什么使用消息??
            我們來看看例子,

            在游戲世界,
            一個玩家想要和別的玩家聊天,
            那么,他輸入好聊天信息,
            客戶端生成一條聊天消息,
            并把聊天的內(nèi)容打包到消息中,
            然后把聊天消息發(fā)送給服務(wù)器,
            請求服務(wù)器把聊天信息發(fā)送給另一個玩家,

            服務(wù)器接收到一條消息,
            此刻,服務(wù)器并不知道當(dāng)前的數(shù)據(jù)是什么東西,
            對于服務(wù)器來講,這段數(shù)據(jù)僅僅來自于網(wǎng)絡(luò)通訊的底層,
            不加以分析的話,沒有任何的信息,
            因為我們的通訊是基于消息機制的,
            我們認為服務(wù)器接收到的任何數(shù)據(jù)都是基于消息的數(shù)據(jù)方式組織的,
            4個字節(jié)消息名,2字節(jié)長度,這個是不會變的,

            通過消息名,服務(wù)器發(fā)現(xiàn)當(dāng)前數(shù)據(jù)是一條聊天數(shù)據(jù),
            通過長度把需要的數(shù)據(jù)還原,校驗,
            然后把這條消息發(fā)送給另一個玩家,

            大家注意,消息是變長的,
            關(guān)于消息的解釋完全在于服務(wù)器和客戶端的應(yīng)用程序,
            可以認為與網(wǎng)絡(luò)傳輸?shù)蛯訜o關(guān),
            比如一條私聊消息可能是這樣的,

            MsgID:4 byte
            Length:2 byte
            TargetPlayerID:2 byte
            String:anybyte < 256

            一條移動消息可能是這樣的,
            MsgID:4 byte
            Length:2 byte
            TargetPlayerID:2 byte
            TargetPosition:4 byte (x,y)

            編程者可以自定義消息的內(nèi)容以滿足不同的需求
            ——————————————————————
            隊列

            [隊列]
            隊列是一個很重要的數(shù)據(jù)結(jié)構(gòu),
            比如說消息隊列,
            服務(wù)器或者客戶端,
            發(fā)送的消息不一定是立即發(fā)送的,
            而是等待一個適當(dāng)時間,
            或者系統(tǒng)規(guī)定的時間間隔以后才發(fā)送,
            這樣就需要創(chuàng)建一個消息隊列,以保存發(fā)送的消息,

            消息隊列的大小可以按照實際的需求創(chuàng)建,
            隊列又可能會滿,
            當(dāng)隊列滿了,可以直接丟棄消息,
            如果你覺得這樣不妥,
            也可以預(yù)先劃分一個足夠大的隊列,

            可以使用一個系統(tǒng)全局的大的消息隊列,
            也可以為每個對象創(chuàng)建一個消息隊列,


            這個我們的一個數(shù)據(jù)隊列的實現(xiàn),
            開發(fā)工具vc.net,使用了C++的模板,
            關(guān)于隊列的算法和基礎(chǔ)知識,我就不多說了,

            DataBuffer.h

            #ifndef __DATABUFFER_H__
            #define __DATABUFFER_H__

            #include <windows.h>
            #include <assert.h>
            #include "g_assert.h"
            #include <stdio.h>

            #ifndef HAVE_BYTE
            typedef unsigned char byte;
            #endif // HAVE_BYTE

            //數(shù)據(jù)隊列管理類
            template <const int _max_line, const int _max_size>
            class DataBufferTPL
            {
            public:

            bool Add( byte *data ) // 加入隊列數(shù)據(jù)
            {
            G_ASSERT_RET( data, false );
            m_ControlStatus = false;

            if( IsFull() )
            {
            //assert( false );
            return false;
            }

            memcpy( m_s_ptr, data, _max_size );

            NextSptr();
            m_NumData++;

            m_ControlStatus = true;
            return true;
            }


            bool Get( byte *data ) // 從隊列中取出數(shù)據(jù)
            {
            G_ASSERT_RET( data, false );
            m_ControlStatus = false;

            if( IsNull() )
            return false;

            memcpy( data, m_e_ptr, _max_size );

            NextEptr();
            m_NumData--;

            m_ControlStatus = true;
            return true;
            }


            bool CtrlStatus() // 獲取操作成功結(jié)果
            {
            return m_ControlStatus;
            }


            int GetNumber() // 獲得現(xiàn)在的數(shù)據(jù)大小
            {
            return m_NumData;
            }

            public:

            DataBufferTPL()
            {
            m_NumData = 0;
            m_start_ptr = m_DataTeam[0];
            m_end_ptr = m_DataTeam[_max_line-1];
            m_s_ptr = m_start_ptr;
            m_e_ptr = m_start_ptr;
            }
            ~DataBufferTPL()
            {
            m_NumData = 0;
            m_s_ptr = m_start_ptr;
            m_e_ptr = m_start_ptr;
            }

            private:

            bool IsFull() // 是否隊列滿
            {
            G_ASSERT_RET( m_NumData >=0 && m_NumData <= _max_line, false );
            if( m_NumData == _max_line )
            return true;
            else
            return false;
            }
            bool IsNull() // 是否隊列空
            {
            G_ASSERT_RET( m_NumData >=0 && m_NumData <= _max_line, false );
            if( m_NumData == 0 )
            return true;
            else
            return false;
            }
            void NextSptr() // 頭位置增加
            {
            assert(m_start_ptr);
            assert(m_end_ptr);
            assert(m_s_ptr);
            assert(m_e_ptr);
            m_s_ptr += _max_size;
            if( m_s_ptr > m_end_ptr )
            m_s_ptr = m_start_ptr;
            }
            void NextEptr() // 尾位置增加
            {
            assert(m_start_ptr);
            assert(m_end_ptr);
            assert(m_s_ptr);
            assert(m_e_ptr);
            m_e_ptr += _max_size;
            if( m_e_ptr > m_end_ptr )
            m_e_ptr = m_start_ptr;
            }

            private:

            byte m_DataTeam[_max_line][_max_size]; //數(shù)據(jù)緩沖
            int m_NumData; //數(shù)據(jù)個數(shù)
            bool m_ControlStatus; //操作結(jié)果

            byte *m_start_ptr; //起始位置
            byte *m_end_ptr; //結(jié)束位置
            byte *m_s_ptr; //排隊起始位置
            byte *m_e_ptr; //排隊結(jié)束位置
            };


            //////////////////////////////////////////////////////////////////////////
            // 放到這里了!

            //ID自動補位列表模板,用于自動列表,無間空順序列表。
            template <const int _max_count>
            class IDListTPL
            {
            public:
            // 清除重置
            void Reset()
            {
            for(int i=0;i<_max_count;i++)
            m_dwList[i] = G_ERROR;
            m_counter = 0;
            }

            int MaxSize() const { return _max_count; }
            int Count() const { return m_counter; }
            const DWORD operator[]( int iIndex ) {

            G_ASSERTN( iIndex >= 0 && iIndex < m_counter );

            return m_dwList[ iIndex ];
            }
            bool New( DWORD dwID )
            {
            G_ASSERT_RET( m_counter >= 0 && m_counter < _max_count, false );

            //ID 唯一性,不能存在相同ID
            if ( Find( dwID ) != -1 )
            return false;

            m_dwList[m_counter] = dwID;
            m_counter++;

            return true;
            }
            // 沒有Assert的加入ID功能
            bool Add( DWORD dwID )
            {
            if( m_counter <0 || m_counter >= _max_count )
            return false;

            //ID 唯一性,不能存在相同ID
            if ( Find( dwID ) != -1 )
            return false;

            m_dwList[m_counter] = dwID;
            m_counter++;
            return true;
            }
            bool Del( int iIndex )
            {
            G_ASSERT_RET( iIndex >=0 && iIndex < m_counter, false );

            for(int k=iIndex;k<m_counter-1;k++)
            {
            m_dwList[k] = m_dwList[k+1];
            }

            m_dwList[k] = G_ERROR;
            m_counter--;
            return true;
            }
            int Find( DWORD dwID )
            {
            for(int i=0;i<m_counter;i++)
            {
            if( m_dwList[i] == dwID )
            return i;
            }

            return -1;
            }

            IDListTPL():m_counter(0)
            {
            for(int i=0;i<_max_count;i++)
            m_dwList[i] = G_ERROR;
            }
            virtual ~IDListTPL()
            {}

            private:

            DWORD m_dwList[_max_count];
            int m_counter;

            };

            //////////////////////////////////////////////////////////////////////////


            #endif //__DATABUFFER_H__
            ——————————————————————
            socket

            我們采用winsock作為網(wǎng)絡(luò)部分的編程接口,

            接下去編程者有必要學(xué)習(xí)一下socket的基本知識,
            不過不懂也沒有關(guān)系,我提供的代碼已經(jīng)把那些麻煩的細節(jié)或者正確的系統(tǒng)設(shè)置給弄好了,
            編程者只需要按照規(guī)則編寫游戲系統(tǒng)的處理代碼就可以了,

            這些代碼在vc6下編譯通過,
            是通用的網(wǎng)絡(luò)傳輸?shù)讓樱?br />這里是socket部分的代碼,

            我們需要安裝vc6才能夠編譯以下的代碼,
            因為接下去我們要接觸越來越多的c++,
            所以,大家還是去看看c++的書吧,

            // socket.h
            #ifndef _socket_h
            #define _socket_h
            #pragma once

            //定義最大連接用戶數(shù)目 ( 最大支持 512 個客戶連接 )
            #define MAX_CLIENTS 512
            //#define FD_SETSIZE MAX_CLIENTS

            #pragma comment( lib, "wsock32.lib" )

            #include <winsock.h>

            class CSocketCtrl
            {
            void SetDefaultOpt();
            public:
            CSocketCtrl(): m_sockfd(INVALID_SOCKET){}
            BOOL StartUp();
            BOOL ShutDown();
            BOOL IsIPsChange();

            BOOL CanWrite();
            BOOL HasData();
            int Recv( char* pBuffer, int nSize, int nFlag );
            int Send( char* pBuffer, int nSize, int nFlag );
            BOOL Create( UINT uPort );
            BOOL Create(void);
            BOOL Connect( LPCTSTR lpszHostAddress, UINT nHostPort );
            void Close();

            BOOL Listen( int nBackLog );
            BOOL Accept( CSocketCtrl& sockCtrl );

            BOOL RecvMsg( char *sBuf );
            int SendMsg( char *sBuf,unsigned short stSize );
            SOCKET GetSockfd(){ return m_sockfd; }

            BOOL GetHostName( char szHostName[], int nNameLength );

            protected:
            SOCKET m_sockfd;

            static DWORD m_dwConnectOut;
            static DWORD m_dwReadOut;
            static DWORD m_dwWriteOut;
            static DWORD m_dwAcceptOut;
            static DWORD m_dwReadByte;
            static DWORD m_dwWriteByte;
            };


            #endif

            // socket.cpp

            #include <stdio.h>
            #include "msgdef.h"
            #include "socket.h"
            // 吊線時間
            #define ALL_TIMEOUT 120000
            DWORD CSocketCtrl::m_dwConnectOut = 60000;
            DWORD CSocketCtrl::m_dwReadOut = ALL_TIMEOUT;
            DWORD CSocketCtrl::m_dwWriteOut = ALL_TIMEOUT;
            DWORD CSocketCtrl::m_dwAcceptOut = ALL_TIMEOUT;
            DWORD CSocketCtrl::m_dwReadByte = 0;
            DWORD CSocketCtrl::m_dwWriteByte = 0;

            // 接收數(shù)據(jù)
            BOOL CSocketCtrl::RecvMsg( char *sBuf )
            {
            if( !HasData() )
            return FALSE;
            MsgHeader header;
            int nbRead = this->Recv( (char*)&header, sizeof( header ), MSG_PEEK );
            if( nbRead == SOCKET_ERROR )
            return FALSE;
            if( nbRead < sizeof( header ) )
            {
            this->Recv( (char*)&header, nbRead, 0 );
            printf( "\ninvalid msg, skip %ld bytes.", nbRead );
            return FALSE;
            }

            if( this->Recv( (char*)sBuf, header.stLength, 0 ) != header.stLength )
            return FALSE;

            return TRUE;
            }

            // 發(fā)送數(shù)據(jù)
            int CSocketCtrl::SendMsg( char *sBuf,unsigned short stSize )
            {
            static char sSendBuf[ 4000 ];
            memcpy( sSendBuf,&stSize,sizeof(short) );
            memcpy( sSendBuf + sizeof(short),sBuf,stSize );

            if( (sizeof(short) + stSize) != this->Send( sSendBuf,stSize+sizeof(short),0 ) )
            return -1;
            return stSize;
            }


            // 啟動winsock
            BOOL CSocketCtrl::StartUp()
            {
            WSADATA wsaData;
            WORD wVersionRequested = MAKEWORD( 1, 1 );

            int err = WSAStartup( wVersionRequested, &wsaData );
            if ( err != 0 )
            {
            return FALSE;
            }


            return TRUE;

            }
            // 關(guān)閉winsock
            BOOL CSocketCtrl::ShutDown()
            {
            WSACleanup();
            return TRUE;
            }

            // 得到主機名
            BOOL CSocketCtrl::GetHostName( char szHostName[], int nNameLength )
            {
            if( gethostname( szHostName, nNameLength ) != SOCKET_ERROR )
            return TRUE;
            return FALSE;
            }

            BOOL CSocketCtrl::IsIPsChange()
            {
            return FALSE;
            static int iIPNum = 0;
            char sHost[300];

            hostent *pHost;
            if( gethostname(sHost,299) != 0 )
            return FALSE;
            pHost = gethostbyname(sHost);
            int i;
            char *psHost;
            i = 0;
            do
            {
            psHost = pHost->h_addr_list[i++];
            if( psHost == 0 )
            break;

            }while(1);
            if( iIPNum != i )
            {
            iIPNum = i;
            return TRUE;
            }
            return FALSE;
            }

            // socket是否可以寫
            BOOL CSocketCtrl::CanWrite()
            {
            int e;

            fd_set set;
            timeval tout;
            tout.tv_sec = 0;
            tout.tv_usec = 0;

            FD_ZERO(&set);
            FD_SET(m_sockfd,&set);
            e=::select(0,NULL,&set,NULL,&tout);
            if(e==SOCKET_ERROR) return FALSE;
            if(e>0) return TRUE;
            return FALSE;
            }

            // socket是否有數(shù)據(jù)
            BOOL CSocketCtrl::HasData()
            {
            int e;
            fd_set set;
            timeval tout;
            tout.tv_sec = 0;
            tout.tv_usec = 0;

            FD_ZERO(&set);
            FD_SET(m_sockfd,&set);
            e=::select(0,&set,NULL,NULL,&tout);
            if(e==SOCKET_ERROR) return FALSE;
            if(e>0) return TRUE;
            return FALSE;
            }

            int CSocketCtrl::Recv( char* pBuffer, int nSize, int nFlag )
            {
            return recv( m_sockfd, pBuffer, nSize, nFlag );
            }

            int CSocketCtrl::Send( char* pBuffer, int nSize, int nFlag )
            {
            return send( m_sockfd, pBuffer, nSize, nFlag );
            }

            BOOL CSocketCtrl::Create( UINT uPort )
            {
            m_sockfd=::socket(PF_INET,SOCK_STREAM,0);
            if(m_sockfd==INVALID_SOCKET) return FALSE;
            SOCKADDR_IN SockAddr;
            memset(&SockAddr,0,sizeof(SockAddr));
            SockAddr.sin_family = AF_INET;
            SockAddr.sin_addr.s_addr = INADDR_ANY;
            SockAddr.sin_port = ::htons( uPort );
            if(!::bind(m_sockfd,(SOCKADDR*)&SockAddr, sizeof(SockAddr)))
            {
            SetDefaultOpt();
            return TRUE;
            }
            Close();
            return FALSE;

            }

            void CSocketCtrl::Close()
            {
            ::closesocket( m_sockfd );
            m_sockfd = INVALID_SOCKET;
            }

            BOOL CSocketCtrl::Connect( LPCTSTR lpszHostAddress, UINT nHostPort )
            {
            if(m_sockfd==INVALID_SOCKET) return FALSE;

            SOCKADDR_IN sockAddr;

            memset(&sockAddr,0,sizeof(sockAddr));
            LPSTR lpszAscii=(LPSTR)lpszHostAddress;
            sockAddr.sin_family=AF_INET;
            sockAddr.sin_addr.s_addr=inet_addr(lpszAscii);
            if(sockAddr.sin_addr.s_addr==INADDR_NONE)
            {
            HOSTENT * lphost;
            lphost = ::gethostbyname(lpszAscii);
            if(lphost!=NULL)
            sockAddr.sin_addr.s_addr = ((IN_ADDR *)lphost->h_addr)->s_addr;
            else return FALSE;
            }
            sockAddr.sin_port = htons((u_short)nHostPort);

            int r=::connect(m_sockfd,(SOCKADDR*)&sockAddr,sizeof(sockAddr));
            if(r!=SOCKET_ERROR) return TRUE;

            int e;
            e=::WSAGetLastError();
            if(e!=WSAEWOULDBLOCK) return FALSE;

            fd_set set;
            timeval tout;
            tout.tv_sec = 0;
            tout.tv_usec = 100000;

            UINT n=0;
            while( n< CSocketCtrl::m_dwConnectOut)
            {
            FD_ZERO(&set);
            FD_SET(m_sockfd,&set);
            e=::select(0,NULL,&set,NULL, &tout);

            if(e==SOCKET_ERROR) return FALSE;
            if(e>0) return TRUE;

            if( IsIPsChange() )
            return FALSE;
            n += 100;
            }

            return FALSE;

            }
            // 設(shè)置監(jiān)聽socket
            BOOL CSocketCtrl::Listen( int nBackLog )
            {
            if( m_sockfd == INVALID_SOCKET ) return FALSE;
            if( !listen( m_sockfd, nBackLog) ) return TRUE;
            return FALSE;
            }

            // 接收一個新的客戶連接
            BOOL CSocketCtrl::Accept( CSocketCtrl& ms )
            {
            if( m_sockfd == INVALID_SOCKET ) return FALSE;
            if( ms.m_sockfd != INVALID_SOCKET ) return FALSE;

            int e;
            fd_set set;
            timeval tout;
            tout.tv_sec = 0;
            tout.tv_usec = 100000;

            UINT n=0;
            while(n< CSocketCtrl::m_dwAcceptOut)
            {
            //if(stop) return FALSE;
            FD_ZERO(&set);
            FD_SET(m_sockfd,&set);
            e=::select(0,&set,NULL,NULL, &tout);
            if(e==SOCKET_ERROR) return FALSE;
            if(e==1) break;
            n += 100;
            }
            if( n>= CSocketCtrl::m_dwAcceptOut ) return FALSE;

            ms.m_sockfd=accept(m_sockfd,NULL,NULL);
            if(ms.m_sockfd==INVALID_SOCKET) return FALSE;
            ms.SetDefaultOpt();

            return TRUE;
            }

            BOOL CSocketCtrl::Create(void)
            {
            m_sockfd=::socket(PF_INET,SOCK_STREAM,0);
            if(m_sockfd==INVALID_SOCKET) return FALSE;
            SOCKADDR_IN SockAddr;

            memset(&SockAddr,0,sizeof(SockAddr));
            SockAddr.sin_family = AF_INET;
            SockAddr.sin_addr.s_addr = INADDR_ANY;
            SockAddr.sin_port = ::htons(0);
            //if(!::bind(m_sock,(SOCKADDR*)&SockAddr, sizeof(SockAddr)))
            {
            SetDefaultOpt();
            return TRUE;
            }
            Close();
            return FALSE;
            }

            // 設(shè)置正確的socket狀態(tài),
            // 主要是主要是設(shè)置非阻塞異步傳輸模式
            void CSocketCtrl::SetDefaultOpt()
            {
            struct linger ling;
            ling.l_onoff=1;
            ling.l_linger=0;
            setsockopt( m_sockfd, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling));
            setsockopt( m_sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0);
            int bKeepAlive = 1;
            setsockopt( m_sockfd, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(int));
            BOOL bNoDelay = TRUE;
            setsockopt( m_sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&bNoDelay, sizeof(BOOL));
            unsigned long nonblock=1;
            ::ioctlsocket(m_sockfd,FIONBIO,&nonblock);
            }
            ——————————————————————
            今天晚上寫了一些測試代碼,
            想看看flash究竟能夠承受多大的網(wǎng)絡(luò)數(shù)據(jù)傳輸,

            我在flash登陸到服務(wù)器以后,
            每隔3毫秒就發(fā)送100次100個字符的串 "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" 給flash,
            然后在flash里面接收數(shù)據(jù)的函數(shù)里面統(tǒng)計數(shù)據(jù),


            var g_nTotalRecvByte = 0;
            var g_time = new Date();
            var g_nStartTime = g_time.getTime();
            var g_nCounter = 0;

            mySocket.onData=function(xmlDoc)
            {
            g_nTotalRecvByte += xmlDoc.length;
            // 每接收超過1k字節(jié)的數(shù)據(jù),輸出一次信息,
            if( g_nTotalRecvByte-g_nCounter > 1024 )
            {
            g_time = new Date();
            var nPassedTime = g_time.getTime()-g_nStartTime;
            trace( "花費時間:"+nPassedTime+"毫秒" );
            g_nCounter = g_nTotalRecvByte;
            trace( "接收總數(shù):"+g_nTotalRecvByte+"字節(jié)" );
            trace( "接收速率:"+g_nTotalRecvByte*1000/nPassedTime+"字節(jié)/秒" );

            }
            結(jié)果十分令我意外,
            這是截取的一段調(diào)試信息,
            //
            花費時間:6953毫秒
            接收總數(shù):343212字節(jié)
            接收速率:49361.7143678988字節(jié)/秒
            花費時間:7109毫秒
            接收總數(shù):344323字節(jié)
            接收速率:48434.800956534字節(jié)/秒
            花費時間:7109毫秒
            接收總數(shù):345434字節(jié)
            接收速率:48591.0817273878字節(jié)/秒
            。。。
            。。。
            。。。
            。。。
            花費時間:8125毫秒
            接收總數(shù):400984字節(jié)
            接收速率:49351.8769230769字節(jié)/秒
            花費時間:8125毫秒
            接收總數(shù):402095字節(jié)
            接收速率:49488.6153846154字節(jié)/秒
            花費時間:8125毫秒
            接收總數(shù):403206字節(jié)
            接收速率:49625.3538461538字節(jié)/秒

            我檢查了幾遍源程序,沒有發(fā)現(xiàn)邏輯錯誤,
            如果程序沒有問題的話,
            那么我們得出的結(jié)論是,flash的xml socket每秒可以接收至少40K的數(shù)據(jù),
            這還沒有計算xmlSocket.onData事件的觸發(fā),調(diào)試代碼、信息輸出占用的時間。

            比我想象中快了一個數(shù)量級,
            夠用了,
            flash網(wǎng)絡(luò)游戲我們可以繼續(xù)往下走了
            ——————————————————————
            有朋友問到lag的問題,
            問得很好,不過也不要過于擔(dān)心,
            lag的產(chǎn)生有的是因為網(wǎng)絡(luò)延遲,
            有的是因為服務(wù)器負載過大,
            對于游戲的設(shè)計者和開發(fā)者來說,
            首先要從設(shè)計的角度來避免或者減少lag產(chǎn)生的機會,
            如果lag產(chǎn)生了,
            也不要緊,找到巧妙的辦法騙過玩家的眼睛,
            這也有很多成熟的方法了,
            比如航行預(yù)測法,路徑插值等等,
            都可以產(chǎn)生很好的效果,
            還有最后的絕招,就是提高服務(wù)器的配置和網(wǎng)絡(luò)帶寬,

            從我開發(fā)網(wǎng)絡(luò)游戲這段時間的經(jīng)驗來看,
            我們的服務(wù)器是vc開發(fā)的,
            普通pc跑幾百個玩家,幾百個怪物是沒有問題的,


            又作了一個flash發(fā)送的測試,

            網(wǎng)絡(luò)游戲的特點是,
            出去的信息比較少,
            進來的信息比較多,

            這個很容易理解,
            人操作游戲的速度是很有限的,
            控制指令的產(chǎn)生也是隨機的,
            離散的,

            但是多人游戲的話,
            因為人多,信息的流量也就區(qū)域均勻分布了,

            在昨天接收數(shù)據(jù)的基礎(chǔ)上,
            我略加修改,
            這次,
            我在_root.enterFrame寫了如下代碼,
            _root.onEnterFrame = function()
            {
            var i;
            for( i = 0; i < 10; i++ )
            mySocket.send( ConvertToMsg( "01234567890123456789012345678901234567890123456789" ) );
            return;
            }

            服務(wù)器端要做的是,
            把所有從flash客戶端收到的信息原封不動的返回來,

            這樣,我又可以通過昨天onData里面的統(tǒng)計算法來從側(cè)面估算出flash發(fā)送數(shù)據(jù)的能力,
            這里是輸出的數(shù)據(jù)
            //
            花費時間:30531毫秒
            接收總數(shù):200236字節(jié)
            接收速率:6558.44878975468字節(jié)/秒
            花費時間:30937毫秒
            接收總數(shù):201290字節(jié)
            接收速率:6506.44858906811字節(jié)/秒
            花費時間:31140毫秒
            接收總數(shù):202344字節(jié)
            接收速率:6497.88053949904字節(jié)/秒
            花費時間:31547毫秒
            接收總數(shù):203398字節(jié)
            接收速率:6447.45934637208字節(jié)/秒

            可以看出來,發(fā)送+接收同時做,
            發(fā)送速率至少可以達到5k byte/s

            有一點要注意,要非常注意,
            不能讓flash的網(wǎng)絡(luò)傳輸滿載,
            所謂滿載就是flash在阻塞運算的時候,
            不斷的有數(shù)據(jù)從網(wǎng)絡(luò)進來,
            而flash又無法在預(yù)計的時間內(nèi)處理我這些信息,
            或者flash發(fā)送數(shù)據(jù)過于頻繁,
            導(dǎo)致服務(wù)器端緩沖溢出導(dǎo)致錯誤,

            對于5k的傳輸速率,
            已經(jīng)足夠了,
            因為我也想不出來有什么產(chǎn)生這么大的數(shù)據(jù)量,
            而且如果產(chǎn)生了這么大的數(shù)據(jù)量,
            也就意味著服務(wù)器每時每刻都要處理所有的玩家發(fā)出的海量數(shù)據(jù),
            還要把這些海量數(shù)據(jù)轉(zhuǎn)發(fā)給其他的玩家,
            已經(jīng)引起數(shù)據(jù)爆炸了,
            所以,5k的上傳從設(shè)計階段就要避免的,
            我想用flash做的網(wǎng)絡(luò)游戲,
            除了動作類游戲可能需要恒定1k以內(nèi)的上傳速率,
            其他的200個字節(jié)/秒以內(nèi)就可以了,
            ——————————————————————
            使用于Flash的消息結(jié)構(gòu)定義

            我們以前討論過,
            通過消息來傳遞信息,
            消息的結(jié)構(gòu)是
            struct msg
            {
            short nLength; // 2 byte
            DWORD dwId; // 4 byte

            ....
            data
            }

            但是在為flash開發(fā)的消息中,
            不能采用這種結(jié)構(gòu),

            首先Flash xmlSocket只傳輸字符串,
            從xmlSocket的send,onData函數(shù)可以看出來,
            發(fā)出去的,收進來的都應(yīng)該是字符串,

            而在服務(wù)器端是使用vc,java等高級語言編寫的,
            消息中使用的是二進制數(shù)據(jù)塊,
            顯然,簡單的使用字符串會帶來問題,

            所以,我們需要制定一套協(xié)議,
            就是無論在客戶端還是服務(wù)器端,
            都用統(tǒng)一的字符串消息,
            通過解析字符串的方式來傳遞信息,

            我想這就是flash采用xml document來傳輸結(jié)構(gòu)化信息的理由之一,
            xml document描述了一個完整的數(shù)據(jù)結(jié)構(gòu),
            而且全部使用的是字符串,
            原來是這樣,怪不得叫做xml socket,
            本來socket和xml完全是不同的概念,
            flash偏偏出了個xml socket,
            一開始令我費解,
            現(xiàn)在,漸漸理解其中奧妙。

            ——————————————————————
            Flash Msg結(jié)構(gòu)定義源代碼和相關(guān)函數(shù)

            在服務(wù)器端,我們?yōu)閒lash定義了一種msg結(jié)構(gòu),
            使用語言,vc6
            #define MSGMAXSIZE 512
            // 消息頭
            struct MsgHeader
            {
            short stLength;
            MsgHeader():stLength( 0 ){}

            };
            // 消息
            struct Msg
            {
            MsgHeader header;
            short GetLength(){ return header.stLength; }
            };
            // flash 消息
            struct MsgToFlashublic Msg
            {
            // 一個足夠大的緩沖,但是不會被整個發(fā)送,
            char szString[MSGMAXSIZE];
            // 計算設(shè)置好內(nèi)容后,內(nèi)部會計算將要發(fā)送部分的長度,
            // 要發(fā)送的長度=消息頭大小+字符串長度+1
            void SetString( const char* pszChatString )
            {
            if( strlen( pszChatString ) < MSGMAXSIZE-1 )
            {
            strcpy( szString, pszChatString );
            header.stLength = sizeof( header )+
            (short)strlen( pszChatString )+1;
            }
            }

            };

            在發(fā)往flash的消息中,整個處理過后MsgToFlash結(jié)構(gòu)將被發(fā)送,
            實踐證明,在flash 客戶端的xmlSocket onData事件中,
            接收到了正確的消息,消息的內(nèi)容是MasToFlash的szString字段,
            是一個字符串,

            比如在服務(wù)器端,
            MsgToFlash msg;
            msg.SetString( "move player0 to 100 100" );
            SendMsg( msg,............. );
            那么,在我們的flash客戶端的onData( xmlDoc )中,
            我們trace( xmlDoc )
            結(jié)果是
            move player0 to 100 100


            然后是flash發(fā)送消息到服務(wù)器,
            我們強調(diào)flash只發(fā)送字符串,
            這個字符串無論是否內(nèi)部擁有有效數(shù)據(jù),
            服務(wù)器都應(yīng)該首先把消息收下來,
            那就要保證發(fā)送給服務(wù)器的消息遵循統(tǒng)一的結(jié)構(gòu),
            在flash客戶端中,
            我們定義一個函數(shù),
            這個函數(shù)把一個字符串轉(zhuǎn)化為服務(wù)器可以識別的消息,

            補充:現(xiàn)在我們約定字符串長度都不大于97個字節(jié)長度,


            var num_table = new array( "0","1","2","3","4","5","6","7","8","9" );
            function ConvertToMsg( str )
            {
            var l = str.length+3;
            var t = "";
            if( l > 10 )
            t = num_table[Math.floor(l/10)]+num_table[Number(l%10)]+str;
            else
            t = num_table[0]+num_table[l]+str;
            return t;
            }

            比如
            var msg = ConvertToMsg( "client login" );
            我們trace( msg );
            看到的是
            15client login

            為什么是這個結(jié)果呢?
            15是消息的長度,
            頭兩個字節(jié)是整個消息的長度的asc碼,意思是整個消息有15個字節(jié)長,
            然后是信息client login,
            最后是一個0(c語言中的字符串結(jié)束符)

            當(dāng)服務(wù)器收到15client login,
            他首先把15給分析出來,
            把"15"字符串轉(zhuǎn)化為15的數(shù)字,
            然后,根據(jù)15這個長度把后面的client login讀出來,
            這樣,網(wǎng)絡(luò)傳輸?shù)牡讓泳屯瓿闪耍?br />client login的處理就交給邏輯層,
            ——————————————————————
            前陣子我開發(fā)了Match3D,
            一個可以把三維動畫輸出成為swf的工具,
            而且實現(xiàn)了swf渲染的實時三維角色動畫,
            這可以說是我真正推出的第一個flash第三方軟件,
            其實這以前,
            我曾經(jīng)開發(fā)過幾個其他的flash第三方軟件,
            都中途停止了,
            因為不實用或者市場上有更好的同類軟件,

            隨著互聯(lián)網(wǎng)的發(fā)展,
            flash的不斷升級,
            我的flash第三方軟件目光漸漸的從美術(shù)開發(fā)工具轉(zhuǎn)移到網(wǎng)絡(luò)互連,
            web應(yīng)用上面來,
            如今已經(jīng)到了2004版本,
            flash的種種新特性讓我眼前發(fā)光,

            我最近在帝國的各個板塊看了很多貼子,
            分析里面潛在的用戶需求,
            總結(jié)了以下的幾個我認為比較有意義的選題,
            可能很片面,

            flash源代碼保護,主要是為了抵御asv之類的軟件進行反編譯和萃取
            flash與遠端數(shù)據(jù)庫的配合,應(yīng)該出現(xiàn)一個能夠方便快捷的對遠程數(shù)據(jù)庫進行操作的方法或者控件,
            flash網(wǎng)際互連,我認為flash網(wǎng)絡(luò)游戲是一塊金子,

            這里我想談?wù)刦lash網(wǎng)絡(luò)游戲,
            我要談的不僅僅是技術(shù),而是一個概念,
            用flash網(wǎng)絡(luò)游戲,
            我本身并不想把flash游戲做成rpg或者其他劇烈交互性的游戲,
            而是想讓flash實現(xiàn)那些節(jié)奏緩慢,玩法簡單的游戲,
            把網(wǎng)絡(luò)的概念帶進來,

            你想玩游戲的時候,登上flash網(wǎng)絡(luò)游戲的網(wǎng)站,
            選擇你想玩的網(wǎng)絡(luò)游戲,
            因為現(xiàn)在幾乎所有上網(wǎng)的電腦都可以播放swf,
            所以,我們幾乎不用下載任何插件,
            輸入你的賬號和密碼,
            就可以開始玩了,

            我覺得battle.net那種方式很適合flash,
            開房間或者進入別人開的房間,
            然后2個人或者4個人就可以交戰(zhàn)了,

            這種游戲可以是棋類,這是最基本的,
            用戶很廣泛,
            我腦海中的那種是類似與寵物飼養(yǎng)的,
            就像當(dāng)年的電子寵物,
            每個玩家都可以到服務(wù)器認養(yǎng)寵物,
            然后在線養(yǎng)成寵物,
            還可以邀請別的玩家進行寵物比武,
            看誰的寵物厲害,

            就這樣簡簡單單的模式,
            配合清新可愛的畫面,
            趣味的玩法,
            加入網(wǎng)絡(luò)的要素,
            也許可以取得以想不到的效果,

            今天就說到這里吧,
            想法那么多,要實現(xiàn)的話還有很多路要走,

            希望大家多多支持,積極參與,
            讓我們的想法不僅僅停留于紙上。
            ——————————————————————
            格斗類游戲和休閑類游戲不同,
            最大的差別在于對響應(yīng)時間的要求不在同一數(shù)量級上,

            休閑類游戲允許很大的延遲,
            而動作類游戲需要盡可能小的延遲,

            服務(wù)器的作用,
            要起到數(shù)據(jù)轉(zhuǎn)發(fā)和數(shù)據(jù)校驗的作用,
            在格斗游戲設(shè)計上,
            如果采用和mmorpg相同的服務(wù)器結(jié)構(gòu),
            會產(chǎn)生較大的延遲,
            你可以想象,當(dāng)我打出一招升龍拳,
            然后把這個信息傳遞給服務(wù)器,
            假設(shè)有100毫秒的延遲,
            服務(wù)器收到以后,
            轉(zhuǎn)發(fā)給其他的人,
            又經(jīng)過100毫秒的延遲,
            也就是說,
            你打出一招,對方要得知需要200毫秒的時間,
            實際情況也許更糟,
            這對于格斗類游戲來說是很大的,
            所以現(xiàn)在市面上幾乎沒有真正意義上的多人格斗類網(wǎng)絡(luò)游戲,

            如果要實現(xiàn)格斗的感覺,
            盡可能減少延遲,
            有很多現(xiàn)有經(jīng)過驗證的方法,
            比如把服務(wù)器建立在兩個對戰(zhàn)玩家的機器上,
            這樣就省掉了一個延遲,
            或者每個玩家機器上都建一個服務(wù)器,

            局域網(wǎng)摸使得聯(lián)機游戲很多采用這種的,
            比如星際爭霸,它的聯(lián)網(wǎng)模式服務(wù)器就是建在玩家主機上的,

            減少網(wǎng)絡(luò)延遲是必要的,
            但是,一個平滑爽快的網(wǎng)絡(luò)游戲,
            他的客戶端需要有很多預(yù)測的策略,
            用來估計未來很短的時間內(nèi),
            某個玩家會做什么,
            這樣,就可以在不等待服務(wù)器返回正確信息的情況下,
            直接估計出正確的結(jié)果或者接近正確的結(jié)果,
            當(dāng)服務(wù)器返回的結(jié)果和預(yù)測結(jié)果的誤差在很小范圍內(nèi),
            則預(yù)測成功,否則,可以強行校正客戶端的狀態(tài),
            這樣做,可以盡量減少網(wǎng)絡(luò)延遲帶來的損失,
            絕大部分成功的聯(lián)網(wǎng)游戲,
            在這個策略上,都花了很大的功夫,
            ——————————————————————
            你提到的那個游戲模式是經(jīng)典模式battle.net,
            也就是戰(zhàn)網(wǎng),
            暗黑,星際,魔獸都有戰(zhàn)網(wǎng),

            你下面提到的是一個緩沖模式,
            當(dāng)用戶發(fā)出一招升龍拳,
            并不立即向服務(wù)器發(fā)送指令,
            而是等待5ms(不過在網(wǎng)絡(luò)游戲中5ms設(shè)定也許太樂觀),
            如果在這期間服務(wù)器下發(fā)了新指令,
            與在緩沖中的升龍拳指令進行運算,
            用來得到是否可以發(fā)出升龍拳的評估,
            這樣做是可以的,
            不過實際開發(fā)過程中,
            這種緩沖統(tǒng)計的模式,
            邏輯非常的復(fù)雜,
            網(wǎng)絡(luò)游戲畢竟是在網(wǎng)絡(luò)上玩的,
            而且是多人玩的,


            你如果經(jīng)常上戰(zhàn)網(wǎng)打星際的話,
            會知道它的菜單里面有一個設(shè)定操作延遲的菜單,
            高延遲的話,
            你的操作會緩沖到一定的程度然后統(tǒng)一向服務(wù)器發(fā)出,
            低延遲的話,
            會盡可能的立刻把你的操作向服務(wù)器發(fā)出,

            我的感覺是,
            網(wǎng)絡(luò)格斗類游戲與單機格斗類游戲是不同的,
            想在網(wǎng)絡(luò)上完美實現(xiàn)單機格斗游戲的內(nèi)涵難度極大,
            對于開發(fā)者來說,絕對是一個挑戰(zhàn),
            我想,可以積極的考慮如何在游戲設(shè)計的層面去規(guī)避一些問題。
            ——————————————————————
            我的最近一條諺語是西點軍校的軍官對新兵的訓(xùn)斥,
            。。。。如果一個愚蠢的方法能夠奏效的話,那他就不是愚蠢的方法。。

            這句話簡直太妙了,
            我立刻刪除了手中那些“完美的”代碼,
            用最簡單的方法快速達到我的目的,
            雖然新寫的代碼張得很丑陋,

            可以說說題外話,
            最近策劃一直希望能夠?qū)崿F(xiàn)一個實時渲染的怪物能夠時不時的眨巴眨巴眼睛

            主流的想法是更換眼部的貼圖,
            也有使用骨骼控制眼部肌肉的方法,
            同時還有其他“技術(shù)含量”更高的解決方案,
            不過經(jīng)過評估,實現(xiàn)這兩者難度不大,但是需要耗費比較多的資源和制作時間,更嚴(yán)重點的是程序需要升級代碼來支持這個新的特性,
            后來,在大家集思廣益之下,
            有人提出了高明的辦法,

            把怪物的眼睛鏤空,
            然后在后面放一個畫有眼睛的矩形,
            這個矩形的正面是一個睜大的眼睛,
            而反面是一個閉上的眼睛
            這樣,眨眼就是這個矩形正反翻轉(zhuǎn)的過程,
            不錯,是個好方法,讓人眼前一亮,
            大家一致通過,
            在現(xiàn)有技術(shù)下,這個“愚蠢”的方法解決了這個“復(fù)雜”的問題,

            在有一個更有趣的例子,
            看似調(diào)侃,其實折射出人的智慧,
            日本人做游戲是非常講究細節(jié)的,
            他們最早做三維格斗游戲的時候遇到一個問題,
            當(dāng)我打出一記重拳,命中的時候,
            我的拳頭在畫面上嵌入到敵方模型的肉里面去了,
            日本人覺得不能接受,拳頭怎么能打到肉里面去呢,
            應(yīng)該剛剛好碰到敵方的身體才行,
            可是大家想想,想要剛剛好碰到,
            談何容易,
            美術(shù)要求嚴(yán)格不說,程序可能要精確的計算,
            還不一定能夠滿足要求,
            于是高招出現(xiàn)了,嵌到肉里就嵌到肉里吧,
            我給你來一個閃光,把有問題的畫面給你全遮住,
            這招可真絕,
            美工,程序,策劃不但偷了懶,把玩家給騙了不說,
            玩家還覺得這個游戲真酷,打中了還有閃光的特效,
            ;),實在是高,

            都一點了,我去睡了,
            ——————————————————————
            關(guān)于flash com server、remoting
            最近研究了一下flash com server
            是個好東西,
            他提供了集成的flash網(wǎng)絡(luò)服務(wù),

            我認為,flash com server最具吸引我的是他的媒體能力,

            案例
            使用flash作為客戶端的圍棋游戲,
            flash實現(xiàn)這個游戲的界面和玩法是沒有問題的,
            但是計算圍棋的死活需要大量運算,
            這恰恰不是flash的強項,
            而且我們是網(wǎng)絡(luò)游戲,
            我們會把計算交給flash comm server,
            但是flash comm server的負擔(dān)也很重,
            他要承擔(dān)視頻,音頻和數(shù)據(jù)流的傳輸,
            而且他是一個面向web應(yīng)用的服務(wù)器,
            并不適合做大量的計算,
            所以我們需要一個專門進行運算的服務(wù)器,


            remoting是一個網(wǎng)關(guān),
            用戶可以通過flash com server+remoting間接的取得應(yīng)用程序服務(wù)器的服務(wù),

            flash客戶端可以僅僅和com server交流,
            不必關(guān)心其他應(yīng)用程序服務(wù)器的存在,

            不過因為要實現(xiàn)應(yīng)用程序服務(wù)器,
            就要用到remoting,
            而mm提供的remoting有for java的和for c#的,
            我本人對java和c#不了解,
            我只會使用c++,
            所以我設(shè)計的體系就放棄了remoting,


            現(xiàn)在這個交互性很強的flash娛樂平臺的體系建立起來了,
            flash comm server提供媒體的服務(wù),
            比如視頻,音頻和數(shù)據(jù)流,
            使用xml socket直連由vc開發(fā)的應(yīng)用服務(wù)器,
            這個應(yīng)用服務(wù)器提供計算服務(wù),

            這樣,就把現(xiàn)在網(wǎng)絡(luò)游戲服務(wù)器的大吞吐量的運算和flash com server的媒體服務(wù)融合到一起,

            網(wǎng)絡(luò)游戲盈利模式明確是眾所周知的,
            而且運營模式已經(jīng)比較成熟了,

            我認為以flash為客戶端的休閑娛樂平臺一定是很有商業(yè)前景的,
            而在此貼中,一個由flash構(gòu)成似于聯(lián)眾的網(wǎng)絡(luò)游戲平臺就這樣誕生了,
            他的特點是不需要安裝專門的插件(幾乎所有的上網(wǎng)電腦都有flashplayer),
            圖形表現(xiàn)力強,
            客戶端可以實時下載,
            這種模式可以統(tǒng)一到現(xiàn)在的網(wǎng)絡(luò)游戲收費模式中,
            因為至此flash的網(wǎng)絡(luò)游戲和普通的網(wǎng)絡(luò)游戲沒有什么區(qū)別了,
            要玩游戲就要買點卡,
            我們還可以提供視頻服務(wù)(這種服務(wù)可能要付更多的錢),
            一邊玩,一邊視頻聊天,

            這種平臺用來運作那些節(jié)奏稍慢的休閑益智類網(wǎng)絡(luò)游戲再適合不過了,
            比如紙牌,棋類,
            flash客戶端開發(fā)成本低,
            運行期間穩(wěn)定,占用資源少,

            我現(xiàn)在已經(jīng)擁有成熟的技術(shù)和解決方案,
            最近考慮投資作這個項目,
            如果有對此感興趣的或者想進行商業(yè)合作的朋友,
            發(fā)email到gamemind@sina.com聯(lián)系我
            ?
            ?????????????????????????????????????? 閃之主宰

            posted on 2007-02-05 15:33 楊粼波 閱讀(992) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            亚洲乱码日产精品a级毛片久久| 久久久无码精品亚洲日韩按摩 | 久久99精品久久久久久久不卡| 久久久久久免费一区二区三区| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 日本久久久久久久久久| …久久精品99久久香蕉国产 | 亚洲国产美女精品久久久久∴| 狠狠色综合网站久久久久久久| 伊人久久综合精品无码AV专区| 青青草国产97免久久费观看| 国产精品久久国产精品99盘| 亚洲中文字幕无码久久综合网 | 伊人久久综在合线亚洲2019 | 久久久国产乱子伦精品作者| 久久久久久久久波多野高潮| 人妻系列无码专区久久五月天| 草草久久久无码国产专区| 国产国产成人精品久久| av午夜福利一片免费看久久| 久久99热只有频精品8| 欧美熟妇另类久久久久久不卡| 久久久久久精品久久久久| 国产精品久久久久久久久久影院| 久久精品无码一区二区app| 国产精品成人久久久久久久| 久久国产精品免费一区二区三区 | 久久99精品久久久久久| 精品久久久久久国产| 亚洲国产二区三区久久| segui久久国产精品| 久久青青草原亚洲av无码 | 久久99热精品| 91精品国产9l久久久久| 热99re久久国超精品首页| 久久久久九国产精品| 久久精品国产亚洲AV影院| 精品久久无码中文字幕| 91久久九九无码成人网站| 日韩AV毛片精品久久久| 久久婷婷五月综合色高清|