起因是这样的,今早安卓开发课布置了写2048小游戏的实验,并且给了源代码,我照着源代码一顿猛敲,做出了个老师要的版本。
想到女朋友也挺喜欢玩2048的,就想得瑟一下,发了个apk过去。
没想到遭到一阵嫌弃,“你这数字都是黑色好模糊啊我都看不清”,“我都快到2048了走错一步,哎呀你这个怎么都不能回退一步的”
emmmmmm好吧,谁叫咱想装逼呢,那么就针对甲方这两个需求,开始敲代码
继昨日后续,甲方说“阿sir,你这个怎么都没有最高分的?”
对哦,身为一个游戏,不断挑战最高分,才是它的好玩之处。着手修改
2048游戏的实现
结果展示
1.结果1.0
2.结果2.0
思路
- 改颜色
改颜色这个很容易,但是要找ARGB的颜色来改Card类中每个数字的颜色 - 实现回退
这里我用四个ArrayList分别来存放当前的位置、当前的数值、上一步的位置、上一步的数值。
每次进行上下左右滑动的时候,就把当前的位置、当前的数值覆盖掉上次记录的上一步的位置、上一步的数值。然后再记录新的当前的位置、当前的数值。
每次回退就把上一步的位置和上一步的数值加载进这个界面。
回退当然也要回退分数,这里用两个整型变量来传递就好了。 - 记录最高分
最简单的我想到就是利用SharedPreferences来保存信息,这里我觉得还需要判断是不是第一次启动,如果是第一次启动那我就创建一个最高分文件来储存,如果不是第一次启动,那就直接从里面拿东西。同样,判断第一次启动我也用的是SharedPreferences。
源码
- 首先看看我的文件树
- Card类
package com.example.minigame;
import android.content.Context;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
public class Card extends FrameLayout {
private TextView text;
private int number=0;
public int getNumber(){
return number;
}
public void setNumber(int number){
this.number=number;
if(number<2){
text.setText("");
}else{
if(number==2){
text.setTextColor(0xFFFFFF00);
}else if(number==4){
text.setTextColor(0xFF00FF00);
}
else if(number==8){
text.setTextColor(0xFF00FFFF);
}
else if(number==16){
text.setTextColor(0xFF0000FF);
}
else if(number==32){
text.setTextColor(0xFFFF00FF);
}
else if(number==64){
text.setTextColor(0xFF7F007F);
}
else if(number==128){
text.setTextColor(0xFFFF7F00);
}
else if(number==256){
text.setTextColor(0xFF996633);
}
else if(number==512){
text.setTextColor(0xFFFF0000);
}
else if(number==1024){
text.setTextColor(0xFF350D04);
}
else if(number==2048){
text.setTextColor(0xFFFF3800);
}
else{
text.setTextColor(0xFF95090C);
}
text.setText(number+"");
}
}
public Card(@NonNull Context context) {
super(context);
text=new TextView(context);
text.setTextSize(28);
text.setBackgroundColor(0x9966cccc);
text.setGravity(Gravity.CENTER);
LayoutParams params=new LayoutParams(-1,-1);
params.setMargins(10,10,0,0);
addView(text,params);
}
}
- GameView类
package com.example.minigame;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.GridLayout;
import android.content.SharedPreferences;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class GameView extends GridLayout {
//定义4*4的卡片布局
private Card cards[][]=new Card[4][4];
//记录空卡片
private List<Point> emptyCards=new ArrayList<Point>();
//随机数
Random rd=new Random();
//分数
int score=0;
int oldscore=0;
int currentscore=0;
int bscore;
public int getBscore() {
return bscore;
}
public void setBscore(int bscore) {
this.bscore = bscore;
}
//记录所有卡片位置及当前分数
private List<Point> backCards=new ArrayList<Point>();
private List<Point> oldbackCards=new ArrayList<Point>();
private List<Integer> backCardsnums=new ArrayList<Integer>();
private List<Integer> oldbackCardsnums=new ArrayList<Integer>();
public GameView(Context context) {
super(context);
initGame();
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
initGame();
}
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initGame();
}
//游戏初始界面
private void initGame() {
setColumnCount(4); //设置列的数量
setBackgroundColor(0xffffcccc); //设置背景颜色
setOnTouchListener(new OnTouchListener() { //触摸监听
private float startX,startY;
private float offsetX,offsetY;
@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: //手势事件结束
Gameover();
offsetX=event.getX()-startX; //获取当前x坐标跟移动前坐标的x差值
offsetY=event.getY()-startY; //获取当前y坐标跟移动前坐标的y差值
if(Math.abs(offsetX)>Math.abs(offsetY)){ //x差值大于y差值,说明是左右移动
if(offsetX<-3){ //左移
moveLeft();
System.out.println("----左");
}else if(offsetX>3){ //右移
moveRight();
System.out.println("----右");
}
}
else{
if(offsetY<-3){
moveUp();
System.out.println("----上");
}else if(offsetY>3){
moveDown();
System.out.println("----下");
}
}
break;
default:
break;
}
return true;
}
});
}
private void moveRight(){
boolean flage=false;//移动是否有效
for(int y=0;y<4;y++){
for(int x=3;x>=0;x--){
for(int x1=x-1;x1>=0;x1--){
if(cards[x1][y].getNumber()>0){//准备往右的这张卡片,需要判断是否有数值
if(cards[x][y].getNumber()<2){//在右边的这张卡片,如果没数值,就用准备往右的这张卡片的数值替代它
cards[x][y].setNumber(cards[x1][y].getNumber());
cards[x1][y].setNumber(0);
x++;
flage=true;
score+=2;
}else if(cards[x][y].getNumber()==cards[x1][y].getNumber()){//在右边的这张卡片,如果数值与准备往右的这张卡片的数值相同,就合并数值
cards[x][y].setNumber(cards[x][y].getNumber()*2);
score+=cards[x][y].getNumber();
cards[x1][y].setNumber(0);
flage=true;
}
break;
}
}
}
}
oldremeberCard();
if(flage){//移动有效就创建新卡片
creatRandomCard();
remeberCard();
}
}
private void moveLeft(){
boolean flage=false;
for(int y=0;y<4;y++){
for(int x=0;x<4;x++){
for(int x1=x+1;x1<4;x1++){
if(cards[x1][y].getNumber()>0){
if(cards[x][y].getNumber()<2){
cards[x][y].setNumber(cards[x1][y].getNumber());
cards[x1][y].setNumber(0);
x--;
flage=true;
score+=2;
}else if(cards[x][y].getNumber()==cards[x1][y].getNumber()){
cards[x][y].setNumber(cards[x][y].getNumber()*2);
score+=cards[x][y].getNumber();
cards[x1][y].setNumber(0);
flage=true;
}
break;
}
}
}
}
oldremeberCard();
if(flage){
creatRandomCard();
remeberCard();
}
}
private void moveDown(){
boolean flage=false;
for(int x=0;x<4;x++){
for(int y=3;y>=0;y--){
for(int y1=y-1;y1>=0;y1--){
if(cards[x][y1].getNumber()>0){
if(cards[x][y].getNumber()<2){
cards[x][y].setNumber(cards[x][y1].getNumber());
cards[x][y1].setNumber(0);
y++;
flage=true;
score+=2;
}else if(cards[x][y].getNumber()==cards[x][y1].getNumber()){
cards[x][y].setNumber(cards[x][y].getNumber()*2);
score+=cards[x][y].getNumber();
cards[x][y1].setNumber(0);
flage=true;
}
break;
}
}
}
}
oldremeberCard();
if(flage){
creatRandomCard();
remeberCard();
}
}
private void moveUp(){
boolean flage=false;
for(int x=0;x<4;x++){
for(int y=0;y<4;y++){
for(int y1=y+1;y1<4;y1++){
if(cards[x][y1].getNumber()>0){
if(cards[x][y].getNumber()<2){
cards[x][y].setNumber(cards[x][y1].getNumber());
cards[x][y1].setNumber(0);
y--;
flage=true;
score+=2;
}else if(cards[x][y].getNumber()==cards[x][y1].getNumber()){
cards[x][y].setNumber(cards[x][y].getNumber()*2);
score+=cards[x][y].getNumber();
cards[x][y1].setNumber(0);
flage=true;
}
break;
}
}
}
}
oldremeberCard();
if(flage){
creatRandomCard();
remeberCard();
}
}
private void Gameover(){//判断游戏是否结束
boolean OverGame=true;
for(int y=0;y<4;y++){
for(int x=0;x<4;x++){
if(cards[x][y].getNumber()<=0 || //有一个卡片数值为0说明有空位
(x>0&&cards[x][y].getNumber()==cards[x-1][y].getNumber())|| //有相同卡片说明可以合并,下同
(x<3&&cards[x][y].getNumber()==cards[x+1][y].getNumber())||
(y>0&&cards[x][y].getNumber()==cards[x][y-1].getNumber())||
(y<3&&cards[x][y].getNumber()==cards[x][y+1].getNumber())){
OverGame=false;
}
}
}
if(OverGame){ //提示游戏结束
//游戏结束时判断最高分
if(score>bscore){
bscore=score;
}
new AlertDialog.Builder(getContext()).setTitle("游戏结束").setMessage("请选择是否继续挑战")
.setPositiveButton("好呀好呀",new AlertDialog.OnClickListener(){ //选择是的话就继续开启新游戏
@Override
public void onClick(DialogInterface dialog, int which) {
GameStart();
score=0;
}
}).setNegativeButton("不啦不啦",null).show();
}
}
private void Addard(int width,int height){
Card c;
for(int y=0;y<4;y++){
for(int x=0;x<4;x++){
c=new Card(getContext());
cards[x][y]=c;
c.setNumber(0);
addView(c,width,height);
}
}
}
@Override
protected void onSizeChanged(int w,int h,int oldw,int oldh){ //界面尺寸变化适应函数
super.onSizeChanged(w,h,oldw,oldh);
int width=(w-10)/4;
Addard(width,width);
GameStart();
}
private void oldremeberCard(){
oldbackCards.clear();
oldbackCardsnums.clear();
oldbackCards.addAll(backCards);
oldbackCardsnums.addAll(backCardsnums);
oldscore=currentscore;
}
private void remeberCard(){ //记录当前操作
backCards.clear();
backCardsnums.clear();
for(int y=0;y<4;y++){ //重新记录卡片位置
for(int x=0;x<4;x++){
Point point=new Point(x,y);
backCards.add(point);
backCardsnums.add(cards[x][y].getNumber());
}
}
currentscore=score; //记录当前分数
}
private void creatRandomCard(){
emptyCards.clear(); //卡片位置为空的数组清空
for(int y=0;y<4;y++){ //重新记录卡片位置为空的数组
for(int x=0;x<4;x++){
if(cards[x][y].getNumber()<2){
Point point=new Point(x,y);
emptyCards.add(point);
}
}
}
int selat=rd.nextInt(emptyCards.size());//写一个随机数
Point p=emptyCards.get(selat);//随机取一个空着的卡片
emptyCards.remove(selat);//取到的卡片准备赋值,所以这个卡片不为空了,踢出去
int number=0;
if(rd.nextInt(10)>4){
number=4;
}else{
number=2;
}
cards[p.x][p.y].setNumber(number);
}
public void GameStart(){//全部卡片置为0,创建两张新卡片
if(score>bscore){
bscore=score;
}
for(int y=0;y<4;y++){
for (int x=0;x<4;x++){
cards[x][y].setNumber(0);
}
}
oldbackCards.clear();
oldbackCardsnums.clear();
creatRandomCard();
creatRandomCard();
remeberCard();
}
public void Gameback(){ //返回上一步操作
if(oldbackCardsnums.isEmpty() && oldbackCards.isEmpty()){
System.out.println("没有值,回退不了");
}
else {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
Point p = oldbackCards.get(y * 4 + x);//拿出位置
cards[p.x][p.y].setNumber(oldbackCardsnums.get(y * 4 + x));//拿出值放在该位置上
}
}
score=oldscore;
remeberCard();
}
}
}
- MainActivity类
package com.example.minigame;
import androidx.annotation.NonNull;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends Activity {
TextView score_show; //分数展示
TextView bestscore_show; //最高分展示
GameView gv; //子控件布局
Button new_game; //新游戏按钮
Button rollback; //反悔按钮
SharedPreferences.Editor editor=null;
Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
int num=msg.arg1;
score_show.setText(num+"");
}
};
Handler handler1=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
int num=msg.arg1;
bestscore_show.setText(num+"");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
score_show=(TextView)findViewById(R.id.tv_score_show);
gv=(GameView)findViewById(R.id.gv_show);
new_game=(Button) findViewById(R.id.tv_newganme);
bestscore_show=(TextView)findViewById(R.id.tv_bestscore_show);
//判断是不是第一次运行
iffirstrun();
bestscore_show.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
editor=getSharedPreferences("mybestscore",0).edit();
editor.putString("bestscore",Integer.toString(gv.getBscore()));
editor.commit();
}
});
new_game.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
gv.GameStart();
gv.score=0;
}
});
rollback=(Button)findViewById(R.id.tv_rockback);
rollback.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
gv.Gameback();
System.out.println("我反悔了");
}
});
//定时器,定时刷新分数
Timer timer=new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Message msg=new Message();
msg.arg1=gv.score;
handler.sendMessage(msg);
}
},80,150);
score_show.setText(100+"");
//定时器,定时刷新最高分数
Timer timer1=new Timer();
timer1.schedule(new TimerTask() {
@Override
public void run() {
Message msg=new Message();
msg.arg1=gv.bscore;
handler1.sendMessage(msg);
}
},80,150);
}
private void iffirstrun() {
SharedPreferences sharedPreferences = this.getSharedPreferences("share", MODE_PRIVATE);
boolean isFirstRun = sharedPreferences.getBoolean("isFirstRun", true);
SharedPreferences.Editor ed = sharedPreferences.edit();
if (isFirstRun){//第一次运行就创建最高分文件
Log.d("debug", "第一次运行");
editor=getSharedPreferences("mybestscore",0).edit();
editor.putString("bestscore",bestscore_show.getText().toString());
editor.commit();
ed.putBoolean("isFirstRun", false);
ed.commit();
}else{
Log.d("debug", "不是第一次运行");
String bestscore=getSharedPreferences("mybestscore",0).getString("bestscore","");
bestscore_show.setText(bestscore);
gv.setBscore(Integer.parseInt(bestscore));
}
}
}
- 布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="2048"
android:textColor="#EE82EE"
android:textSize="30dp"
android:gravity="center_horizontal"
android:textStyle="bold"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_score_text"
android:textSize="18dp"
android:textColor="#000000"
android:padding="8dp"
android:text="得分:"/>
<TextView
android:id="@+id/tv_score_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="0"
android:textColor="#000000"
android:textSize="20dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18dp"
android:textColor="#000000"
android:layout_marginLeft="70dp"
android:padding="8dp"
android:text="最高分:"/>
<TextView
android:id="@+id/tv_bestscore_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="0"
android:textColor="#000000"
android:textSize="20dp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_newganme"
android:textSize="18dp"
android:textColor="#000000"
android:paddingTop="8dp"
android:paddingLeft="20dp"
android:text="新的一局" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18dp"
android:textColor="#000000"
android:paddingTop="8dp"
android:paddingLeft="10dp"
android:id="@+id/tv_rockback"
android:text="我要反悔" />
</LinearLayout>
<com.example.minigame.GameView
android:id="@+id/gv_show"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"/>
</LinearLayout>