贪吃蛇游戏是一款比较经典的休闲游戏,文章包括项目的部分源码以及运行界面的一些图片,项目也参考学习了许多大佬的文章,会在文章最后面贴出参考文章,有什么问题也请大家指正。
一、游戏界面及游戏简介
说明:点击【START】开始游戏,点击【UP】【DOWN】【LEFT】【RIGHT】分别实现蛇头向【上】【下】【左】【右】方向移动,若蛇头位置超过边界则从另一侧穿出,若蛇头碰到随机生成的食物(及蓝色小方块)则蛇长增加;若蛇头碰撞到自己身上的部位,则视为蛇死亡,游戏结束!
二、游戏界面的布局xml
此布局使用的是ConstraintLayout约束性布局,实现基础游戏界面布局
代码如下
<?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="com.example.snack.MainActivity">
<com.example.snack.SnakePanelView
android:id="@+id/snake_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/top_btn"
android:layout_width="75dp"
android:layout_height="45dp"
android:layout_marginTop="28dp"
android:background="@drawable/direction_btn_background"
android:text="UP"
android:textColor="@android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.532"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/snake_view" />
<Button
android:id="@+id/left_btn"
android:layout_width="75dp"
android:layout_height="45dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:background="@drawable/direction_btn_background"
android:text="LEFT"
android:textColor="@android:color/white"
app:layout_constraintEnd_toStartOf="@+id/top_btn"
app:layout_constraintHorizontal_bias="0.659"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/top_btn" />
<Button
android:id="@+id/right_btn"
android:layout_width="75dp"
android:layout_height="45dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:background="@drawable/direction_btn_background"
android:text="RIGHT"
android:textColor="@android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.565"
app:layout_constraintStart_toEndOf="@+id/top_btn"
app:layout_constraintTop_toTopOf="@+id/left_btn" />
<Button
android:id="@+id/bottom_btn"
android:layout_width="75dp"
android:layout_height="45dp"
android:layout_marginBottom="24dp"
android:background="@drawable/direction_btn_background"
android:text="DOWN"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.532"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/top_btn"
app:layout_constraintVertical_bias="0.334" />
<Button
android:id="@+id/start_btn"
android:layout_width="75dp"
android:layout_height="45dp"
android:layout_marginStart="36dp"
android:layout_marginTop="8dp"
android:background="@drawable/restart_btn_background"
android:text="START"
android:textColor="@android:color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/snake_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
三、生成食物
随机生成,但不能生成在蛇身上。食物(蓝色小方块)是通过random产生随机坐标,存放到食物的Arraylist中,然后调用父类的方法将图片画在view上。
//生成food
private void generateFood() {
Random random = new Random();
int foodX = random.nextInt(mGridSize - 1);
int foodY = random.nextInt(mGridSize - 1);
for (int i = 0; i < mSnakePositions.size() - 1; i++) {
if (foodX == mSnakePositions.get(i).getX() && foodY == mSnakePositions.get(i).getY()) {
foodX = random.nextInt(mGridSize - 1);
foodY = random.nextInt(mGridSize - 1);
//如果生成在蛇身上则重新循环
i = 0;
}
}
mFoodPosition.setX(foodX);
mFoodPosition.setY(foodY);
refreshFood(mFoodPosition);
}
四、蛇的移动
蛇的移动是通过界面的重新绘制实现的,点击方向键就改变蛇头的坐标,往某一方向进行x坐标或y坐标的增加。通过遍历食物和障碍物的Arraylist进行检测是否吃到食物或撞到障碍物,如果超过边界坐标,则从另一边穿出。
private void moveSnake(int snakeDirection) {
switch (snakeDirection) {
case GameType.LEFT:
if (mSnakeHeader.getX() - 1 < 0) {//边界判断:如果到了最左边 让他穿过屏幕到最右边
mSnakeHeader.setX(mGridSize - 1);
} else {
mSnakeHeader.setX(mSnakeHeader.getX() - 1);
}
mSnakePositions.add(new GridPosition(mSnakeHeader.getX(), mSnakeHeader.getY()));
break;
case GameType.TOP:
if (mSnakeHeader.getY() - 1 < 0) {
mSnakeHeader.setY(mGridSize - 1);
} else {
mSnakeHeader.setY(mSnakeHeader.getY() - 1);
}
mSnakePositions.add(new GridPosition(mSnakeHeader.getX(), mSnakeHeader.getY()));
break;
case GameType.RIGHT:
if (mSnakeHeader.getX() + 1 >= mGridSize) {
mSnakeHeader.setX(0);
} else {
mSnakeHeader.setX(mSnakeHeader.getX() + 1);
}
mSnakePositions.add(new GridPosition(mSnakeHeader.getX(), mSnakeHeader.getY()));
break;
case GameType.BOTTOM:
if (mSnakeHeader.getY() + 1 >= mGridSize) {
mSnakeHeader.setY(0);
} else {
mSnakeHeader.setY(mSnakeHeader.getY() + 1);
}
mSnakePositions.add(new GridPosition(mSnakeHeader.getX(), mSnakeHeader.getY()));
break;
}
}
五、判断碰撞(生或死)
通过遍历食物和障碍物的Arraylist进行检测是否吃到食物或撞到自己,若撞到自己则游戏结束。
private void checkCollision() {
//检测是否咬到自己
GridPosition headerPosition = mSnakePositions.get(mSnakePositions.size() - 1);
for (int i = 0; i < mSnakePositions.size() - 2; i++) {
GridPosition position = mSnakePositions.get(i);
if (headerPosition.getX() == position.getX() && headerPosition.getY() == position.getY()) {
//咬到自己 停止游戏
mIsEndGame = true;
showMessageDialog();
return;
}
}
//判断是否吃到食物,吃到食物蛇长加一,游戏继续
if (headerPosition.getX() == mFoodPosition.getX()
&& headerPosition.getY() == mFoodPosition.getY()) {
mSnakeLength++;
generateFood();
}
}
//结束游戏的弹窗
private void showMessageDialog() {
post(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(getContext()).setMessage("Game " + "Over!")
.setCancelable(false)
.setPositiveButton("重新开始", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
reStartGame();
}
})
.setNegativeButton("退出", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.create()
.show();
}
});
}
六、Mainactivity
在窗体中设置一些按键的监听
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private SnakePanelView mSnakePanelView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSnakePanelView = findViewById(R.id.snake_view);
findViewById(R.id.left_btn).setOnClickListener(this);
findViewById(R.id.right_btn).setOnClickListener(this);
findViewById(R.id.top_btn).setOnClickListener(this);
findViewById(R.id.bottom_btn).setOnClickListener(this);
findViewById(R.id.start_btn).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.left_btn:
mSnakePanelView.setSnakeDirection(GameType.LEFT);
break;
case R.id.right_btn:
mSnakePanelView.setSnakeDirection(GameType.RIGHT);
break;
case R.id.top_btn:
mSnakePanelView.setSnakeDirection(GameType.TOP);
break;
case R.id.bottom_btn:
mSnakePanelView.setSnakeDirection(GameType.BOTTOM);
break;
case R.id.start_btn:
mSnakePanelView.reStartGame();
break;
}
}
}
作者:刘淑慧
原文地址