>【前言】
别小看了一个飞行棋,它其实包含了非常多的内容。尤其是可支持局域网对战、甚至是进行复盘的飞行棋,它将包含的知识点内容更将会有:
没关系,这篇博客我会给出详细的实例代码和素材包,并进行设计分析,可能会分为好几篇进行发布。
>【前期设计】
1.UI设计·适配
说到UI设计,我们就不得不提到安卓的UI设计。
安卓机型众多,分辨率、尺寸也各分千秋,因此适配问题一直是一个老大难。好在安卓已经推出了一个百分比布局库,有兴趣的朋友可以自行去查阅。
但我今天准备分享的另一种方法,利用绝对布局来进行屏幕适配。在我准备分享这个方法时,我上网搜了一下资料,发现这个方法早已有大手子给出了详细的分析,我也就不再赘述了,可以移步这里:http://blog.csdn.net/lmj623565791/article/details/45460089。
2.分析游戏棋盘
如图所示,这是我自制的一张1080*1080px分辨率的PNG图片,我给它分成了36*36格,每格显然是30px的正方形。这样,我们描述某个棋格,就可以用一个(x,y)坐标系来进行描述了。
你们可以到这里下载我所使用的自制素材包:
http://download.csdn.net/detail/shenpibaipao/9815604
3.设计棋子对象
接下来,就会开始涉及代码部分了。
为防有同学直接原封不动地copy我的代码去交作业,我在一些写法、命名、小地方动了手脚,你很难看出来,但你们的老师一眼就能看出来。还是那句话,我注重的实例开发,是一个思路的过程,直接copy什么的,是不太好的。
我们定义一个类:
public class Chessman{
//状态量
//常量值
//方法
}
状态量应该有哪些呢?我们知道,飞行棋的棋子有两种状态,飞行中和停在机场、完成飞行停在机场和没完成飞行停在机场。所以,应当有两个状态量:
private boolean Flying,Completed;
同时,还应该有一个用于记录当前棋子已移动步数的值,相信我,这个值到时候会非常好用:
private int now_pos;//当前已移动步数
常量值有哪些呢?很显然应当有这一些:
private int Faction,num;//棋子阵营,编号
private ImageView img;//绑定图像
private static int kuan,left_top_pos;//棋局格子宽度 和左上角坐标
对于静态量kuan,left_top_pos这两个值,你可能暂时还无法理解。这个kuan,记录的是该次游戏中每一个棋格的宽度,比如30px,left_top_pos记录的是棋盘左上角的坐标,用于定位用的。存为静态量,所有Chessman对象公用一份,主要用于做屏幕适配。
除此之外,我们也应当为其设定各种方法,如get()、set()、isXXX()等,同时还有一些特殊的方法: public void resetStatusALL(){
//初始化棋子状态量,用于重启游戏
}
public void move(int POSX,int POSY){
//按坐标移动棋子
}
public void move(int steps){
//按roll点移动棋子
}
public void Fly(){
//飞机起飞
}
public void Killed(){
//棋子被击杀
}
下面是我完整的实例代码,可能现在还无法完全读懂,但是在之后的讲解中你会慢慢理解:
import android.provider.Settings;
import android.widget.ImageView;
/**
* Created by Shenpibaipao
*/
public class Cheesman {
private boolean Completed,Flying;//棋子状态
private int now_pos;//当前已移动步数
private int Faction,num;//棋子阵营,编号
private ImageView img;//绑定图像
private static int kuan,left_top_pos;//棋局格子宽度 和左上角坐标
public Cheesman(int FACTION,int NUM,ImageView IMAGEVIEW){
resetStatusALL();
Faction=FACTION;
img=IMAGEVIEW;
num=NUM;
now_pos=0;
}
public void resetStatusALL(){
Completed=false;
Flying=false;
now_pos=0;
}
public void move(int POSX,int POSY){
img.setX(POSX*kuan);
img.setY(left_top_pos+POSY*kuan);
}
public void move(int steps){
//最多57格 0~26
int cha=now_pos+steps-Value.Teminal;
if(cha<0)cha=0;
if(isFlying()){
switch(Faction){
case Value.red:
move(Value.redPathx[now_pos+steps-cha],Value.redPathy[now_pos+steps-cha]);
break;
case Value.yellow:
move(Value.yellowPathx[now_pos+steps-cha],Value.yellowPathy[now_pos+steps-cha]);
break;
case Value.blue:
move(Value.bluePathx[now_pos+steps-cha],Value.bluePathy[now_pos+steps-cha]);
break;
case Value.green:
move(Value.greenPathx[now_pos+steps-cha],Value.greenPathy[now_pos+steps-cha]);
break;
}
}
now_pos+=steps;
}
public int getX(){
return (int)img.getX()/kuan;
}
public int getY(){
return (int)(img.getY()-left_top_pos)/kuan;
}
public int getFaction(){
return Faction;
}
public int getNum(){
return num;
}
public int getNow_pos(){
return now_pos;
}
public boolean isCompleted(){
return Completed;
}
public boolean isFlying(){
return Flying;
}
//功能类
public void CompletedTour(){
now_pos=0;
setFlying(false);
setCompleted(true);
}
public void Fly(){
now_pos=0;
switch (Faction){
case Value.red:
move(Value.redPathx[now_pos],Value.redPathy[now_pos]);
break;
case Value.yellow:
move(Value.yellowPathx[now_pos],Value.yellowPathy[now_pos]);
break;
case Value.blue:
move(Value.bluePathx[now_pos],Value.bluePathy[now_pos]);
break;
case Value.green:
move(Value.greenPathx[now_pos],Value.greenPathy[now_pos]);
break;
default:
System.out.println("注意:起飞失败");
}
setFlying(true);
}
public void Killed(){
now_pos=0;
setFlying(false);
System.out.println("注意:棋子被击杀");
}
public static void load_qipan(int KUAN,int LEFT_TOP_POS){
kuan=KUAN;
left_top_pos=LEFT_TOP_POS;
}
public ImageView getImg(){
return img;
}
private void setFlying(boolean bool){
Flying=bool;
}
private void setCompleted(boolean bool){
Completed=bool;
}
}
我们的目标是制作一个可供本地多人或网上对战的飞行棋,因此我们需要一个ConfigHelper,你可以把它设计为一个单实例。我在下面的代码里没有这么写,因为我不想写好了给别人Copy。请自行修改。
在这个类中,需要读取到该次游戏中有几个玩家、几个AI、几个网上对手,由于人类玩家可设置成“托管”状态,所以应该给出相应的方法,进行数值操作。
下面是完整的实例代码:
import android.text.Layout;
import android.view.View;
/**
* Created by a on 2017/4/15.
*/
public class ConfigHelper {
private int LocalHumanNum,OnlineHumanNum,AINum;
private int GameType;
private int PlayerType[]=new int[4];
private boolean Host;
//private int redType,yellowType,blueType,greenType;
public ConfigHelper(int GAMETYPE){
GameType=GAMETYPE;
if(GAMETYPE==Value.Online){
LocalHumanNum=1;
OnlineHumanNum=3;
AINum=0;
}
else if(GAMETYPE==Value.Local){
LocalHumanNum=4;
OnlineHumanNum=0;
AINum=0;
}
Host=true;//默认为主机
PlayerType[Value.red]=Value.LocalHuman;
PlayerType[Value.yellow]=Value.LocalHuman;
PlayerType[Value.blue]=Value.LocalHuman;
PlayerType[Value.green]=Value.LocalHuman;
}
public void cgPlayerType(int FACTION,int TYPE){
if(TYPE==Value.LocalHuman) LocalHumanNum++;
else if(TYPE==Value.OnlineHuman)OnlineHumanNum++;
else if(TYPE==Value.AI)AINum++;
int t=0;
if(FACTION==Value.red){
t=PlayerType[Value.red];
setRedType(TYPE);
}
else if(FACTION==Value.yellow){
t=PlayerType[Value.yellow];
setYellowType(TYPE);
}
else if(FACTION==Value.blue){
t=PlayerType[Value.blue];
setBlueType(TYPE);
}
else if(FACTION==Value.green){
t=PlayerType[Value.green];
setGreenType(TYPE);
}
switch(t){
case Value.LocalHuman:
LocalHumanNum--;
break;
case Value.OnlineHuman:
OnlineHumanNum--;
break;
case Value.AI:
AINum--;
break;
}
}
public void resetStatusALL(){
if(GameType==Value.Online){
LocalHumanNum=1;
OnlineHumanNum=3;
AINum=0;
}
else if(GameType==Value.Local){
LocalHumanNum=4;
OnlineHumanNum=0;
AINum=0;
}
PlayerType[Value.red]=Value.LocalHuman;
PlayerType[Value.yellow]=Value.LocalHuman;
PlayerType[Value.blue]=Value.LocalHuman;
PlayerType[Value.green]=Value.LocalHuman;
}
public void setRedType(int TYPE){
PlayerType[Value.red]=TYPE;
}
public void setYellowType(int TYPE){
PlayerType[Value.yellow]=TYPE;
}
public void setBlueType(int TYPE){
PlayerType[Value.blue]=TYPE;
}
public void setGreenType(int TYPE){
PlayerType[Value.green]=TYPE;
}
public void setHost(boolean host) {
Host = host;
}
public int getGameType(){
return GameType;
}
public boolean isHost() {
return Host;
}
public int getPlayerType(int FACTION) {
return PlayerType[FACTION];
}
public int getRedType(){
return PlayerType[Value.red];
}
public int getYellowType(){
return PlayerType[Value.yellow];
}
public int getBlueType(){
return PlayerType[Value.blue];
}
public int getGreenType(){
return PlayerType[Value.green];
}
}
5.常用值的封装
你可能注意到了我使用了不少Value.xxxx,这是哪里来的呢?
为了代码阅读性考虑,我另外封装了一个值类Value.Class,里面记录了所有的常用值,我暂时在这里给出一部分:
public class Value {
static final int red=0;
static final int yellow=1;
static final int blue=2;
static final int green=3;
static final int AI=0;
static final int OnlineHuman=1;
static final int LocalHuman=2;
static final int Online=0;
static final int Local=1;
static final String PlayerName[]=new String[]{"Red","Yellow","Blue","Green"};
}
其实有了这些对象,完成这个游戏已经不难了。剩下要做的,就是制定行棋规则、UI更新等。这个我们留到下次再说。