网页游戏中PK系统的实现

     在游戏开发过程中,写过一个简单的PK系统面板,涉及到前端和后端的交互,我将自己制作的流程分享给大家,大概流程是这样:前端发送PK邀请给后端,后端受到请求后将信息返回给前端处理,先来看下整个流程图及思路:

image

     整个流程如上图所示,经过服务器和客户端的几次交互来确定PK双方的状态及是否能受到攻击。服务端将要给我五条协议,大概如下:

message m_role_pk_invite_tos<29301>[router=role,mod_role_handle]{
   required double                          role_id                 = 1;//邀请PK的对象ID
}
message m_role_pk_invite_toc<29302>{
 required int32     err_code     = 1;//0是已向对方发送邀请,请等待回应
}
message m_role_pk_info_toc<29304>{
   required string                          invite_name             = 1;//邀请者名称
   required double                          invite_id                 = 1;//邀请者ID
   required int32                           invite_level           = 1;//邀请者level
   required double                          invite_time          = 1;//计时结束时间戳
}
message m_role_pk_answer_tos<29305>[router=role,mod_role_handle]{
   required int32                          back_type                 = 1;//0为同意,1为拒绝,2为超时
   required double                          to_id          = 1;//同意/拒绝/超时的对象
}
message m_role_pk_answer_toc<29306>{
   required int32                          err_code                 = 1;//0为成功(提示:开始PK)
   required double                          to_id          = 1;     //成功时读取PK对象ID
}

     其中toc代表服务端发给客户端的数据,tos表示客户端发给服务端的消息,他们都是封装好的数据结构信息,这些都是我们和后端约定好的协议,一般后端发给前端我们都会制定一个err_code,它表示该协议是否成功发送到客户端。

     前端的代码处理比较简单,在编码过程中,将数据和视图分别放在不同的模块中,便于管理,在接受邀请后,我们会给被邀请方一个弹窗显示并给60秒的时间来处理。重点看下服务端发给客户端处理的代码:

package modules.invitePK {
  /* 五条协议 */
 import proto.m_role_pk_answer_toc;
 import proto.m_role_pk_answer_tos;
 import proto.m_role_pk_info_toc;      
 import proto.m_role_pk_invite_toc;
 import proto.m_role_pk_invite_tos;

 public class InvitePKCase extends BaseModule {

  public function InvitePKCase() {
   super();
  }
  private var invitePKPanel:InvitePKPanel;
  private static var instance:InvitePKCase;

  public static function getInstance():InvitePKCase {
   return instance ||= new InvitePKCase(); //单例模式
  }

  override protected function initListeners():void {
   addMessageListener(ModuleCommand.INVITE_PK, toInvite); //调用侦听事件邀请PK
   addMessageListener(ModuleCommand.INVITE_ANSWER, toAnswer)  
   addMessageListener(ModuleCommand.INVITE_OPENPANEL, toOpenPanel) //出面板
   /* 监听服务端发来的数据,包括三条协议 */
   addSocketListener(SocketCommand.ROLE_PK_INFO, onPKInfo);
   addSocketListener(SocketCommand.ROLE_PK_INVITE, onInvite);
   addSocketListener(SocketCommand.ROLE_PK_ANSWER, onAnswer);
  }

  private function toOpenPanel(vo:m_role_pk_info_toc):void {
   invitePKPanel = new InvitePKPanel(vo.invite_name, vo.invite_id, vo.invite_level, vo.invite_time); //new一个模板
   invitePKPanel.durTime = vo.invite_time;
   invitePKPanel.open(); //打开面板
   invitePKPanel.readTime(); //开始计时
  }

  private function onAnswer(vo:m_role_pk_answer_toc):void {
   if (vo.err_code == 0) {
    if (vo.pk_type != 0) {
     FightModule.getInstance().toOpenPanel(vo);//显示PK双方的信息
     InvitePKManager.pkRoleID = vo.to_id;
     MangerTime.last_time = 30;   //MangerTime是管理时间类
     MangerTime.endTime(); //读取时间
    } else {
     InvitePKManager.pkRoleID = 0;
     Dispatcher.dispatch(ModuleCommand.FIGHT_CLOSEPANEL); //发送关闭消息
    }
   } else {
    TopTip.addMouseTipsMsg(ErrorCode.getError(vo.err_code));
   }
  }

  private function toAnswer(back_type:int, id:Number):void {
   var vo:m_role_pk_answer_tos = new m_role_pk_answer_tos();  //发给服务端
   vo.back_type = back_type;
   vo.to_id = id;
   sendSocketMessage(vo);
  }

  private function onPKInfo(vo:m_role_pk_info_toc):void {
   dispatch(ModuleCommand.INVITE_OPENPANEL, vo); //开面板,显示邀请者的信息
  }

  private function onInvite(vo:m_role_pk_invite_toc):void {
   // 发给客户端的消息,提示我已经邀请你pk
   if (vo.err_code == 0) {
    TopTip.addMouseTipsMsg("已邀请PK,请等待回应");  //表示发送成功
   } else {
    TopTip.addMouseTipsMsg(ErrorCode.getError(vo.err_code)); //返回失败的原因
   }

  }

  private function toInvite(tarid:Number):void {
   //这里面有调用者的ID,由客户端发给服务端
   var vo:m_role_pk_invite_tos = new m_role_pk_invite_tos();
   vo.role_id = tarid;
   sendSocketMessage(vo); //发送消息
  }
 }
}

     上面的MangerTime类主要是用于PK双方时间的限定,如果在双方都接受邀请之后30秒内没有动作,或者30秒有攻击但是接着停下来没有继续攻击,时间又要重新开始计时,我这里用一个静态类来处理:

package modules.fight {
import com.managers.Dispatcher;
import com.scene.sceneManager.LoopManager;
import modules.ModuleCommand;
import modules.fight.FightView;
    public class MangerTime {
        public static var last_time:int = 30;  //静态30秒,每次攻击玩可以重置这个时间
        public static function updateEndTime():void {
        last_time--;
        if (last_time < 0) {
        last_time = 0;
        LoopManager.removeFromSceond("updateTime");
        FightView.getInstance().toCloseWindow(); //pk结束,关闭面板
   } 
}
    public static function readTime():void {
    LoopManager.addToSecond("updateTime", updateEndTime);  //启动一个定时器开始计时
    }
  }
}

     在监听服务器发过来的fight事件的时候,可以改变last_time对时间进行重置,加入计时器,30秒到后就可以关闭双方的面板,但是一定要注意的是,我们这里用到AS3中的字典来存储函数的名称,所以在命名的时候一定不要重复命名,不然可能会导致程序运行出错或者PK双方只关闭一个面板。

     总结:在遇到和服务器交互的时候只需要针对对应协议做出相应的逻辑判断,代码分别用模块化的形式来清晰表达出自己的思想,后面开发的同志看代码也就不会很困难,游戏行业的开发速度非常快,有好的框架开发起来速度会非常快,所以平时应该多专研这些面板底层机制是如何实现的,并记录一些自己的想法。

posted @ 2014-12-21 16:39  杠上开花  阅读(876)  评论(0编辑  收藏  举报