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

一、选择开发语言
后台: 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 ";

意思就是从地图表里,获得横坐标xxxx。纵坐标xxxx的所有小图块。比如20个。假设我们写个函数showMap(x,y),把获得的数据全显示出来。地图可以有很多层。
每个小图块都是一个div具体的控制就用css就行了。小图块可以当作div的背景。也可以用作div里的图片。控制好divlefttop就行了。(lefttop就是小图块相对于大图块的绝对坐标)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 
 3  // 处理拖动的js代码。(网上抄的。。感谢这位大大。)
 4  < script >
 5     function  fDragging(obj, e, limit){ 
 6           if ( ! e) e = window.event; 
 7           var  x = parseInt(obj.style.left); 
 8           var  y = parseInt(obj.style.top); 
 9           
10           var  x_ = e.clientX - x; 
11           var  y_ = e.clientY - y; 
12           
13           if (document.addEventListener){ 
14              document.addEventListener( ' mousemove ' , inFmove,  true ); 
15              document.addEventListener( ' mouseup ' , inFup,  true ); 
16              document.body.style.cursor = " move " ;
17          }  else   if (document.attachEvent){ 
18              document.attachEvent( ' onmousemove ' , inFmove); 
19              document.attachEvent( ' onmouseup ' , inFup); 
20              document.body.style.cursor = " move " ;
21          } 
22           
23          inFstop(e);     
24          inFabort(e) 
25           
26           function  inFmove(e){ 
27              
28               var  evt; 
29               if ( ! e)e = window.event; 
30               
31               if (limit){ 
32                   var  op = obj.parentNode; 
33                   var  opX = parseInt(op.style.left); 
34                   var  opY = parseInt(op.style.top); 
35                   
36                   if ((e.clientX - x_) < 0 return   false
37                   else   if ((e.clientX - x_ + obj.offsetWidth + opX) > (opX + op.offsetWidth))  return   false
38                   
39                   if (e.clientY - y_ < 0 return   false
40                   else   if ((e.clientY - y_ + obj.offsetHeight + opY) > (opY + op.offsetHeight))  return   false
41                   // status=e.clientY-y_; 
42              } 
43               
44              obj.style.left = e.clientX - x_ + ' px '
45              obj.style.top = e.clientY - y_ + ' px '
46               
47              inFstop(e); 
48          }  //  shawl.qiu script 
49           function  inFup(e){ 
50               var  evt; 
51               if ( ! e)e = window.event; 
52               
53               if (document.removeEventListener){ 
54                  document.removeEventListener( ' mousemove ' , inFmove,  true ); 
55                  document.removeEventListener( ' mouseup ' , inFup,  true ); 
56              }  else   if (document.detachEvent){ 
57                  document.detachEvent( ' onmousemove ' , inFmove); 
58                  document.detachEvent( ' onmouseup ' , inFup); 
59              } 
60               
61              inFstop(e); 
62              
63              document.body.style.cursor = " auto " ;
64              
65               // 实现类似google地图的拖动效果。
66              ajaxRead( ' map.php?mapx= ' + (e.clientX - x_) + ' &mapy= ' + (e.clientY - y_) + '' , ' 2 ' );
67              
68          }  //  shawl.qiu script 
69    
70           function  inFstop(e){ 
71               if (e.stopPropagation)  return  e.stopPropagation(); 
72               else   return  e.cancelBubble = true ;             
73          }  //  shawl.qiu script 
74           function  inFabort(e){ 
75               if (e.preventDefault)  return  e.preventDefault(); 
76               else   return  e.returnValue = false
77          }  //  shawl.qiu script 
78      } 
79  // ]]> 
80  < / 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 
24               case   " 2 "
25                  document.getElementById( ' display ' ).innerHTML  =  xmlObj.responseText; // 这里的display是你在页面上层的id。上面的地图代码都需要放到这个层里。如<div id=display name=display></div>写id和name,是为了方便firefox和ie的兼容。
26                  
27               break
28          }
29      
30      }
31      
32      xmlObj.onreadystatechange  =   function ()
33      {    
34           /*
35          if(xmlObj.readyState == 1 )//loading状态。
36          {
37              document.getElementById('xianshi2').innerHTML = "正在载入";
38          }
39           */
40           if (xmlObj.readyState  ==   4 ) // 完成状态时。
41          {
42              ajaxDo(action);
43          }
44      }
45      
46      xmlObj.open ( ' GET ' , file,  true );
47       // xmlObj.reload('GET', file, true);
48      xmlObj.send ( null );
49       // xmlObj.abort ('');    
50  }

整个代码的意思就是:
当拖动地图释放鼠标后,显示层重新获得数据。并无刷新的显示出来。地图里的图片都用的png32的透明图。Ie7ff3都没问题。遇到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  // 获得状态加成
15  getState()
16  {
17     获得状态加成信息。
18  }
19 
20       // 把上面获得的信息相加或者相减,或者调整。
21 
22  //返回角色数据。
23      Return  xxx
24  }

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

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   
 9   //首先判断道具id是否属于唯一道具。(比如普通道具1-10000。唯一道具id的从10001开始。如果觉得这样不好,那么基础道具表里,加个字段。判断道具是否唯一)                 
10 
11  if  (道具唯一)
12    {
13        // 从唯一道具表获得原始道具id和附魔id
14       //根据原始道具id,或者道具基础信息。
15       //根据附魔id,获得附魔加成信息。
16       //两边值相加。
17       Return  道具信息。                      
18  }
19  else
20  {
21      直接获得道具信息。
22  }
23 
24    }
25  }

附魔功能:
道具A。(基础道具)+道具B(基础道具) =道具C(唯一道具)
也就是唯一道具是在附魔功能执行的时候生成。以背包举例。没附魔前。
背包内道具Aid1
背包内道具Bid2
当执行附魔功能后。道具A,道具Bid都置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  // 比如移动
19  class  Move  implements  Action
20  {
21  function  doAction()
22      {
23    具体执行函数
24        什么时候该这行哪一个过程。都在这里判断。
25  }
26       function  beginAction()
27      {
28      事件开始时候执行。
29      这里可以把数据存到记时器里。以后就从记时器里取数据了。
30  }
31       function  processAction()
32      {
33      从记时器里取数据。
34      事件执行的过程。比如用户刷新页面的时候。如果仍然在倒计时。那么就是调用这里了。
35  }
36       function  endAction()
37      {
38      从记时器里取数据。
39      事件结束的过程。
40      记时到后,完成事件。
41  }
42  }
43 
44  // 第一次调用的时候。
45  $Action   =   new  ActionFactory();
46  $InstanceAction   =   $Action -> getAction( " Move " );
47  $InstanceAction -> set ( $parameter );
48  $InstanceAction -> doAction();
49 
50  // 以后调用的时候。
51  $Action   =   new  ActionFactory();
52  $InstanceAction   =   $Action -> getAction( " Move " );
53  // 这时候,事件的参数或数据都从记时器里取得。
54  $InstanceAction -> doAction();

 

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

至少包含:
前台:

自动接收邀请信息。Ajax
显示战斗过程。Ajax
回合倒计时间。javascript

后台:
发送邀请,接受,拒绝,超时。一个表。战斗数据。一个表。保存双方或多方的数据,包括回合时间,第几回合等。
战斗控制。一系列函数。处理玩家的操作,将操作存到战斗数据表里。时间到后执行操作。 

出兵后,直接返回战报。

写在事件里就行了。

1  function  endAction()
2 
3      {
4         从记时器里取数据。
5 
6      生成回合,生成战报。
7 
8  }

注:由于本人工作原因,此系列可能要暂停一段时间才更新,望大家见谅。。。




 

 

你可能感兴趣的:(网页游戏开发入门教程三(简单程序应用))