求植物大战僵尸主题餐厅的安卓主题

这里使用安卓最基本的API实现双人四子棋游戏(无AI),开发语言为java,开发环境为Android Studio 2.1.2,目标SDK版本为24,最低为15;
界面采用植物大战僵尸主题,图片资源来源于网络,进行了PS加工,非原创;
游戏界面基本可以适配所有安卓手机分辨率,不过在分辨率太大或太小的手机上整体效果会有影响;
Github源码:
视频演示:
这里另外使用表格布局的方式又写了一个四子棋基础版本,逻辑思路和本文的也不一样了,表格布局虽然没有绝对布局灵活,但容易适配屏幕操作简单。
github源码:
1.游戏规则功能目标和界面预览
四子棋的游戏规则和五子棋不同,虽然都是横向纵向或对角线方向上有五个或四个相同的棋子即胜利,但四子棋落子有限制,必须要从棋盘底部往上堆叠,不是可以下在任意位置,就相当于将棋盘竖立起来一样,同时四子棋的棋盘是6x7宫格,横向7个,纵向6个,因此出现平局的现象很正常。
这里游戏首先有一个开始界面,点击开始界面的开始游戏按钮进入游戏界面,游戏界面需要实现提示玩家轮流下子,以及胜利和平局等状态。
2.UI界面布局设计
基本的静态界面使用xml来布局,开始界面整体使用绝对布局(为了方便实现小动画),放一张背景图片,开始按钮按照sin函数上下浮动,天空中几朵云按照不同的速度来回飘动:
游戏界面主要分三部分,头部,棋盘和底部,背景图片被PS分割成三张图片作为三部分的背景,三部分按照权重高度比例为1:5:2,这样棋盘高度占屏幕高度的5/8,手机屏幕宽高比最大一般就是10:16了,这种情况下棋盘高度刚好和宽度相等,而棋盘实际宽高为6:7,可以保证棋盘足以放下所有棋子了。布局上头部使用相对布局,主要放一些按钮;棋盘因为需要获取点击的坐标进行精确定位,所以要使用绝对布局;底部布局都可以只要将剩余的空间填满就好。
3.开始界面逻辑实现(动画)
为了实现开始界面的小动画,单独开一个线程,线程放一个while循环结合sleep函数制造一个update定时更新环境:
对于开始按钮,使其按照sin正弦函数上下摆动,通过振幅参数控制上下摆动的幅度,以及每次更新增长的步幅参数来控制上下摆动的速度;
乌云动画是在界面上布置三个ImageView,然后在每次更新中按照速度参数水平移动云彩的位置,通过速度参数控制云移动的速度和方向,当云超出屏幕足够远时将其重置回合适的位置,调整让三朵云的移动速度不同从而制造一种交错运动的自然效果。
4.游戏界面逻辑实现
1.棋盘的绘制更新
这里棋盘中棋子的更换采用图片资源替换的方式,即游戏初始化时将棋盘每个棋子的位置都放置一个ImageView,图片为一张透明图片,当需要放置棋子或者更换更换棋子时,只要给出指定坐标位置,然后根据该坐标对应的棋子状态替换相应的图片资源即可。
整个棋盘的数据模型是一个二维数组,数组元素的类型是一个自定义的数据结构(包含棋子状态值和对应ImageView的引用):
/*** 棋子节点数据结构 ***/
class Piece{
value = 0;
image = null;
void Reset(){
value = 0;
image.setImageResource(R.drawable.empty);
棋盘模型:
private Piece[][] ChessBord = null;
棋盘初始化时:
* 绘制棋盘
public void DrawChessBoard(){
for(int i = 0 ; i&V_NUM ; i++){
for (int j = 0 ; j&H_NUM ; j++){
ImageView view = new ImageView(this);
view.setImageResource(R.drawable.chess_bg);
view.setMaxHeight(CellWidth);
view.setMaxWidth(CellWidth);
view.setX(CellWidth*j+LEFT_GAP);
view.setY(CellWidth*i);
CBLayout.addView(view);
ImageView piece = new ImageView(this);
piece.setImageResource(R.drawable.empty);
piece.setMaxHeight(CellWidth);
piece.setMaxWidth(CellWidth);
piece.setX(CellWidth*j + LEFT_GAP);
piece.setY(CellWidth*(V_NUM-i-1));
ChessBord[j][i].image =
CBLayout.addView(piece);
更新棋子图片的方法:
ChessBord[point.x][point.y].image.setImageResource(R.drawable.chess_p1_win);
2.玩家下子事件
首先是获取在棋盘中玩家触点的坐标,坐标系原点在绝对布局的棋盘的左上角,根据坐标可以计算玩家选择的是哪一列:然后根据当前期盼状态可以定位到某个棋子,然后可以更新棋盘状态和棋盘界面:
CBLayout.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
float touchX = event.getX() - LEFT_GAP;
int column = (int)(touchX/CellWidth);
SetPiece(CalCoord(column));
return false;
3.判断是否有人赢
每当有人下棋时都要进行一次是否有人胜利检查,这里定义一个数据结构来记录以当前棋子位置为中心在横向、纵向、两个对角线四个方向上的相同棋子的个数,初始值都为1因为已经中心位置已经有一个棋子了,统计结束后如果四个方向上有数量大于4的则当前玩家胜利:
/*** 记录双方棋局状态数据结构 ***/
class CheckNode{
int H // 水平方向上相同棋子的个数
// 竖直方向...
// 主对角线...
// 辅对角线...
CheckNode(){
Horizontal = 1;
Vertical = 1;
MDiagonal = 1;
ADiagonal = 1;
void Reset(){
Horizontal = 1;
Vertical = 1;
MDiagonal = 1;
ADiagonal = 1;
具体统计过程通过一个循环即可实现,以当前点为中心向八个方向扩散,遇到对方棋子则停止,当八个方向都遇到对方棋子时循环终止:
public int CheckResult(Point point) {
int stopCount = 0;
int[] stopDir = new int[8];
int counter = 0;
while (stopCount & 8) {
stopCount = 0;
if ((stopDir[0]==1) || (point.y + counter &= V_NUM) || ChessBord[point.x][point.y + counter].value != gameResult) {
stopDir[0]=1;
checkNode[gameResult - 1].Vertical++;
if ((stopDir[1]==1) || (point.y - counter & 0) || ChessBord[point.x][point.y - counter].value != gameResult) {
stopDir[1]=1;
checkNode[gameResult - 1].Vertical++;
if ((stopDir[2]==1) || (point.x - counter & 0) || ChessBord[point.x - counter][point.y].value != gameResult) {
stopDir[2]=1;
checkNode[gameResult - 1].Horizontal++;
if ((stopDir[3]==1) || (point.x + counter &= H_NUM) || ChessBord[point.x + counter][point.y].value != gameResult) {
stopDir[3]=1;
checkNode[gameResult - 1].Horizontal++;
if ((stopDir[4]==1) || (point.x - counter & 0) || (point.y + counter &= V_NUM) || (ChessBord[point.x - counter][point.y + counter].value != gameResult)) {
stopDir[4]=1;
checkNode[gameResult - 1].ADiagonal++;
if ((stopDir[5]==1) || (point.x + counter &= H_NUM) || (point.y - counter & 0) || (ChessBord[point.x + counter][point.y - counter].value != gameResult)) {
stopDir[5]=1;
checkNode[gameResult - 1].ADiagonal++;
if ((stopDir[6]==1) || (point.x + counter &= H_NUM) || (point.y + counter &= V_NUM) || (ChessBord[point.x + counter][point.y + counter].value != gameResult)) {
stopDir[6]=1;
checkNode[gameResult - 1].MDiagonal++;
if ((stopDir[7]==1) || (point.x - counter & 0) || (point.y - counter & 0) || (ChessBord[point.x - counter][point.y - counter].value != gameResult)) {
stopDir[7]=1;
checkNode[gameResult - 1].MDiagonal++;
for(int i =0 ; i&8 ;i++){
stopCount += stopDir[i];
if (checkNode[gameResult-1].MDiagonal&=4 || checkNode[gameResult-1].ADiagonal&=4 || checkNode[gameResult-1].Horizontal&=4 || checkNode[gameResult-1].Vertical&=4){
return gameR
4.悔棋功能
悔棋的实现很简单,只要每次下棋都将所下的棋子的位置入栈即可,悔棋依次将节点从栈取出然后根据节点信息更新UI,更新棋盘数据等相关状态,当所有棋子都取出即栈为空时停止悔棋响应:
ArrayList&Point&PieceStack = null;
PieceStack = new ArrayList&Point&();
PieceStack.add(point);
* 悔棋一步
public void WithDraw(){
if (PieceStack == null || PieceStack.size() == 0)
Point point = PieceStack.remove(PieceStack.size()-1);
ChessBord[point.x][point.y].Reset();
if (gameResult == 1){
gameResult = 2;
UpdateUI(gameResult);
gameResult = 1;
UpdateUI(gameResult);
5.特殊显示获胜棋子功能
要特殊显示获胜棋子难点在于要找到同一个方向上个数大于4的棋子,来对其更换图片。当然可以在当时判断是否有人赢时将可能获胜的棋子保存起来,但每次下棋都要判断都要保存,因此操作冗余太大。这里是在确定有人赢之后再根据计算得到的各方向相同棋子个数重新搜索大于4个棋子的行列来确定获胜的棋子:
public void SearchWonPieces(Point point){
// 判断是哪个玩家赢了
int winimg = 0
if (gameResult == 1)
winimg = R.drawable.chess_p1_win
else if (gameResult == 2)
winimg = R.drawable.chess_p2_win
System.out.print("error!")
// 当前下的一个棋子直接特殊显示
ChessBord[point.x][point.y].image.setImageResource(winimg)
// 某个方向上最多的相同棋子个数肯定不会比最大宽度或者高度棋子数多
int maxStep = H_NUM & V_NUM ? H_NUM : V_NUM
for (int i=1
if (checkNode[gameResult-1].Vertical &= 4 && (point.y + i & V_NUM) && ChessBord[point.x][point.y + i].value == gameResult) {
// 满足不越棋盘的界,并且是当前获胜玩家的棋子
UpdateWinImage(new Point(point.x, point.y+i), new Point(point.x, point.y+i-1), winimg)
if (checkNode[gameResult-1].Vertical &= 4 && (point.y - i &= 0) && ChessBord[point.x][point.y - i].value == gameResult) {
UpdateWinImage(new Point(point.x, point.y-i), new Point(point.x, point.y-i+1), winimg)
if (checkNode[gameResult-1].Horizontal &= 4 && (point.x - i &= 0) && ChessBord[point.x - i][point.y].value == gameResult) {
UpdateWinImage(new Point(point.x-i, point.y), new Point(point.x-i+1, point.y), winimg)
if (checkNode[gameResult-1].Horizontal &= 4 && (point.x + i & H_NUM) && ChessBord[point.x + i][point.y].value == gameResult) {
UpdateWinImage(new Point(point.x+i, point.y), new Point(point.x+i-1, point.y), winimg)
if (checkNode[gameResult-1].ADiagonal &= 4 && (point.x - i &= 0) && (point.y + i & V_NUM) && (ChessBord[point.x - i][point.y + i].value == gameResult)) {
UpdateWinImage(new Point(point.x-i, point.y+i), new Point(point.x-i+1, point.y+i-1), winimg)
if (checkNode[gameResult-1].ADiagonal &= 4 && (point.x + i & H_NUM) && (point.y - i &= 0) && (ChessBord[point.x + i][point.y - i].value == gameResult)) {
UpdateWinImage(new Point(point.x+i, point.y-i), new Point(point.x+i-1, point.y-i+1), winimg)
if (checkNode[gameResult-1].MDiagonal &= 4 && (point.x + i & H_NUM) && (point.y + i & V_NUM) && (ChessBord[point.x + i][point.y + i].value == gameResult)) {
UpdateWinImage(new Point(point.x+i, point.y+i), new Point(point.x+i-1, point.y+i-1), winimg)
if (checkNode[gameResult-1].MDiagonal &= 4 && (point.x - i &= 0) && (point.y - i &= 0) && (ChessBord[point.x - i][point.y - i].value == gameResult)) {
UpdateWinImage(new Point(point.x-i, point.y-i), new Point(point.x-i+1, point.y-i+1), winimg)
public void UpdateWinImage(Point curPoint, Point prePoint, int winimg){
if (ChessBord[curPoint.x][curPoint.y].value == ChessBord[prePoint.x][prePoint.y].value){
ChessBord[curPoint.x][curPoint.y].image.setImageResource(winimg)
// 出现不连续获胜棋子则将这一排剩下的所有获胜玩家的棋子全部排除掉(事实上顶多出现两个不连续的获胜玩家棋子,因为H_NUM为7)
int delX = curPoint.x - prePoint.x
int delY = curPoint.y - prePoint.y
Point nextPoint = new Point(curPoint.x+delX, curPoint.y+delY)
while (nextPoint.x&=0 && nextPoint.x&H_NUM && nextPoint.y&=0 && nextPoint.y&V_NUM){
ChessBord[nextPoint.x][nextPoint.y].value = 0
nextPoint.x += delX
nextPoint.y += delY
5.声音特效的添加(MediaPlayer & SoundPool)
安卓中基本的声音组件是MediaPlayer,通过create函数初始化之后就可以调用start等函数很简单的控制声音播放暂停等等,另外可以为MediaPlayer对象添加***事件,***试音播放结束从而进行其他操作,比如这里开始界面在点击开始游戏按钮后播放僵尸笑的音效,然后需要在声音播放结束后再进入游戏界面,这就需要用到这个***了,具体用法示例如下:
MediaPlayer media_start = MediaPlayer.create(this, R.raw.zombiesmile);
media_start.start();
media_start.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
另外可以通过***播放结束实现背景音乐的循环播放,即在播放结束时再调用start函数重新播放该音乐。
MediaPlayer虽然容易操作,但只能管理一个音乐,延迟较大,不支持多个音乐同时播放,且消耗资源较大,对于游戏中反复使用的多个短促音效通常使用SoundPool来进行管理操作,这里游戏界面用到的音效都使用SoundPool来管理了,背景音乐仍然采用MediaPlayer单独管理,具体用法如下:
// 定义音效池。第一个参数是管理的音乐片段的最大数量,系统会根据这个参数分配适当的缓存空间;第二个参数表示将音效定义为系统音效;最后一个参数是音效的品质,品质越高相应对系统消耗越大
SoundPool soundPool = new SoundPool(4, AudioManager.STREAM_SYSTEM,5)
// 这里依次添加四段音效,最后一个参数指的是播放冲突时的播放优先级,加载的先后顺序决定了每段音效的ID号(从1开始),之后播放是通过ID判断播放哪一段音效
soundPool.load(this, R.raw.game_bg, 1)
soundPool.load(this, R.raw.peng, 2)
soundPool.load(this, R.raw.dingdong, 3)
soundPool.load(this, R.raw.dingdong, 4)
// 播放:第一个参数是音效的ID,也就是第4个加载的音效(平局音效);第2和3个参数分别表示左右声道的音量;第四个参数表示播放优先级,数值越大优先级越高;第五个参数表示是否循环播放,0为不循环,-1为循环;最后一个参数指定播放的比率,数值可从0.5到2, 1为正常比率
soundPool.play(4,1,1,0,0,1)
6.简单游戏数据的存储
这里简单存储两个玩家各自获胜的次数两个数字,使用sharedpreferance保存维护两个键值对,sharedpreferance的基本用法如下,其中获取的时候第二个参数是在数据不存在时的默认值,正好实现当第一次获取时两个键值对还不存在,返回默认的0同时创建了该键值对,然后进行存储以及后面的数据获取,也就是默认的0有且只有使用到依次来进行初始化:
* 更新数据
public void setData(int player){
SharedPreferences sharedPreferences = getSharedPreferences(PREFS_NAME,0);
SharedPreferences.Editor editor = sharedPreferences.edit();
int dataP1 = sharedPreferences.getInt("data_player1",0);
int dataP2 = sharedPreferences.getInt("data_player2",0);
if (player == 1){
editor.putInt("data_player1", dataP1+1);
}else if (player == 2){
editor.putInt("data_player2", dataP2+1);
7.java源码预览
MainActivity.java
package com.example.albeeert.
import android.app.D
import android.content.I
import android.content.SharedP
import android.media.MediaP
import android.os.B
import android.support.v7.app.AppCompatA
import android.util.DisplayM
import android.view.LayoutI
import android.view.V
import android.widget.B
import android.widget.ImageV
import android.widget.TextV
public class MainActivity extends AppCompatActivity {
public final String PREFS_NAME = "JXHFile";
private ImageView cloud1 = null;
private ImageView cloud2 = null;
private ImageView cloud3 = null;
private final float cloudspeed1 = 8.0f;
private final float cloudspeed2 = 15.0f;
private final float cloudspeed3 = 12.0f;
private ImageView ready = null;
private Button startGameBtn = null;
private Button dataButton = null;
private View dialogV
private final float range = 10.0f;
private final int Interval = 100;
private double sinAngle = 0;
private float initY = 0;
private float ScreenW = 0;
private float ScreenH = 0;
private MyThread animationThread = null;
private boolean isActive = true;
private static MediaPlayer media_
private static MediaPlayer media_
private static MediaPlayer media_
* Activity life cycle
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AudioInit();
setListener();
animationThread = new MyThread();
new Thread(animationThread).start();
protected void onPause() {
super.onPause();
media_bg.pause();
ready.setVisibility(View.INVISIBLE);
isActive = false;
protected void onRestart() {
super.onRestart();
isActive = true;
media_bg.start();
new Thread(animationThread).start();
public void onBackPressed(){
System.exit(0);
界面初始化
public void UIInit(){
setContentView(R.layout.activity_main);
startGameBtn = (Button)findViewById(R.id.startbutton);
dataButton = (Button)findViewById(R.id.databutton);
cloud1 = (ImageView) findViewById(R.id.cloud1);
cloud2 = (ImageView) findViewById(R.id.cloud2);
cloud3 = (ImageView) findViewById(R.id.cloud3);
ready = (ImageView) findViewById(R.id.start_ready);
dialogView= LayoutInflater.from(this).inflate(
R.layout.data_dialog, null);
dialog = new Dialog(MainActivity.this);
dialog.setTitle("Player Data");
dialog.setContentView(dialogView);
dialog.setCanceledOnTouchOutside(true);
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
ScreenH = dm.heightP
ScreenW = dm.widthP
initY = ScreenH*2/3;
* 音效设置
public void AudioInit(){
media_bg = MediaPlayer.create(this, R.raw.menu_bg);
media_bg.start();
media_bg.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
media_bg.start();
media_start = MediaPlayer.create(this, R.raw.zombiesmile);
media_start.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
Intent intent = new Intent(getBaseContext(),GameActivity.class);
startActivity(intent);
media_button = MediaPlayer.create(this, R.raw.yoho);
* 事件***
public void setListener(){
startGameBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
media_start.start();
ready.setVisibility(View.VISIBLE);
isActive = false;
dataButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
media_button.start();
SharedPreferences sharedPreferences = getSharedPreferences(PREFS_NAME,0);
int dataP1 = sharedPreferences.getInt("data_player1",0);
int dataP2 = sharedPreferences.getInt("data_player2",0);
TextView player1DataShow = (TextView) dialogView.findViewById(R.id.player1datashow);
TextView player2DataShow = (TextView) dialogView.findViewById(R.id.player2datashow);
player1DataShow.setText(String.valueOf(dataP1));
player2DataShow.setText(String.valueOf(dataP2));
dialog.show();
class MyThread implements Runnable {
public void run() {
while (isActive) {
Thread.sleep(Interval);
sinAngle += 0.3f;
startGameBtn.setY(initY + (float)(range*Math.sin(sinAngle)));
dataButton.setX((float)(range/3*Math.sin(sinAngle)));
if (cloud1.getX() & -500)
cloud1.setX(ScreenW + 10.0f);
cloud1.setX(cloud1.getX() - cloudspeed1);
if (cloud2.getX() & ScreenW)
cloud2.setX(-1000.0f);
cloud2.setX(cloud2.getX()+cloudspeed2);
if (cloud3.getX() & ScreenW)
cloud3.setX(-1200.0f);
cloud3.setX(cloud3.getX()+cloudspeed3);
}catch (Exception e) {
e.printStackTrace();
System.out.print("thread error!");
GameActivity.java
package com.example.albeeert.
import android.content.SharedP
import android.graphics.P
import android.media.AudioM
import android.media.MediaP
import android.media.SoundP
import android.os.B
import android.support.v7.app.AppCompatA
import android.util.DisplayM
import android.view.MotionE
import android.view.V
import android.widget.AbsoluteL
import android.widget.B
import android.widget.ImageV
import android.widget.TextV
import java.util.ArrayL
/*** 1.记录双方棋局状态数据结构 ***/
class CheckNode{
CheckNode(){
Horizontal = 1;
Vertical = 1;
MDiagonal = 1;
ADiagonal = 1;
void Reset(){
Horizontal = 1;
Vertical = 1;
MDiagonal = 1;
ADiagonal = 1;
/*** 2.棋子节点数据结构 ***/
class Piece{
value = 0;
image = null;
void Reset(){
value = 0;
image.setImageResource(R.drawable.empty);
/*** 游戏界面 ***/
public class GameActivity extends AppCompatActivity {
public final String PREFS_NAME = "JXHFile";
public static final int H_NUM = 7;
public static final int V_NUM = 6;
private int LEFT_GAP = 10;
private float CBWidth = 0;
private int CellWidth = 0;
private Button ReStartButton = null;
private Button WithDrawButton = null;
private TextView StatusTop = null;
private TextView StatusLeft = null;
private TextView StatusRight = null;
private ImageView IconLeft = null;
private ImageView IconRight = null;
private AbsoluteLayout CBLayout = null;
private CheckNode[] checkNode = null;
private int gameResult = 0;
private Piece[][] ChessBord = null;
ArrayList&Point&PieceStack = null;
private int pieceCount = 0;
private SoundPool soundPool = null;
private MediaPlayer media_bg = null;
* 游戏界面初始化入口
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
ActivityInit();
GameInit();
* 返回键***
public void onBackPressed(){
media_bg.stop();
this.finish();
* 全局初始化(只初始化一次)
public void ActivityInit(){
ReStartButton = (Button)findViewById(R.id.button_restart);
WithDrawButton = (Button)findViewById(R.id.button_withdraw);
StatusTop = (TextView)findViewById(R.id.topStatus);
StatusLeft = (TextView)findViewById(R.id.status_left);
StatusRight = (TextView)findViewById(R.id.status_right);
IconLeft = (ImageView)findViewById(R.id.icon_left);
IconRight = (ImageView)findViewById(R.id.icon_right);
CBLayout = (AbsoluteLayout) findViewById(R.id.layout_chessboard);
soundPool = new SoundPool(4, AudioManager.STREAM_SYSTEM,5);
soundPool.load(this, R.raw.game_bg, 1);
soundPool.load(this, R.raw.peng, 2);
soundPool.load(this, R.raw.dingdong, 3);
soundPool.load(this, R.raw.dingdong, 4);
media_bg = MediaPlayer.create(this, R.raw.game_bg);
media_bg.start();
media_bg.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
media_bg.start();
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int ScreenW = dm.widthP
CBWidth = ScreenW - 2*LEFT_GAP;
CellWidth = (int)(CBWidth/H_NUM);
ChessBord = new Piece[H_NUM][V_NUM];
for (int h=0; h&H_NUM; h++){
for (int v=0; v&V_NUM; v++){
ChessBord[h][v] = new Piece();
checkNode = new CheckNode[2];
checkNode[0] = new CheckNode();
checkNode[1] = new CheckNode();
DrawChessBoard();
ReStartButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
RestartGame();
WithDrawButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
WithDraw();
* 游戏初始化(每次重新游戏都要初始化)
public void GameInit(){
CBLayout.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
float touchX = event.getX() - LEFT_GAP;
int column = (int)(touchX/CellWidth);
SetPiece(CalCoord(column));
return false;
gameResult = 1;
UpdateUI(1);
StatusTop.setText("");
pieceCount = 0;
PieceStack = new ArrayList&Point&();
* 游戏结束
public void GameOver(){
CBLayout.setOnTouchListener(null);
PieceStack = null;
* 重新开始游戏
public void RestartGame(){
for (int h=0; h&H_NUM; h++){
for (int v=0; v&V_NUM; v++){
ChessBord[h][v].Reset();
GameInit();
* 绘制棋盘
public void DrawChessBoard(){
for(int i = 0 ; i&V_NUM ; i++){
for (int j = 0 ; j&H_NUM ; j++){
ImageView view = new ImageView(this);
view.setImageResource(R.drawable.chess_bg);
view.setMaxHeight(CellWidth);
view.setMaxWidth(CellWidth);
view.setX(CellWidth*j+LEFT_GAP);
view.setY(CellWidth*i);
CBLayout.addView(view);
ImageView piece = new ImageView(this);
piece.setImageResource(R.drawable.empty);
piece.setMaxHeight(CellWidth);
piece.setMaxWidth(CellWidth);
piece.setX(CellWidth*j + LEFT_GAP);
piece.setY(CellWidth*(V_NUM-i-1));
ChessBord[j][i].image =
CBLayout.addView(piece);
* 计算落子坐标
public Point CalCoord(int column){
if (column &= H_NUM)
column = H_NUM-1;
for (int i = 0 ; i&V_NUM ; i++){
if (ChessBord[column][i].value == 0){
Point point = new Point(column,i);
return null;
* 落子并计算更新游戏状态
public void SetPiece(Point point){
if (point == null)
PieceStack.add(point);
DrawPiece(point);
ChessBord[point.x][point.y].value = gameR
if (pieceCount == V_NUM*H_NUM){
UpdateUI(0);
GameOver();
int result = CheckResult(point);
if (result == 0){
if (gameResult == 1){
gameResult = 2;
UpdateUI(gameResult);
gameResult = 1;
UpdateUI(gameResult);
/** 已经有人赢了 **/
SearchWonPieces(point);
setData(gameResult);
if (gameResult == 1){
UpdateUI(11);
UpdateUI(22);
GameOver();
checkNode[0].Reset();
checkNode[1].Reset();
* 显示获胜的棋子
public void SearchWonPieces(Point point){
int winimg = 0;
if (gameResult == 1)
winimg = R.drawable.chess_p1_
else if (gameResult == 2)
winimg = R.drawable.chess_p2_
System.out.print("error!");
ChessBord[point.x][point.y].image.setImageResource(winimg);
int maxStep = H_NUM & V_NUM ? H_NUM : V_NUM;
for (int i=1 ; i&maxS i++){
if (checkNode[gameResult-1].Vertical &= 4 && (point.y + i & V_NUM) && ChessBord[point.x][point.y + i].value == gameResult) {
UpdateWinImage(new Point(point.x, point.y+i), new Point(point.x, point.y+i-1), winimg);
if (checkNode[gameResult-1].Vertical &= 4 && (point.y - i &= 0) && ChessBord[point.x][point.y - i].value == gameResult) {
UpdateWinImage(new Point(point.x, point.y-i), new Point(point.x, point.y-i+1), winimg);
if (checkNode[gameResult-1].Horizontal &= 4 && (point.x - i &= 0) && ChessBord[point.x - i][point.y].value == gameResult) {
UpdateWinImage(new Point(point.x-i, point.y), new Point(point.x-i+1, point.y), winimg);
if (checkNode[gameResult-1].Horizontal &= 4 && (point.x + i & H_NUM) && ChessBord[point.x + i][point.y].value == gameResult) {
UpdateWinImage(new Point(point.x+i, point.y), new Point(point.x+i-1, point.y), winimg);
if (checkNode[gameResult-1].ADiagonal &= 4 && (point.x - i &= 0) && (point.y + i & V_NUM) && (ChessBord[point.x - i][point.y + i].value == gameResult)) {
UpdateWinImage(new Point(point.x-i, point.y+i), new Point(point.x-i+1, point.y+i-1), winimg);
if (checkNode[gameResult-1].ADiagonal &= 4 && (point.x + i & H_NUM) && (point.y - i &= 0) && (ChessBord[point.x + i][point.y - i].value == gameResult)) {
UpdateWinImage(new Point(point.x+i, point.y-i), new Point(point.x+i-1, point.y-i+1), winimg);
if (checkNode[gameResult-1].MDiagonal &= 4 && (point.x + i & H_NUM) && (point.y + i & V_NUM) && (ChessBord[point.x + i][point.y + i].value == gameResult)) {
UpdateWinImage(new Point(point.x+i, point.y+i), new Point(point.x+i-1, point.y+i-1), winimg);
if (checkNode[gameResult-1].MDiagonal &= 4 && (point.x - i &= 0) && (point.y - i &= 0) && (ChessBord[point.x - i][point.y - i].value == gameResult)) {
UpdateWinImage(new Point(point.x-i, point.y-i), new Point(point.x-i+1, point.y-i+1), winimg);
* 替换获胜棋子图片(这里要同时检验当前获胜棋子是否和上一个连续,如果中间有对方棋子间隔那么当前这个获胜玩家的棋子并不是真的获胜棋子)
public void UpdateWinImage(Point curPoint, Point prePoint, int winimg){
if (ChessBord[curPoint.x][curPoint.y].value == ChessBord[prePoint.x][prePoint.y].value){
ChessBord[curPoint.x][curPoint.y].image.setImageResource(winimg);
int delX = curPoint.x - prePoint.x;
int delY = curPoint.y - prePoint.y;
Point nextPoint = new Point(curPoint.x+delX, curPoint.y+delY);
while (nextPoint.x&=0 && nextPoint.x&H_NUM && nextPoint.y&=0 && nextPoint.y&V_NUM){
ChessBord[nextPoint.x][nextPoint.y].value = 0;
nextPoint.x += delX;
nextPoint.y += delY;
* 更新UI显示状态
public void UpdateUI(int gameResult){
if (gameResult == 0){
StatusTop.setText("Draw");
StatusLeft.setText("");
StatusRight.setText("");
IconLeft.setImageResource(R.drawable.chess_p1_win);
IconRight.setImageResource(R.drawable.chess_p2_win);
soundPool.play(4,1,1,0,0,1);
}else if (gameResult == 1){
StatusLeft.setText("
StatusRight.setText("");
IconLeft.setImageResource(R.drawable.chess_p1_win);
IconRight.setImageResource(R.drawable.chess_p2);
}else if (gameResult == 2){
StatusLeft.setText("");
StatusRight.setText("!
IconLeft.setImageResource(R.drawable.chess_p1);
IconRight.setImageResource(R.drawable.chess_p2_win);
}else if(gameResult == 11){
StatusLeft.setText("Win!");
StatusRight.setText("");
soundPool.play(3,1,1,2,0,1);
}else if (gameResult == 22){
StatusLeft.setText("");
StatusRight.setText("Win!");
soundPool.play(3,1,1,2,0,1);
* 绘制指定棋子
public void DrawPiece(Point point){
soundPool.play(2,1,1,1,0,1);
ImageView piece = ChessBord[point.x][point.y].
if (gameResult == 1){
piece.setImageResource(R.drawable.chess_p1);
piece.setImageResource(R.drawable.chess_p2);
* 检查某个棋子处各个方向上相同棋子的个数,用于判断游戏胜负(返回0表示游戏继续,1表示玩家1赢,2表示玩家2赢)
public int CheckResult(Point point) {
int stopCount = 0;
int[] stopDir = new int[8];
int counter = 0;
while (stopCount & 8) {
stopCount = 0;
if ((stopDir[0]==1) || (point.y + counter &= V_NUM) || ChessBord[point.x][point.y + counter].value != gameResult) {
stopDir[0]=1;
checkNode[gameResult - 1].Vertical++;
if ((stopDir[1]==1) || (point.y - counter & 0) || ChessBord[point.x][point.y - counter].value != gameResult) {
stopDir[1]=1;
checkNode[gameResult - 1].Vertical++;
if ((stopDir[2]==1) || (point.x - counter & 0) || ChessBord[point.x - counter][point.y].value != gameResult) {
stopDir[2]=1;
checkNode[gameResult - 1].Horizontal++;
if ((stopDir[3]==1) || (point.x + counter &= H_NUM) || ChessBord[point.x + counter][point.y].value != gameResult) {
stopDir[3]=1;
checkNode[gameResult - 1].Horizontal++;
if ((stopDir[4]==1) || (point.x - counter & 0) || (point.y + counter &= V_NUM) || (ChessBord[point.x - counter][point.y + counter].value != gameResult)) {
stopDir[4]=1;
checkNode[gameResult - 1].ADiagonal++;
if ((stopDir[5]==1) || (point.x + counter &= H_NUM) || (point.y - counter & 0) || (ChessBord[point.x + counter][point.y - counter].value != gameResult)) {
stopDir[5]=1;
checkNode[gameResult - 1].ADiagonal++;
if ((stopDir[6]==1) || (point.x + counter &= H_NUM) || (point.y + counter &= V_NUM) || (ChessBord[point.x + counter][point.y + counter].value != gameResult)) {
stopDir[6]=1;
checkNode[gameResult - 1].MDiagonal++;
if ((stopDir[7]==1) || (point.x - counter & 0) || (point.y - counter & 0) || (ChessBord[point.x - counter][point.y - counter].value != gameResult)) {
stopDir[7]=1;
checkNode[gameResult - 1].MDiagonal++;
for(int i =0 ; i&8 ;i++){
stopCount += stopDir[i];
if (checkNode[gameResult-1].MDiagonal&=4 || checkNode[gameResult-1].ADiagonal&=4 || checkNode[gameResult-1].Horizontal&=4 || checkNode[gameResult-1].Vertical&=4){
return gameR
* 悔棋一步
public void WithDraw(){
if (PieceStack == null || PieceStack.size() == 0)
Point point = PieceStack.remove(PieceStack.size()-1);
ChessBord[point.x][point.y].Reset();
if (gameResult == 1){
gameResult = 2;
UpdateUI(gameResult);
gameResult = 1;
UpdateUI(gameResult);
* 更新数据
public void setData(int player){
SharedPreferences sharedPreferences = getSharedPreferences(PREFS_NAME,0);
SharedPreferences.Editor editor = sharedPreferences.edit();
int dataP1 = sharedPreferences.getInt("data_player1",0);
int dataP2 = sharedPreferences.getInt("data_player2",0);
if (player == 1){
editor.putInt("data_player1", dataP1+1);
}else if (player == 2){
editor.putInt("data_player2", dataP2+1);
orz……有问题吐槽请留言,帮到您烦请帮顶一下,谢谢^_^
本文已收录于以下专栏:
相关文章推荐
中文版:一步步学OpenGL3.3+
英文版:OpenGL Step by Step
源码下载地址:here
windows下的环境配置
以VS2013为例
1.下载源码,解压
/thread-.html
现在,我们来到了这个教程的第5个阶段。
在这个阶段,我们会修改一个漏洞,这个漏洞允许植物开火只要有僵尸与植物处于...
上期已经和大家讲解了做游戏的前提要求,今天要给大家讲述的是游戏界面如何绘制。
一:如何获取到surface:
        (1)首先我们需要获取一个surface,我们先通过s...
现在,我们来到了这个教程的第5个阶段。
在这个阶段,我们会修改一个漏洞,这个漏洞允许植物开火只要有僵尸与植物处于同一行,不论僵尸是在植物的左边还是右边。同时我们也会增加僵尸攻击植物的功能。
他的最新文章
讲师:王哲涵
讲师:王渊命
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

参考资料

 

随机推荐