概述
看了极客的安卓2048的开发教程,大概了解了一下思路,然后自己就开始写了。后来发现这个设计思路不是太好,不方便加移动动画,就只加了创建卡片和合并的动画,不过用来练手还可以。
游戏截图如下:
如果想拷贝到本地运行的话,注意修改和包名相关的地方
或者在创建工程的时候按照以下命名:
项目名: Game2048
包名: pers.hurric.game2048
AndroidManifest.xml
仅需设置screenOrientation
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pers.hurric.game2048">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textScore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/score"
android:textColor="@color/colorPrimary"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/guideline4"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline6"
app:layout_constraintTop_toTopOf="@+id/guideline3" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<Button
android:id="@+id/buttonReplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_replay"
android:textColor="@android:color/holo_blue_light"
app:layout_constraintBottom_toTopOf="@+id/guideline4"
app:layout_constraintEnd_toStartOf="@+id/guideline7"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline3" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.12" />
<TextView
android:id="@+id/textHighestScore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/highest_score"
android:textColor="@android:color/holo_purple"
android:textSize="18sp"
android:textStyle="italic"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toStartOf="@+id/guideline7"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="parent" />
<pers.hurric.game2048.GameView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/guideline5"
app:layout_constraintEnd_toStartOf="@+id/guideline7"
app:layout_constraintStart_toStartOf="@+id/guideline6"
app:layout_constraintTop_toTopOf="@+id/guideline4" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.26" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.9" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.05109489" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.95" />
</androidx.constraintlayout.widget.ConstraintLayout>
string.xml
<resources>
<string name="app_name">Game2048</string>
<string name="score">Score : 0</string>
<string name="highest_score">Highest Score : 0</string>
<string name="button_replay">Restart</string>
</resources>
color.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
<color name="gameViewBackgroundColor">#D2B48C</color>
<color name="backgroundColor0">#DEB887</color>
<color name="textColor0">#00000000</color>
<color name="textColor2">#333300</color>
<color name="textColor4">#333300</color>
<color name="textColorCommon">#FFFAFA</color>
<color name="backgroundColor2">#FDF5E6</color>
<color name="backgroundColor4">#FFE4B5</color>
<color name="backgroundColor8">#FFE4C4</color>
<color name="backgroundColor16">#FFDAB9</color>
<color name="backgroundColor32">#FF7F50</color>
<color name="backgroundColor64">#FF4500</color>
<color name="backgroundColor128">#FF8C00</color>
<color name="backgroundColor256">#FFD700</color>
<color name="backgroundColor512">#9ACD32</color>
<color name="backgroundColor1024">#483D8B</color>
<color name="backgroundColor2048">#4B0082</color>
<color name="backgroundColorBiggerThan2048">#000000</color>
</resources>
MainActivity.java
package pers.hurric.game2048;
import androidx.appcompat.app.AppCompatActivity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private final String FILE_NAME = "MyData";
private final String HIGHEST_SCORE = "highest_score";
private TextView textScore;
private TextView textHighestScore;
private Button buttonReplay;
private int score = 0;
private int highestScore = 0;
public static MainActivity mainActivity;
public MainActivity(){
mainActivity = this;
}
public void addScore(int score) {
this.score += score;
textScore.setText("Score : " + this.score);
//更新最高分
updateHighestScore(this.score);
}
private void updateHighestScore(int score){
if(score > highestScore){
highestScore = score;
textHighestScore.setText("HighestScore : " + score);
//存储最高分
SharedPreferences shp = getSharedPreferences(FILE_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = shp.edit();
editor.putInt(HIGHEST_SCORE, highestScore);
editor.apply();
}
}
public void clearScore(){
score = 0;
textScore.setText("Score : " + 0);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textScore = findViewById(R.id.textScore);
textHighestScore = findViewById(R.id.textHighestScore);
buttonReplay = findViewById(R.id.buttonReplay);
//读取最高分
SharedPreferences shp = getSharedPreferences(FILE_NAME, MODE_PRIVATE);
highestScore = shp.getInt(HIGHEST_SCORE, 0);
textHighestScore.setText("HighestScore : " + highestScore);
buttonReplay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GameView.gameView.replayGame();
}
});
}
private boolean isExit = false;
class ExitHandler extends Handler{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == 0){
isExit = false;//修改状态为退出
}
}
};
ExitHandler mHandler = new ExitHandler();
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
if(!isExit){
isExit = true;
Toast.makeText(this, "再按一次退出游戏", Toast.LENGTH_SHORT).show();
//延迟更改状态信息
mHandler.sendEmptyMessageDelayed(0, 2000);
}
else{
finish();
}
}
return false;
}
}
GameView.java
自定义游戏面板
package pers.hurric.game2048;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.widget.GridLayout;
import android.widget.Toast;
import java.util.Random;
public class GameView extends GridLayout {
public static GameView gameView;
private Card[][] cards = new Card[4][4];
public GameView(Context context) {
super(context);
gameView = this;
initGame();
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
gameView = this;
initGame();
}
public GameView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
gameView = this;
initGame();
}
public void initGame(){
this.setBackgroundColor(getResources().getColor(R.color.gameViewBackgroundColor));
setColumnCount(4);
int cardWidth = GetCardWidth();
addCards(cardWidth, cardWidth);
randomCreateCard(2);
setListener();
}
public void replayGame(){
MainActivity.mainActivity.clearScore();
for(int i = 0; i < 4; ++i){
for(int j = 0; j < 4; ++j){
cards[i][j].setNum(0);
}
}
randomCreateCard(2);
}
/*
* 监听Touch事件
*/
private void setListener(){
setOnTouchListener(new OnTouchListener() {
private float staX, staY, endX, endY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
staX = event.getX();
staY = event.getY();
break;
case MotionEvent.ACTION_UP:
endX = event.getX();
endY = event.getY();
boolean swiped = false;//记录是否有效滑动了
//水平移动更多
if(Math.abs(endX - staX) > Math.abs(endY - staY)){
if(endX - staX > 10){
if(swipeRight()){
swiped = true;
}
}
else if(endX - staX < -10){
if(swipeLeft()){
swiped = true;
}
}
}
else{
if(endY - staY < -10){
if(swipeUp()){
swiped = true;
}
}
else if(endY - staY > 10){
if(swipeDown()){
swiped = true;
}
}
}
//滑动后创建新块,并检查当前状态是否能滑动
if(swiped){
randomCreateCard(1);
if(!canSwipe()){
gameOver();
}
}
break;
}
return true;
}
});
}
/*
* 返回该次滑动是否有效(有卡片移动或合并)
*/
private boolean swipeUp(){
boolean flag = false;
for(int j = 0; j < 4; ++j){
int ind = 0;
//从上往下依次处理
for(int i = 1; i < 4; ++i){
//如果是存在数字的,往上遍历
if(cards[i][j].getNum() != 0){
for(int ii = i - 1; ii >= ind; --ii){
//如果这块是空的,将数字上移
if(cards[ii][j].getNum() == 0){
cards[ii][j].setNum(cards[i][j].getNum());
cards[i][j].setNum(0);
i--;//上移
flag = true;
}
//如果这块是相同的数,合并,合并的块不能一下合并两次,更新ind,不再遍历合并的块
else if(cards[ii][j].getNum() == cards[i][j].getNum()){
cards[ii][j].setNum((cards[i][j].getNum() * 2));
cards[i][j].setNum(0);
flag = true;
ind = ii + 1;//已经合过,该点不再合成
MainActivity.mainActivity.addScore(cards[ii][j].getNum() / 2);
//播放合并动画
playMergeAnimation(ii, j);
break;
}
//上面的块数字不同,退出循环
else break;
}
}
}
}
return flag;
}
private boolean swipeDown(){
boolean flag = false;
for(int j = 0; j < 4; ++j){
int ind = 4;
for(int i = 2; i >= 0; --i){
if(cards[i][j].getNum() != 0){
for(int ii = i + 1; ii < ind; ++ii){
if(cards[ii][j].getNum() == 0){
cards[ii][j].setNum(cards[i][j].getNum());
cards[i][j].setNum(0);
flag = true;
i++;
}
else if(cards[ii][j].getNum() == cards[i][j].getNum()){
cards[ii][j].setNum((cards[i][j].getNum() * 2));
cards[i][j].setNum(0);
flag = true;
ind = ii;
MainActivity.mainActivity.addScore(cards[ii][j].getNum() / 2);
playMergeAnimation(ii, j);
break;
}
else break;
}
}
}
}
return flag;
}
private boolean swipeLeft(){
boolean flag = false;
for(int i = 0; i < 4; ++i){
int ind = 0;
for(int j = 1; j < 4; ++j){
if(cards[i][j].getNum() != 0){
for(int jj = j - 1; jj >= ind; --jj){
if(cards[i][jj].getNum() == 0){
cards[i][jj].setNum(cards[i][j].getNum());
cards[i][j].setNum(0);
flag = true;
j--;
}
else if(cards[i][jj].getNum() == cards[i][j].getNum()){
cards[i][jj].setNum((cards[i][j].getNum() * 2));
cards[i][j].setNum(0);
flag = true;
ind = jj + 1;
MainActivity.mainActivity.addScore(cards[i][jj].getNum() / 2);
playMergeAnimation(i, jj);
break;
}
else break;
}
}
}
}
return flag;
}
private boolean swipeRight(){
boolean flag = false;
for(int i = 0; i < 4; ++i){
int ind = 4;
for(int j = 2; j >= 0; --j){
if(cards[i][j].getNum() != 0){
for(int jj = j + 1; jj < ind; ++jj){
if(cards[i][jj].getNum() == 0){
cards[i][jj].setNum(cards[i][j].getNum());
cards[i][j].setNum(0);
flag = true;
j++;
}
else if(cards[i][jj].getNum() == cards[i][j].getNum()){
cards[i][jj].setNum((cards[i][j].getNum() * 2));
cards[i][j].setNum(0);
flag = true;
ind = jj;
MainActivity.mainActivity.addScore(cards[i][jj].getNum() / 2);
playMergeAnimation(i, jj);
break;
}
else break;
}
}
}
}
return flag;
}
/**
*如果存在空白块,或者相邻的数字相同的块,则可以继续滑动
*/
private boolean canSwipe(){
for(int i = 0; i < 4; ++i){
for(int j = 0; j < 4; ++j){
if(cards[i][j].getNum() == 0){
return true;
}
else if(i != 3 && cards[i][j].getNum() == cards[i + 1][j].getNum()){
return true;
}
else if(j != 3 && cards[i][j].getNum() == cards[i][j + 1].getNum()){
return true;
}
}
}
return false;
}
private void addCards(int width, int height){
Card c;
for(int i = 0; i < 4; ++i){
for(int j = 0; j < 4; ++j){
c = new Card(getContext());
addView(c, width, height);
cards[i][j] = c;
}
}
}
private void gameOver(){
Toast.makeText(getContext(), "游戏结束", Toast.LENGTH_SHORT).show();
}
private int GetCardWidth() {
//获取屏幕信息
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
//根据布局,GameView是占屏幕宽度的90%,除以4就是卡片边长
return (int)((displayMetrics.widthPixels * 0.9f) / 4);
}
/*
* 递归随机,玄学复杂度,期望递归次数小于 16 次,偷了个懒
* 最好是把可用方块加入到一个列表中,然后在列表中随机
*/
private void randomCreateCard(int cnt){
Random random = new Random();
int r = random.nextInt(4);
int c = random.nextInt(4);
//该处已经存在数字,重新随机r, c
if(cards[r][c].getNum() != 0){
randomCreateCard(cnt);
return;
}
int rand = random.nextInt(10);
if(rand >= 2) rand = 2;
else rand = 4;
cards[r][c].setNum(rand);
//播放创建动画
playCreateAnimation(r, c);
if(cnt >= 2){
randomCreateCard(cnt - 1);
}
}
/*
* 播放创建新块动画
*/
private void playCreateAnimation(int r, int c){
AnimationSet animationSet = new AnimationSet(true);
//旋转
RotateAnimation anim = new RotateAnimation(0,360,RotateAnimation.RELATIVE_TO_SELF,0.5f, RotateAnimation.RELATIVE_TO_SELF,0.5f);
anim.setDuration(250);
anim.setRepeatCount(0);
anim.setInterpolator(new LinearInterpolator());
//缩放
ScaleAnimation anim2 = new ScaleAnimation(0,1,0,1,
Animation.RELATIVE_TO_SELF,0.5f,
Animation.RELATIVE_TO_SELF,0.5f
);
anim2.setDuration(250);
anim2.setRepeatCount(0);
animationSet.addAnimation(anim);
animationSet.addAnimation(anim2);
cards[r][c].startAnimation(animationSet);
}
/*
* 播放合并动画
*/
private void playMergeAnimation(int r, int c){
ScaleAnimation anim = new ScaleAnimation(1,1.2f,1,1.2f,
Animation.RELATIVE_TO_SELF,0.5f,
Animation.RELATIVE_TO_SELF,0.5f
);
anim.setDuration(150);
anim.setRepeatCount(0);
anim.setRepeatMode(Animation.REVERSE);
cards[r][c].startAnimation(anim);
}
}
Card.java
代表一个格子
package pers.hurric.game2048;
import android.content.Context;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import java.util.HashMap;
import java.util.Map;
public class Card extends FrameLayout {
TextView textView;
private int num;
static Map<Integer, Integer> backgroundColorIdMap = new HashMap<>();
static Map<Integer, Integer> textColorIdMap = new HashMap<>();
static {
textColorIdMap.put(0, R.color.textColor0);
textColorIdMap.put(2, R.color.textColor2);
textColorIdMap.put(4, R.color.textColor4);
backgroundColorIdMap.put(0, R.color.backgroundColor0);
backgroundColorIdMap.put(2, R.color.backgroundColor2);
backgroundColorIdMap.put(4, R.color.backgroundColor4);
backgroundColorIdMap.put(8, R.color.backgroundColor8);
backgroundColorIdMap.put(16, R.color.backgroundColor16);
backgroundColorIdMap.put(32, R.color.backgroundColor32);
backgroundColorIdMap.put(64, R.color.backgroundColor64);
backgroundColorIdMap.put(128, R.color.backgroundColor128);
backgroundColorIdMap.put(256, R.color.backgroundColor256);
backgroundColorIdMap.put(512, R.color.backgroundColor512);
backgroundColorIdMap.put(1024, R.color.backgroundColor1024);
}
public Card(@NonNull Context context) {
super(context);
textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setText(getNum() + "");
textView.setTextSize(50);
setNum(0);
LayoutParams lp = new LayoutParams(-1, -1);
lp.setMargins(10, 10, 10, 10);
addView(textView, lp);
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
textView.setText(num + "");
//数字改变时,同时改变改变字体大小和颜色
changeColor(num);
changeSize(num);
}
private void changeSize(int num){
if(num >= 1024){
textView.setTextSize(25);
}
else if(num >= 128){
textView.setTextSize(35);
}
else if(num >= 16){
textView.setTextSize(42);
}
else{
textView.setTextSize(50);
}
}
private void changeColor(int num){
if(num >= 8){
textView.setTextColor(getResources().getColor(R.color.textColorCommon));
}
else{
textView.setTextColor(getResources().getColor(textColorIdMap.get(num)));
}
if(num >= 2048){
textView.setBackgroundColor(getResources().getColor(R.color.backgroundColorBiggerThan2048));
}
else{
textView.setBackgroundColor(getResources().getColor(backgroundColorIdMap.get(num)));
}
}
}
最后
以上就是安详砖头为你收集整理的安卓小游戏 2048 新手练手项目 完整代码(含注释)的全部内容,希望文章能够帮你解决安卓小游戏 2048 新手练手项目 完整代码(含注释)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复