Java 多玩家 libgdx 教程

开发 后端
这篇文章中我们看到如何用AppWarp开发多人游戏。 我们在一个现成的libgdx超级跳跃例子基础上用 AppWarp Cloud 特性进行拓展。

StartScreen java multiplayer libgdx tutorial

我们如何去做?

  • 在 libgdx的主页修改libgdx样本“superjumper".

  • 使用 AppWarp Cloud将它转化为2个玩家的实时游戏.

  • 本游戏将匹配玩家并且用户需要到达城堡来赢得游戏的胜利.

  •  用户将获得其他用户成绩的实时反馈以增加了游戏的刺激性。 

Eclipse 项目设置

 接下来,您需要从这个git repository下载libgdx游戏样本(superjumper项目。.

在Eclipse中打开下载的superjumper解决方案。你将看到项目如下:

SetUp java multiplayer libgdx tutorial

为了创建多玩家,我使用了 AppWarp Java SDK (1.5 as of now)

关于libgdx的依赖
 

正如 libgdx的官网上提到的,这个示例依赖于libgbx。如果你试图运行libgbx网站上的superjumper示例程序,你会得到关于gdx,gdx-backend-lwjgl, gdx-jnigen, gdx-openal的错误。你需要将这些工程设置为你的应用(superjumper)的依赖库工程来解决这些错误。

但我已经将这些库包含在了superjumper git仓库中的libs文件夹下。观看这个视频或阅读这个教程来了解更多关于libgdx工程的安装设置。

取得你的AppWarp application keys

如果你要与AppWarp云服务集成,你需要从ShepHertz开发者面板AppHq取得你的application keys.这些key能够在ShepHertz云服务中识别的你应用空间,而且AppWarp云需要用它们隔离不同应用间的消息。

AppWarp网站上按步骤注册(免费)并取得你的application keys.

现在打开superjumper示例工程中的 WarpController.java 文件并在其中添加这些值。例如:

  1. public static String AppKey = "14a611b4b3075972be364a7270d9b69a5d2b24898ac483e32d4dc72b2df039ef";  
  2. public static String SecretKey = "55216a9a165b08d93f9390435c9be4739888d971a17170591979e5837f618059"

运行多用户sample
 

既然你已经准备好了, 我们可以实际运行并观察这个游戏了。因为这个游戏有单人或多人游戏的选项,为了玩多人游戏你需要在2个模拟器/设备上同时运行它。

当你按了multiplayer按扭, 这个游戏会连接AppWarp并加入一个游戏房间。一旦进入这个游戏房间, 这个客户端在游戏开始前会一直等待第二个玩家加入该房间。

[[113667]]

现在你需要在第二个模拟器/设备上做同样的操作,AppWarp的匹配API会将第二个玩家连接到相同的游戏房间,然后游戏开始。玩家需要到达城堡来完成这个游戏。同时用户会发现他们的对手在实时地运动。这个游戏内实时通信正是AppWarp的强大之处。

[[113668]]

游戏会在这三个条件下完成

  1. 用户离开:当一个玩家离开游戏时,另一个玩家被判定为胜利者。因为他的对手已经离开的游戏。

  2. 闯关成功:到达城堡的玩家成为胜利者

  3. 游戏结束:如果玩家碰到了松鼠或者玩家掉了下来那另一个玩家会成为胜利者。

GameOver java multiplayer libgdx tutorial 

#p#

怎样与AppWarp集成
 

开始游戏

首先你需要用你的应用密钥初始化Warpclient单例(WarpController.java).

  1. WarpClient.initialize(apiKey, secretKey); 

接下你需要连接到AppWarp云端并且加入一个游戏房间(WarpController.java)

注意: AppWarp SDK 提供通过异步API提供它的功能。这意味着你只需简单的将请求监听器添加到WarpClient实例中区接受响应和通知即可。

这个文件 (WarpController.java) 有我们这步所需的所有代码。它创建连接请求,房间请求,区域请求(如果有必要创建一个房间)。因此我们添加相关监听器到OnStart()方法中。

  1. public WarpController()  
  2. {  
  3.     initAppwarp();  
  4.     warpClient.addConnectionRequestListener(new ConnectionListener(this));  
  5.     warpClient.addChatRequestListener(new ChatListener(this));  
  6.     warpClient.addZoneRequestListener(new ZoneListener(this));  
  7.     warpClient.addRoomRequestListener(new RoomListener(this));  
  8.     warpClient.addNotificationListener(new NotificationListener(this));  
  9. }  
  1. private void initAppwarp(){  
  2.     try {  
  3.         WarpClient.initialize(apiKey, secretKey);  
  4.         warpClient = WarpClient.getInstance();  
  5.     } catch (Exception e) {  
  6.         e.printStackTrace();  
  7.     }  
  8. }  

设置好listener后我们可以继续创建连接。用户需要传入一个惟一用户名(username)以连接到AppWarp云。在示例中我只是使用了一个随机字符串(你也可以从用户或像facebook的第三方服务处取得来惟一标识用户)。随机字符串是在MainMenuScreen.java文件中生成的。

  1. WarpClient.connectWithUserName(userName); 

连接的结果会交给以下回调函数。

  1. public void onConnectDone(ConnectEvent e) {  
  2.     if(e.getResult()==WarpResponseResultCode.SUCCESS){  
  3.         callBack.onConnectDone(true);  
  4.     }else{  
  5.         callBack.onConnectDone(false);  
  6.     }  
  7. }  
  1. public void onConnectDone(boolean status){  
  2.     if(status){  
  3.         warpClient.initUDP();  
  4.         warpClient.joinRoomInRange(11false);  
  5.     }else{  
  6.         isConnected = false;  
  7.         handleError();  
  8.     }  

如果连接成功,我们会试着加入一个房间。我们也可以选择初始化UDP(稍后的游戏玩家会用到)。为了加入房间,我们使用JoinRoomInRange方法并传入参数(1,1),它会请求服务器将客户端加入只有一个用户的房间。如果失败我们会新建并加入一个容纳两个玩家的房间。

  1. public void onJoinRoomDone(RoomEvent event){  
  2.     if(event.getResult()==WarpResponseResultCode.SUCCESS){// success case  
  3.         this.roomId = event.getData().getId();  
  4.         warpClient.subscribeRoom(roomId);  
  5.     }else if(event.getResult()==WarpResponseResultCode.RESOURCE_NOT_FOUND){// no such room found  
  6.         HashMap<string, object=""> data = new HashMap<string, object="">();  
  7.         data.put("result""");  
  8.         warpClient.createRoom("superjumper""shephertz"2, data);  
  9.     }else{  
  10.         warpClient.disconnect();  
  11.         handleError();  
  12.     }  
  13. }  

一旦加入某个房间(不管是现在还是创建新房间之后),客户端需要订阅这个房间来接收房间的通知(这在游戏中是必须的)。这里详细解释了这些概念。订阅之后我们要调用getLiveRoomInfo来检查房间是否有两个玩家了,如果是我们就开始游戏,否则就等待其他玩家加入这个房间。

  1. WarpClient.getLiveRoomInfo(roomId); 
  1. public void onGetLiveRoomInfo(String[] liveUsers){  
  2.     if(liveUsers!=null){  
  3.         if(liveUsers.length==2){  
  4.             startGame();      
  5.         }else{  
  6.             waitForOtherUser();  
  7.         }  
  8.         }else{  
  9.             warpClient.disconnect();  
  10.             handleError();  
  11.     }  
  12. }  

开始游戏

进行游戏的代码在MultiplayerGameScreen.java文件中。如果用户进入了这个界面,那就意味着有两个用户在这个房间中且游戏开始了。玩家玩这个游戏,并且他也要更新其他玩家的状态。其他玩家在你的界面上显示成绿色小怪物。

随着玩家在界面上移动以完成游戏关卡,需要绘制它的移动轨迹,也要将位置更新发送给远程玩家。参见WorldRenderer.java(multiplayer)

  1. private void renderBob () {  
  2. {  
  3. ...  
  4. ...  
  5.     if (side < 0){  
  6.         batch.draw(keyFrame, world.local_bob.position.x + 0.5f, world.local_bob.position.y - 0.5f, side * 11);  
  7.         sendLocation(world.local_bob.position.x + 0.5f, world.local_bob.position.y - 0.5f, side * 11);  
  8.     }else{  
  9.         batch.draw(keyFrame, world.local_bob.position.x - 0.5f, world.local_bob.position.y - 0.5f, side * 11);  
  10.         sendLocation(world.local_bob.position.x - 0.5f, world.local_bob.position.y - 0.5f, side * 11);  
  11.     }  
  12. }  

消息通过我们在这个示例中所写的工具方法来发送。WarpClient允许客户端将字节数组广播给它所在的房间。可以使用TCP(默认)或UDP来发送。记住我们已经在成功连接到云服务后的***个界面中初始化了UDP。

  1. private void sendLocation(float x, float y, float width, float height){  
  2.   try {  
  3.     JSONObject data = new JSONObject();  
  4.     data.put("x", x);  
  5.     data.put("y", y);  
  6.     data.put("width", width);  
  7.     data.put("height", height);  
  8.     WarpController.getInstance().sendGameUpdate(data.toString());  
  9.     } catch (Exception e) {  
  10.         // exception in sendLocation  
  11.     }  
  12.   }  

发送给房间的消息是通过onUpdatePeersReceived的回调方法提供的。在这个回调中我们要解析这个消息并识别发送者,消息类型和与此消息绑定的数据。我们根据这些消息做相应的处理。

  1. public void onUpdatePeersReceived(UpdateEvent event) {  
  2.     callBack.onGameUpdateReceived(new String(event.getUpdate()));  
  3. }  
  1. try {  
  2.     JSONObject data = new JSONObject(message);  
  3.     float x = (float)data.getDouble("x");  
  4.     float y = (float)data.getDouble("y");  
  5.     float width = (float)data.getDouble("width");  
  6.     float height = (float)data.getDouble("height");  
  7.     renderer.updateEnemyLocation(x, y, width, height);  
  8. catch (Exception e) {  
  9.     // exception   
  10. }  

#p#

游戏结束

当游戏结束后我们只需要更新房间的属性。其他玩家收到通知后需要根据此消息更新他们的UI。

  1. public void updateResult(int code, String msg){  
  2.     if(isConnected){  
  3.         STATE = COMPLETED;  
  4.         HashMap<string, object=""> properties = new HashMap<string, object="">();  
  5.         properties.put("result", code);  
  6.         warpClient.lockProperties(properties);  
  7.     }  
  8. }  

lockProperties

当两个远程玩家同玩游戏时,他们有可能会同时结束游戏,而这会引起资源竞争。这种情况***交由服务器解决,所以我们使用了lockProperties API。所以当游戏结束时用户向服务器发送一个lockProperties请求将结果属性锁定。一旦这个结果被某个用户锁定,服务器会放弃处理后续对同一个属性的lockProperties请求。点击此处以了解更多此AppWarp仲裁方式。

随着游戏的结束,其他用户得到通知,StartMultiplayerScreen.java根据以下代码将游戏结束的原因显示到界面上。

  1. public void onGameFinished (int code) {  
  2.     if(code==WarpController.GAME_WIN){  
  3.         this.msg = game_loose;  
  4.     }else if(code==WarpController.GAME_LOOSE){  
  5.         this.msg = game_win;  
  6.     }else if(code==WarpController.ENEMY_LEFT){  
  7.         this.msg = enemy_left;  
  8.     }  
  9.     update();  
  10.     game.setScreen(this);  
  11. }  

我们也要离开并取消订阅此房间,并且取消监听器;如果游戏不在运行状态我们也要删除房间。由于在这个游戏中我们使用的是AppWarp 动态房间,在使用完后***立即删除(尽管空动态房间在60分钟后都会被自动删除)。

  1. public void handleLeave(){  
  2.     if(isConnected){  
  3.         warpClient.unsubscribeRoom(roomId);  
  4.         warpClient.leaveRoom(roomId);  
  5.         if(STATE!=STARTED){  
  6.             warpClient.deleteRoom(roomId);  
  7.         }  
  8.         warpClient.disconnect();  
  9.     }  
  10. }  
  1. private void disconnect(){  
  2.     warpClient.removeConnectionRequestListener(new ConnectionListener(this));  
  3.     warpClient.removeChatRequestListener(new ChatListener(this));  
  4.     warpClient.removeZoneRequestListener(new ZoneListener(this));  
  5.     warpClient.removeRoomRequestListener(new RoomListener(this));  
  6.     warpClient.removeNotificationListener(new NotificationListener(this));  
  7.     warpClient.disconnect();  
  8. }  

用户可以在这里点击并返回MainMenuScreen,然后我们可以重新进行这个过程。但这次我们只需要找到一个房间就可以开始了(因为我们已经连接到了服务器)。

总结

这篇文章中我们看到如何用AppWarp开发多人游戏。 我们在一个现成的libgdx超级跳跃例子基础上用 AppWarp Cloud 特性进行拓展。我们同样看到客户端怎样连接到AppWarp上,怎样加入游戏房间。继承概念不受libgdx的影响,并且可以应有与其他任何Java程序中。

使用Robovm发布到iOS

你可以使用 Robovm 来将超级跳跃游戏发布到iOS上. 下面是几步是任何其它项目中都需要做的。另外你需要做如下改变。

1. 将这些属性添加到 robovm.xml  

  1. <forcelinkclasses>  
  2.   <pattern>org.apache.harmony.xnet.provider.jsse.OpenSSLProvider</pattern>  
  3.   <pattern>org.apache.harmony.security.provider.cert.DRLCertFactory</pattern>  
  4.   <pattern>com.android.org.bouncycastle.jce.provider.BouncyCastleProvider</pattern>  
  5.   <pattern>org.apache.harmony.security.provider.crypto.CryptoProvider</pattern>  
  6.   <pattern>org.apache.harmony.xnet.provider.jsse.JSSEProvider</pattern>  
  7.   <pattern>com.android.org.bouncycastle.jce.provider.JCEMac$SHA1</pattern>  
  8. </forcelinkclasses>  

2. 使用如下代码从背景中改变屏幕。

  1. Gdx.app.postRunnable(new Runnable() {  
  2.   @Override 
  3.   public void run () {  
  4.       game.setScreen(new MultiplayerGameScreen(game, StartMultiplayerScreen.this));  
  5.   }  
  6. });  

这里另有要求, 我们得到如下错误,因为AppWarp的回调不在UI线程中。

    Exception in thread "MessageDispatchThread" java.lang.IllegalArgumentException:     Error compiling shader

3. 超级跳跃中声音不可用了,这是因为iOS中的声音是使用RoboVm。

英文原文: Java Multiplayer libgdx Tutorial

译文链接:http://www.oschina.net/translate/java-multiplayer-libgdx-tutorial

责任编辑:林师授 来源: 开源中国社区 编译
相关推荐

2013-11-06 11:31:28

Android游戏

2013-12-04 13:30:45

Android游戏引擎libgdx教程

2013-12-06 10:22:42

Android游戏引擎libgdx教程

2013-12-06 10:31:14

Android游戏引擎libgdx教程

2013-12-04 17:27:10

Android游戏引擎libgdx教程

2013-12-06 09:59:53

Android游戏引擎libgdx教程

2013-12-06 10:12:49

Android游戏引擎libgdx教程

2022-01-07 13:34:25

Java时间格式化

2013-12-04 16:07:27

Android游戏引擎libgdx教程

2013-12-04 17:14:57

Android游戏引擎libgdx教程

2013-12-04 16:21:02

Android游戏引擎libgdx教程

2021-01-19 11:07:34

内存条标签硬件

2023-05-18 07:15:35

SSDHDD游戏

2013-12-06 10:35:28

Android游戏引擎libgdx教程

2013-12-04 16:28:29

Android游戏引擎libgdx教程

2021-08-26 21:55:38

DPU架构数据

2011-12-24 21:59:02

iPhone

2009-03-23 13:50:40

Nehalem服务器高性能

2012-05-18 10:06:59

暗黑破坏神3服务器
点赞
收藏

51CTO技术栈公众号