一、选择开发语言
后台:
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
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的透明图。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
//
获得状态加成
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。(唯一道具)
也就是唯一道具是在附魔功能执行的时候生成。以背包举例。没附魔前。
背包内道具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
//
比如移动
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
}
注:由于本人工作原因,此系列可能要暂停一段时间才更新,望大家见谅。。。