本帖最后由 ianhom 于 2018-9-2 17:47 编辑
第二周作业:制作2048游戏
开发环境RTT + PerimmonUI + 野火RT1052Mini + 5寸800*480触屏
开发过程关于环境的搭建可以参考上
一篇帖子,这次2048的作业分成两部分,游戏本身的
逻辑 及
UI实现。
2048的游戏逻辑
网上有很多
源码例程,其核心算法如下:
遍历数组从数组的当前位置的
下一个 开始遍历,找不是0的位置
如果没找到什么也不做
如果找到
如果当前位置是0,那么像当前位置与下一个进行互换,将下标减一
如果当前位置和下一个位置相等,将当前位置数据*2,下个位置数据置0
有了这个算法,做上下左右操作的时候,只需要把4*4的二维数组进行转换,然后使用这个算法计算,最后再转换回原来的方向即可。
除了核心算法,还有一些其他功能需要注意:1、在空白的随机位置生成2或4的数据;2、输赢的判断;3、撤销操作;4、得分计算;5、不同数据的方块颜色计算。这些就不详细介绍了,看源码可能更清晰一些。
var arrOri = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]];
var arrTempI = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]];
var arrTempS = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]];
var arrRedo = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]];
var arrRTT = [[0,2,4,8],[16,32,64,128],[256,512,1024,1024],[1024,1024,1024,1024]];
var Score = 0;
var ScoreRedo = 0;
var Res = 0; //1=win,2=lose;
var BASECLR = 0xFFFFF48F;
var OFFSET = 100;
function printArr(arr)
{
for(var i=0;i<4;i++){
console.log(arr
[0],arr[1],arr[2],arr[3]);
}
}
function arrCp(arrSrc,arrDes)
{
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
arrDes[j] = arrSrc[j];
}
}
}
function arrCmp(arrSrc,arrDes)
{
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
if(arrDes[j] != arrSrc[j]){
return 0;
}
}
}
return 1;
}
function arrSpan(arrSrc,arrDes)
{
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
arrDes[j] = arrSrc[j];
}
}
}
function arrInvt(arrSrc,arrDes)
{
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
arrDes[j] = arrSrc[3-j];
}
}
}
function addNum()
{
var blankCnt = 0;
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
if(0 == arrOri[j]){
blankCnt += 1;
}
}
}
var luckyNum = Math.floor(Math.random()*2+1)*2;
var luckyBox = Math.floor(Math.random()*blankCnt+1);
blankCnt = 0;
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
if(0 == arrOri[j]){
blankCnt += 1;
if(blankCnt == luckyBox)
{
arrOri[j] = luckyNum;
}
}
}
}
}
function getColor(val)
{
var temp = (BASECLR + (OFFSET *( Math.log(val)/Math.log(2))));
return temp;
}
function gameCheck()
{
for(var i=0;i<4;i++){
for(var j=0;j<3;j++){
if(arrOri[j] == arrOri[j+1]){
return;
}
}
}
for(var j=0;j<4;j++){
for(var i=0;i<3;i++){
if(arrOri[j] == arrOri[i+1][j]){
return;
}
}
}
for(var j=0;j<4;j++){
for(var i=0;i<4;i++){
if(0 == arrOri[j]){
return;
}
}
}
Res = 2;
}
function getData(arr)
{
//遍历数组从数组的的当前位置的下一个开始遍历,找不是0的位置()
// 如果没找到什么也不做
// 如果找到
//如果当前位置是0,那么像当前位置与下一个进行互换(当前位置获得下一个位置的数据,并且将下一个位置数据置为0,将下标减一)
//如果当前位置和下一个位置相等,将当前位置数据*2,下个位置数据置0
var i,nextI,len,m;
len = arr.length;
for (i = 0; i < len; i += 1) {
//先找nextI
nextI = -1;
for (m = i+1; m < len; m++){
if(arr[m] !== 0) {
nextI = m;
break;
}
}
if (nextI !== -1) {
//存在下个不为0的位置
if (arr === 0) {
arr = arr[nextI];
arr[nextI] = 0;
i -= 1;
} else if (arr === arr[nextI]) {
arr = arr*2;
Score += arr;
arr[nextI] = 0;
if(2048 == arr){
Res = 1;
}
}
}
}
}
var page = {
onButton: function(e)
{
if((e.target.id != "buttonRedo")&&(2 != Res)){
arrCp(arrOri,arrRedo);
ScoreRedo = Score;
}
switch (e.target.id) {
case "buttonStart":
arrOri = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]];
Score = 0;
Res = 0;
this.setData({buttonStart : {value :'Restart!!', refresh : true}})
this.setData({label19: {value : "", refresh : true}});
this.setData({imagebox2 : {value :'', refresh : true}})
addNum();
break;
case "buttonLeft":
for(var i = 0; i < 4; i++){
getData(arrOri);
}
break;
case "buttonRight":
arrInvt(arrOri,arrTempI);
for(var i = 0; i < 4; i++){
getData(arrTempI);
}
arrInvt(arrTempI,arrOri);
break;
case "buttonUp":
arrSpan(arrOri,arrTempS);
for(var i = 0; i < 4; i++){
getData(arrTempS);
}
arrSpan(arrTempS,arrOri);
break;
case "buttonDown":
arrSpan(arrOri,arrTempS);
arrInvt(arrTempS,arrTempI);
for(var i = 0; i < 4; i++){
getData(arrTempI);
}
arrInvt(arrTempI,arrTempS);
arrSpan(arrTempS,arrOri);
break;
case "buttonRedo":
arrCp(arrRedo,arrOri);
Score = ScoreRedo;
Res = 0;
break;
case "buttonRTT":
arrCp(arrRTT,arrOri);
Score = 999999999;
Res = 0;
break;
}
if((e.target.id != "buttonRedo")&&(0 == arrCmp(arrOri,arrRedo))){
addNum()
}
if(arrOri[0][0] == 0){
this.setData({label1 : {value : ""}});
}else{
this.setData({label1 : {value : arrOri[0][0]}});
}
this.setData({label1 : {background: getColor(arrOri[0][0]), refresh : true}});
if(arrOri[0][1] == 0){
this.setData({label2 : {value : ""}});
}else{
this.setData({label2 : {value : arrOri[0][1]}});
}
this.setData({label2 : {background: getColor(arrOri[0][1]), refresh : true}});
if(arrOri[0][2] == 0){
this.setData({label3 : {value : ""}});
}else{
this.setData({label3 : {value : arrOri[0][2]}});
}
this.setData({label3 : {background: getColor(arrOri[0][2]), refresh : true}});
if(arrOri[0][3] == 0){
this.setData({label4 : {value : ""}});
}else{
this.setData({label4 : {value : arrOri[0][3]}});
}
this.setData({label4 : {background: getColor(arrOri[0][3]), refresh : true}});
if(arrOri[1][0] == 0){
this.setData({label5 : {value : ""}});
}else{
this.setData({label5 : {value : arrOri[1][0]}});
}
this.setData({label5 : {background: getColor(arrOri[1][0]), refresh : true}});
if(arrOri[1][1] == 0){
this.setData({label6 : {value : ""}});
}else{
this.setData({label6 : {value : arrOri[1][1]}});
}
this.setData({label6 : {background: getColor(arrOri[1][1]), refresh : true}});
if(arrOri[1][2] == 0){
this.setData({label7 : {value : ""}});
}else{
this.setData({label7 : {value : arrOri[1][2]}});
}
this.setData({label7 : {background: getColor(arrOri[1][2]), refresh : true}});
if(arrOri[1][3] == 0){
this.setData({label8 : {value : ""}});
}else{
this.setData({label8 : {value : arrOri[1][3]}});
}
this.setData({label8 : {background: getColor(arrOri[1][3]), refresh : true}});
if(arrOri[2][0] == 0){
this.setData({label9 : {value : ""}});
}else{
this.setData({label9 : {value : arrOri[2][0]}});
}
this.setData({label9 : {background: getColor(arrOri[2][0]), refresh : true}});
if(arrOri[2][1] == 0){
this.setData({label10: {value : ""}});
}else{
this.setData({label10 : {value : arrOri[2][1]}});
}
this.setData({label10: {background: getColor(arrOri[2][1]), refresh : true}});
if(arrOri[2][2] == 0){
this.setData({label11: {value : ""}});
}else{
this.setData({label11 : {value : arrOri[2][2]}});
}
this.setData({label11: {background: getColor(arrOri[2][2]), refresh : true}});
if(arrOri[2][3] == 0){
this.setData({label12: {value : ""}});
}else{
this.setData({label12 : {value : arrOri[2][3]}});
}
this.setData({label12: {background: getColor(arrOri[2][3]), refresh : true}});
if(arrOri[3][0] == 0){
this.setData({label13: {value : ""}});
}else{
this.setData({label13 : {value : arrOri[3][0]}});
}
this.setData({label13: {background: getColor(arrOri[3][0]), refresh : true}});
if(arrOri[3][1] == 0){
this.setData({label14: {value : ""}});
}else{
this.setData({label14 : {value : arrOri[3][1]}});
}
this.setData({label14: {background: getColor(arrOri[3][1]), refresh : true}});
if(arrOri[3][2] == 0){
this.setData({label15: {value : ""}});
}else{
this.setData({label15 : {value : arrOri[3][2]}});
}
this.setData({label15: {background: getColor(arrOri[3][2]), refresh : true}});
if(arrOri[3][3] == 0){
this.setData({label16: {value : ""}});
}else{
this.setData({label16 : {value : arrOri[3][3]}});
}
this.setData({label16: {background: getColor(arrOri[3][3]), refresh : true}});
gameCheck();
if(1 == Res){
this.setData({label19: {value : "Win!!", refresh : true}});
}else if(2 == Res){
this.setData({label19: {value : "lose!!", refresh : true}});
}
else{
this.setData({label19: {value : "", refresh : true}});
}
this.setData({label17: {value : Score, refresh : true}});
}
};
Page(page);
-----------------------------------------------分界线-----------------------------------------------------------
有了游戏的逻辑,使用柿饼GUI做界面就很轻松了。
我们需要做17个label(16个用于显示4*4的数组,1个显示得分),和6个按键(上下左右键,开始键,撤销键。。。。其实还有一个隐藏神奇按钮),分布如图所示。

对label的处理,首先空间名称要尽可能按序来设置,例如label1,label2.....,这样便于后续历遍,空间文本可以不写,游戏中计算的结果会刷新到这里。通过setData功能,后面介绍,这里建议配置好一个label之后,复制粘贴,这样比较有效率,然后利用界面工具来对齐,很方便。

然后对按键空间进行配置,首先在bindtap中写入onButton,即在该按键按下时,会触发onButton这个函数(按下事件的回调函数)。然后为每个按键填入易读且不同的空间名称,该名称用于按键事件中区别是哪一个按键被按下(回调函数的参数)。这里可以对按键替换下图片,按下和常态的图片不同,有按下的效果。别忘了清除控件文本。

完成界面配置后,我们可以编写js的部分。如上述步骤,onButton作为page的方法,在按键被按下后将会被调用,然后根据控件名称找到对应的分支,比如buttonLeft,我们就要刷新数组的数据getData()。

完成数据刷新后,就要刷到显示上,这里使用setData功能,可以设置控件的本文(修改方块值)、修改控件背景颜色(修改方块颜色)及其他属性(位置,大小,是否显示等)

有了上述操作,我们就完成了一个基本的2048游戏了,附件是柿饼工程,这里抛砖引玉,期待大家更完善、更有趣的版本。
效果展示
先在模拟器上试验效果

开发板上的效果

总结
控件丰富,玩法也比较灵活,期待更多空间。
2048逻辑蛮有意思的,JavaScript也很容易入门,JS+GUI设计器确实很方便开发复杂的图形功能。
我实现的界面刷新效率不高,期待大佬的优化版本。
柿饼工程
下载附件[hw2-4.rar]
查看更多