网页游戏开发入门教程三(简单程序应用)

2013-03-28
作者:LimDeng


网页游戏开发入门教程一(webgame+design)
网页游戏开发入门教程二(游戏模式+系统)
网页游戏开发入门教程三(简单程序应用)



一、选择开发语言

后台:java .net  php

前台:flex javascript ajax

数据库:mysql mssql

用哪种组合,真的不重要。重要的是时间和成本。复杂的地方在数据的交互和完善,而不在技术或效果的实现。往往遇到一些问题。比如地图如何编?人物移动如何实现?其实这些问题从技术上实现都比较容易。难在实现后,数据如何交互。没有解决数据交互的问题,实现这些技术点的意义不大。我用的是php+javascript+mysql。

原因:简单,上手快。可以比较快速的出产品。

二、程序简单应用。

1、模板

为了方便UI的修改。所以用模板。smart template还算方便。很简单。代码也可以嵌套在模板里。唯一的问题是如果美术不会程序,修改模板还得程序来。不科学啊。

smart template的教程网上有。只说一点。可以在模板(.html的文件)里用<?php ?>嵌套任何代码。获得传值。用$_obj[‘xxx’]或者用$_stack[0][‘’]可以和{xxx}写法的代码嵌套。跟.php的文件一样,没任何区别。

2、地图

因为游戏类型不是ogame模式的,所以地图并不是自动生成。而是全从数据库里调用。思路很简单。地图是一整张大图。切成多个小图块。数据库里记录下每个小图块对应大图的绝对坐标。显示的时候,调用相应坐标区域的小图块。

代码类似:

  1. $sql="select * from map where mapx between $xxx and $xxx and  mapy between $ yyy and  $yyy ";
复制代码

意思就是从地图表里,获得横坐标xx到xx。纵坐标xx到xx的所有小图块。比如20个。假设我们写个函数showMap(x,y),把获得的数据全显示出来。地图可以有很多层。

每个小图块都是一个div。具体的控制就用css就行了。小图块可以当作div的背景。也可以用作div里的图片。控制好div的left和top就行了。(left和top就是小图块相对于大图块的绝对坐标)showMap(x,y)就放在下面两个层的里面。

一个层处理地图大小:

  1. <div style="position:relative;width:".$mapwidth."px;height:".$mapheight."px;overflow:hidden" >
复制代码

一个层处理拖动:

  1. <div style="position:absolute;z-index:10;left:2px;top:2px;width:".$mapwidth."px;height:".$mapheight."px;" onmousedown="fDragging(this, event,false);">

  2. //处理拖动的js代码。(网上抄的。。感谢这位大大。)
  3. <script>
  4.    function fDragging(obj, e, limit){
  5.          if(!e) e=window.event;
  6.          var x=parseInt(obj.style.left);
  7.          var y=parseInt(obj.style.top);
  8.          
  9.          var x_=e.clientX-x;
  10.          var y_=e.clientY-y;
  11.          
  12.          if(document.addEventListener){
  13.              document.addEventListener('mousemove', inFmove, true);
  14.              document.addEventListener('mouseup', inFup, true);
  15.              document.body.style.cursor="move";
  16.          } else if(document.attachEvent){
  17.              document.attachEvent('onmousemove', inFmove);
  18.              document.attachEvent('onmouseup', inFup);
  19.              document.body.style.cursor="move";
  20.          }
  21.          
  22.          inFstop(e);     
  23.          inFabort(e)
  24.          
  25.          function inFmove(e){
  26.             
  27.              var evt;
  28.              if(!e)e=window.event;
  29.               
  30.              if(limit){
  31.                  var op=obj.parentNode;
  32.                  var opX=parseInt(op.style.left);
  33.                  var opY=parseInt(op.style.top);
  34.                   
  35.                  if((e.clientX-x_)<0) return false;
  36.                  else if((e.clientX-x_+obj.offsetWidth+opX)>(opX+op.offsetWidth)) return false;
  37.                   
  38.                  if(e.clientY-y_<0) return false;
  39.                  else if((e.clientY-y_+obj.offsetHeight+opY)>(opY+op.offsetHeight)) return false;
  40.                  //status=e.clientY-y_;
  41.              }
  42.               
  43.              obj.style.left=e.clientX-x_+'px';
  44.              obj.style.top=e.clientY-y_+'px';
  45.               
  46.              inFstop(e);
  47.          } // shawl.qiu script
  48.          function inFup(e){
  49.              var evt;
  50.              if(!e)e=window.event;
  51.               
  52.              if(document.removeEventListener){
  53.                  document.removeEventListener('mousemove', inFmove, true);
  54.                  document.removeEventListener('mouseup', inFup, true);
  55.              } else if(document.detachEvent){
  56.                  document.detachEvent('onmousemove', inFmove);
  57.                  document.detachEvent('onmouseup', inFup);
  58.              }
  59.               
  60.              inFstop(e);
  61.             
  62.              document.body.style.cursor="auto";
  63.             
  64.              //实现类似google地图的拖动效果。
  65.              ajaxRead('map.php?mapx='+(e.clientX-x_)+'&mapy='+(e.clientY-y_)+'','2');
  66.             
  67.          } // shawl.qiu script
  68.    
  69.          function inFstop(e){
  70.              if(e.stopPropagation) return e.stopPropagation();
  71.              else return e.cancelBubble=true;            
  72.          } // shawl.qiu script
  73.          function inFabort(e){
  74.              if(e.preventDefault) return e.preventDefault();
  75.              else return e.returnValue=false;
  76.          } // shawl.qiu script
  77.      }
  78. //]]>
  79. </script>
复制代码

注意下面这段代码:

  1. ajaxRead('map.php?mapx='+(e.clientX-x_)+'&mapy='+(e.clientY-y_)+'','2');
复制代码

这句代码的位置,是在拖动层后,释放鼠标的时候触发的。你可以用alert(“地图拖动到了这里”); 替换。测试下效果。这句代码的意思是,根据当前地图被拖动的坐标。调用一个ajax。也就是重新从数据库里获得地图信息。AjaxRead()是一个ajax的调用函数。你可以全部自己写。也可以用如prototype.js之类的框架写。

  1. //处理ajax的代码。(还是网上抄的,有轻微的改动。。。唉,怎么老抄呢。。主要是为了节约开发时间。。还有一点就是我的JavaScript很垃圾的(*^__^*) 嘻嘻)
  2. function ajaxRead(file,action)
  3. {
  4.      var xmlObj = null;
  5.      if(window.XMLHttpRequest)
  6.      {
  7.          xmlObj = new XMLHttpRequest();
  8.      }
  9.      else if(window.ActiveXObject)
  10.      {
  11.          xmlObj = new ActiveXObject("Microsoft.XMLHTTP");
  12.      }
  13.      else
  14.      {
  15.          return;
  16.      }
  17.      
  18.      
  19.      function ajaxDo(action)
  20.      {
  21.          switch(action)
  22.          {

  23.              case "2":
  24.                  document.getElementById('display').innerHTML = xmlObj.responseText;//这里的display是你在页面上层的id。上面的地图代码都需要放到这个层里。如<div id=display name=display></div>写id和name,是为了方便firefox和ie的兼容。
  25.                  
  26.              break;
  27.          }
  28.      
  29.      }
  30.      
  31.      xmlObj.onreadystatechange = function()
  32.      {   
  33.          /*
  34.          if(xmlObj.readyState == 1 )//loading状态。
  35.          {
  36.              document.getElementById('xianshi2').innerHTML = "正在载入";
  37.          }
  38.          */
  39.          if(xmlObj.readyState == 4)//完成状态时。
  40.          {
  41.              ajaxDo(action);
  42.          }
  43.      }
  44.      
  45.      xmlObj.open ('GET', file, true);
  46.      //xmlObj.reload('GET', file, true);
  47.      xmlObj.send (null);
  48.      //xmlObj.abort ('');   
  49. }
复制代码

整个代码的意思就是:

当拖动地图释放鼠标后,显示层重新获得数据。并无刷新的显示出来。地图里的图片都用的png32的透明图。

Ie7和ff3都没问题。遇到ie6的话。。用gif的替代吧。map.php的功能。根据获得的x,y显示相应的一谢谢小图块。这个功能其实就是上面说的showMap(x,y),这个很像google地图的拖动。不过简单了很多。简单,效果还不错。2、角2、角色属性

因为设定的要求。角色需要有装备加成,有状态加成(buff,debuff)。这时候,把所有需要的加成,都放到角色类里。是一个很好的方法。

大概像这样:

  1. class role
  2. {
  3. //获得角色数据。
  4. getRloe()
  5. {
  6.   从数据库里获得角色信息。
  7. }
  8. //获得装备加成。
  9.   getEquip()
  10. {
  11.      获得装备加成信息。
  12. }

  13. //获得状态加成
  14. getState()
  15. {
  16.     获得状态加成信息。
  17. }

  18.      //把上面获得的信息相加或者相减,或者调整。

  19. //返回角色数据。
  20.     Return xxx
  21. }
复制代码

专门把这条提出来说。是因为没把加成放到角色对象里时。每次要战斗或者要干点什么的时候。获得角色数据后,还要加一大堆代码处理加成。重复太多。一让代码前置,世界就清静了。。。

3、道具

道具比较特殊。因为种类繁多,使用方式多,可能有多个存放地点,可能有唯一道具。有天看了web魔兽的代码。发现他的道具只有一个表。有一个字段,来处理道具位置,如(1,拍卖行,2,背包,3,仓库,4,商店)这个办法挺好的。不过,如果道具的复杂度上去了。比如不同的仓库,不同的拍卖行,需要合成等等。还是只有分表。

基础道具表:
id

itemname 名称

itemprice  价格

itemimage  图片

itemtype 类型

uptype  增加类型

uppoint 增加点数

addtype  增加类型(永久)

addpoint 增加点数(永久)

cleardebuff 清除debuff

addbuff  增加buff

从uptype开始。都可以写成xx|yy|zz的形式。最好一一对应。分割符号可以自己选。
调用和处理数据的时候,可以用类似下面的方式:

  1. $uptype = explode("|", $iteminfo['uptype']);
  2. $uppoint = explode("|", $iteminfo['uppoint']);
  3. for ($j=0;$j<count($uptype);$j++)
  4. {
  5.       echo $uptype[$j];
  6.   echo $uppoint[$j];
  7. }
复制代码

仓库,拍卖行,商店,背包等等。承载道具的地方。只要有个id字段来存道具id就可以。至于是横表或者是纵表,根据实际需要选择。目前为止,道具看上去处理得还不错。这时候,策划说。道具需要有唯一的,需要能附魔。ok,那么你把所有组合都填到道具表里吧。合成也就是a+b=c而已。。一计算。比如40个可能附魔的东西。200个可以附魔的道具。40*200=8000。显然,策划不会同意的。那么头痛的就是程序了。怎么处理呢。加表吧。

唯一道具表:

id 唯一道具id(与普通道具id不能重复。方便背包等等调用)

temp_id 临时id(默认0。合成道具的时候可能会用到。)
itemid 原始道具id(获得道具的初始值)

fumo_id 附魔id。(默认0,即无附魔)


附魔表:(即增加的属性)
id
uptype  增加类型

uppoint 增加点数

cleardebuff 清除debuff

addbuff  增加buff


现在看功能修改
首先是道具类:

  1. class Item
  2. {
  3.    getItem()
  4.    {
  5.    //以前是直接根据id获得道具信息就ok了。

  6.   //现在增加了附魔
  7.   
  8.   //首先判断道具id是否属于唯一道具。(比如普通道具1-10000。唯一道具id的从10001开始。如果觉得这样不好,那么基础道具表里,加个字段。判断道具是否唯一)                 

  9. if (道具唯一)
  10.    {
  11.       //从唯一道具表获得原始道具id和附魔id
  12.       //根据原始道具id,或者道具基础信息。
  13.       //根据附魔id,获得附魔加成信息。
  14.       //两边值相加。
  15.      Return 道具信息。                     
  16. }
  17. else
  18. {
  19.      直接获得道具信息。
  20. }

  21.    }
  22. }
复制代码

附魔功能:

道具A。(基础道具)+道具B。(基础道具) =道具C。(唯一道具)

也就是唯一道具是在附魔功能执行的时候生成。以背包举例。没附魔前。

背包内道具A。id为1。

背包内道具B。id为2。

当执行附魔功能后。道具A,道具B的id都置0(横表),或者删除了(纵表)。生成一个唯一数。temp_id。(md5生成就行了。)生成一个唯一道具。这时候,根据temp_id,让A的背包再次获得唯一道具的id。道具,比较完善的解决了。

以下部分均涉及到一些商业问题,所以只能给思路,及很少的代码。



4、记时器

处理等待xx时间后,执行xx的问题。php自带一个sleep()函数。等待时间也可以控制。

但是显然,不管从运用还是效率上讲。都不足以支持游戏计时的。思路很简单。将需要倒计时的事件的所有参数,以及开始时间、结束时间。都存储到一个表里。前台用javascript倒计时,时间到后,通过ajax调用时间到后的处理程序。后台每隔一定时间,自动执行一次调用时间到后的处理程序。

至少需要三个php页面。

一个用来写存取定时的内容。

一个处理前台时间到时,结束操作。

一个处理后台定时刷新,判断时间到了就执行结束,时间未到不作处理。

miracle:计时器是不同的计时器对应不同的事件,还是可以多个事件都调用同一个计时器,如果一个玩家他调用了一个计时器计时一个建筑建设多长时间,在之中又调用了这个计时器用来计时另一个建筑建设多长时间,这样行不行的?会不会有冲突?


键盘上的烟灰:

多个事件对应1个计时器。

你可以在timer里增加一个字段。比如叫做actiontype(事件类型)

每个用户可以同时处理多件事。只是每个事情都有固定编号。

比如你的用户允许同时做5件事情。那么actiontype里直接编号为1-5。调用计时器的时候,根据不同的编号,你就知道这是用户的第某个“线程”。

miracle

如果是不同的用户,调用同一个计时器是不会发生冲突的吧

键盘上的烟灰:

当然不会。你看。userid可以用来确定某一个用户。actiontype可以用来确定是第几个线程。

5、事件控制

结合记时器,处理开始(),过程(),结束()

  1. interface Action
  2. {
  3.      function doAction();
  4.      function beginAction();
  5.      function processAction();
  6.      function endAction();
  7. }
  8. //简单事件工厂
  9. class ActionFactory
  10. {
  11.      public function getAction($what)
  12.     {
  13.        $ActionName = $what;
  14.        return new $ActionName;
  15.     }
  16. }

  17. //比如移动
  18. class Move implements Action
  19. {
  20. function doAction()
  21.      {
  22.    具体执行函数
  23.        什么时候该这行哪一个过程。都在这里判断。
  24. }
  25.      function beginAction()
  26.      {
  27.      事件开始时候执行。
  28.      这里可以把数据存到记时器里。以后就从记时器里取数据了。
  29. }
  30.      function processAction()
  31.      {
  32.      从记时器里取数据。
  33.      事件执行的过程。比如用户刷新页面的时候。如果仍然在倒计时。那么就是调用这里了。
  34. }
  35.      function endAction()
  36.      {
  37.      从记时器里取数据。
  38.      事件结束的过程。
  39.      记时到后,完成事件。
  40. }
  41. }

  42. //第一次调用的时候。
  43. $Action = new ActionFactory();
  44. $InstanceAction = $Action->getAction("Move");
  45. $InstanceAction->set ($parameter);
  46. $InstanceAction->doAction();

  47. //以后调用的时候。
  48. $Action = new ActionFactory();
  49. $InstanceAction = $Action->getAction("Move");
  50. //这时候,事件的参数或数据都从记时器里取得。
  51. $InstanceAction->doAction();
复制代码


6、战斗

即时和半即时的回合战斗(两人或多人即时回合制战斗)比较繁琐。

至少包含:

前台:

自动接收邀请信息。Ajax

显示战斗过程。Ajax

回合倒计时间。javascript

后台:

发送邀请,接受,拒绝,超时。一个表。战斗数据。一个表。保存双方或多方的数据,包括回合时间,第几回合等。

战斗控制。一系列函数。处理玩家的操作,将操作存到战斗数据表里。时间到后执行操作。

出兵后,直接返回战报。

写在事件里就行了。

  1. function endAction()

  2.      {
  3.         从记时器里取数据。

  4.      生成回合,生成战报。

  5. }
复制代码


最新评论
暂无评论
参与评论

商务合作 查看更多

编辑推荐 查看更多
【爆款新游】【潜力佳作】分析系列
推广