【木棉花】基于JAVA UI开发的小游戏——推箱子(上) 原创 精华
前言
在上期文章中,分享了关于项目的效果预览图,从这一期开始,将逐步分享这个项目的构建流程。实际上,笔者在进行开发的过程中,并不是写完一个界面的内部逻辑,就开始对界面进行美化,而是先让所有的东西可以正常地跑起来,再谈美化。因此本系列文章前半部分会重点讨论游戏以及界面之间的核心逻辑,后半部分则会分享美化界面的部分。
项目创建
打开DevEco Studio,创建一个新项目,选择JAVA作为开发语言,将项目保存至合适的位置。
根据上期分享的开发思路,先完成UI交互部分的框架。
可以看到,这里需要新建三个Slice(原本自带一个MainAbilitySlice):
下面对四个
MainAbilitySlice:打开应用时,首先显示的界面,也就是用户主界面。
SelectSlice:关卡选择界面,用户可以在这个界面选择将要跳转的关卡。
InitSlice:加载界面。当用户选择关卡之后,会进入加载界面,仿照游戏加载资源。(实际上啥都没干)
GameSlice:最后一个界面,也就是这个游戏的核心界面,所有的游戏逻辑都将在这个页面中进行,因此它将是本篇文章的核心讲解部分。
至此,我们可以简单地梳理一下四个界面以及他们包含的组件之间的关系:用户进入MainAbilitySlice之后,通过“开始游戏”按键进入SelectSlice,在SelectSlice中有三个按键,会对应跳转到三个不同的关卡,但是进入关卡之前会先进入InitSlice,加载过后在进入最后的GameSlice,从而开始游戏。以上就是开发的时候要理清楚的页面跳转关系。
核心代码分析
MainAbilitySlice
里面有四个按钮,那可以简单的给他们分个类,例如:开始游戏的按钮要实现的功能是页面跳转,直接与其他界面关联,分为一类;历史记录与关于游戏可以用弹出窗口来实现,不需要额外界面,归为一类;退出游戏按钮直接结束应用进程,也是单独一类。明确之后,就可以给各个按钮添加点击事件了:
//开始游戏按钮
startBtn.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
//页面跳转
present(new SelectSlice(),new Intent());
}
});
//历史记录按钮
recordBtn.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
//历史记录弹窗
}
});
//关于游戏按钮
aboutBtn.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
//关于游戏弹窗
}
});
//退出游戏按钮
exitBtn.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
//退出游戏提示
CommonDialog commonDialog = new CommonDialog(getContext());
commonDialog.setTitleText("提示");
commonDialog.setContentText("是否退出游戏");
commonDialog.setButton(1, "确定", new IDialog.ClickedListener() {
@Override
public void onClick(IDialog iDialog, int i) {
terminateAbility();
}
});
commonDialog.setButton(2, "取消", new IDialog.ClickedListener() {
@Override
public void onClick(IDialog iDialog, int i) {
commonDialog.destroy();
}
});
commonDialog.show();
}
});
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
在开发中,由于历史记录跟关于游戏这两个功能并不是核心,因此,最开始也只是做个壳子放在这,以便自己能专注于游戏主逻辑的开发,这也是我想分享的一种思路:先搭壳子再填东西。因此,阅读本系列时,如果碰到代码中只有注释,没有实现内容时,那是因为当时做到这一步的时候,并不会去关注具体如何实现,只会想个大概,先放着。
到这里之后,实际上已经完成了游戏的退出以及从MainAbilitySlice页面到SelectSlice页面的导航,便可进行到我们的下一步。
SelectSlice
这一个界面主要有三个按钮,如何实现按下不同的按钮,跳转到同一个加载界面,但是加载完后又跳转到不同的游戏界面?这里使用Intent对跳转时的数据进行打包传输,具体实现如下:
firstBtn.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent i = new Intent();
i.setParam("关卡",1);
present(new InitSlice(),i);
}
});
secondBtn.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent i = new Intent();
i.setParam("关卡",2);
present(new InitSlice(),i);
}
});
thirdBtn.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent i = new Intent();
i.setParam("关卡",3);
present(new InitSlice(),i);
}
});
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
这里Intent的key都是“关卡”,但是有不同的value,对应不同的关卡。实际上,到这里SelectSlice已经完成了它的功能了。接下来进入InitSlice。
InitSlice
在加载界面中,我预想的是一个动态的画面,然后加上一个进度条,因此我可能需要用到能够播放gif的组件,以及进度条组件。那要怎么实现加载的时候进度条跟进?我的实现方式是使用两个定时器(其实用一个也完全能搞定)
//onStart外定义
Timer t1 = new Timer();
Timer t2 = new Timer();
//onStart内
TimerTask task1 = new TimerTask() {
@Override
public void run() {
//页面跳转
present(new GameSlice(),intent);
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
//进度条更新
int value = progressBar.getProgress();
progressBar.setProgressValue(value+20);
}
};
t1.schedule(task1, 5000);
t2.schedule(task2,0,1000);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
关于如何播放gif,本不应该在此讲解,因为与主线任务无关,但是这里用到的第三方组件,后面游戏界面频繁使用,因此在这进行介绍。
这里用到了第三方组件Glide,关于组件如何使用,可具体看这篇文章,只需几行代码即可完成gif的播放,十分方便。
int imageResourceId = ResourceTable.Media_gifimg;
Glide.with(this)
.asGif()
.load(imageResourceId)
.into(draweeView);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
至此,从InitSlice跳转到GameSlice的逻辑也写好了,并且携带着从SelectSlice打包过来的数据,接下来重点讲解游戏界面的实现。
GameSlice
制作一个可以玩的游戏界面最首要的任务就是绘制地图,因此定义了一个JAVA类GameMap用于地图的绘制。
在说明如何实现GameMap之前,我想先简单阐述一下推箱子游戏的实现逻辑:实际上每一次操作的都是图片,逻辑判断依赖的是图片所绑定的属性值(只涉及加减运算),举个例子:我在程序中将路设置为0,将墙体设置为1,将宝可梦设置为2和3,将空球设置为4,那收服之后的球应该设置为:2+4=6、3+4=7,这样就实现了你把空球推向宝可梦时,显示的是已经收复的球的状态;而如果将人设置为8,那8+2=10、8+3=11也必须是人,这样才能实现你移动到宝可梦上面时,是以原人物的方式呈现。这是在设置属性值需要注意的,其他方面,例如怎么判断墙体之类的,只需要if语句判断即可。
还有一点,如果我们要实现回退功能,就需要用到栈的一些相关操作。
核心代码如下:
//GameMap继承于PositionLayout布局,方便对图片进行渲染
public class GameMap extends PositionLayout{
private final static int size = 110;
//用二维数组来存储地图
private Integer[][] gameMap;
//定义x,y坐标
private Pair<Integer,Integer> map_position;
//标识是否绘制过地图(画过一次后,后面所有的操作都只能是进行刷新,防止重复生成对象)
private Boolean isDrew = Boolean.FALSE;
//定义移动方式枚举,方便外部调用进行选择
public enum MOVE_WAY{
MOVE_UP,
MOVE_DOWN,
MOVE_LEFT,
MOVE_RIGHT
}
//设置每种物体的属性值
private final static int ROAD = 0;
private final static int WALL = 1;
private final static int LABA = 2;
private final static int YIBU = 3;
private final static int BOBO = 4;
private final static int MINI = 5;
private final static int MIAO = 6;
private final static int BALL_EMPTY = 7;
private final static int BALL_FULL1 = 9;
private final static int BALL_FULL2 = 10;
private final static int BALL_FULL3 = 11;
private final static int BALL_FULL4 = 12;
private final static int BALL_FULL5 = 13;
private final static int PEOPLE1 = 14;
private final static int PEOPLE2 = 16;
private final static int PEOPLE3 = 17;
private final static int PEOPLE4 = 18;
private final static int PEOPLE5 = 19;
private final static int PEOPLE6 = 20;
//定义存储地图用的栈
private Stack<Integer[][]> stack;
//可使用此构造函数绘制不同大小的地图,这个是预留的接口,项目中使用的是直接在xml文件中加入这个组件(因为继承了PositionLayout所以可以在xml文件中使用),当然也可以直接用构造函数创建,留给读者自己发挥。
public GameMap(Context context,Integer[][] map,int x,int y) {
super(context);
gameMap = map;
map_position = new Pair<>(x,y);
}
//外部设置地图接口
public void setMap(Integer[][] map) {
gameMap = map;
map_position = new Pair<>(map.length,map[0].length);
stack = new Stack<>();
}
//绘制地图接口
public void drawMap() {
setHeight(size * map_position.s);
setWidth(size * map_position.f);
for (int i = 0; i < map_position.f; i++) {
for (int j = 0; j < map_position.s; j++) {
DraweeView draweeView = new DraweeView(getContext());
draweeView.setComponentSize(size,size);
draweeView.setContentPosition(size * j, size * i);
int index = gameMap[i][j];
switch (index) {
case ROAD:
Glide.with(getContext())
.load(ResourceTable.Media_road)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case WALL:
Glide.with(getContext())
.load(ResourceTable.Media_wall)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case LABA:
Glide.with(getContext())
.load(ResourceTable.Media_laba)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case YIBU:
Glide.with(getContext())
.load(ResourceTable.Media_yibu)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case BOBO:
Glide.with(getContext())
.load(ResourceTable.Media_bobo)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case MINI:
Glide.with(getContext())
.load(ResourceTable.Media_mini)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case MIAO:
Glide.with(getContext())
.load(ResourceTable.Media_miao)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case BALL_EMPTY:
Glide.with(getContext())
.load(ResourceTable.Media_ballEmpty)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case BALL_FULL1:
case BALL_FULL2:
case BALL_FULL3:
case BALL_FULL4:
case BALL_FULL5:
Glide.with(getContext())
.load(ResourceTable.Media_ballFull)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case PEOPLE1:
case PEOPLE2:
case PEOPLE3:
case PEOPLE4:
case PEOPLE5:
case PEOPLE6:
Glide.with(getContext())
.load(ResourceTable.Media_people)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
}
this.addComponent(draweeView);
}
}
isDrew = Boolean.TRUE;
}
//外部刷新地图接口
public void flushMap(){
for (int i = 0; i < map_position.f; i++) {
for (int j = 0; j < map_position.s; j++) {
DraweeView draweeView = (DraweeView) getComponentAt(i * map_position.s + j);
draweeView.setComponentSize(size,size);
draweeView.setContentPosition(size * j, size * i);
int index = gameMap[i][j];
switch (index) {
case ROAD:
Glide.with(getContext())
.load(ResourceTable.Media_road)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case WALL:
Glide.with(getContext())
.load(ResourceTable.Media_wall)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case LABA:
Glide.with(getContext())
.load(ResourceTable.Media_laba)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case YIBU:
Glide.with(getContext())
.load(ResourceTable.Media_yibu)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case BOBO:
Glide.with(getContext())
.load(ResourceTable.Media_bobo)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case MINI:
Glide.with(getContext())
.load(ResourceTable.Media_mini)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case MIAO:
Glide.with(getContext())
.load(ResourceTable.Media_miao)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case BALL_EMPTY:
Glide.with(getContext())
.load(ResourceTable.Media_ballEmpty)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case BALL_FULL1:
case BALL_FULL2:
case BALL_FULL3:
case BALL_FULL4:
case BALL_FULL5:
Glide.with(getContext())
.load(ResourceTable.Media_ballFull)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
case PEOPLE1:
case PEOPLE2:
case PEOPLE3:
case PEOPLE4:
case PEOPLE5:
case PEOPLE6:
Glide.with(getContext())
.load(ResourceTable.Media_people)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
}
}
}
}
//获取当前操作的人物坐标
public Pair<Integer,Integer> getMyPosition(){
for (int i = 0; i < map_position.f; i++)
{
for (int j = 0; j < map_position.s; j++)
{
if (gameMap[i][j] == PEOPLE1 || gameMap[i][j] == PEOPLE2 ||
gameMap[i][j] == PEOPLE3 || gameMap[i][j] == PEOPLE4 ||
gameMap[i][j] == PEOPLE5 || gameMap[i][j] == PEOPLE6)
{
return new Pair<>(i,j);
}
}
}
return new Pair<>(-1,-1);
}
//给地图里任意一张图设置对应的值,移动的时候需要此接口
protected void setValue(int x,int y,int value){
gameMap[x][y] += value;
}
//判断是否能够移动
protected Boolean isMove(int i,int j){
if (gameMap[i][j] == ROAD || gameMap[i][j] == LABA ||
gameMap[i][j] == YIBU || gameMap[i][j] == BOBO ||
gameMap[i][j] == MINI || gameMap[i][j] == MIAO)
return Boolean.TRUE;
return Boolean.FALSE;
}
//判断是不是球
protected Boolean isBall(int i,int j){
if (gameMap[i][j] == BALL_EMPTY || gameMap[i][j] == BALL_FULL1 ||
gameMap[i][j] == BALL_FULL2 || gameMap[i][j] == BALL_FULL3 ||
gameMap[i][j] == BALL_FULL4 || gameMap[i][j] == BALL_FULL5)
{
return true;
}
return Boolean.FALSE;
}
//外部接口,每一次移动完判断游戏是否结束
public Boolean isWin(){
for (int i = 0; i < map_position.f; i++)
{
for (int j = 0; j < map_position.s; j++)
{
if (gameMap[i][j] == LABA || gameMap[i][j] == YIBU ||
gameMap[i][j] == BOBO || gameMap[i][j] == MINI ||
gameMap[i][j] == MIAO) return Boolean.FALSE;
}
}
return Boolean.TRUE;
}
//外部移动接口
public void move(@NotNull MOVE_WAY move_way){
Pair<Integer,Integer> position = getMyPosition();
Integer[][] oldMap = new Integer[map_position.f][map_position.s];
for (int i = 0; i < map_position.f; i++) {
if (map_position.s >= 0) System.arraycopy(gameMap[i], 0, oldMap[i], 0, map_position.s);
}
stack.push(oldMap);
switch (move_way){
case MOVE_UP:
if (this.isMove(position.f - 1, position.s))
{
this.setValue(position.f, position.s, -PEOPLE1);
this.setValue(position.f - 1, position.s, PEOPLE1);
}
if (this.isBall(position.f - 1, position.s))
{
if (this.isMove(position.f - 2, position.s))
{
this.setValue(position.f, position.s, -PEOPLE1);
this.setValue(position.f - 1, position.s, BALL_EMPTY);
this.setValue(position.f - 2, position.s, BALL_EMPTY);
}
}
break;
case MOVE_DOWN:
if (this.isMove(position.f + 1, position.s))
{
this.setValue(position.f, position.s, -PEOPLE1);
this.setValue(position.f + 1, position.s, PEOPLE1);
}
if (this.isBall(position.f + 1, position.s))
{
if (this.isMove(position.f + 2, position.s))
{
this.setValue(position.f, position.s, -PEOPLE1);
this.setValue(position.f + 1, position.s, BALL_EMPTY);
this.setValue(position.f + 2, position.s, BALL_EMPTY);
}
}
break;
case MOVE_LEFT:
if (this.isMove(position.f, position.s - 1))
{
this.setValue(position.f, position.s, -PEOPLE1);
this.setValue(position.f, position.s - 1, PEOPLE1);
}
if (this.isBall(position.f, position.s - 1))
{
if (this.isMove(position.f, position.s - 2))
{
this.setValue(position.f, position.s, -PEOPLE1);
this.setValue(position.f, position.s - 1, BALL_EMPTY);
this.setValue(position.f, position.s - 2, BALL_EMPTY);
}
}
break;
case MOVE_RIGHT:
if (this.isMove(position.f, position.s + 1))
{
this.setValue(position.f, position.s, -PEOPLE1);
this.setValue(position.f, position.s + 1, PEOPLE1);
}
if (this.isBall(position.f, position.s + 1))
{
if (this.isMove(position.f, position.s + 2))
{
this.setValue(position.f, position.s, -PEOPLE1);
this.setValue(position.f, position.s + 1, BALL_EMPTY);
this.setValue(position.f, position.s + 2, BALL_EMPTY);
}
}
break;
}
flushMap();
}
//外部回退原先地图接口
public void back(){
if(stack.empty()) return;
Integer[][] temp = stack.peek();
for (int i = 0; i < map_position.f; i++) {
if (map_position.s >= 0) System.arraycopy(temp[i], 0, gameMap[i], 0, map_position.s);
}
flushMap();
stack.pop();
}
public Boolean getIsDrew(){
return isDrew;
}
public GameMap(Context context) {
super(context);
}
public GameMap(Context context, AttrSet attrSet) {
super(context, attrSet);
}
public GameMap(Context context, AttrSet attrSet, String styleName) {
super(context, attrSet, styleName);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
- 331.
- 332.
- 333.
- 334.
- 335.
- 336.
- 337.
- 338.
- 339.
- 340.
- 341.
- 342.
- 343.
- 344.
- 345.
- 346.
- 347.
- 348.
- 349.
- 350.
- 351.
- 352.
- 353.
- 354.
- 355.
- 356.
- 357.
- 358.
- 359.
- 360.
- 361.
- 362.
- 363.
- 364.
- 365.
- 366.
- 367.
- 368.
- 369.
- 370.
- 371.
- 372.
- 373.
- 374.
- 375.
- 376.
- 377.
- 378.
- 379.
- 380.
- 381.
- 382.
- 383.
- 384.
- 385.
- 386.
- 387.
- 388.
- 389.
- 390.
- 391.
- 392.
- 393.
- 394.
- 395.
- 396.
- 397.
- 398.
- 399.
- 400.
- 401.
至此,完成了地图类的代码,可以开始绘制GameSlice了。在预览图中看到,核心部分有很多:一个退出界面按钮,一个设置按钮,一个倒计时器,还有一张地图,一个后退地图操作的按钮,按照我自己的想法先进行分类。后退地图操作的按钮和地图是刚需,优先实现,代码如下:
//在外部定义变量
float start_x;
float start_y;
Integer[][] map;
Integer[][] map1 = {
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 0, 0, 1, 1},
{1, 0, 1, 0, 1, 7, 2, 1, 1},
{1, 0, 0, 0, 0, 7, 3, 1, 1},
{1, 0, 1, 0, 1, 7, 4, 1, 1},
{1, 0, 0, 0, 1, 0, 0, 1, 1},
{1, 1, 1, 1, 1, 0, 14, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1}
};
Integer[][] map2 = {
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 0, 0, 0, 3, 0, 1, 1},
{1, 1, 0, 1, 0, 1, 0, 1, 1},
{1, 1, 0, 7, 14, 7, 0, 1, 1},
{1, 1, 0, 1, 0, 1, 5, 1, 1},
{1, 1, 0, 7, 6, 0, 0, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1}
};
Integer[][] map3 = {
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 2, 0, 7, 0, 1, 1, 1},
{1, 1, 0, 7, 6, 7, 0, 1, 1},
{1, 3, 4, 5, 1, 0, 5, 1, 1},
{1, 0, 1, 7, 0, 7, 0, 1, 1},
{1, 0, 7, 0, 1, 2, 7, 1, 1},
{1, 0, 14, 0, 0, 0, 0, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1}
};
//onStart方法内
switch (intent.getIntParam("关卡",0))
{
case 1:
map = map1;
break;
case 2:
map = map2;
break;
case 3:
map = map3;
break;
}
gameMap.setMap(map);
if(!gameMap.getIsDrew()) gameMap.drawMap();
else gameMap.flushMap();
//滑动屏幕移动角色
gameMap.setTouchEventListener(new Component.TouchEventListener() {
@Override
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
int action = touchEvent.getAction();
switch (action) {
case TouchEvent.PRIMARY_POINT_DOWN:
MmiPoint startPoint = touchEvent.getPointerPosition(0);
start_x = startPoint.getX();
start_y = startPoint.getY();
break;
case TouchEvent.PRIMARY_POINT_UP:
MmiPoint endPoint = touchEvent.getPointerPosition(0);
if(endPoint.getX() > start_x && Math.abs(endPoint.getY() - start_y) < 100){//right
gameMap.move(GameMap.MOVE_WAY.MOVE_RIGHT);
}
else if(endPoint.getX() < start_x && Math.abs(endPoint.getY() - start_y) < 100){//left
gameMap.move(GameMap.MOVE_WAY.MOVE_LEFT);
}
else if(endPoint.getY() < start_y && Math.abs(endPoint.getX() - start_x) < 100){//up
gameMap.move(GameMap.MOVE_WAY.MOVE_UP);
}
else if(endPoint.getY() > start_y && Math.abs(endPoint.getX() - start_x) < 100){//down
gameMap.move(GameMap.MOVE_WAY.MOVE_DOWN);
}
if(gameMap.isWin()){
//赢了之后该干嘛
}
break;
}
return true;
}
});
stackBtn.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
gameMap.back();
}
});
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
游戏整体框架(不包括数据存储)大概就是这些,至此,已经实现了这个游戏最基础的功能了,从开始界面到游戏界面,以及各种游戏操作,接下来的事情就是对这些零零散散的组件,用一个漂亮的布局整合起来,再根据自己的兴趣添加一些其他功能,下篇将重点给出美化UI交互界面的代码,敬请期待!
微信扫码分享
先搭壳子再填东西的思路很好,编程不能走一步看一步。
是因为游戏界面加载太快所以只能象征的加个加载界面了么
加载界面可以用于一些请求,比如游戏要联网,可以在加载界面配置好,因为这个游戏比较简单,所以只是象征性写了这个界面😂
Excellent!