Red5作做服务器的玩家移动信息同步例子
http://www.sujun.org/feed.asp?cateID=15
以前的旧文章,归下类而已.....目前也在用red5做服务器开发一款比较小的flash网络游戏,呵呵...期待...
玩家移动信息同步例子,我已经写过了两个版本的啦,一个java的,一个是fms的,现在又写了个red5的,其实这些的原理都是差不多的。以前好象没怎么讲解原理,这些就写得详细点吧。用red5做服务器,其实客户端基本是fms一样的。(事实上就是一样,哈哈)。无非就是客户端呼叫服务端的方法,服务端呼叫客户端的方法。(这是同步是没有采用ShareObject的,过阵可能会写吧)
先看下图片吧
首先先来看看最基本的(具体的内容我就不写了,代码里有详细的解释,在这里我只说说思路和逻辑)
var mync:NetConnection = new NetConnection(); mync.connect("rtmp://localhost/game");连接到服务器
mync.onStatus = function(info)
{
if (info.code == "NetConnection.Connect.Success")
{
trace("接通");
//通知服务器,有新用户登陆了
this.call("userLogin",null,userName);
}
};
看这段代码。当客户端连接服务器成功后,马上呼叫服务端的用户登陆方法,并把新登陆的用户名传了过去。那么我们再看服务端
/**
* 用户登陆
* @param userName:用户名
*/
public void userLogin(String userName)
{
System.out.println("新用户登陆:" + userName);
//检查是否已经有用户登陆,有登陆,则呼叫其他用户接受新登陆用户的信息
if(map.size() > 0)
{
for(Iterator<IServiceCapableConnection> it = map.values().iterator(); it.hasNext();)
{
IServiceCapableConnection ic = it.next();
//发送用户名过去
ic.invoke("userLogin", new Object[]{userName});
}
}
//放进map对象中保存
map.put(userName,getCurrentConn());
}
当客户端call的时候,服务器就执行userLogin里的代码。在这里个方法里服务器只做了两件事,一是把map对象里的所有的客户端连接对象进行遍历,把新登陆用户的信息发送给所有已经登陆的客户端。然后再把当前的客户端连接对象保存在服务端的map对象中。接着来看ic.invoke ("userLogin", new Object[]{userName})这句代码,他是呼叫客户端方法;既然服务端呼叫客户端的userLogin方法,那么我们可以过来客户端的 userLogin方法。
//当有新用户登陆时,被服务器呼叫
mync.userLogin = function(name:String)
{
//根据名字复制一个新的mc,最近登陆的用户
var mc:MovieClip = _root.attachMovie("personMC","personMC" + name,depth++);
userSet.put(name,mc);
mc.setName(name);
//马上呼叫客户端,把自己的位置告诉新登陆的客户端
mync.call("userInfo",null,userName,name,personMC._x,personMC._y);
};
在客户端里,该userLogin方法也是做了两件事,一是马上根据传过来用户名,生成了有一个新的mc(在实际中就是玩家,而且真正应用中,传的还不止一个参数),接着又呼叫服务端的一个方法userInfo,因为它必须把自己的信息通知给刚刚登陆的用户,包括自己的名字,位置等等。 接下来我们就看服务端的userInfo方法啦
/**
* 已经存在的用户的信息
* @param userName:用户名
* @param x:x坐标
* @param y:y坐标
*/
public void userInfo(String selfName, String userName,double x,double y)
{
Object[] ōbject = new Object[3];
object[0] = selfName;
object[1] = new Double(x);
object[2] = new Double(y);
map.get(userName).invoke("createUser",object);
}
服务端的这个方法就简单啦,只做了一件非常简单的时,就是把当前的传过来的用户信息,转发一个指定的用户就行了(其实就是最新登陆的那个用户) map.get(userName).invoke("createUser",object),呼叫了客户端的createUser方法。客户端:
//更新已经登陆的用户
mync.createUser = function(userName,x,y)
{
//根据名字复制一个新的mc
var mc:MovieClip = _root.attachMovie("personMC","personMC" + userName,depth++);
userSet.put(userName,mc);
mc.setName(userName);
mc._x = x;
mc._y = y;
}
客户端的这个方法也很简单,就是根据服务端转发过来的信息,生成一个新的mc(玩家),同时还有位置。
好啦,到了,一个用户登陆到服务器,在所有客户端同步显示的步骤就完成啦。
接下来就把最后的功能完成,就是当一个客户端移动时,在其他客户端的也做对应的动作。呵~~不用我说什么也应该想了吧。恩,其实也是很简单,就是当前用户操作时,做自己的操作命令通过服务端转发其他所有的客户端,就达到同步的目的了。例如这一句代码:if( Key.isDown( Key.UP ) )
{
personMC.up();
mync.call("userAction",null,userName,"up");
}
当按下键盘的UP时,客户端就呼叫服务起的userAction的方法,同时把自己的名字(才知道哪个用户的动作)和动作(具体哪个动作)传给了服务器,下面是服务端的代码
public void userAction(String userName, String action)
{
Object[] ōbject = new Object[2];
object[0] = userName;
object[1] = action;
//对所有的用户进行遍历
if(map.size() > 1)
{
//取出当前用户的conn
IServiceCapableConnection temp = getCurrentConn();
for(Iterator<IServiceCapableConnection> it = map.values().iterator(); it.hasNext();)
{
IServiceCapableConnection iconn = it.next();
//如果不是当前用户,则发送信息
if(temp != iconn)
{
iconn.invoke("userAction", object);
}
}
}
}
恩,这个方法也简单,就是把收到的信息发送给所有的客户端,注意这句代码
IServiceCapableConnection iconn = it.next();
//如果不是当前用户,则发送信息
if(temp != iconn)
{
iconn.invoke("userAction", object);
}
这是为了防止把该信息发送给自己,因为服务器是做遍历的,也就是说会对所有登陆该服务器的用户进行遍历,所以应该排除发送该命令的用户。接下来就看客户端的userAction方法啦
//当其他用户有动作时,被服务器呼叫
mync.userAction = function(name:String,action:String)
{
var mc:MovieClip = userSet.get(name);
//执行对应的方法
mc[action]();
};
它也只做了一件事,就是找出该用户对应的mc(玩家),然后执行相应的动作
OK~~~代码解释就到这里啦。光看我写的这个还是不行,大家还有把源代码提供去看看,大家有什么问题,就到red5的专有论坛去讨论吧。www.openred5.com. 这个网站。那里也有red5的配置教什么的。这里所以这里我就不在讲述啦。呵~本来想弄些漂亮的人物进去。不过没什么时间啦,今天是牺牲中午睡眠时间写的,大概花了一个小时,呵呵。如果很多人喜欢的话,往后会继续发布该系列的东西,例如用户说话,攻击,自己的名字是红色的等等,呵呵。
Red5源代码下载
以前的旧文章,归下类而已.....目前也在用red5做服务器开发一款比较小的flash网络游戏,呵呵...期待...
玩家移动信息同步例子,我已经写过了两个版本的啦,一个java的,一个是fms的,现在又写了个red5的,其实这些的原理都是差不多的。以前好象没怎么讲解原理,这些就写得详细点吧。用red5做服务器,其实客户端基本是fms一样的。(事实上就是一样,哈哈)。无非就是客户端呼叫服务端的方法,服务端呼叫客户端的方法。(这是同步是没有采用ShareObject的,过阵可能会写吧)
先看下图片吧
首先先来看看最基本的(具体的内容我就不写了,代码里有详细的解释,在这里我只说说思路和逻辑)
程序代码
var mync:NetConnection = new NetConnection(); mync.connect("rtmp://localhost/game");连接到服务器
mync.onStatus = function(info)
{
if (info.code == "NetConnection.Connect.Success")
{
trace("接通");
//通知服务器,有新用户登陆了
this.call("userLogin",null,userName);
}
};
看这段代码。当客户端连接服务器成功后,马上呼叫服务端的用户登陆方法,并把新登陆的用户名传了过去。那么我们再看服务端
程序代码
/**
* 用户登陆
* @param userName:用户名
*/
public void userLogin(String userName)
{
System.out.println("新用户登陆:" + userName);
//检查是否已经有用户登陆,有登陆,则呼叫其他用户接受新登陆用户的信息
if(map.size() > 0)
{
for(Iterator<IServiceCapableConnection> it = map.values().iterator(); it.hasNext();)
{
IServiceCapableConnection ic = it.next();
//发送用户名过去
ic.invoke("userLogin", new Object[]{userName});
}
}
//放进map对象中保存
map.put(userName,getCurrentConn());
}
当客户端call的时候,服务器就执行userLogin里的代码。在这里个方法里服务器只做了两件事,一是把map对象里的所有的客户端连接对象进行遍历,把新登陆用户的信息发送给所有已经登陆的客户端。然后再把当前的客户端连接对象保存在服务端的map对象中。接着来看ic.invoke ("userLogin", new Object[]{userName})这句代码,他是呼叫客户端方法;既然服务端呼叫客户端的userLogin方法,那么我们可以过来客户端的 userLogin方法。
程序代码
//当有新用户登陆时,被服务器呼叫
mync.userLogin = function(name:String)
{
//根据名字复制一个新的mc,最近登陆的用户
var mc:MovieClip = _root.attachMovie("personMC","personMC" + name,depth++);
userSet.put(name,mc);
mc.setName(name);
//马上呼叫客户端,把自己的位置告诉新登陆的客户端
mync.call("userInfo",null,userName,name,personMC._x,personMC._y);
};
在客户端里,该userLogin方法也是做了两件事,一是马上根据传过来用户名,生成了有一个新的mc(在实际中就是玩家,而且真正应用中,传的还不止一个参数),接着又呼叫服务端的一个方法userInfo,因为它必须把自己的信息通知给刚刚登陆的用户,包括自己的名字,位置等等。 接下来我们就看服务端的userInfo方法啦
程序代码
/**
* 已经存在的用户的信息
* @param userName:用户名
* @param x:x坐标
* @param y:y坐标
*/
public void userInfo(String selfName, String userName,double x,double y)
{
Object[] ōbject = new Object[3];
object[0] = selfName;
object[1] = new Double(x);
object[2] = new Double(y);
map.get(userName).invoke("createUser",object);
}
服务端的这个方法就简单啦,只做了一件非常简单的时,就是把当前的传过来的用户信息,转发一个指定的用户就行了(其实就是最新登陆的那个用户) map.get(userName).invoke("createUser",object),呼叫了客户端的createUser方法。客户端:
//更新已经登陆的用户
mync.createUser = function(userName,x,y)
{
//根据名字复制一个新的mc
var mc:MovieClip = _root.attachMovie("personMC","personMC" + userName,depth++);
userSet.put(userName,mc);
mc.setName(userName);
mc._x = x;
mc._y = y;
}
客户端的这个方法也很简单,就是根据服务端转发过来的信息,生成一个新的mc(玩家),同时还有位置。
好啦,到了,一个用户登陆到服务器,在所有客户端同步显示的步骤就完成啦。
接下来就把最后的功能完成,就是当一个客户端移动时,在其他客户端的也做对应的动作。呵~~不用我说什么也应该想了吧。恩,其实也是很简单,就是当前用户操作时,做自己的操作命令通过服务端转发其他所有的客户端,就达到同步的目的了。例如这一句代码:if( Key.isDown( Key.UP ) )
{
personMC.up();
mync.call("userAction",null,userName,"up");
}
当按下键盘的UP时,客户端就呼叫服务起的userAction的方法,同时把自己的名字(才知道哪个用户的动作)和动作(具体哪个动作)传给了服务器,下面是服务端的代码
程序代码
public void userAction(String userName, String action)
{
Object[] ōbject = new Object[2];
object[0] = userName;
object[1] = action;
//对所有的用户进行遍历
if(map.size() > 1)
{
//取出当前用户的conn
IServiceCapableConnection temp = getCurrentConn();
for(Iterator<IServiceCapableConnection> it = map.values().iterator(); it.hasNext();)
{
IServiceCapableConnection iconn = it.next();
//如果不是当前用户,则发送信息
if(temp != iconn)
{
iconn.invoke("userAction", object);
}
}
}
}
恩,这个方法也简单,就是把收到的信息发送给所有的客户端,注意这句代码
IServiceCapableConnection iconn = it.next();
//如果不是当前用户,则发送信息
if(temp != iconn)
{
iconn.invoke("userAction", object);
}
这是为了防止把该信息发送给自己,因为服务器是做遍历的,也就是说会对所有登陆该服务器的用户进行遍历,所以应该排除发送该命令的用户。接下来就看客户端的userAction方法啦
//当其他用户有动作时,被服务器呼叫
mync.userAction = function(name:String,action:String)
{
var mc:MovieClip = userSet.get(name);
//执行对应的方法
mc[action]();
};
它也只做了一件事,就是找出该用户对应的mc(玩家),然后执行相应的动作
OK~~~代码解释就到这里啦。光看我写的这个还是不行,大家还有把源代码提供去看看,大家有什么问题,就到red5的专有论坛去讨论吧。www.openred5.com. 这个网站。那里也有red5的配置教什么的。这里所以这里我就不在讲述啦。呵~本来想弄些漂亮的人物进去。不过没什么时间啦,今天是牺牲中午睡眠时间写的,大概花了一个小时,呵呵。如果很多人喜欢的话,往后会继续发布该系列的东西,例如用户说话,攻击,自己的名字是红色的等等,呵呵。
Red5源代码下载
AS3与Red5之间的参数传递
差不大一年没去动red5了,现在因为项目需要又开始使用red5,呵呵,先写些基本应用吧
参数传递是最基本的,之前是as2,现在用as3与red5 0.63了,几乎没什么变化.不过flash这边的可以传递的参数也就多了一些.就基本的是
String,int,Number,Boolean,Array,对应到red5这边是String,int,double,boolean,List
下面看基本的代码吧:
Flash:
/**
* @(#)ParamRed5.as
* @author soda.C
* @version 1.0
* <br>Copyright (C), 2007 soda.C
* <br>This program is protected by copyright laws.
* <br>Program Name:GameHall
* @data 2008-2-19
*/
package org.sujun.red5.test
{
import flash.display.Sprite;
import flash.net.NetConnection;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Responder;
/**
* 测试flash与red5之间参数的传递
*/
public class ParamRed5 extends Sprite
{
private var netConnection:NetConnection;
public function ParamRed5():void
{
netConnection = new NetConnection();
netConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
netConnection.connect("rtmp://localhost/paramtest");
}
private function netStatusHandler(event:NetStatusEvent):void
{
trace("连接状态:" + event.info["code"]);
switch (event.info["code"])
{
case "NetConnection.Connect.Success":
trace("连接成功.....");
//呼叫服务器的baseParam方法,传递基本参数,string,int,number,Boolean
netConnection.call("baseParam", new Responder(baseParamResult),"soda.C",24,1000.1,false);
//封装数组,int
var ary:Array = new Array();
ary.push(1);
ary.push(2);
ary.push(3);
//封装数组,String
var ary1:Array = new Array();
ary1.push("a");
ary1.push("b");
ary1.push("c");
netConnection.call("receiveArray", new Responder(baseParamResult),ary,ary1);
break;
case "NetStream.Play.StreamNotFound":
trace("Stream not found: ");
break;
}
}
private function baseParamResult(obj:Object):void
{
trace(obj);
trace("响应了.....");
}
private function securityErrorHandler(event:SecurityErrorEvent):void
{
trace("securityErrorHandler: " + event);
}
}
}
接下来看java代码
ParamRed5App.java,该类继承了ApplicationAdapt
package org.sujun.red5.test;
import java.util.List;
import org.red5.server.adapter.ApplicationAdapter;
/**
* 存放被flash客户端调用的方法
*/
public class ParamRed5App extends ApplicationAdapter
{
public ParamRed5App()
{
System.out.println("被初始化了......");
}
/**
* 接受服务器传过来的基本参数
*/
public void baseParam(String name, int age, double value, boolean flag)
{
System.out.println("----name----" + name);
System.out.println("----age----" + age);
System.out.println("----value----" + value);
System.out.println("----flag----" + flag);
}
/**
* 接受客户端传递过来的数组
*/
public void receiveArray(List<Integer> intArray, List<String> strArray)
{
for(int i = 0; i < intArray.size(); i++)
{
System.out.println("----intArray----" + intArray.get(i).intValue());
}
for(int i = 0; i < intArray.size(); i++)
{
System.out.println("----strArray----" + strArray.get(i));
}
}
}
代码很简单.......直接复制过去,建立一个red5应用就可以使用了
看结果...
不过,还是我是传上源代码
点击下载此源代码
参数传递是最基本的,之前是as2,现在用as3与red5 0.63了,几乎没什么变化.不过flash这边的可以传递的参数也就多了一些.就基本的是
String,int,Number,Boolean,Array,对应到red5这边是String,int,double,boolean,List
下面看基本的代码吧:
Flash:
程序代码
/**
* @(#)ParamRed5.as
* @author soda.C
* @version 1.0
* <br>Copyright (C), 2007 soda.C
* <br>This program is protected by copyright laws.
* <br>Program Name:GameHall
* @data 2008-2-19
*/
package org.sujun.red5.test
{
import flash.display.Sprite;
import flash.net.NetConnection;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Responder;
/**
* 测试flash与red5之间参数的传递
*/
public class ParamRed5 extends Sprite
{
private var netConnection:NetConnection;
public function ParamRed5():void
{
netConnection = new NetConnection();
netConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
netConnection.connect("rtmp://localhost/paramtest");
}
private function netStatusHandler(event:NetStatusEvent):void
{
trace("连接状态:" + event.info["code"]);
switch (event.info["code"])
{
case "NetConnection.Connect.Success":
trace("连接成功.....");
//呼叫服务器的baseParam方法,传递基本参数,string,int,number,Boolean
netConnection.call("baseParam", new Responder(baseParamResult),"soda.C",24,1000.1,false);
//封装数组,int
var ary:Array = new Array();
ary.push(1);
ary.push(2);
ary.push(3);
//封装数组,String
var ary1:Array = new Array();
ary1.push("a");
ary1.push("b");
ary1.push("c");
netConnection.call("receiveArray", new Responder(baseParamResult),ary,ary1);
break;
case "NetStream.Play.StreamNotFound":
trace("Stream not found: ");
break;
}
}
private function baseParamResult(obj:Object):void
{
trace(obj);
trace("响应了.....");
}
private function securityErrorHandler(event:SecurityErrorEvent):void
{
trace("securityErrorHandler: " + event);
}
}
}
接下来看java代码
ParamRed5App.java,该类继承了ApplicationAdapt
程序代码
package org.sujun.red5.test;
import java.util.List;
import org.red5.server.adapter.ApplicationAdapter;
/**
* 存放被flash客户端调用的方法
*/
public class ParamRed5App extends ApplicationAdapter
{
public ParamRed5App()
{
System.out.println("被初始化了......");
}
/**
* 接受服务器传过来的基本参数
*/
public void baseParam(String name, int age, double value, boolean flag)
{
System.out.println("----name----" + name);
System.out.println("----age----" + age);
System.out.println("----value----" + value);
System.out.println("----flag----" + flag);
}
/**
* 接受客户端传递过来的数组
*/
public void receiveArray(List<Integer> intArray, List<String> strArray)
{
for(int i = 0; i < intArray.size(); i++)
{
System.out.println("----intArray----" + intArray.get(i).intValue());
}
for(int i = 0; i < intArray.size(); i++)
{
System.out.println("----strArray----" + strArray.get(i));
}
}
}
代码很简单.......直接复制过去,建立一个red5应用就可以使用了
看结果...
不过,还是我是传上源代码
点击下载此源代码