【一起做网游吧10.0】Start Game:加载地图

虽然从目前的角度来说,这个教程在不断的前进着。但从寻找资源来说,进展困难,我本来想让10.0的教程的内容为实现一个玩家在地图中行走,但资源找来找去,只找到了相对合适的地图资源,人物的资源一直没有找到,因此就先实现了地图资源。

考虑到目前客户端j2me的局限,我决定j2me的jar中,仅留有代码,所有的地图资源等,都通过服务器下载。因此服务器端必须对这一点进行支持。也因此增加了两个命令。而代码的更新,更大的体现在客户端。

先看下目前客户端的截图吧:

上述截图的资源完全来自开源的daimonin,我本身不会进行美工的工作内容,如果可以的话,就不用这么辛苦的找资源了。

为了实现资源的下载,在客户端,我修改了底层传输支持的Netstream的机制:

        int stillbytes = 0;
                if ( ( stillbytes = is.available() ) > 0 ) {
                    byte[] buf = new byte[stillbytes];
                    is.read( buf );
                    int buf_index = 0;
                    while (stillbytes > 0) {
                        int read_size = stillbytes;
                        if ( stillbytes + iReceiveEndindex > RECEIVEBUFLENGTH ) {
                            System.out.println("buf too small stillbytes=" + stillbytes + "receiveendindex" + iReceiveEndindex);
                            read_size = RECEIVEBUFLENGTH - iReceiveEndindex;
                        }
                        System.arraycopy( buf, buf_index, iReceivebuf, iReceiveEndindex, read_size );
                        iReceiveEndindex += read_size;
                        buf_index += read_size;
                        stillbytes -= read_size;
                        for ( ; iReceiveEndindex >= 8; ) {
                            int curindex = 0;
                            int alength = bytetoint( iReceivebuf, curindex );
                            curindex += 4;
                            int cmd = bytetoint( iReceivebuf, curindex );
                            curindex += 4;
                            alength -= 4;
                            System.out.println("get");
                            for ( int i = 0; i < alength+8; i++ ) {
                                int high = 0;
                                int low = 0;
                                high = ((iReceivebuf[i] & 0xf0) >> 4);
                                low = (iReceivebuf[i] & 0x0f);
                                System.out.print(" " + DIGITS[high]+DIGITS[low] );
                            }
                            System.out.println("");
                            if ( curindex + alength > RECEIVEBUFLENGTH ) {
                                System.out.println("error data");
                            } else if ( curindex + alength > iReceiveEndindex ) {
                                System.out.println("wait data" + curindex + "::"+alength+"::"+iReceiveEndindex);
                                break;
                            } else {
                                Packet packet = new Packet( alength, cmd, iReceivebuf, curindex );
                                curindex += alength;
                                System.arraycopy( iReceivebuf, curindex, iReceivebuf, 0, RECEIVEBUFLENGTH - curindex );
                                iReceiveEndindex -= curindex;
                                iPacketArray.addElement( packet );
                            }
                        }
                    }
                    {
                        buf = null;
                    };
                }
       

中间有一段打印接收到数据的16进制的代码,可以屏蔽掉,现在保留主要是为了方便调试用。

由于使用TCP/IP进行的传输,从底层硬件来讲,不会出现漏掉某个数据包,但如果程序接收算法不对的话,则可能会漏掉。我在测试的时候,服务器最多每次传输1024字节,但客户端每次却可能接收超过4096字节,因此,必须按照规则自己分包。

对于daimonin的地图编辑器,虽然写了个插件来导出资源,但有些内容还是不清楚如何导出,比如每个地图元件的详细信息。而客户端中,我将所有的动画都在播放,然后场景中有一个门的开关动画,就一直在那里开合开合的播放。对于动画的导出,我用erlang写了一个简单的导出脚本:

       %% @doc 对动画中每一帧的文件名生成列表.
animation([]) ->
    [];
animation([Name|Rest]) ->   
    L = length(Name),
    [<>, Name] ++ animation(Rest).

%% @doc 根据参数写动画文件,Name1为动画名称,Face为面数,List为帧列表。均不需要添加data/的路径,客户端请求数据的时候没有data/的前缀,文件读取自动添加.
write_ani(Name1, Face, List) ->
    Name = "data/"++Name1,
    case file:open(Name, [write, binary, raw]) of
        {ok, S} ->
            Length = length(List),
            Data1 = list_to_binary([<>, <>, animation(List)]),
            CRC32 = erlang:crc32(Data1),
            Data = list_to_binary([<>, Data1]),
            case file:pwrite(S, 0, Data) of
                ok ->
                    map:register_obj([{"data/"++File, "data/"++File, file}|| File <- List]);
                {error, Reason} ->
                    {error, Reason}
            end
    end.
       

上出代码转换动画信息,并将动画中的每个元件注册到数据库中,因为只有注册到数据库中的元件才被允许传给客户端。具体的用法可以看map:test_load。

现在服务器的启动,因为需要做太多的设置,因此我也做了个辅助测试的,调用start-dev.sh启动之后,可以调用server:start_test来启动5个服务器的节点,启动3个属于test1服务器,2个属于test2服务器。接下来调用map:test_load(),可以完成相关的地图设置。这些调用,只用在第一次编译之后调用一次即可,以后不用再重新设置。接下来还是找资源紧要。

最后附上目前的服务器结构图:

发布者

rix

如果连自己都不爱自己,哪还有谁来爱你