游戏布局的设计
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/score"/>
<TextView
android:id="@+id/show_score"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- 铺满剩余空间-->
<!-- 将类和xml绑定到一起-->
<com.jiangjiang.game2048.GameView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/gameView">
</com.jiangjiang.game2048.GameView>
</LinearLayout>
实现2048游戏的主类
- 新建一个类GridView,继承自GridLayout
从xml资源中能够访问,添加能够传入的构造方法,三个构造方法。 - 初始化的方法,从哪个构造方法中都能执行到。
- 绑定xml,拷贝类的全路径到xml文件中
public class GameView extends GridLayout {
public GameView(Context context) {
super(context);
initGameView();
}
public GameView(Context context, AttributeSet attrs)
{
super(context, attrs);
initGameView();
}
public GameView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
initGameView();
}
}
android平台的触控交互设计
- 纪录用户手指按下和离开的位置,即可判断用户的意图
- onTouch()方法的返回值一定是True;如果是false的话,只会侦听到一个TouchDown的事件。TouchMove和TouchUp事件是侦听不到的。返回false,是通知系统我的TouchDown没有触发成功,然后后续事件是不会触发的。
private float startX,startY,offsetX,offsetY;//纪录x、y 轴的偏移
@Override
public boolean onTouch(View v, MotionEvent event) {
//纪录用户手指按下和离开的位置,即可判断用户的意图
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
startX = event.getX();//纪录x的位置
startY = event.getY();//纪录y的位置
break;
case MotionEvent.ACTION_UP:
offsetX = event.getX()-startX;//计算x的偏移量
offsetY = event.getY()-startY;//计算y的偏移量
if (Math.abs(offsetX)>Math.abs(offsetY))
//判断x的绝对值大还是y的绝对值,x>y说明是水平方向
{
if(offsetX<-5)//向左,给一定的误差范围5
{
swipeLeft();
}else if (offsetX > 5)//向右
{
swipeRight();
}
}else{
if (offsetY<-5)//向上
{
swipeUp();
}else if(offsetY > 5)//向下
{
swipeDown();
}
}
break;
}
return true;//一定要返回true
}});
游戏卡片类的实现
- 将游戏的方格抽象成卡片
public class Card extends FrameLayout {
public Card(Context context) {
super(context);
label = new TextView(getContext());
label.setTextSize(32);
label.setBackgroundColor(0x33ffffff);
label.setGravity(Gravity.CENTER);
LayoutParams lp = new LayoutParams(-1,-1);
lp.setMargins(10,10,0,0);//设置字之间的间隔
addView(label,lp);
setNum(0);//默认情况设置0
}
private int num = 0;//与卡片绑定的number
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
if(num <= 0 )
{
label.setText("");
}else {
label.setText(num + "");//setText的参数是字符串
}
}
public boolean equals(Card c) {
return getNum() == c.getNum();//判断卡片是否相同
}
private TextView label;
}
添加卡片类
将方块抽象成卡片
public class Card extends FrameLayout {
public Card(Context context) {
super(context);
label = new TextView(getContext());//初始化呈现文字的textview
label.setTextSize(32);
label.setBackgroundColor(0x33ffffff);//设置文本框的背景
label.setGravity(Gravity.CENTER);
LayoutParams lp = new LayoutParams(-1,-1);
//填充满整个父级容器
lp.setMargins(10,10,0,0);//设置卡片之间的间隔
addView(label,lp);//添加进
setNum(0);//默认的number
}
private int num = 0;//与卡片绑定的number
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
if(num <= 0 )
{
label.setText("");
}else {
label.setText(num + "");
//setText的参数是字符串。所以不能直接传num进去
}
}
//判断卡片是否相同
public boolean equals(Card c) {
return getNum() == c.getNum();
}
private TextView label;
}
添加卡片
- 每一种手机的宽高都不相同,为了让卡片的大小适应手机,所以需要动态的计算宽高,这样子在任何手机看起来卡片都铺满整个手机
- 动态计算,需要重写父类的方法 onSetChanged()
- 为了不让手机屏幕发生水平直立的变化,需要设置一下:
android:screenOrientation="portrait"//屏幕是竖直的,这样子onSetChanged()方法就只会在布局创建的时候被执行。
//动态的计算卡片的宽高
@Overrideprotected
void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int cardWidth = (Math.min(w,h)-10)/4;
//卡片的宽高,求宽高的最小值,减一个数字是为了在屏幕边缘留下空隙
addCard(cardWidth,cardWidth);//添加卡片
startGame();
}
- 添加卡片
private void addCard(int cardWidth,int cardHeight){
Card c ;
//添加16张卡片
for(int y = 0;y<4;y++)//四列
{
for(int x = 0;x<4;x++)//四行
{
c = new Card(getContext());
c.setNum(0);//初始时全部添加0
addView(c,cardWidth,cardHeight);//添加到当前的GridVIew中
cardsMap[x][y] = c;//将创建的卡片纪录到二维数组中
}
}
}
- 需要显示4列:
setColumnCount(4);//说明GridLayout 是4列的
在游戏中添加随机数
private void addRandomNum(){
emptyPoints.clear();
for(int y = 0;y<4;y++)//对所有的卡片进行遍历
{
for(int x = 0;x<4;x++)
{
if (cardsMap[x][y].getNum()<= 0) //空点
{
emptyPoints.add(new Point(x,y)); //空点才能添加数字
}
}
}
Point p = emptyPoints.remove((int)(Math.random()*emptyPoints.size()));//随机移除一个点
//将这个点对应的值设置成2,或者4,比例是9:1
cardsMap[p.x][p.y].setNum(Math.random()>0.1?2:4);//2:4 = 9:1
}
- 开始游戏
首先是清理元素。将卡片中的元素清除
然后是添加随机数
private void startGame(){
MainActivity.getMainActivity().clearScore();
for(int y = 0;y<4;y++)
{
for(int x = 0;x<4;x++)
{
cardsMap[x][y].setNum(0);
}
}
addRandomNum();//要显示两个数字,所以要添加两次
addRandomNum();
}
实现2048 的逻辑
- 往左滑的效果
private void swipeLeft(){
boolean merge = false;
for(int y= 0;y<4;y++)
{
for(int x = 0;x<4;x++)//一行一行遍历
{
//从当前位置往右遍历
for(int x1 = x+1;x1<4;x1++)
{
if (cardsMap[x1][y].getNum()>0)//如果右边不为空
{
if (cardsMap[x][y].getNum()<=0)//如果当前位置的卡片为空
{
//将右边的值放到右边去
cardsMap[x][y].setNum(cardsMap[x1][y].getNum()) ;
cardsMap[x1][y].setNum(0);
x--;//再遍历一次
merge = true;
}else if(cardsMap[x][y].equals(cardsMap[x1][y]))//如果卡片的数字相同
{
cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2);//合并
cardsMap[x1][y].setNum(0);
MainActivity.getMainActivity().addScore(cardsMap[x][y].getNum());
merge = true;
}
break;
}
}
}
}
if (merge)
{
addRandomNum();
checkComplete();
}
}
游戏的计分
- 有合并就有添加分数。再有合并的完成之后,数字是几就加几分。
- 在游戏开始的时候要清零
public MainActivity(){
mainActivity = this;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showScore = (TextView)findViewById(R.id.show_score);
}
public void clearScore(){//将score重新归0
score = 0;
showScore();
}
private void showScore(){//呈现分数
showScore.setText(score+"");
}
public void addScore(int s){//添加score
score += s;
showScore();
}
private int score = 0;
private TextView showScore;
检查游戏的结束
- 卡片没有空位置
- 没有任意两个相邻的位置上的数字是相同的
private void checkComplete(){
boolean complete = true;
ALL:
for(int y = 0;y<4;y++)
{
for(int x = 0;x<4;x++)
{
if (cardsMap[x][y].getNum() == 0||
(x>0&&cardsMap[x][y].equals(cardsMap[x-1][y]))||
(x<3&&cardsMap[x][y].equals(cardsMap[x+1][y]))||
(y>0&&cardsMap[x][y].equals(cardsMap[x][y-1]))||
(y<3&&cardsMap[x][y].equals(cardsMap[x][y+1]))
)
{
complete = false;
break ALL;
}
}
}
if (complete)
{
new AlertDialog.Builder(getContext()).setTitle("你好").setMessage("游戏结束").setPositiveButton("重来", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startGame();
}
}).show();
}
}
网友评论