连网数据同步示意
趁着今天有空更新写下自己用fms做连网游戏的同步数据传输的处理方法~
大家用fms做连网游戏有一个概念,是一定要注意的,那就是同一个客户端文件因为为不同客户所用,当我们编写脚本时就得理清客户端既作为我自己的客户端的同时,也作为别人的客户端。
在这个的前提下我们就要涉及数据传输的问题了~
我个人认为,为了减轻服务端的负担,不是太重要的处理事件就得尽量写到客户端的脚本里去的~~,那样做有利于我们文件运行时的效率,而在那前提下数据同步的问题就不得不好好的在客户端写好了
下边看看我写的一个例子可能脚本会多了点:
//////////////////////////////////////////服务端脚本//////////////////////////////////////
application.onAppStart = function () {
menber = [];
id = 0;
ingame_num=0
};
application.onConnect = function (newClient, arr) {
ingame_num++
menber.push (arr[0][0]);
application.acceptConnection (newClient);
if(ingame_num<=100){
for (var i = 0; i < application.clients.length; i++) {
if (application.clients[i] == newClient) {
trace("呼叫ready")
application.clients[i].call ("ready", null, ingame_num);
}
}}
};
Client.prototype.reCall = function (obj) {
if (obj.type_ == "public" || obj.type_ == true) {
//广播信息
application.broadcastMsg (obj.func, obj.info);
}
//私聊机制
else if (obj.type_ == "private" || obj.type_ == false) {
for (var i = 0; i < application.clients.length; i++) {
if (menber[i] == obj.condition) {
application.clients[i].call (obj.func, null, obj.info);
}
}
}
//调用服务器自身函数
else if (obj.type_ == "main") {
this[obj.func](obj.info);
}
};
/////////////////////////////////////客户端部分////////////////////////////////////////////
/////////////////////////////上传数据帧听处理类////////////////////////
class watch_func {
function watch_func(obj:Object, name:String, info_arr:Array, mync2) {
var func = function (prop, oldVal, newVal, info_arr) {
if (arguments[1].x != arguments[2].x || arguments[1].y != arguments[2].y) {
function call_appliaction(who, func, info, type, condition) {
var Send = {};
Send.func = func;
Send.info = info;
Send.type_ = type;
Send.condition = condition;
//呼叫服务器中介函数
who.call("reCall", null, Send);
}
//移动物更新坐标~
info_arr[8][1] = obj.info;
//服务器调用的更新信息处理函数(根据type的定义类型为广播),用于同步更新其他客户端处于更新状态的移动物的信息
call_appliaction(mync2, "call_move", info_arr, true);
}
return newVal;
};
obj.watch(name, func, info_arr);
}
}
/////////////////////////////帧听数据及影射同步类//////////////////////////////////
class watch_func_E {
function watch_func_E(obj:Object, name:String, map:MovieClip, name_, info) {
var w = [map, name_, info];
var Em_func = function (prop, oldVal, newVal, w) {
//arguments[3] 在这里是参数w的内容
//arguments[3][0]这里等于类的自身参数map元件,这里用于表示路径
//arguments[3][0][arguments[2][8][1].name_等于场景中map元件内的元件(具体元件名由obj所帧听的对象决定)
for (var i in arguments[3][0][arguments[2][8][1].name_]) {
//遍历mc的属性
for (var j = 0; j //i 为属性名,arguments[2][j][0]为更新对象的信息,if条件用于检测属性名是否一致
if (i == arguments[2][j][0]) {
//将更新的属性值赋值给场景中对应的元件属性
arguments[3][0][arguments[2][8][1].name_][i] = arguments[2][j][1];
}
}
}
//更新场景对应的元件的坐标
trace("x="+arguments[3][0][arguments[2][8][1].name_].info.x)
trace("y="+arguments[3][0][arguments[2][8][1].name_].info.y)
arguments[3][0][arguments[2][8][1].name_]._x = arguments[3][0][arguments[2][8][1].name_].info.x;
arguments[3][0][arguments[2][8][1].name_]._y = arguments[3][0][arguments[2][8][1].name_].info.y;
};
obj.watch(name, Em_func, w);
}
}
/////////////////////////////////////客户端部分//////////////////////////////////////////
//////////////////导入as部分///////////////////
function init_info(me, menber, info_arr) {
//for (var i =1; i <=11; i++) {
//var me = where["menber" + i];
me.side = info_arr[0];
me.status = info_arr[1];
me.appointment = info_arr[2];
me.power = info_arr[3];
me.spess = info_arr[4];
me.shoot = info_arr[5];
me.lost = info_arr[6];
me.id = i;
me.info = {name_:me._name, x:me._x, y:me._y};
menber.push(me);
//}
return menber;
}
//求两者距离
function dis_func(mc1, mc2) {
if (typeof (mc1) != "movieclip") {
var dx = mc1.x-mc2._x;
var dy = mc1.y-mc2._y;
} else if (typeof (mc2) != "movieclip") {
var dx = mc1._x-mc2.x;
var dy = mc1._y-mc2.y;
} else {
var dx = mc1._x-mc2._x;
var dy = mc1._y-mc2._y;
}
dis = Math.sqrt(Math.pow(dx, 2)+Math.pow(dy, 2));
return dis;
}
//寻最近者
function fine_nearest(arr, ball) {
//将距离放入数组
for (var i = 0; i arr[i][4] = dis_func(ball, arr[i][0]);
}
//排序
for (var i = 0; i for (var j = i+1; j if (arr[i][4]>arr[j][4]) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
if (arr[i][4] == arr[j][4]) {
}
}
}
//返回
var Obj = {};
Obj.near = arr[0];
Obj.arr = arr;
return Obj;
}
//距离
function dis_init(arr, target_obj) {
for (var i = 0; i var m = this["m"+i];
if (dis_func(m, target_obj)<60) {
follow_move(target_obj, m, m.spees);
}
}
}
//方向
function degree_func(trget_obj, move_obj) {
if (typeof (trget_obj) != "movieclip") {
var dx = move_obj._x-trget_obj.x;
var dy = move_obj._y-trget_obj.y;
} else if (typeof (move_obj) != "movieclip") {
var dx = move_obj.x-trget_obj._x;
var dy = move_obj.y-trget_obj._y;
} else {
var dx = move_obj._x-trget_obj._x;
var dy = move_obj._y-trget_obj._y;
}
if (dy == 0) {
if (dx>0) {
degree = 90;
} else {
degree = 270;
}
} else {
if (dy>0) {
degree = Math.atan(dx/dy)*180/Math.PI;
} else {
degree = Math.atan(dx/dy)*180/Math.PI+180;
}
}
move_obj.degree = -degree;
return move_obj.degree;
}
//跟随函数
function follow_move(trget_obj, move_obj, speed) {
var degree = -degree_func(trget_obj, move_obj);
if (dis_func(trget_obj, move_obj)<=10) {
} else {
move_obj._y += speed*Math.cos(degree*(Math.PI/180))*-1;
move_obj._x += speed*Math.sin(degree*(Math.PI/180))*-1;
}
return move_obj;
}
//斜率
function k_(obj1, obj2) {
if (typeof (obj1) != "movieclip") {
var k = (obj1.y-obj2._y)/(obj1.x-obj2._x);
} else if (typeof (obj2) != "movieclip") {
var k = (obj1._y-obj2.y)/(obj1._x-obj2.x);
} else {
var k = (obj1._y-obj2._y)/(obj1._x-obj2._x);
}
return k;
}
//////////////////第一帧///////////////////
stop();
var ID;
#include "player.as"
var mync2 = new NetConnection();
mync2.ready = function(what) {
ID = what;
gotoAndStop("play_game");
};
mync2.init_full = function(what) {
gotoAndStop("full");
};
play_btn.onPress = function() {
if (txt.text != "") {
ID_ = txt.text;
list_arr = [[ID_, 123456]];
mync2.connect("rtmp://localhost/ball_v1", list_arr);
mync2.onStatus = function(info) {
if (info.code == "NetConnection.Connect.Success") {
} else {
gotoAndStop("lost_game");
}
};
}
};
///////////////////第2帧/////////////////////////////////////
import flash.geom.Point;
stop();
////////中介函数////////
function call_appliaction(who, func, info, type, condition) {
var Send = {};
Send.func = func;
Send.info = info;
Send.type_ = type;
Send.condition = condition;
who.call("reCall", null, Send);
}
///广播我方成员变更信息给其他玩家函数
mync2.call_move = function(info) {
if (int(info[info.length-1]) != ID) {
for (var i = 0; i<=11; i++) {
EM_arr[i].info = info;
}
} else {
}
};
////////编历储存信息函数//////
function putin(what, arr) {
for (var i in what) {
arr.push([i, mm[i]]);
}
}
///////
info_Marr = [];
info_Earr = [];
info_arr1 = ["m", "attack", 1, 5, 8, 1, 1];
info_arr2 = ["e", "attack", 1, 5, 8, 1, 1];
var cnt = 1;
ME_arr = [];
EM_arr = [];
for (var i = 1; i<=11; i++) {
////创建对象////
var ME = "ME"+i;
var EM = "EM"+i;
ME = {};
EM = {};
ME_arr.push(ME);
EM_arr.push(EM);
////创建对象////
////记录信息////
var m = map["m"+i];
var e = map["e"+i];
m.gotoAndStop(2);
e.gotoAndStop(3);
init_info(m, info_Marr, info_arr1);
init_info(e, info_Earr, info_arr2);
////记录信息////
}
///区分玩家敌我类型///
if (ID%2 == 0) {
var conter = map.m1;
play_side = "m";
} else {
var conter = map.e1;
play_side = "e";
}
for (var i = 1; i<=11; i++) {
var mm = map[play_side+i];
///创建信息数组
var info_arr = "info_arr"+i;
info_arr = [];
//编历储存对象信息
putin(mm, info_arr);
//信息整理
info_arr.reverse();
//添加id信息
info_arr.push(ID);
///////创建影射帧听实例/////
Em = EM_arr[i];
Em.info;
new watch_func_E(Em, "info", map, name_, info);
//创建玩家帧听控制对象帧听实例
var player = ME_arr[i-1];
player.info = mm.info;
new watch_func(player, "info", info_arr, mync2);
}
this.point_x = new Point(map[play_side+1]._x, map[play_side+1]._y);
/////////////////////玩家控制执行部分/////////////////
this.onEnterFrame = function() {
if (down) {
this.point_x = new Point(_xmouse, _ymouse);
}
for (var i = 1; i<=11; i++) {
var mm_ = map[play_side+i];
mm_.info = {name_:mm_._name, x:mm_._x, y:mm_._y};
var mc_ = map[play_side+i];
ME_arr[i-1].info = mm_.info;
if (mc_.hitTest(_xmouse, _ymouse, true)) {
conter = mc_;
break;
}
}
follow_move(this.point_x, conter, 5);
};
this.onMouseDown = function() {
down = true;
};
this.onMouseUp = function() {
down = false;
};
///////////////////////////////////////////////////////////////////////////////////////////////
当我们处理涉及多个元件的数据同步时~一般的过程是:帧听数据对象数据改变-------------》将要传输的数据放到一个对象或数组里------------》经由服务器传输,--------------》在服务器处理返回形式------------》解释返回数据-------------------》影射同步数据
上边的例子可能大家也发现了我服务端调用的函数很少,大多情况都是调用了我写的那个中介函数来处理,服务端主要的职能在这里只是用于数据的传输,运算的过程主要集中在客户端的。
我们在客户段要做的其实就是帧听对象的数据改变否,改变了我们就给服务端发送信息,然后再经由服务端分析要传输的类型,在返回客户端,再由客户端解释返回数据,然后解释,并影射相关数据。
希望有新学的人能好好看看脚本~已经作了相关注释,希望对大家有用~~~