本人一直想找时间系统整理一下之前做过的一些实验,便于后续用到的时候可以尽快的使用,po出来也便于大家交流学习,有问题欢迎交流指正,与诸君共勉!
这次是Java的课程设计,现在看起来更像是一个安卓开发的课程设计。设计了一个贪吃蛇游戏,有友好的GUI界面和较为全面的功能。
实现效果
图1 开始动画
图2 登录界面
注册
下载GreedySnake软件并打开后,首次使用需要点击登陆界面的“没有账号?点此注册”按钮进入GamePlayer用户注册界面进行用户注册,如图3。
按提示输入账号:用户名/手机号/邮箱,并设置密码:数字组合,如图4。
注册成功后弹出提示:注册成功,并跳转至登录界面,如图5。
图3 注册界面
图4 用户注册
图5 注册成功
登录
登陆界面输入用户名和密码,勾选“我同意用户服务协议和隐私政策”,点击登录按钮,进入游戏菜单界面,如图7。
图7 游戏菜单界面
贪吃蛇游戏
从游戏菜单界面点击“新游戏“,进入贪吃蛇游戏初始界面,如图9,小蛇初始身长为4。默认开始播放游戏背景音乐。
单击“开始游戏“按钮,进入游戏主界面,如图10。用户可点击“向右”、“向左”、“向上”、“向下”四个按钮控制小蛇爬动的方向,食物随机出现在游戏界面中,食物种类从下图六种水果中随机产生。
图8 六种食物种类
游戏中,默认背景音乐暂停,小蛇每吃到食物一次,会播放相应音效。
图9 游戏初始界面
图10 游戏中
游戏中,点击”游戏暂停“按钮,会播放按钮点击音效并默认继续播放背景音乐,屏幕中央提示”点击按钮,继续游戏“字样,如图11。
单击主界面右上角喇叭图标,图标更换为静音模式,如图12。按钮音效和背景音乐将切换至静音模式。
图11 游戏中点击“游戏暂停”按钮
图12 游戏切换为静音模式
游戏过程中,根据不同得分,将展示不同效果,具体规则如下:
小蛇吃到一次食物加10分,每吃到一个食物,播放win音效;
得分小于250,分数每增加50,播放一次鼓掌音效;
得分大于等于300,小于400,分数每增加20,播放一次鼓掌音效;
得分大于等于400,分数每增加10,播放一次鼓掌音效,屏幕中央显示提示字样“YOU ARE A WINNER!”,中央”游戏暂停”按钮字样变为“WIN”。
分数达到500,自动跳转最终胜利界面,如图13。
查看历史最高分数
每轮游戏过后,若本轮得分超过本用户历史最高分数,将自动更新历史最高分数。在游戏菜单界面点击“查看历史最高分数”可查看最高分数记录,如图14。
在游戏菜单界面点击“退出游戏“按钮,可返回GamePlayer登录界面。
二、数据说明
本客户端无服务器,故用户账户密码数据、历史最高分数数据、游戏界面静音状态数据均存放在手机本地文件与数据库中。
国密SM4加密
对于用户注册信息,将用户密码使用国家密码管理局发布的SM4.0密码算法进行加密后存储,增强用户信息安全性。在Android Studio平台模拟运行,注册后,可看到日志信息如图15,用户密码已被加密处理。
图15 注册账户Log信息
SQLite数据库存储
用户账号密码数据统一存储在snakeUser.db数据库的user表中。
图16 snakeUser.db数据库
文件存储
用户历史最高分数、静音按键状态信息分别存储在文件名为“用户名+scorefile.xml”、“用户名+VolumeFile.xml”的文件中,如图17。
图17 最高分数、音量文件信息
代码说明
类说明
共设计16个Java文件:
Macro:用于定义游戏界面中的固定参数、文件名称等
User:用于创建用户信息
UserService:用于用户注册、登录功能实现
StartActivity:用于软件开始动画的3秒显示
MainActivity: 用于主游戏界面小蛇移动、游戏进行状态、静音状态、得分反应的逻辑控制;
MyDatabaseHelper:用于连接SQLite数据库,创建user表,存储用户信息
SharedPre:用于用户历史最高分数、静音按钮状态信息存储的文件操作
RegisterActivity:用于注册界面的显示及按钮事件响应
LoginActivity:用于登录界面的显示及按钮事件的响应
FinalwinActivity:用于用户最终胜利界面的显示
SM4:用于国密SM4算法的工具类
Encryption:用于国密SM4算法的加解密
Images:用于游戏主界面的小蛇、食物等动态构件绘制
ChooseActivity:游戏菜单界面的显示及按钮事件响应
SoundPlay:用于游戏中的按钮音效、吃到食物音效、游戏失败音效的短音频控制
BGMServer:用于游戏中的背景音乐(长音频)控制
共设计6个布局文件:
activity_beginning.xml:开始动画界面布局
activity_finalwin.xml:最终胜利界面布局
activity_login.xml:登录界面布局
activity_main.xml:游戏主界面布局
activity_register.xml:注册界面布局
activity_start_view.xml:游戏菜单界面布局
源代码
java文件
BGMServer.java
package com.example.android_snake;
import android.content.Context;
import android.media.MediaPlayer;
/**
* 贪吃蛇背景音乐工具类
*/
public class BGMserver {
private static MediaPlayer mp =null;
public static void play(Context context, int resource){
stop(context);
mp = MediaPlayer.create(context, resource);
mp.setLooping(true);
mp.start();
}
public static void stop(Context context) {
// TODO Auto-generated method stub
if(mp!= null){
mp.stop();
mp.release();
mp = null;
}
}
}
ChooseActivity.java
package com.example.android_snake;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.util.HashMap;
/**
* The type Star view:游戏开始界面
*/
public class ChooseActivity extends AppCompatActivity implements View.OnClickListener {
private Button Startbutton,Sbutton,Backbutton;
private String usern;
/**
* The Shared:实例化存储对象
*/
SharedPre shared;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_view);
Init();
}
/**
* Init:初始化该活动界面信息
*/
void Init(){
usern = LoginActivity.usern;
Startbutton =findViewById(R.id.star_game);
Sbutton=findViewById(R.id.MostBUtton);
Backbutton=findViewById(R.id.BackButton);
shared=new SharedPre(usern,this);
//soundp = new SoundPlay();
//soundp.initSoundPool();//初始化声音池
Startbutton.setOnClickListener(this);
Sbutton.setOnClickListener(this);
Backbutton.setOnClickListener(this);
}
/**
* onClick:该界面按钮监听事件
*/
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.star_game:
//sp.playSound(1, 0);//播放1号声音资源button,且播放一次
Intent intent=new Intent(ChooseActivity.this,MainActivity.class);
startActivity(intent);
break;
case R.id.BackButton:
//sp.playSound(1, 0);//播放1号声音资源button,且播放一次
finish();
break;
case R.id.MostBUtton:
//sp.playSound(1, 0);//播放1号声音资源button,且播放一次
Sbutton.setText(Macro.H_MOST_SCORE+shared.read());
break;
}
}
}
SM4.java
package com.example.android_snake;
import java.util.Arrays;
public class SM4 {
private static SM4 mSm4 = null;
public static SM4 getInstance() {
if (mSm4 == null) {
mSm4 = new SM4();
}
return mSm4;
}
public static final int DECRYPT = 0; // 解密
public static final int ENCRYPT = 1; // 加密
public static final int ROUND = 32;
private static final int BLOCK = 16;
private byte[] Sbox = { (byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe,
(byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6,
0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67,
(byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3,
(byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06,
(byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91,
(byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43,
(byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4,
(byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8,
(byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa,
0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7,
(byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83,
0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8,
0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda,
(byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56,
(byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1,
(byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87,
(byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27,
0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4,
(byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a,
(byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3,
(byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15,
(byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4,
(byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32,
0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d,
(byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca,
0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f,
(byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd,
(byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b,
0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb,
(byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41,
0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31,
(byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d,
0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4,
(byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c,
(byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09,
(byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0,
0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79,
(byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48 };
private int[] CK = { 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, 0xe0e7eef5,
0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81,
0x888f969d, 0xa4abb2b9, 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d,
0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25,
0x2c333a41, 0x484f565d, 0x646b7279 };
private int Rotl(int x, int y) {
return x << y | x >>> (32 - y);
}
private int ByteSub(int A) {
return (Sbox[A >>> 24 & 0xFF] & 0xFF) << 24
| (Sbox[A >>> 16 & 0xFF] & 0xFF) << 16
| (Sbox[A >>> 8 & 0xFF] & 0xFF) << 8 | (Sbox[A & 0xFF] & 0xFF);
}
private int L1(int B) {
return B ^ Rotl(B, 2) ^ Rotl(B, 10) ^ Rotl(B, 18) ^ Rotl(B, 24);
// return B^(B<<2|B>>>30)^(B<<10|B>>>22)^(B<<18|B>>>14)^(B<<24|B>>>8);
}
private int L2(int B) {
return B ^ Rotl(B, 13) ^ Rotl(B, 23);
// return B^(B<<13|B>>>19)^(B<<23|B>>>9);
}
void SMS4Crypt(byte[] Input, byte[] Output, int[] rk) {
int r, mid;
int[] x = new int[4];
int[] tmp = new int[4];
for (int i = 0; i < 4; i++) {
tmp[0] = Input[0 + 4 * i] & 0xff;
tmp[1] = Input[1 + 4 * i] & 0xff;
tmp[2] = Input[2 + 4 * i] & 0xff;
tmp[3] = Input[3 + 4 * i] & 0xff;
x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
// x[i]=(Input[0+4*i]<<24|Input[1+4*i]<<16|Input[2+4*i]<<8|Input[3+4*i]);
}
for (r = 0; r < 32; r += 4) {
mid = x[1] ^ x[2] ^ x[3] ^ rk[r + 0];
mid = ByteSub(mid);
x[0] = x[0] ^ L1(mid); // x4
mid = x[2] ^ x[3] ^ x[0] ^ rk[r + 1];
mid = ByteSub(mid);
x[1] = x[1] ^ L1(mid); // x5
mid = x[3] ^ x[0] ^ x[1] ^ rk[r + 2];
mid = ByteSub(mid);
x[2] = x[2] ^ L1(mid); // x6
mid = x[0] ^ x[1] ^ x[2] ^ rk[r + 3];
mid = ByteSub(mid);
x[3] = x[3] ^ L1(mid); // x7
}
// Reverse
for (int j = 0; j < 16; j += 4) {
Output[j] = (byte) (x[3 - j / 4] >>> 24 & 0xFF);
Output[j + 1] = (byte) (x[3 - j / 4] >>> 16 & 0xFF);
Output[j + 2] = (byte) (x[3 - j / 4] >>> 8 & 0xFF);
Output[j + 3] = (byte) (x[3 - j / 4] & 0xFF);
}
}
private void SMS4KeyExt(byte[] Key, int[] rk, int CryptFlag) {
int r, mid;
int[] x = new int[4];
int[] tmp = new int[4];
for (int i = 0; i < 4; i++) {
tmp[0] = Key[0 + 4 * i] & 0xFF;
tmp[1] = Key[1 + 4 * i] & 0xff;
tmp[2] = Key[2 + 4 * i] & 0xff;
tmp[3] = Key[3 + 4 * i] & 0xff;
x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
}
x[0] ^= 0xa3b1bac6;
x[1] ^= 0x56aa3350;
x[2] ^= 0x677d9197;
x[3] ^= 0xb27022dc;
for (r = 0; r < 32; r += 4) {
mid = x[1] ^ x[2] ^ x[3] ^ CK[r + 0];
mid = ByteSub(mid);
rk[r + 0] = x[0] ^= L2(mid); // rk0=K4
mid = x[2] ^ x[3] ^ x[0] ^ CK[r + 1];
mid = ByteSub(mid);
rk[r + 1] = x[1] ^= L2(mid); // rk1=K5
mid = x[3] ^ x[0] ^ x[1] ^ CK[r + 2];
mid = ByteSub(mid);
rk[r + 2] = x[2] ^= L2(mid); // rk2=K6
mid = x[0] ^ x[1] ^ x[2] ^ CK[r + 3];
mid = ByteSub(mid);
rk[r + 3] = x[3] ^= L2(mid); // rk3=K7
}
// 解密时轮密钥使用顺序:rk31,rk30,...,rk0 //解密时轮密钥反向
if (CryptFlag == DECRYPT) {
for (r = 0; r < 16; r++) {
mid = rk[r];
rk[r] = rk[31 - r];
rk[31 - r] = mid;
}
}
}
public int sms4(byte[] in, int inLen, byte[] key, byte[] out, int CryptFlag) {
int point = 0;
int[] round_key = new int[ROUND];
SMS4KeyExt(key, round_key, CryptFlag);
byte[] input = new byte[16];
byte[] output = new byte[16];
while (inLen >= BLOCK) {
input = Arrays.copyOfRange(in, point, point + 16);
SMS4Crypt(input, output, round_key);
System.arraycopy(output, 0, out, point, BLOCK);
inLen -= BLOCK;
point += BLOCK;
}
return 0;
}
}
Encryption.java
package com.example.android_snake;
import android.util.Log;
public class Encryption {
/**
* SM4加解密封装
* @param data 传入参数
* @param flag 加密为1&解密为0
* @param key 16字节密钥
* @return 加解密结果
*/
public static String deal(String data, int flag, byte[] key)
{
StringBuilder builder = new StringBuilder();
SM4 sm4 = SM4.getInstance();
String strData = "";
byte[] bInData;
byte[] bOutData = new byte[16];
int nIndex;
int nDataLen = data.length();
int nCount = 0;
int nLastLen = nDataLen%32;
if (nLastLen > 0) {
nCount = nDataLen/32 + 1;
if (flag ==0) {
Log.i("SM4", "数据长度不正确,需解密数据应是32的倍数");
}
} else {
nCount = nDataLen/32;
}
for (int i = 0; i < nCount; i++) {
if ((i+1) == nCount && nLastLen > 0) {
nIndex = i*32;
strData = data.substring(nIndex, nIndex+nLastLen);
strData = zero(strData);
bInData = hexStringToBytes(strData);
sm4.sms4(bInData,bInData.length,key,bOutData,flag);
strData = bytesToHexString(bOutData,0,16);
builder.append(strData);
} else {
nIndex = i*32;
strData = data.substring(nIndex, nIndex+32);
bInData = hexStringToBytes(strData);
sm4.sms4(bInData,bInData.length,key,bOutData,flag);
strData = bytesToHexString(bOutData,0,16);
builder.append(strData);
}
}
return builder.toString();
}
/**
* 补位方法,SM4 分块加密 ,每块16字节,此处不足补0. 扩展说明:有得规则是首字节补80,之后补00,还有区分左补和右补,如果此加解密不能满足要求,可以往这方面去做
* @param data 需补位的数据
* @return 补位后的字符串
*/
public static String zero(String data) {
StringBuilder builder = new StringBuilder();
builder.append(data);
int nLen = data.length();
while (nLen < 32) {
builder.append("0");
nLen++;
}
return builder.toString();
}
/**
* hexString转byte[]数组
* @param str 字符串,原串为“112233”
* @return 转换后的数据, 转换后为 0x11,0x22,0x33
*/
public static byte[] hexStringToBytes(String str) {
int charIndex, value;
int datalen = str.length() / 2;
byte[] data = new byte[datalen];
for (int i = 0; i < datalen; i++) {
charIndex = i * 2;
value = Integer.parseInt(str.substring(charIndex, charIndex + 2), 16);
data[i] = (byte) value;
}
return data;
}
/**
* byte[]数组转hexString
* @param data 需转换的数组 0x11,0x22,0x33
* @param offset 需转换开始位置
* @param len 需转换长度
* @return 转换后的字符传为“112233”
*/
public static String bytesToHexString(byte[] data, int offset, int len) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++)
sb.append(String.format("%02X", data[i + offset] & 0xff));
return sb.toString();
}
}
FinalwinActivity.java
package com.example.android_snake;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import androidx.appcompat.app.AppCompatActivity;
public class FinalwinActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//隐藏标题栏以及状态栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_finalwin);
}
}
Images.java
package com.example.android_snake;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
import android.view.WindowManager;
import android.util.DisplayMetrics;
import java.util.Random;
public class Images extends View {
private int BitmapX,BitmapY;
private int FoodX,FoodY;
private int[] snakeX =new int[Macro.MAX_LENGTH];
private int[] snakeY =new int[Macro.MAX_LENGTH];
private int length;
private int direction;
private boolean islive;//是否存活 状态
private Bitmap rbitmaphead,lbitmaphead,ubitmaphead,dbitmaphead,bitmapbody,bitmapfood;//四个方向的头及蛇的身体、食物
private Bitmap bitmapfoods[] = new Bitmap [6];
private Bitmap bling;
public boolean isIslive() {
return islive;
}
public void setIslive(boolean islive) {
this.islive = islive;
}
public int getFoodX() {
return FoodX;
}
public void setFoodX(int foodX) {
FoodX = foodX;
}
public void setBitmapfood(int x){
if(x < 6 && x >= 0)
bitmapfood = bitmapfoods[x];
}
public int getFoodY() {
return FoodY;
}
public void setFoodY(int foodY) {
FoodY = foodY;
}
public int getDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getBitmapX() {
return BitmapX;
}
public void setBitmapX(int bitmapX) {
BitmapX = bitmapX;
}
public int getBitmapY() {
return BitmapY;
}
public void setBitmapY(int bitmapY) {
BitmapY = bitmapY;
}
public Images(Context context) {
super(context);
}
/**
* 小蛇初始化
*/
public void Init(){
BitmapX=Macro.INIT_SNAKEX;
// BitmapX = MainActivity.screenWidth/2; //手机屏幕宽度的一半
BitmapY=Macro.INIT_SNAKEY;
FoodX=Macro.INIT_FOODX;
FoodY=Macro.INIT_FOODY;//初始坐标
Random r = new Random(); //用于生成随机数
int randomN = r.nextInt(3);//随机生成[0,3)的整数
snakeX[0]=BitmapX;
snakeY[0]=BitmapY;//蛇头的位置
length =Macro.INIT_LENGHT;//蛇的初始长度
islive=true;
switch (randomN){//设置蛇的初始状态
case 0://初始方向向右
direction=Macro.Right;//蛇的初始方向
//画出初始的蛇身
for(int i = 1; i< length; i++){
snakeX[i]= snakeX[i-1]-Macro.UNIT;
snakeY[i]= snakeY[0]; //初始状态
}
break;
case 1://初始方向向上
direction=Macro.Up;//蛇的初始方向
//画出初始的蛇身
for(int i = 1; i< length; i++){
snakeY[i]= snakeY[i-1]+Macro.UNIT;
snakeX[i]= snakeX[0]; //初始状态
}
break;
case 2://初始方向向下
direction=Macro.Down;//蛇的初始方向
//画出初始的蛇身
for(int i = 1; i< length; i++){
snakeY[i]= snakeY[i-1]-Macro.UNIT;
snakeX[i]= snakeX[0]; //初始状态
}
break;
}
}
/**
* 引用小蛇图片,并设置图片大小
*/
public void Size(){
//避免大图片解析问题--头部
BitmapFactory.Options headoptions = new BitmapFactory.Options();
//调整
headoptions.inSampleSize= Macro.IMAGE_HEAD_SIZE; //蛇头缩放倍数
headoptions.inJustDecodeBounds=false;
//避免大图片解析问题--身体
BitmapFactory.Options bodyoptions = new BitmapFactory.Options();
bodyoptions.inSampleSize=Macro.IMAGE_BODY_SIZE; //蛇身的缩放倍数
bodyoptions.inJustDecodeBounds=false;
//六种食物随机产生
bitmapfoods[0]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food);
bitmapfoods[1]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food1);
bitmapfoods[2]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food2);
bitmapfoods[3]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food3);
bitmapfoods[4]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food4);
bitmapfoods[5]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food50);
rbitmaphead= BitmapFactory.decodeResource(this.getResources(),R.drawable.rhead0, headoptions); //
ubitmaphead= BitmapFactory.decodeResource(this.getResources(),R.drawable.uhead0, headoptions);
lbitmaphead= BitmapFactory.decodeResource(this.getResources(),R.drawable.lhead0, headoptions);
dbitmaphead= BitmapFactory.decodeResource(this.getResources(),R.drawable.dhead0, headoptions);
bitmapbody= BitmapFactory.decodeResource(this.getResources(),R.drawable.body_0, bodyoptions);
Random r = new Random(); //用于生成随机数
int randomN = r.nextInt(6);//随机生成[0,6)的整数
bitmapfood= bitmapfoods[randomN];
}
/**
* 画蛇,将蛇画在屏幕上 canvas用于绘制背景的画布
* Canvas 画布
* Paint 画笔
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); //Canvas(画纸,通常称为画布)
Paint paint=new Paint(); //生成一个画笔对象
//画食物
/**
* @param bitmap The bitmap to be drawn
* @param left The position of the left side of the bitmap being drawn
* @param top The position of the top side of the bitmap being drawn
* @param paint The paint used to draw the bitmap (may be null)
*/
canvas.drawBitmap(bitmapfood, FoodX, FoodY, paint);//画第一食物, 第一个参数为要绘制的bitmap对象,第四个参数为Paint对象。
snakeX[0]=BitmapX;
snakeY[0]=BitmapY;
switch (direction){
case Macro.Right:
canvas.drawBitmap(rbitmaphead, snakeX[0], snakeY[0],paint);//蛇头往右,读取x、y坐标,填充画笔
//canvas.drawBitmap(bling, snakeX[0], snakeY[0],paint);
break;
case Macro.Up:
canvas.drawBitmap(ubitmaphead, snakeX[0], snakeY[0],paint);
// canvas.drawBitmap(bling, snakeX[0], snakeY[0],paint);//蛇头往右,读取x、y坐标,填充画笔
break;
case Macro.Left:
canvas.drawBitmap(lbitmaphead, snakeX[0], snakeY[0],paint);
break;
case Macro.Down:
canvas.drawBitmap(dbitmaphead, snakeX[0], snakeY[0],paint);
break;
}
//画出小蛇身体的位置
for(int i = 1; i< length; i++) {
canvas.drawBitmap(bitmapbody, snakeX[i], snakeY[i], paint);
}
//小蛇咬住身体了 生存位置零
for(int i = 1; i< length; i++){
if(snakeX[0]== snakeX[i]&& snakeY[0]== snakeY[i]){
islive=false;
}
}
//身体从最后一节向前一节位置移动
for(int i = length; i>0; i--){
snakeX[i]= snakeX[i-1];
snakeY[i]= snakeY[i-1];
}
/**
* Returns true if this bitmap has been recycled. If so, then it is an error
* to try to access its pixels, and the bitmap will not draw.
**如果该位图已被回收,则返回true。如果是,则是false
* *试图访问其像素,位图将不会绘制。
* *如果位图已经被回收,返回true
* @return true if the bitmap has been recycled
*/
//图片回收
if(rbitmaphead.isRecycled()){
rbitmaphead.recycle();
}
if(bitmapbody.isRecycled()){
bitmapbody.recycle();
}
if(ubitmaphead.isRecycled()){
rbitmaphead.recycle();
}
if(lbitmaphead.isRecycled()){
rbitmaphead.recycle();
}
if(dbitmaphead.isRecycled()){
rbitmaphead.recycle();
}
}
}
LoginActivity.java
package com.example.android_snake;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class LoginActivity extends AppCompatActivity{
/**
* 定义私有成员文本 账号密码etc
*/
private EditText username;
private EditText password;
private int remarks;//初始值为0
private Button Loginbutton; //登录按钮
private Button goRegisterButton;//打开注册界面
static public String usern; //工具字符串,用于用户分数信息存储文件命名
private Button Rbtlogin;
boolean agree = false;//是否同意用户协议 标志位
private void findViews(){
username = (EditText) findViewById(R.id.username_login);
password = (EditText) findViewById(R.id.password_login);
//remarks = 0;//初始化为0
Loginbutton = (Button) findViewById(R.id.bt_login);
goRegisterButton =(Button) findViewById(R.id.bt_goRegister);
Rbtlogin = (Button) findViewById(R.id.rbt_login);
}
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);//即activity_login.xml
findViews();
//设置按钮点击监听器
Rbtlogin.setOnClickListener(new View.OnClickListener() {
//按钮点击
@Override
public void onClick(View view) {
if (view.getId() == R.id.rbt_login) {//同意协议
Log.i("TAG","同意用户协议");
agree = true;
}
}
});
Loginbutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//MainActivity.playSound();
if(!agree) {//未勾选同意用户协议
Toast.makeText(LoginActivity.this,"请先勾选同意用户协议",Toast.LENGTH_LONG).show();
return;
}
String name = username.getText().toString().trim();
//System.out.println(name);
String pass = password.getText().toString().trim();
//System.out.println(pass);
//对密码进行国密SM4加密,密文与用户数据库中数据表字段比较
String strKey = "0123456789abcdeffedcba9876543210";//密钥
byte[] bKey = Encryption.hexStringToBytes(strKey);
String CPass = Encryption.deal(pass,SM4.ENCRYPT,bKey);//密码加密后的密文CPass
Log.i("TAG","username_"+name+"_ CPassword_"+CPass);
UserService uService = new UserService(LoginActivity.this);
boolean flag = uService.login(name,CPass);
if(flag){
Log.i("TAG","登陆成功");
Toast.makeText(LoginActivity.this,"登录成功",Toast.LENGTH_LONG).show();
usern = name;//便于用户最高分数文件存储 ,为了安全性,只赋值用户名
//登录成功,跳转游戏选择界面
Intent intent = new Intent(LoginActivity.this,ChooseActivity.class);
startActivity(intent);
}else{
Log.i("TAG","登陆失败");
Toast.makeText(LoginActivity.this,"登录失败",Toast.LENGTH_LONG).show();
}
}
});
goRegisterButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {//跳转注册界面
startActivity(new Intent(LoginActivity.this,RegisterActivity.class));
}
});
}
}
Macro.java
package com.example.android_snake;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.WindowManager;
/**
* The type Macro:宏的定义
*/
public class Macro {
/**
* The constant Up:定义小蛇头部方向向上
*/
public static final int Up=0x11;
/**
* The constant :定义小蛇头部方向向下
*/
public static final int Down=0x12;
/**
* The constant Left:定义小蛇头部方向向左
*/
public static final int Left=0x13;
/**
* The constant Right:定义小蛇头部方向向右
*/
public static final int Right=0x14;
/**
* The constant UNIT:定义小蛇移动的基本单位
*/
public static final int UNIT=50;
/**
* The constant LEAST_SIZE:小蛇生活区最小单位
*/
public static final int LEAST_SIZE=1;
/**
* The constant DELAY:定时器响应run延时
*/
public static final int DELAY=100;
/**
* The constant PERIOD:定时器启动的间隔时间
*/
public static final int PERIOD=300;
/**
* The constant INIT_SNAKEX:小蛇初始头部的X坐标
*/
// public static final int INIT_SNAKEX =600;
public static final int INIT_SNAKEX =MainActivity.screenWidth/2;
/**
* The constant INIT_SNAKEY:小蛇初始头部的Y坐标
*/
public static final int INIT_SNAKEY =400;
/**
* The constant INIT_FOODX:食物初始的X坐标
*/
public static final int INIT_FOODX=400;
/**
* The constant INIT_FOODY:食物初始的Y坐标
*/
public static final int INIT_FOODY=500;
/**
* The constant INIT_LENGHT:小蛇初始长度
*/
public static final int INIT_LENGHT=4;
/**
* The constant IMAGE_HEAD_SIZE:小蛇头图片缩小倍数
*/
//public static final int IMAGE_HEAD_SIZE=20;
public static final int IMAGE_HEAD_SIZE=1;
/**
* The constant IMAGE_BODY_SIZE:小蛇身体图片缩小倍数
*/
//public static final int IMAGE_BODY_SIZE=46;
public static final int IMAGE_BODY_SIZE=1;
/**
* The constant SCORE_UNIT:每次加分
*/
public static final int SCORE_UNIT=10;
/**
* The constant MAX_LENGTH:小蛇最大长度
*/
public static final int MAX_LENGTH =900;
/**
* The constant SPEED:小蛇的初始速度
*/
public static final int SPEED=20;
/**
* The constant FOOD:小蛇吃到食物就会加速的基本单位
*/
public static final int FOOD=40;
/**
* The constant MAX_SPEED:小蛇移动的最大速度
*/
public static final int MAX_SPEED =270;
/**
* The constant SCORE:此局的分数显示
*/
public static final String SCORE="当前分数:";
/**
* The constant MOST_SCORE:历史最高分数显示
*/
public static final String MOST_SCORE="最高分数:";
/**
* The constant H_MOST_SCORE:主界面查询分数显示
*/
public static final String H_MOST_SCORE="最高分数(点击更新):";
/**
* The constant Pname:分数存储文件名统一名称后缀
*/
public static final String Pname="scorefile";
/**
* The constant Pname:静音选中存储文件名统一名称后缀
*/
public static final String PnameFlag="Volumefile";
/**
* 获得屏幕的宽度
* @param ctx
* @return
*/
public static int getScreenWidth(Context ctx) {
// 从系统服务中获取窗口管理器
WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
// 从默认显示器中获取显示参数保存到dm对象中
wm.getDefaultDisplay().getMetrics(dm);
return dm.widthPixels; // 返回屏幕的宽度数值
}
/**
* 获得屏幕的高度
*/
public static int getScreenHeight(Context ctx) {
// 从系统服务中获取窗口管理器
WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
// 从默认显示器中获取显示参数保存到dm对象中
wm.getDefaultDisplay().getMetrics(dm);
return dm.heightPixels; // 返回屏幕的高度数值
}
}
MainActivity.java
package com.example.android_snake;
import static com.example.android_snake.BGMserver.play;
import static com.example.android_snake.BGMserver.stop;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.HashMap;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
/**
* The type Main activity:小蛇移动及方向控制逻辑处理
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private String usern;
private FrameLayout Living_Space;
private boolean isstart =false;
private boolean volumeflag = true;
private Timer timer;
private Button BT_isstar,BT_RIGHT,BT_UP,BT_DOWN,BT_LEFT,BT_Volume;
private TextView prompt;
private TextView ScoreView;
private int direction;
private int weight,height;
public static int screenWidth,screenHeight;
private int countVolume;//计数点击音量按键的次数 判断奇偶性
private boolean islive;
private int score,time,mostscore;
Images images;
MoveSnake movesnake;
SharedPre shared;
private Handler handler;
// RelativeLayout frameLayout_add_imageview;
SoundPool sp;//声明SoundPool的引用
HashMap<Integer, Integer> hm;//声明HashMap来存放声音文件
int currStaeamId;//当前正播放的streamId
private void initSoundPool() {//初始化声音池
sp = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);//创建SoundPool对象
hm = new HashMap<Integer, Integer>();//创建HashMap对象
//加载声音文件,并且设置为1号 2号 3号声音放入hm中
hm.put(1, sp.load(this, R.raw.button0, 1));
hm.put(2, sp.load(this, R.raw.win, 1));
hm.put(3, sp.load(this, R.raw.fail, 1));
hm.put(4, sp.load(this, R.raw.applause, 1));
}
private void playSound(int sound, int loop) {//获取AudioManager引用
AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
//获取当前音量
float streamVolumeCurrent = am.getStreamVolume(AudioManager.STREAM_MUSIC);
//获取系统最大音量
float streamVolumeMax = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
//计算得到播放音量
float volume = streamVolumeCurrent / streamVolumeMax;
//调用SoundPool的play方法来播放声音文件
currStaeamId = sp.play(hm.get(sound), volume, volume, 1, loop, 1.0f);
}
//BGM设置工具函数
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
stop(this);
}
//BGM设置工具函数
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
play(this, R.raw.bgm);
}
class MoveSnake extends Thread{
@SuppressLint("HandlerLeak")
// @Override
public void run() {
Looper.prepare();//调用 Looper.prepare()来给线程创建消息循环
handler=new Handler(){
public void handleMessage(Message msg) {
if(isstart ==true&& islive==true){
//方向控制
if(msg.what==Macro.Right) {
images.setBitmapX(images.getBitmapX() + Macro.UNIT);//The constant UNIT:定义小蛇移动的基本单位,x轴+一个移动单位
images.setBitmapY(images.getBitmapY());
}
if(msg.what==Macro.Up) {
images.setBitmapY(images.getBitmapY()-Macro.UNIT);
images.setBitmapX(images.getBitmapX());
}
if(msg.what==Macro.Left) {
images.setBitmapX(images.getBitmapX()-Macro.UNIT);
images.setBitmapY(images.getBitmapY());
}
if(msg.what==Macro.Down) {
images.setBitmapY(images.getBitmapY()+Macro.UNIT);
images.setBitmapX(images.getBitmapX());
}
//吃掉食物处理
if((images.getBitmapX()==images.getFoodX())&&(images.getBitmapY()==images.getFoodY())){
//加吃到食物的光效
// ImageView imageView = new ImageView(MainActivity.this);
// imageView.setImageResource(R.drawable.bling);
// FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(100,
// 30);//为添加图片的大小
// imageView.setLayoutParams(params);
// //imageView.setBackgroundColor();
// frameLayout_add_imageview.addView(imageView);
// if(shared.readVolumeFlag()==1) playSound(2, 0);//播放2号声音资源win,且播放一次
if(volumeflag) playSound(2, 0);//播放2号声音资源win,且播放一次
images.setLength(images.getLength()+1);//蛇身长度加1
int FoodX=(int)(Math.random()*(weight/Macro.UNIT))*Macro.UNIT; //食物随机位置出现
int FoodY=(int)(Math.random()*(height/Macro.UNIT))*Macro.UNIT;
if(FoodX==images.getBitmapX()&&FoodY==images.getBitmapY()){
FoodX= FoodX+Macro.UNIT;
FoodY= FoodY-Macro.UNIT;
}
Random r = new Random(); //用于生成随机数
int randomN = r.nextInt(6);//随机生成[0,6)的整数
images.setBitmapfood(randomN);//随机生成食物种类
images.setFoodX(FoodX);
images.setFoodY(FoodY);
score+=Macro.SCORE_UNIT;
if(score%Macro.FOOD==0){
timer.cancel();
if(time<=Macro.MAX_SPEED)
time+=Macro.SPEED;
StartView(time); //循环任务, sendmessage
}
}
images.invalidate();//invalidate()方法是用来刷新重绘当前的View的,如果当前View的布局尺寸、位置没有变化,仅仅是绘制内容变化了,那么我们就可以调用invalidate()方法。
//线程中更新控件,用来计分
runOnUiThread(new Runnable() {
@Override
public void run() {
ScoreView.setText(Macro.SCORE+String.valueOf(score));
if(shared.readVolumeFlag()==0) onPause();
//if(!volumeflag)onPause();//检测到静音设置,停止播放背景音
if(score % 50 == 0 && score > 0 && score < 250 && shared.readVolumeFlag()==1) //250分以下,每满50分 掌声响起一次
playSound(4, 0);//播放2号声音资源applause,且播放一次
if(score >= 300 && score < 400 && score%20==0 && shared.readVolumeFlag()==1) //分数满300分,小于400分 每20分 掌声响起一次
{
playSound(4, 0);//播放声音资源applause,且播放一次
}
if(score >= 400 ) //分数大于四百 提示胜利祝贺 每10分掌声响一次
{
if(shared.readVolumeFlag()==1) playSound(4, 0);//播放2号声音资源applause,且播放一次
prompt.setText(R.string.congratulations);//屏幕提示:YOU ARE A WINNER!
prompt.setVisibility(View.VISIBLE);
BT_isstar.setText("WIN!");
}
if(score == 500 ) //分数等于五百 提示奖励祝贺
{
//游戏胜利,跳转奖励界面!
Intent intent = new Intent(MainActivity.this,FinalwinActivity.class);
startActivity(intent);
}
}
});
//死亡判断
if(images.getBitmapX()>=weight||images.getBitmapX()<0
||images.getBitmapY()<0||images.getBitmapY()>=height
||images.isIslive()==false){
islive=false;
//小蛇死亡提醒
runOnUiThread(new Runnable() {
@Override
public void run() {
if(shared.readVolumeFlag()==1) playSound(3, 0);//播放3号声音资源fail,且播放一次
BT_isstar.setText("重新开始");
prompt.setText(R.string.promptrestar);
prompt.setVisibility(View.VISIBLE);
if(score>mostscore)
mostscore=score;
shared.save(usern,mostscore);//创分数新纪录 存入文件
}
});
}
}
super.handleMessage(msg);
}
};
Looper.loop();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MoveSnake movesnake =new MoveSnake();
movesnake.start(); //启动一个线程
Init();
StartView(time);
if(shared.readVolumeFlag()==1)onResume();//播放BGM
screenWidth = Macro.getScreenWidth(MainActivity.this);
screenHeight = Macro.getScreenHeight(MainActivity.this);
//将图片加载到布局中
Living_Space.addView(images);
//获取活动空间的宽和高
Living_Space.post(new Runnable() {
@Override
public void run() {
weight=Living_Space.getWidth();
height=Living_Space.getHeight();
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
timer.cancel();
}
/**
* Init:初始化该界面控件及参数
*/
//初始化
void Init(){
initSoundPool();//初始化声音池的方法
usern = LoginActivity.usern;
Living_Space=findViewById(R.id.framelayout);
BT_isstar=findViewById(R.id.isstar);
BT_RIGHT=findViewById(R.id.Right);
BT_UP=findViewById(R.id.Up);
BT_DOWN=findViewById(R.id.Down);
BT_LEFT=findViewById(R.id.Left);
BT_Volume = findViewById(R.id.Volume);
prompt=findViewById(R.id.prompt);
ScoreView=findViewById(R.id.Score);
TextView mostScore = findViewById(R.id.MostScore);
// if(shared.readVolumeFlag()==1){
// volumeflag = true;
// //BT_Volume.setBackground(R.drawable.volumeon);
// }else{
// volumeflag = false;
// BT_Volume.setBackgroundDrawable(getResources().getDrawable(R.drawable.volumeoff));//背景设置为静音
// }
countVolume = 0;
images=new Images(MainActivity.this);
shared=new SharedPre(usern,MainActivity.this);
direction=Macro.Right;
images.Init();
//direction = images.getDirection();
images.Size();
islive=true;
score=0;
mostscore=shared.read();
time=0;
if(shared.readVolumeFlag()==1)onResume();
BT_isstar.setOnClickListener(this);
BT_RIGHT.setOnClickListener(this);
BT_UP.setOnClickListener(this);
BT_LEFT.setOnClickListener(this);
BT_DOWN.setOnClickListener(this);
BT_Volume.setOnClickListener(this);
mostScore.setText(Macro.MOST_SCORE+shared.read());
}
/**
* onClick:该界面按钮监听事件
*/
@Override
public void onClick(View v) {
switch (v.getId()){
//游戏的开始、暂停按钮
case R.id.isstar:
if(volumeflag)onResume();
if(volumeflag)playSound(1, 0);//播放1号声音资源,且播放一次
//提示播放即时音效
//Toast.makeText(MainActivity.this, "播放音效", Toast.LENGTH_SHORT).show();
isstart =!isstart;
if(isstart &&islive) {
BT_isstar.setText(R.string.stop);
onPause();//bgm停止
prompt.setVisibility(View.GONE);
}
else if(isstart ==false&&islive==true){
BT_isstar.setText("继续游戏");
prompt.setText("点击按钮继续游戏");
prompt.setVisibility(View.VISIBLE);
}
else if(islive==false){
// finish();
Intent intent=new Intent(MainActivity.this,MainActivity.class);
startActivity(intent);
}
break;
//控制小蛇方向向右
case R.id.Right:
if(isstart &&direction!=Macro.Left) {
direction = Macro.Right;
images.setDirection(direction);
}
break;
//控制小蛇方向向上
case R.id.Up:
if(isstart &&direction!=Macro.Down) {
direction = Macro.Up;
images.setDirection(direction);
}
break;
//控制小蛇方向向左
case R.id.Left:
if(isstart &&direction!=Macro.Right) {
direction = Macro.Left;
images.setDirection(direction);
}
break;
//控制小蛇方向向下
case R.id.Down:
if(isstart &&direction!=Macro.Up) {
direction = Macro.Down;
images.setDirection(direction);
}
break;
case R.id.Volume:
countVolume++;
if(countVolume%2!=0){
volumeflag = false;
shared.saveFlag(volumeflag); //存入文件
BT_Volume.setBackgroundDrawable(getResources().getDrawable(R.drawable.volumeoff));//背景设置为静音
}
else
{
volumeflag = true;
shared.saveFlag(volumeflag); //存入文件
BT_Volume.setBackgroundDrawable(getResources().getDrawable(R.drawable.volumeon));//背景设置
}
}
}
/**
* 定时器,定时发送小蛇方向信息,控制小蛇速度
* @param time
*/
void StartView(int time) {
timer=new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(direction);
}
};
timer.schedule(task,Macro.DELAY,Macro.PERIOD-time);
}
}
MydatabaseHelper.java
package com.example.android_snake;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.content.Context;
import android.widget.Toast;
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_User = "create table user ("
+ "id integer primary key autoincrement, "
+ "userName varchar(20), "
+ "Password varchar(40), "
+ "Remarks integer)";
private Context mContext;
static int dbVersion = 1;//版本
static String name = "snakeUser.db";//数据库名
/**
*
* @param context
*/
public MyDatabaseHelper( Context context) {
super(context, name, null, dbVersion);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_User);
// Toast.makeText(mContext, "用户信息创建成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
RegisterActivity.java
package com.example.android_snake;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class RegisterActivity extends AppCompatActivity {
private EditText username;
private EditText password;
private Button register;
private void findViews(){
username = (EditText) findViewById(R.id.username_register);
password = (EditText) findViewById(R.id.password_register);
register = (Button) findViewById(R.id.registerButton);
}
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
findViews();
register.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
//MainActivity.playSound();
String name = username.getText().toString().trim();
String pass = password.getText().toString().trim();
//对密码进行国密SM4加密,密文存入数据库
String strKey = "0123456789abcdeffedcba9876543210";//密钥
//String strKey = "0123456789abcdef";//密钥
byte[] bKey = Encryption.hexStringToBytes(strKey);
String CPass = Encryption.deal(pass,SM4.ENCRYPT,bKey);//密码加密后的密文CPass
int remarks = 0;
Log.i("TAG","username_"+name+"_ CPassword_"+CPass);
Log.i("TAG","注册成功");
UserService uService = new UserService(RegisterActivity.this);
User user = new User();
user.setUsername(name);
user.setPassword(CPass);
uService.register(user);
Toast.makeText(RegisterActivity.this,"注册成功",Toast.LENGTH_LONG).show();
//注册成功,跳转登录界面
Intent intent = new Intent(RegisterActivity.this,LoginActivity.class);
startActivity(intent);
}
});
}
}
SharedPre.java
package com.example.android_snake;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
/**
* 用户最高分数记录信息
*/
public class SharedPre {
private int mostscore;
private Context context;
String FileName;
String FileNameVolume;
private boolean volumeflag;//是否选中静音
public SharedPre() {
}
public SharedPre(String usern,Context context) {
FileName = usern+Macro.Pname; //文件名:用户名加scorefile
FileNameVolume = usern+Macro.PnameFlag; //文件名:用户名加VolumeFile
this.context = context;
volumeflag = true; //默认选中
}
public void setvolumeFlag(boolean flag){
volumeflag = flag;
}
/**
* 存储最高游戏分数记录
* @param usern 用户名
* @param mscore 最高分数
*/
public void save(String usern,int mscore) {
SharedPreferences sp = context.getSharedPreferences(FileName, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putInt("score",mscore);
editor.apply();//文件存储结束
//更新用户数据库
UserService uService = new UserService(context);
uService.updateRemarks(usern,mscore);
// //更新用户数据库
// MyDatabaseHelper dbHelper = new MyDatabaseHelper(context);
//
// SQLiteDatabase db = dbHelper.getWritableDatabase();
// /*实例化内容值*/
// ContentValues values = new ContentValues();
//
// /*在values中添加内容*/
// values.put("Remarks", mscore);
// /*修改条件*/
// String whereClause = "userName=?";
// /*修改添加参数*/
// String[] whereArgs = {usern};
// /*修改*/
// db.update("user", values, whereClause, whereArgs);
// db.close();
}
//读出并显示最高游戏分数记录
public int read(){
SharedPreferences sharedPreferences=context.getSharedPreferences(FileName,Context.MODE_PRIVATE);
mostscore=sharedPreferences.getInt("score",0);
return mostscore;
}
/**
* 存储静音开关是否选中信息
* @param flag
*/
public void saveFlag(boolean flag) {
SharedPreferences sp = context.getSharedPreferences(FileNameVolume, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
if(flag) editor.putInt("state",1);//选中存1
else editor.putInt("state",0);
editor.apply();//文件存储结束
}
/**
* 读出静音开关状态
* @return
*/
public int readVolumeFlag(){
SharedPreferences sharedPreferences=context.getSharedPreferences(FileNameVolume,Context.MODE_PRIVATE);
int state=sharedPreferences.getInt("state",1);//默认1
return state;
}
}
SoundPlay.java
package com.example.android_snake;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import androidx.appcompat.app.AppCompatActivity;
import java.util.HashMap;
/**
* 贪吃蛇button/win/fail音效工具类
*/
public class SoundPlay extends AppCompatActivity {
static SoundPool sp;//声明SoundPool的引用
static HashMap<Integer, Integer> hm;//声明HashMap来存放声音文件
static int currStaeamId;//当前正播放的streamId
public void initSoundPool() {//初始化声音池
sp = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);//创建SoundPool对象
hm = new HashMap<Integer, Integer>();//创建HashMap对象
//加载声音文件,并且设置为1号 2号 3号声音放入hm中
hm.put(1, sp.load(this, R.raw.button0, 1));
hm.put(2, sp.load(this, R.raw.win, 1));
hm.put(3, sp.load(this, R.raw.fail, 1));
hm.put(4, sp.load(this, R.raw.applause, 1));
}
public void playSound(int sound, int loop) {//获取AudioManager引用
AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
//获取当前音量
float streamVolumeCurrent = am.getStreamVolume(AudioManager.STREAM_MUSIC);
//获取系统最大音量
float streamVolumeMax = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
//计算得到播放音量
float volume = streamVolumeCurrent / streamVolumeMax;
//调用SoundPool的play方法来播放声音文件
currStaeamId = sp.play(hm.get(sound), volume, volume, 1, loop, 1.0f);
}
}
StartActivity.java
package com.example.android_snake;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.Window;
import android.view.WindowManager;
public class StartActivity extends Activity {
private final int SPLASH_DISPLAY_LENGHT = 3000; //延迟3秒
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//隐藏标题栏以及状态栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_beginning);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(StartActivity.this, LoginActivity.class);
StartActivity.this.startActivity(intent);
StartActivity.this.finish();
}
}, SPLASH_DISPLAY_LENGHT);
}
// private Handler handler = new Handler() {
// @Override
// public void handleMessage(Message msg) {
// super.handleMessage(msg);
// }
// };
/**
* 跳转至选择界面
*/
public void getHome(){
Intent intent = new Intent(StartActivity.this, ChooseActivity.class);
startActivity(intent);
finish();
}
}
User.java
package com.example.android_snake;
import java.io.Serializable;
public class User implements Serializable {
private int id;
private String username;
private String password;
private int remarks;
public User(){
super();
}
public User(String username,String password,int remarks){
super();
this.username = username;
this.password = password;
this.remarks = remarks;
}
public int getId(){
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername(){
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword(){
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getRemarks(){
return remarks;
}
public void setRemarks(int remarks) {
this.remarks = remarks;
}
/**
* 描述用户信息
* @return
*/
public String desString(){
return "User [id="+id+",username=" + username +",password="
+password+",remarks="+remarks+"]";
}
}
UserService.java
package com.example.android_snake;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
/**
* 本类用于用户注册登录/更新数据库user表中最高分数功能的实现
*/
public class UserService {
private MyDatabaseHelper dbHelper;
public UserService(Context context){
dbHelper = new MyDatabaseHelper(context);
}
public boolean login(String username,String password){
SQLiteDatabase sdb = dbHelper.getReadableDatabase();
String sql = "select * from user where username=? and password=?";
Cursor cursor = sdb.rawQuery(sql, new String[]{username,password});
if(cursor.moveToFirst()==true){
cursor.close();
return true;
}
return false;
}
public boolean register(User user){
SQLiteDatabase sdb = dbHelper.getReadableDatabase();
String sql = "insert into user(username,password,remarks) values(?,?,?)";
Object[] obj = {user.getUsername(),user.getPassword(),user.getRemarks()};
sdb.execSQL(sql,obj);
return true;
}
public void updateRemarks(String username,int remarks){
SQLiteDatabase sdb = dbHelper.getReadableDatabase();
String remarksS = String.valueOf(remarks);
String updateSql = "update user set Remarks=? where userName=?";
sdb.execSQL(updateSql, new String[]{remarksS, username});
}
}
布局文件
activity_beginning.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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/cover_02">
<ImageView
android:id="@+id/imageView6"
android:layout_width="match_parent"
android:layout_height="575dp"
app:srcCompat="@drawable/cover_02" />
<!-- <TextView-->
<!-- android:id="@+id/textView2"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="200dp"-->
<!-- android:layout_weight="1"-->
<!-- android:text="Play games to please yourself!"-->
<!-- android:textColor="@color/black"-->
<!-- android:textSize="25dp" />-->
<TextView
android:id="@+id/txt_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="70dp"
android:text="Game player ⭐⭐⭐小游戏"
android:textSize="30sp"
android:textColor="@color/white"/>
</LinearLayout>
Activity_finalwin.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="252dp"
android:layout_height="445dp"
android:layout_centerInParent="true"
android:layout_weight="1"
app:srcCompat="@drawable/finalwin" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/finalwin"
android:layout_centerHorizontal="true"
android:textColor="@color/purple_500"
android:textSize="22sp" />
</RelativeLayout>
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/top_login"
android:layout_width="match_parent"
android:layout_height="180dp"
android:background="@drawable/pic_top2" />
<TextView
android:id="@+id/login_tip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="70dp"
android:text="@string/welcome"
android:textSize="25sp" />
<EditText
android:id="@+id/username_login"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@id/top_login"
android:layout_marginLeft="35dp"
android:layout_marginTop="20dp"
android:layout_marginRight="35dp"
android:background="@drawable/input_line1"
android:hint="@string/login_tip1"
android:inputType="text"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textSize="18sp"
tools:ignore="autofill" />
<EditText
android:id="@+id/password_login"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@id/username_login"
android:layout_marginLeft="35dp"
android:layout_marginTop="10dp"
android:layout_marginRight="35dp"
android:background="@drawable/input_line1"
android:hint="@string/login_tip2"
android:inputType="textPassword"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textSize="18sp"
tools:ignore="autofill" />
<Button
android:id="@+id/bt_login"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@id/password_login"
android:layout_marginLeft="35dp"
android:layout_marginTop="15dp"
android:layout_marginRight="35dp"
android:background="@drawable/start_button"
android:text="@string/login_tip3"
android:textColor="@color/white" />
<RadioButton
android:id="@+id/rbt_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/bt_login"
android:layout_marginStart="25dp"
android:layout_marginLeft="25dp"
android:layout_marginTop="10dp"
android:checked="false"
android:text="@string/login_tip5"
android:textSize="14sp" />
<Button
android:id="@+id/bt_goRegister"
android:layout_width="match_parent"
android:layout_height="18dp"
android:layout_below="@id/rbt_login"
android:layout_marginLeft="145dp"
android:layout_marginTop="19dp"
android:layout_marginRight="35dp"
android:background="@null"
android:text="@string/login_tip6"
android:textColor="@color/grey"
android:textSize="14sp" />
<TextView
android:id="@+id/txt_loginother"
android:layout_width="match_parent"
android:layout_height="25dp"
android:layout_below="@id/rbt_login"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_marginLeft="65dp"
android:layout_marginTop="145dp"
android:layout_marginRight="65dp"
android:gravity="center"
android:text="@string/login_tip4"
android:textSize="14sp" />
<Button
android:id="@+id/bt_wechat"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_below="@id/txt_loginother"
android:layout_marginStart="65dp"
android:layout_marginTop="30dp"
android:background="@drawable/logo_login4"
android:layout_marginLeft="65dp" />
<Button
android:id="@+id/bt_QQ"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_below="@id/txt_loginother"
android:layout_marginStart="55dp"
android:layout_marginLeft="55dp"
android:layout_marginTop="30dp"
android:layout_toEndOf="@id/bt_wechat"
android:layout_toRightOf="@id/bt_wechat"
android:background="@drawable/logo_login5" />
<Button
android:id="@+id/bt_weibo"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_below="@id/txt_loginother"
android:layout_toEndOf="@id/bt_QQ"
android:layout_marginStart="55dp"
android:layout_marginTop="30dp"
android:background="@drawable/logo_login1"
android:layout_toRightOf="@id/bt_QQ"
android:layout_marginLeft="55dp" />
</RelativeLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context="com.example.android_snake.MainActivity">
<FrameLayout
android:id="@+id/framelayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@id/RelativeLayout"
android:layout_marginBottom="145dp"
android:background="@color/pink_1">
<TextView
android:id="@+id/prompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="点击按钮,开始游戏!"
android:textColor="@color/red_1"
android:textSize="25sp" />
<TextView
android:id="@+id/Score"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="当前分数"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:id="@+id/MostScore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="200dp"
android:text="@string/MOSTSCORE"
android:textColor="#000000"
android:textSize="20sp" />
<Button
android:id="@+id/Volume"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="350dp"
android:background="@drawable/volumeon" />
<RelativeLayout
android:id="@+id/addBling"
android:layout_width="110dp"
android:layout_height="60dp"
android:layout_marginTop="70dp"
/>
</FrameLayout>
<RelativeLayout
android:id="@+id/RelativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<Button
android:id="@+id/isstar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/Up"
android:layout_centerHorizontal="true"
android:background="@drawable/downbutton"
android:text="@string/star" />
<Button
android:id="@+id/Up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_gravity="center"
android:background="@drawable/upbutton"
android:text="@string/UP" />
<Button
android:id="@+id/Left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/Up"
android:layout_toStartOf="@id/Up"
android:layout_toLeftOf="@id/Up"
android:background="@drawable/leftbutton"
android:text="@string/LEFT" />
<Button
android:id="@+id/Down"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/Left"
android:layout_centerHorizontal="true"
android:background="@drawable/upbutton"
android:text="@string/DOWN" />
<Button
android:id="@+id/Right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/Up"
android:layout_toEndOf="@id/Up"
android:layout_toRightOf="@id/Up"
android:background="@drawable/rightbutton"
android:text="@string/RIGHT" />
</RelativeLayout>
</RelativeLayout>
activity_register.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/top_register"
android:layout_width="match_parent"
android:layout_height="180dp"
android:background="@drawable/pic_top2" />
<TextView
android:id="@+id/register_tip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="70dp"
android:text="@string/welcomeregister"
android:textSize="25sp" />
<EditText
android:id="@+id/username_register"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@id/top_register"
android:layout_marginLeft="35dp"
android:layout_marginTop="20dp"
android:layout_marginRight="35dp"
android:background="@drawable/input_line1"
android:hint="@string/login_tip1"
android:inputType="text"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textSize="18sp"
tools:ignore="autofill" />
<EditText
android:id="@+id/password_register"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@id/username_register"
android:layout_marginLeft="35dp"
android:layout_marginTop="10dp"
android:layout_marginRight="35dp"
android:background="@drawable/input_line1"
android:hint="@string/register_tip1"
android:inputType="textPassword"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textSize="18sp"
tools:ignore="autofill" />
<Button
android:id="@+id/registerButton"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@id/password_register"
android:layout_marginLeft="35dp"
android:layout_marginTop="15dp"
android:layout_marginRight="35dp"
android:background="@drawable/start_button"
android:text="@string/register_tip5"
android:textColor="@color/white" />
<!-- <RadioButton-->
<!-- android:id="@+id/rbt_register"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_below="@id/registerButton"-->
<!-- android:layout_marginStart="25dp"-->
<!-- android:layout_marginLeft="25dp"-->
<!-- android:layout_marginTop="10dp"-->
<!-- android:checked="false"-->
<!-- android:text="@string/login_tip5"-->
<!-- android:textSize="14sp" />-->
<TextView
android:id="@+id/txt_2"
android:layout_width="match_parent"
android:layout_height="25dp"
android:layout_below="@id/registerButton"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_marginLeft="65dp"
android:layout_marginTop="145dp"
android:layout_marginRight="65dp"
android:gravity="center"
android:text="@string/login_tip4"
android:textSize="14sp" />
<Button
android:id="@+id/bt_wechat"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_below="@id/txt_2"
android:layout_marginStart="65dp"
android:layout_marginLeft="65dp"
android:layout_marginTop="30dp"
android:background="@drawable/logo_login4" />
<Button
android:id="@+id/bt_QQ"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_below="@id/txt_2"
android:layout_marginStart="55dp"
android:layout_marginLeft="55dp"
android:layout_marginTop="30dp"
android:layout_toEndOf="@id/bt_wechat"
android:layout_toRightOf="@id/bt_wechat"
android:background="@drawable/logo_login5" />
<Button
android:id="@+id/bt_weibo"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_below="@id/txt_2"
android:layout_toEndOf="@id/bt_QQ"
android:layout_marginStart="55dp"
android:layout_marginTop="30dp"
android:background="@drawable/logo_login1"
android:layout_toRightOf="@id/bt_QQ"
android:layout_marginLeft="55dp" />
</RelativeLayout>
activity_start_view.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"
tools:context="com.example.android_snake.ChooseActivity"
android:orientation="vertical"
android:background="@color/white"
>
<TextView
android:id="@+id/top_1"
android:layout_width="match_parent"
android:layout_height="180dp"
android:background="@drawable/pic_top2" />
<TextView
android:id="@+id/txt_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="70dp"
android:text="Game player\n贪吃蛇小游戏"
android:textSize="40sp" />
<Button
android:id="@+id/star_game"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="50dp"
android:layout_marginTop="100dp"
android:layout_marginRight="50dp"
android:background="@drawable/start_button"
android:text="@string/stargame" />
<Button
android:id="@+id/MostBUtton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:layout_marginTop="10dp"
android:layout_marginRight="50dp"
android:background="@drawable/start_button"
android:text="@string/historyscore" />
<Button
android:id="@+id/BackButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/back"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:layout_marginTop="10dp"
android:background="@drawable/start_button"/>
</LinearLayout>
个人体会及存在的问题
本次课程设计目的在于使用java语言设计一款手机贪吃蛇小游戏,为了达到最好的用户体验效果,我学习使用Android Studio进行项目开发,在项目开发过程中,学到了很多新的知识与技能,切实体会到项目开发的完整流程,并在此过程中不断增加需求、完善功能、优化结构,从最开始的游戏逻辑到用户界面一步步进行完善与修改,从数据库连接、文件操作到apk打包等种种操作,见证自己的第一个apk诞生的全过程,获得感颇丰。
下面例举一些项目开发中遇到的问题及解决方案:
一些资源文件引用、调用错误:如引入png图片文件时文件路径不能有中文,value中的颜色 定义文件colors.xml中定义了一个格式不支持的颜色;音频文件引用时的后缀名称问题,提示编码错误,改为.text后缀类型即可正常使用
一些代码逻辑需要学习巩固:如贪吃蛇游戏中的多线程处理、Android Studio中的SQLite连接及文件操作、布局xml文件的代码编写方法、布局与构件种类、SM4国密密码算法等。
一些小功能实现需要去学习:隐藏标题栏、隐藏手机状态栏、开机动画停留3秒、各个界面之间的自动跳转、获取手机屏幕宽度、音频导入与播放、APK打包、APK属性信息设置。
一些小bug需要一个个改正:如贪吃蛇初始化时头的方向若向左,初始运动方向设置为向右便会出场阵亡;食物的图标尺寸大于运动单元,则会出现小蛇看上去吃到食物实际上却并没有到达那个坐标;小蛇的初始位置太偏不容易调整初始进入游戏的状态;由于未在AndroidManifest.xml文件中注册界面的Activity,导致界面跳转时出现软件闪退情况;游戏菜单界面的三个按钮都加音效会导致软件闪退等等……
解决各种问题的过程正是自我提高的过程,弥足珍贵。
开发过程中遇到的这样各种各样的小问题还有很多,现已难以尽数列出……
GreedySnake这个程序虽然已经尽力去完善与改进了,但仍然存在一些小问题,如游戏界面中背景音乐播放使用了MediaPlayer类,按钮音效等短音频处理使用了SoundPool,而MediaPlayer仅支持同时播放一个音频,故播放背景音乐时按钮音效并不能体现……
错误和bug不让人感觉快乐,但程序员的乐趣就在于解决一个又一个报错,修改一个又一个bug。