Qt C++ 2048

这里写图片描述

C++

  • 使用pointer的时候要注意在Vector里用“&”,如vector

References

游戏部分:

2048网页版:http://2048game.com

ToDo

1. UI 成型(3 Dys)

2. Board的Class和随机生成一两个Cells和Cells的值(2 Dys)

3. 让这两个随机的Cell能够上下左右移动的动画效果(2 Dys)

4. 把能够合并的Cell合并,并保证每次在空白格生成Cell(2 Dys)

  1. Debug (n Dys)

  2. 分数存储

– 响应式的UI设计打算把程序写完了再改。

Window

初始的创建Qt项目,会带着个form的ui尾缀文件,在designer里面设计可以生成XML的文件,这里的XML和Android项目一样,但格式不同。如果使用这个form则需要在代码中找到这个ui 组件然后设置在程序里自由的活动方式。如果只是简单的程序,不需要对Form有任何效果,使用form能增加建立代码效率。但优点对于我来说几乎是不存在的。
这个2048使用的是纯手写代码运行来的。我把凡是和ui form 相关的代码文件都删除了,并不会有任何影响。

其实Matlab的App项目和Qt在UI设计上是差不多的。

Layout

这里写图片描述

使用Gridlayout时,或任何Layout,w是棕色widget,如果用QGridLayout *boardLayout = new QGridLayout(w);会出现左边的效果,应该使用w.setlayout(boardLayout) 来达到右边的效果。

Game Plan

这里写图片描述

Silly Problem

使用for loop生成数组,从0生成到第n个数字: i <= n(每次都愚蠢地漏掉“=”, 错误地想着i++会帮它实现+1的)

随机

在C++里,调用long random()/ int rand()前,srand都会默认先执行,如果不在使用这两个Functions前设置srand的算子(时间),那么random/rand每次出来的值都一样。
这里写图片描述

Qt Click Listener

在Qt里使用的不是listener,为了让上面纯手打UI代码的Q widget 感应到键盘,但把它设置setfocusoolicy() 是不管用的,也不能让Widget w, 做w->keypreeEvent(Key_Up)这种操作,因为这个Key Press的方程是内在使用(Protected)会报错(incompleted blablabla),所以唯一的解决办法:必须让整个Widget作为一个自定义的UI Class 然后引用Key_Event 的 keypressEvent Function。如果一个QWidget的自定义UI Class里包含多个Q widget,则需要用focus的Function去选择确定一个Widget,否则,KeyEvent一样没反应。

Qt Widget Class

可以定义一个UI Class为乱七八糟(比如我的BoardGUI),然后用: QWidget 作为implemented 的方法。一定要用:

Header File

Q_OBJECT
public:
    explicit BoardGUI(QWidget *parent = 0);

CPP File

BoardGUI::BoardGUI(QWidget *w) : QWidget(w){...}

因为本身整个class就是个GUI,所以在添加控件的时候只用使用:this。试过使用我自己设置的w或者0(我以为用0表示这是parent,这个Class的GUI)都不行。

MOVE U/D/L/R
ActionTypePos Key
UP (PosKey starts from -1)01
LEFT (PosKey starts from -1)00
DOWN (PosKey starts from 4)11
RIGHT (PosKey starts from 4)10

根据上表,UP 和 LEFT 有相同点,则可以合在一个For Loop。所以4个Actions只需要两个Functions即可。唯一优点可以整合代码和减少Compile的时间。分成4个也可以。LEFT的Board坐标是[ii, jj], 则 LEFT的坐标可以为[jj, ii].
PosKey即下图所示的玫红色星星。
使用00,01,10,11来区分方向,移动并更新Cells,例子如下(左移):

for(int ii = 0; ii <= 3; ii++){
        int Pos = -1;
        for(int jj = 0; jj <= 3; jj++){
           /*-----------UP--------------------
            ** 做 UP 部分,判断Board[jj,ii]
            **(a)如果有非空位Cell/合并相同的Cells
            **(b-1)更新Pos和移动当前Cell的位置到new_X/y 
            **(b-2)Move Animation()
            */

            /*-----------LEFT--------------------
            ** 做 LEFT 部分,判断Board[ii,jj]
            **(a)如果有非空位Cell/合并相同的Cells
            **(b-1)更新Pos和移动当前Cell的位置到new_X/y
            **(b-2)Move Animation()
            */

        }
}

现阶段结果:起始是错开的2和4,先走的左边使2和4合并到了一个Column里。

相加两个格子

在Cell Class 里除了Cell的value还加一个Propertybool hasDone为判断每移动一次,Cell的相加只会操作一次。使用一个Vector 数组存储for loop 里操作过的Cell,然后在每次移动前依次清除这个数组里的Cell的hasDone的值到default。

中途遇到的Bugs

(1)当2与2合并在它们自己前一个位置,但忘记挪动更新后的位置了(这个Bug让我郁闷了好久,以为就快到达真相了,结果貌似要推倒重来,真的已经在推倒重来路上的时候,突然发现只是差了句代码,然后试了好久才发现真正差的那句代码是加在哪里)
这里写图片描述
解决方法:在合并后只需要更新Pos_key(相当于移动的指针)的位置,不需要重新遍历一遍再从头开始挪位置:如LEFT 和 UP 的操作:添加Pos_Key -- 的代码

每次移动增加一个新的Cell

判断是否可以移动

因为如果可以移动,Pos_Key 会被更新到非负一(-1是LEFT和UP的default的值)的状态。所以整个移动的内容,Pos_Key是关键。但由于PosKey 是在Loop时每个Row或Column都会重置的量,可以使用奇偶验证的思路,使用 bool hasMoved = false; 因此只用最多1次的Pos_Key移动痕迹后,把 hasMoved = true,就算多次赋值True也不影响已经有至少1次移动的事实,紧接着Move做完,就可以Generate 一个新的Cell.

移动的动画制作

用以下式子执行Cell Class(每个Cell)的动画:从Cell的一个位置移动到另一个位置(根据上面的sudo Code, 这步Animation只是动画,属于伪移动,不对真正的Board的内容做改动)这个不是太难,但是很tricky, 加代码的位置和怎么加是我遇到的问题。

中途遇到的bugs
  • 真的把我的Cell移走了,把逻辑和GUI混合了,但是一旦改变了Cell的位置对我的Board就不好跟踪了。
QPropertyAnimation animation(cell, "geometry");

这里写图片描述
解决方法:
不要用interval,而是在Start里直接设值,做timeout()的工作。在相应的Class的Header里声明Slot:

private slots:
    void drawBoardUI();

在某个地方插入下面代码,好让Cell的动作完成再更新整个Board的UI。

QTimer *timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(drawBoardUI()));
//timer->setInterval(300);
timer->start(300);
  • 由上面的解决办法里,又出来新的问题:由于Cell(QLabel)是分布建立在Grid Layout里的,如果移动一个Cell,就会有很混乱的场景

这里写图片描述
解决方法: 加入另一个Widget在同一个位置,并设置背景,让原来的Widget背景为透明,这个可以遮盖移动后的空缺。

  • 另一个Bug:有动画错误轨迹的残影和更新整个界面的闪烁
    animation->start(QAbstractAnimation::DeleteWhenStopped);
    并不能在Cell的Animation之后人为设为空的,实际上Animation本身还存在着,所以会有之前的残影。所以需要Call它自己的Delete Function进行“自杀”。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值