android游戏开发实例-可局域网对战的飞行棋(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Shenpibaipao/article/details/70232690

这一次,我们接着来谈AI策略和网络连接的架构。


在上一篇中我们设计了几个回合,我们简单来回忆一下这个流程:

game_begin() -> turn_begin() -> 回合中 -> turn_end() -> next_turn();

其中,我们把收信发信和AI策略都放在turn_begin()和turn_end()中。再看一眼我们是怎么设计的turn_begin():

private void turn_begin(){
        //回合开始
        resetRoll();//重置骰子
        System.out.println("玩家:["+Value.PlayerName[whosTurn]+"]的回合!Type:"+chdm.getPlayerType(whosTurn)+",请投掷骰子!");
        //判断当前玩家是人还是AI
        if(chdm.getGameType()==Value.Local){
            //如果不是网络对战
            if(chdm.getPlayerType(whosTurn)==Value.AI){
                //当前玩家是AI
                //AIMethod(Value.AIType[0]);//为什么不这么写
                myHandler.postDelayed(AIDelay,2000);//执行AI策略
            }
            return ;//本地游戏的话,不执行下面的语句
        }
        //如果是网络对战
        else if(chdm.isHost()){
            //如果当前玩家是服务器,需要进行转发、AI()策略
            if(chdm.getPlayerType(whosTurn)==Value.Online){
                //服务器收信
                myHandler.post(HostrecMsg);
            }
            else if(chdm.getPlayerType(whosTurn)==Value.AI){
                myHandler.postDelayed(AIDelay,2000);
            }
            else return;//服务主自己的回合,可以开始正常游玩
        }
        else {
            //是客户端
            if(chdm.getPlayerType(whosTurn)!=Value.LocalHuman){
                //网络用户或AI时,收取服务器信息并更新UI
                myHandler.post(ClientrecMsg);
            }
            else return;//客户端自己的回合,可以开始游玩
        }
    }


>其中___recMsg()就是我们要实现的收信发信规则之一;

如果是客户端,ClientrecMsg(Chessman c)可以这么写:

try {  
	//Socket socket = new Socket(ip, port);  
	//建立输入流  
	ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());   
	//输入对象, 一定要flush
	oos.writeObject(c);
	oos.flush();        
	oos.close();  
	//socket.close();  
} 
catch (UnknownHostException e) {  
	e.printStackTrace();
}
这样,直接就把序列化的Chessman c直接发送到客户端去了。你可能会注意到为什么我把第二行注释掉了。因为这个连接的建立应该是长期有效的通道。应该把它在game_begin()期间就开个新的子进程进行通道的建立。一旦TCP socket建立,利用handler机制通知主线程,然后进行turn_begin()的操作。关于TCP连接你们可以到这里查看相关内容:

http://blog.csdn.net/shenpibaipao/article/details/70176038

我们只说说怎么通知主线程启动游戏吧,假设客户端利用try_connected()来建立连接,主线程(UI线程)中的Handler名为mHandler:

private final Runnable NetRunable = new Runnable() {
        @Override
        public void run() {
            try{
		try_connected();
		Bundle b=new Bundle;
		b.putBoolean("isOk",true);
		Message msg=Message.obtain();
		msg.setData(b);
		mHandler.sendMessage(msg);
	    } catch(Exception e){
		//建立失败,给出你的解决方法,比如
		Bundle b=new Bundle;
		b.putBoolean("isOk",false);
		Message msg=Message.obtain();
		msg.setData(b);
		mHandler.sendMessage(msg);
	    }
        }
    };
private final Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Bundle b=msg.getData();
            boolean done=b.getBoolean("isOK");
            if(done)game_begin();//游戏开始
	    else finish();
        }
    };

而如果是服务器,它不仅要接收其他玩家送来的数据包,还要把数据包发送到其他玩家那里去。假设服务器建立了N个Socket[] socket,分别对应着不同的玩家,那么在已知当前回合玩家whos_turn这个全局变量的情况下,它应该这么写

try {  
<//从当前网络玩家那里收包 
ObjectInputStream oos = new ObjectInputStream(socket[whos_turn].getInputStream()); 
ObjectOutputStream oos; 
Object obj = ois.readObject();
//转发给其他玩家
for(int i;i<N;i++){
	if(i!=whos_turn){
		oos=new ObjectOutputStream(socket[i].getOutputStream());
		oos.writeObject(c);  
		oos.flush();        
		oos.close();
		}
	}
} 


同样,在turn_end()时,要把当前玩家的操作发送出去,代码逻辑与上面相同,只不过把Output改成Input、Output改成Input罢了。

需要注意的一点,所有的AI策略均在服务器运行,如果当前的玩家是一个AI,那么这个AI移动的Chessman c也要由服务器在turn_end()发送出去,服务器负责接收。


由于我们发送的是序列化的Chessman,所以我们现在要对Chessman.java进行修改:

import java.io.Serializable;  
  
public class Chessman implements Serializable {  
    private static final long serialVersionUID = 1L;  
	//其他照旧
}


你可能要问,为什么不单单发送一个坐标过去,而是发送一个对象过去呢?

传参一致性原则,可以使代码更好读。如果只发送一个坐标,那么在收信方还要写一个把坐标转化成具体Chessman对象的方法,挺麻烦的。反正我们的设计目标是局域网对战,流量什么的,不在需求优化之列。


>接下来,我们来写AI策略。

作为AI,我们要让它做的就是模拟人类的操作。

先让我们回忆一下一个人类玩家在正常操作时,要怎么进行游戏:

roll() -> select_a_chessman() -> go(c)

所以,这个AI策略可以这么写:

private void AIMethod(int AITYPE){
        roll();//AI滚动骰子
        if(canMove()){//AI判断是否有子可走
            //如果有,帮助AI选择一个棋子
            if(AITYPE==Value.AIType[0])c=AIChooseChessman_Type0(whosTurn);//AI选择棋子
            else c=AIChooseChessman_Type0(whosTurn);//默认选择ai类型

            go(c);
        }
        else{
            turn_end();//回合结束并发送信息
        }
        //Todo包装信息
    }
你们可以看到我写了一行:

if( AITYPE==Value.AIType[0] ) c=AIChooseChessman_Type0(whosTurn);
由于我们可能扩展出数种行为的AI,所以我们给AI策略的逻辑AIMethod(int AITYPE)传递了一个AI类型,然后进行相应的AI择棋方法的选择。

比如我现在准备完成一个具有如下行为表型的AI:"温和的"、"更趋向于奔向终点"、"不会主动击杀其他玩家"的AI类型。所以我可以这么写:

private Cheesman AIChooseChessman_Type0(int FACTION){
        Cheesman c[]=new Cheesman[4];
        switch (FACTION){//选择阵营
            case Value.red:
                for(int i=0;i<4;i++)c[i]=red[i];break;
            case Value.yellow:
                for(int i=0;i<4;i++)c[i]=yellow[i];break;
            case Value.blue:
                for(int i=0;i<4;i++)c[i]=blue[i];break;
            case Value.green:
                for(int i=0;i<4;i++)c[i]=green[i];break;
        }
        //是否有可起飞的棋子
        if(roll_num==6){
            for(int i=0;i<4;i++){
                if(!c[i].isFlying())return c[i];
            }
        }
        //是否有马上要赢的棋子?显然其必然已起飞
        for(int i=0;i<4;i++){
            if(c[i].getNow_pos()+roll_num==Value.Teminal)return c[i];
        }
        //是否有可跳跃的棋子,其必须已起飞
        for(int i=0;i<4;i++){
            if(c[i].isFlying() && (c[i].getNow_pos()-2)%4==0)return c[i];
        }
        //选择可移动的,目前行程最远的可飞行棋子
        int maxI=0,maxPos=0;
        for(int i=0;i<4;i++){
            if(c[i].isFlying()){
                if(c[i].getNow_pos()>=maxPos){
                    maxPos=c[i].getNow_pos();
                    maxI=i;
                }
            }
        }
        return c[maxI];
    }



等你完成了这一些逻辑行为的组装之后,你就可以得到这样一款"可供局域网对战、具有AI行为、玩家可随时选择“托管”行为"的飞行棋游戏了。如图所示:



游戏愉快: )

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页