0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于JAVA UI开发的“推箱子”小游戏

OpenHarmony技术社区 来源:OpenHarmony技术社区 2023-01-05 09:32 次阅读

本文我们将逐步分享基于 JAVA UI 开发的“推箱子”小游戏这个项目的构建流程。

实际上,笔者在进行开发的过程中,并不是写完一个界面的内部逻辑,就开始对界面进行美化,而是先让所有的东西可以正常地跑起来,再谈美化。

因此本系列文章前半部分会重点讨论游戏以及界面之间的核心逻辑,后半部分则会分享美化界面的部分。

项目创建

打开 DevEco Studio,创建一个新项目,选择 JAVA 作为开发语言,将项目保存至合适的位置。

11a68738-8c76-11ed-bfe3-dac502259ad0.png

根据上期分享的开发思路,先完成 UI 交互部分的框架。

11bbd4c6-8c76-11ed-bfe3-dac502259ad0.png

可以看到,这里需要新建三个 Slice(原本自带一个 MainAbilitySlice):

11db0d46-8c76-11ed-bfe3-dac502259ad0.png

下面对四个 UI 交互功能进行讲解。MainAbilitySlice:打开应用时,首先显示的界面,也就是用户主界面。11e85aaa-8c76-11ed-bfe3-dac502259ad0.png

SelectSlice:关卡选择界面,用户可以在这个界面选择将要跳转的关卡。

11f29312-8c76-11ed-bfe3-dac502259ad0.png

InitSlice:加载界面。当用户选择关卡之后,会进入加载界面,仿照游戏加载资源。(实际上啥都没干)

11f99c7a-8c76-11ed-bfe3-dac502259ad0.pngGameSlice:最后一个界面,也就是这个游戏的核心界面,所有的游戏逻辑都将在这个页面中进行,因此它将是本篇文章的核心讲解部分。1201f4a6-8c76-11ed-bfe3-dac502259ad0.png

至此,我们可以简单地梳理一下四个界面以及他们包含的组件之间的关系:用户进入 MainAbilitySlice 之后,通过“开始游戏”按键进入 SelectSlice。

在 SelectSlice 中有三个按键,会对应跳转到三个不同的关卡,但是进入关卡之前会先进入 InitSlice,加载过后在进入最后的 GameSlice,从而开始游戏。以上就是开发的时候要理清楚的页面跳转关系。

核心代码分析

①MainAbilitySlice

里面有四个按钮,那可以简单的给他们分个类,例如:开始游戏的按钮要实现的功能是页面跳转,直接与其他界面关联,分为一类。

历史记录与关于游戏可以用弹出窗口来实现,不需要额外界面,归为一类;退出游戏按钮直接结束应用进程,也是单独一类。

明确之后,就可以给各个按钮添加点击事件了:

//开始游戏按钮
startBtn.setClickedListener(newComponent.ClickedListener(){
@Override
publicvoidonClick(Componentcomponent){
//页面跳转
present(newSelectSlice(),newIntent());
}
});
//历史记录按钮
recordBtn.setClickedListener(newComponent.ClickedListener(){
@Override
publicvoidonClick(Componentcomponent){
//历史记录弹窗
}
});
//关于游戏按钮
aboutBtn.setClickedListener(newComponent.ClickedListener(){
@Override
publicvoidonClick(Componentcomponent){
//关于游戏弹窗
}
});
//退出游戏按钮
exitBtn.setClickedListener(newComponent.ClickedListener(){
@Override
publicvoidonClick(Componentcomponent){
//退出游戏提示
CommonDialogcommonDialog=newCommonDialog(getContext());
commonDialog.setTitleText("提示");
commonDialog.setContentText("是否退出游戏");
commonDialog.setButton(1,"确定",newIDialog.ClickedListener(){
@Override
publicvoidonClick(IDialogiDialog,inti){
terminateAbility();
}
});
commonDialog.setButton(2,"取消",newIDialog.ClickedListener(){
@Override
publicvoidonClick(IDialogiDialog,inti){
commonDialog.destroy();
}
});
commonDialog.show();
}
});
在开发中,由于历史记录跟关于游戏这两个功能并不是核心,因此,最开始也只是做个壳子放在这,以便自己能专注于游戏主逻辑的开发,这也是我想分享的一种思路:先搭壳子再填东西。因此,阅读本系列时,如果碰到代码中只有注释,没有实现内容时,那是因为当时做到这一步的时候,并不会去关注具体如何实现,只会想个大概,先放着。到这里之后,实际上已经完成了游戏的退出以及从 MainAbilitySlice 页面到 SelectSlice 页面的导航,便可进行到我们的下一步。

②SelectSlice

这一个界面主要有三个按钮,如何实现按下不同的按钮,跳转到同一个加载界面,但是加载完后又跳转到不同的游戏界面?

这里使用 Intent 对跳转时的数据进行打包传输,具体实现如下:

firstBtn.setClickedListener(newComponent.ClickedListener(){
@Override
publicvoidonClick(Componentcomponent){
Intenti=newIntent();
i.setParam("关卡",1);
present(newInitSlice(),i);
}
});
secondBtn.setClickedListener(newComponent.ClickedListener(){
@Override
publicvoidonClick(Componentcomponent){
Intenti=newIntent();
i.setParam("关卡",2);
present(newInitSlice(),i);
}
});
thirdBtn.setClickedListener(newComponent.ClickedListener(){
@Override
publicvoidonClick(Componentcomponent){
Intenti=newIntent();
i.setParam("关卡",3);
present(newInitSlice(),i);
}
});
这里 Intent 的 key 都是“关卡”,但是有不同的 value,对应不同的关卡。实际上,到这里 SelectSlice 已经完成了它的功能了。接下来进入 InitSlice。③InitSlice在加载界面中,我预想的是一个动态的画面,然后加上一个进度条,因此我可能需要用到能够播放 gif 的组件,以及进度条组件。

那要怎么实现加载的时候进度条跟进?我的实现方式是使用两个定时器(其实用一个也完全能搞定)

//onStart外定义
Timert1=newTimer();
Timert2=newTimer();
//onStart内
TimerTasktask1=newTimerTask(){
@Override
publicvoidrun(){
//页面跳转
present(newGameSlice(),intent);
}
};
TimerTasktask2=newTimerTask(){
@Override
publicvoidrun(){
//进度条更新
intvalue=progressBar.getProgress();
progressBar.setProgressValue(value+20);
}
};
t1.schedule(task1,5000);
t2.schedule(task2,0,1000);
关于如何播放 gif,本不应该在此讲解,因为与主线任务无关,但是这里用到的第三方组件,后面游戏界面频繁使用,因此在这进行介绍。

这里用到了第三方组件 Glide,关于组件如何使用,可具体看这篇文章,只需几行代码即可完成 gif 的播放,十分方便。

https://ost.51cto.com/posts/8635
intimageResourceId=ResourceTable.Media_gifimg;
Glide.with(this)
.asGif()
.load(imageResourceId)
.into(draweeView);
至此,从 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布局,方便对图片进行渲染
publicclassGameMapextendsPositionLayout{

privatefinalstaticintsize=110;

//用二维数组来存储地图
privateInteger[][]gameMap;
//定义x,y坐标
privatePairmap_position;
//标识是否绘制过地图(画过一次后,后面所有的操作都只能是进行刷新,防止重复生成对象)
privateBooleanisDrew=Boolean.FALSE;
//定义移动方式枚举,方便外部调用进行选择
publicenumMOVE_WAY{
MOVE_UP,
MOVE_DOWN,
MOVE_LEFT,
MOVE_RIGHT
}

//设置每种物体的属性值
privatefinalstaticintROAD=0;
privatefinalstaticintWALL=1;

privatefinalstaticintLABA=2;
privatefinalstaticintYIBU=3;
privatefinalstaticintBOBO=4;
privatefinalstaticintMINI=5;
privatefinalstaticintMIAO=6;

privatefinalstaticintBALL_EMPTY=7;
privatefinalstaticintBALL_FULL1=9;
privatefinalstaticintBALL_FULL2=10;
privatefinalstaticintBALL_FULL3=11;
privatefinalstaticintBALL_FULL4=12;
privatefinalstaticintBALL_FULL5=13;
privatefinalstaticintPEOPLE1=14;
privatefinalstaticintPEOPLE2=16;
privatefinalstaticintPEOPLE3=17;
privatefinalstaticintPEOPLE4=18;
privatefinalstaticintPEOPLE5=19;
privatefinalstaticintPEOPLE6=20;
//定义存储地图用的栈
privateStackstack;
//可使用此构造函数绘制不同大小的地图,这个是预留的接口,项目中使用的是直接在xml文件中加入这个组件(因为继承了PositionLayout所以可以在xml文件中使用),当然也可以直接用构造函数创建,留给读者自己发挥。
publicGameMap(Contextcontext,Integer[][]map,intx,inty){
super(context);
gameMap=map;
map_position=newPair<>(x,y);
}
//外部设置地图接口
publicvoidsetMap(Integer[][]map){
gameMap=map;
map_position=newPair<>(map.length,map[0].length);
stack=newStack<>();
}
//绘制地图接口
publicvoiddrawMap(){
setHeight(size*map_position.s);
setWidth(size*map_position.f);
for(inti=0;i< map_position.f; i++) {
            for(intj=0;j< map_position.s; j++) {
                DraweeView draweeView = newDraweeView(getContext());
draweeView.setComponentSize(size,size);
draweeView.setContentPosition(size*j,size*i);
intindex=gameMap[i][j];
switch(index){
caseROAD:
Glide.with(getContext())
.load(ResourceTable.Media_road)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseWALL:
Glide.with(getContext())
.load(ResourceTable.Media_wall)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseLABA:
Glide.with(getContext())
.load(ResourceTable.Media_laba)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseYIBU:
Glide.with(getContext())
.load(ResourceTable.Media_yibu)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseBOBO:
Glide.with(getContext())
.load(ResourceTable.Media_bobo)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseMINI:
Glide.with(getContext())
.load(ResourceTable.Media_mini)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseMIAO:
Glide.with(getContext())
.load(ResourceTable.Media_miao)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseBALL_EMPTY:
Glide.with(getContext())
.load(ResourceTable.Media_ballEmpty)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseBALL_FULL1:
caseBALL_FULL2:
caseBALL_FULL3:
caseBALL_FULL4:
caseBALL_FULL5:
Glide.with(getContext())
.load(ResourceTable.Media_ballFull)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
casePEOPLE1:
casePEOPLE2:
casePEOPLE3:
casePEOPLE4:
casePEOPLE5:
casePEOPLE6:
Glide.with(getContext())
.load(ResourceTable.Media_people)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
}

this.addComponent(draweeView);
}
}
isDrew=Boolean.TRUE;
}
//外部刷新地图接口
publicvoidflushMap(){
for(inti=0;i< map_position.f; i++) {
            for(intj=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);
                intindex=gameMap[i][j];
switch(index){
caseROAD:
Glide.with(getContext())
.load(ResourceTable.Media_road)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseWALL:
Glide.with(getContext())
.load(ResourceTable.Media_wall)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseLABA:
Glide.with(getContext())
.load(ResourceTable.Media_laba)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseYIBU:
Glide.with(getContext())
.load(ResourceTable.Media_yibu)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseBOBO:
Glide.with(getContext())
.load(ResourceTable.Media_bobo)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseMINI:
Glide.with(getContext())
.load(ResourceTable.Media_mini)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseMIAO:
Glide.with(getContext())
.load(ResourceTable.Media_miao)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseBALL_EMPTY:
Glide.with(getContext())
.load(ResourceTable.Media_ballEmpty)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
caseBALL_FULL1:
caseBALL_FULL2:
caseBALL_FULL3:
caseBALL_FULL4:
caseBALL_FULL5:
Glide.with(getContext())
.load(ResourceTable.Media_ballFull)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
casePEOPLE1:
casePEOPLE2:
casePEOPLE3:
casePEOPLE4:
casePEOPLE5:
casePEOPLE6:
Glide.with(getContext())
.load(ResourceTable.Media_people)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(draweeView);
break;
}

}
}
}
//获取当前操作的人物坐标
publicPairgetMyPosition(){
for(inti=0;i< map_position.f; i++)
        {
            for(intj=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)
{
returnnewPair<>(i,j);
}

}
}
returnnewPair<>(-1,-1);
}
//给地图里任意一张图设置对应的值,移动的时候需要此接口
protectedvoidsetValue(intx,inty,intvalue){
gameMap[x][y]+=value;
}
//判断是否能够移动
protectedBooleanisMove(inti,intj){
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)
returnBoolean.TRUE;
returnBoolean.FALSE;
}
//判断是不是球
protectedBooleanisBall(inti,intj){
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)
{
returntrue;
}
returnBoolean.FALSE;
}
//外部接口,每一次移动完判断游戏是否结束
publicBooleanisWin(){
for(inti=0;i< map_position.f; i++)
        {
            for(intj=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)returnBoolean.FALSE;
}
}
returnBoolean.TRUE;
}
//外部移动接口
publicvoidmove(@NotNullMOVE_WAYmove_way){
Pairposition=getMyPosition();
Integer[][]oldMap=newInteger[map_position.f][map_position.s];
for(inti=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){
caseMOVE_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;
caseMOVE_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;
caseMOVE_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;
caseMOVE_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();
}
//外部回退原先地图接口
publicvoidback(){
if(stack.empty())return;
Integer[][]temp=stack.peek();
for(inti=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();
}

publicBooleangetIsDrew(){
returnisDrew;
}

publicGameMap(Contextcontext){
super(context);
}

publicGameMap(Contextcontext,AttrSetattrSet){
super(context,attrSet);
}

publicGameMap(Contextcontext,AttrSetattrSet,StringstyleName){
super(context,attrSet,styleName);
}
}
至此,完成了地图类的代码,可以开始绘制 GameSlice 了。在预览图中看到,核心部分有很多:一个退出界面按钮,一个设置按钮,一个倒计时器,还有一张地图,一个后退地图操作的按钮,按照我自己的想法先进行分类。

后退地图操作的按钮和地图是刚需,优先实现,代码如下:

//在外部定义变量
floatstart_x;
floatstart_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))
{
case1:
map=map1;
break;
case2:
map=map2;
break;
case3:
map=map3;
break;
}
gameMap.setMap(map);
if(!gameMap.getIsDrew())gameMap.drawMap();
elsegameMap.flushMap();

//滑动屏幕移动角色
gameMap.setTouchEventListener(newComponent.TouchEventListener(){
@Override
publicbooleanonTouchEvent(Componentcomponent,TouchEventtouchEvent){
intaction=touchEvent.getAction();
switch(action){
caseTouchEvent.PRIMARY_POINT_DOWN:
MmiPointstartPoint=touchEvent.getPointerPosition(0);
start_x=startPoint.getX();
start_y=startPoint.getY();
break;
caseTouchEvent.PRIMARY_POINT_UP:
MmiPointendPoint=touchEvent.getPointerPosition(0);
if(endPoint.getX()>start_x&&Math.abs(endPoint.getY()-start_y)< 100){//right
gameMap.move(GameMap.MOVE_WAY.MOVE_RIGHT);
}
elseif(endPoint.getX()< start_x && Math.abs(endPoint.getY()-start_y)< 100){//left
gameMap.move(GameMap.MOVE_WAY.MOVE_LEFT);
}
elseif(endPoint.getY()< start_y && Math.abs(endPoint.getX()-start_x)< 100){//up
gameMap.move(GameMap.MOVE_WAY.MOVE_UP);
}
elseif(endPoint.getY()>start_y&&Math.abs(endPoint.getX()-start_x)< 100){//down
gameMap.move(GameMap.MOVE_WAY.MOVE_DOWN);
}
if(gameMap.isWin()){
//赢了之后该干嘛

}
break;
}
returntrue;
}

});
stackBtn.setClickedListener(newComponent.ClickedListener(){
@Override
publicvoidonClick(Componentcomponent){
gameMap.back();
}
});
游戏整体框架(不包括数据存储)大概就是这些,至此,已经实现了这个游戏最基础的功能了,从开始界面到游戏界面,以及各种游戏操作,接下来的事情就是对这些零零散散的组件。

用一个漂亮的布局整合起来,再根据自己的兴趣添加一些其他功能,下篇将重点给出美化 UI 交互界面的代码,敬请期待!

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 应用
    +关注

    关注

    2

    文章

    438

    浏览量

    34138
  • JAVA
    +关注

    关注

    19

    文章

    2958

    浏览量

    104553
  • 代码
    +关注

    关注

    30

    文章

    4750

    浏览量

    68357
收藏 人收藏

    评论

    相关推荐

    JAE ST12系列/式卡用连接器产品概要

    日本航空电子工业现已开发并推出了符合microSD 卡的/式卡用连接器— “ST12系列”,该连接器适用于智能手机、平板电脑、游戏PC等小型ICT(信息通信技术)设备。
    的头像 发表于 09-10 10:10 214次阅读
    JAE ST12系列<b class='flag-5'>推</b>/<b class='flag-5'>推</b>式卡用连接器产品概要

    ​介绍一款Java开发的开源MES系统

    ​介绍一款Java开发的开源MES系统,万界星空科技开源的MES系统。该系统基于Java开发,具有广泛的适用性和高度的可定制性,能够满足不同行业、不同规模企业的智能制造需求。
    的头像 发表于 09-05 17:39 577次阅读
    ​介绍一款<b class='flag-5'>Java</b><b class='flag-5'>开发</b>的开源MES系统

    华纳云:java web和java有什么区别java web和java有什么区别

    Java Web和Java是两个不同的概念,它们在功能、用途和实现方式上存在一些区别,下面将详细介绍它们之间的区别。 1. 功能和用途: – Java是一种编程语言,它提供了一种用于开发
    的头像 发表于 07-16 13:35 695次阅读
    华纳云:<b class='flag-5'>java</b> web和<b class='flag-5'>java</b>有什么区别<b class='flag-5'>java</b> web和<b class='flag-5'>java</b>有什么区别

    鸿蒙ArkUI:【从代码到UI显示的整体渲染流程】

    方舟开发框架(简称ArkUI)是鸿蒙开发UI框架,提供如下两种开发范式,我们 **只学声明式开发范式**
    的头像 发表于 05-13 16:06 806次阅读
    鸿蒙ArkUI:【从代码到<b class='flag-5'>UI</b>显示的整体渲染流程】

    FPGA实现的“俄罗斯方块”游戏系统设计

    本项目主要在FPGA上实现了一个经典小游戏“俄罗斯方块”。本项目基本解决方案是,使用Xilinx Zynq系列开发板 ZedBoard 作为平台,实现主控模块,通过VGA接口来控制屏幕进行显示。
    发表于 03-28 10:41 1919次阅读
    FPGA实现的“俄罗斯方块”<b class='flag-5'>游戏</b>系统设计

    基于芯海科技CS32L015的LCD彩屏UI高效开发方案

    在智能家电、健康设备以及消费电子领域,精美直观的LCD彩屏显示,往往能够为用户带来更佳的使用体验。然而,丰富的彩屏UI界面带来了开发周期和数据存储成本的增加,也成为困扰客户产品开发迭代的痛点。 对此
    发表于 02-22 14:43 722次阅读
    基于芯海科技CS32L015的LCD彩屏<b class='flag-5'>UI</b>高效<b class='flag-5'>开发</b>方案

    鸿蒙实战开发-全局UI方法的功能

    使用全局UI的方法定义日期滑动选择器弹窗并弹出。
    的头像 发表于 02-02 17:13 560次阅读
    鸿蒙实战<b class='flag-5'>开发</b>-全局<b class='flag-5'>UI</b>方法的功能

    java后端能转鸿蒙app开发

    java后端转鸿蒙app开发好。 还是前端呢
    发表于 01-29 18:15

    Oracle 2024年Java发展蓝图分析

    Oracle 的 Java 开发者布道师 Nicolai Parlog 于近日发布一段视频,介绍了 2024 年的 Java 工作规划。
    的头像 发表于 01-26 14:27 1283次阅读

    java环境搭建及配置教程

    Java是一种广泛使用的编程语言,用于开发各种应用程序。在开始学习和使用Java之前,您需要搭建和配置Java开发环境。本教程将提供详细的
    的头像 发表于 12-06 15:50 750次阅读

    idea怎么创建Java项目

    : 安装Java开发环境 一个Java项目需要在计算机上安装Java开发环境(JDK),我们可以从Oracle官方网站上下载并安装最新版本的
    的头像 发表于 12-06 14:09 883次阅读

    基于springboot和vue框架的Java

    本文将详细介绍基于Spring Boot和Vue框架进行Java应用开发的实践。首先,将介绍Spring Boot和Vue框架的基本概念及其优点。然后,将详细介绍如何搭建Spring Boot
    的头像 发表于 12-03 15:15 963次阅读

    HarmonyOS开发案例分享:万能卡片也能用来玩游戏

    直接在桌面上玩游戏吗? 通过对万能卡片相关文档的阅读,我认为想要实现一些简单的游戏应该没有问题,思考再三,我决定做一个井字棋小游戏,希望能给各位开发者提供些
    发表于 12-01 09:35

    开发java应用程序的基本步骤是

    ava是一种面向对象的编程语言,广泛用于开发各种类型的应用程序。在开发Java应用程序时,有一些基本步骤需要遵循,以确保应用程序的正确性和可靠性。 1.确定需求:这是开发任何应用程序的
    的头像 发表于 11-28 16:52 1524次阅读

    开发java程序的一般步骤是

    开发Java程序的一般步骤通常包括需求分析、设计、编码、测试和部署等阶段。下面将详细介绍Java程序开发的一般步骤。 需求分析阶段: 在开始开发
    的头像 发表于 11-28 16:43 1572次阅读