No Coding! Just Copy-Paste করেই তৈরি করো Android Snake Game

📌 Download this resources file.

🖥️ XML Code in activity_main.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#121212"
    tools:context=".MainActivity" >  
    
<!-- Score Display -->  
<TextView  
    android:id="@+id/scoreTextView"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:layout_alignParentTop="true"  
    android:layout_centerHorizontal="true"  
    android:text="Score: 0"  
    android:textSize="24sp"  
    android:textColor="#4CAF50"  
    android:padding="16dp"  
    android:textStyle="bold"  
    android:fontFamily="sans-serif-medium" />  

<!-- Game Over Text -->  
<TextView  
    android:id="@+id/gameOverTextView"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:layout_centerInParent="true"  
    android:text="Game Over!\nTap RESTART to play again"  
    android:textSize="24sp"  
    android:textColor="#FF5252"  
    android:textStyle="bold"  
    android:visibility="invisible"  
    android:gravity="center"  
    android:fontFamily="sans-serif-medium"  
    android:background="#CC000000"  
    android:padding="20dp"  
    android:elevation="8dp" />  

<!-- Game View -->  
<FrameLayout  
    android:id="@+id/gameContainer"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:layout_above="@+id/controlsLayout"  
    android:layout_below="@+id/scoreTextView"  
    android:background="#1E1E1E" />  

<!-- Control Buttons - Modern Joystick Style -->  
<LinearLayout  
    android:id="@+id/controlsLayout"  
    android:layout_width="match_parent"  
    android:layout_height="wrap_content"  
    android:layout_alignParentBottom="true"  
    android:orientation="vertical"  
    android:padding="16dp"  
    android:background="#1A1A1A">  

    <!-- Up Button -->  
    <ImageButton  
        android:id="@+id/upButton"  
        android:layout_width="60dp"  
        android:layout_height="60dp"  
        android:layout_gravity="center_horizontal"  
        android:background="@drawable/button_up_selector"  
        android:scaleType="center"  
        android:layout_marginBottom="8dp" />  

    <!-- Middle Row: Left, Right Buttons -->  
    <LinearLayout  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_gravity="center"  
        android:orientation="horizontal">  

        <ImageButton  
            android:id="@+id/leftButton"  
            android:layout_width="60dp"  
            android:layout_height="60dp"  
            android:background="@drawable/button_left_selector"  
            android:scaleType="center"  
            android:layout_marginEnd="100dp" />  

        <ImageButton  
            android:id="@+id/rightButton"  
            android:layout_width="60dp"  
            android:layout_height="60dp"  
            android:background="@drawable/button_right_selector"  
            android:scaleType="center" />  

    </LinearLayout>  

    <!-- Down Button -->  
    <ImageButton  
        android:id="@+id/downButton"  
        android:layout_width="60dp"  
        android:layout_height="60dp"  
        android:layout_gravity="center_horizontal"  
        android:background="@drawable/button_down_selector"  
        android:scaleType="center"  
        android:layout_marginTop="8dp" />  

    <androidx.cardview.widget.CardView  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        app:cardCornerRadius="10dp">  

        <LinearLayout  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:orientation="horizontal"  
            android:weightSum="2">  

            <!-- Restart Button -->  
            <Button  
                android:id="@+id/restartButton"  
                android:layout_width="150dp"  
                android:layout_height="48dp"  
                android:layout_weight="1"  
                android:text="RESTART"  
                android:shadowColor="#D9C514"  
                android:background="@drawable/button_restart_selector"  
                android:textColor="#FFFFFF"  
                android:textStyle="bold"  
                android:fontFamily="sans-serif-medium"  
                android:layout_margin="5dp" />  

            <!-- Speed Control Dropdown -->  
            <LinearLayout  
                android:id="@+id/speedControlLayout"  
                android:layout_width="wrap_content"  
                android:layout_height="wrap_content"  
                android:layout_weight="1"  
                android:orientation="horizontal"  
                android:padding="16dp">  

                <TextView  
                    android:layout_width="wrap_content"  
                    android:layout_height="wrap_content"  
                    android:text="Speed: "  
                    android:textColor="#000000"  
                    android:textStyle="bold"  
                    android:textSize="16sp"  
                    android:layout_gravity="center_vertical"  
                    android:layout_marginEnd="8dp" />  

                <Spinner  
                    android:id="@+id/speedSpinner"  
                    android:layout_width="100dp"  
                    android:layout_height="30dp"  
                    android:background="@drawable/spinner_background"  
                    android:popupBackground="#E7E9E9"  
                    android:spinnerMode="dropdown" />  

            </LinearLayout>  

        </LinearLayout>  

    </androidx.cardview.widget.CardView>  

</LinearLayout>  


</RelativeLayout >  

    

📌 Paste this code in MainActivity.java


package com.nahid.snakegame;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private SnakeView snakeView;
    private TextView scoreTextView;
    private TextView gameOverTextView;
    private ImageButton upButton, downButton, leftButton, rightButton;
    private Button restartButton;
    private FrameLayout gameContainer;
    private Spinner speedSpinner;

    private int currentSpeed = 150; // Default medium speed

    // Speed options in milliseconds (higher value = slower speed)
    private final List speedValues = Arrays.asList(300, 150, 75);
    private final List speedLabels = Arrays.asList("Easy", "Medium", "Hard");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize views
        scoreTextView = findViewById(R.id.scoreTextView);
        gameOverTextView = findViewById(R.id.gameOverTextView);
        gameContainer = findViewById(R.id.gameContainer);
        speedSpinner = findViewById(R.id.speedSpinner);

        // Create SnakeView dynamically and add to container
        snakeView = new SnakeView(this, null);
        gameContainer.addView(snakeView);

        // Initialize control buttons
        upButton = findViewById(R.id.upButton);
        downButton = findViewById(R.id.downButton);
        leftButton = findViewById(R.id.leftButton);
        rightButton = findViewById(R.id.rightButton);
        restartButton = findViewById(R.id.restartButton);

        // Set up speed spinner
        setupSpeedSpinner();

        // Set up button listeners with touch feedback
        setupButton(upButton, Snake.Direction.UP);
        setupButton(downButton, Snake.Direction.DOWN);
        setupButton(leftButton, Snake.Direction.LEFT);
        setupButton(rightButton, Snake.Direction.RIGHT);

        restartButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                restartGame();
            }
        });
    }

    private void setupSpeedSpinner() {
        // Create adapter for spinner
        ArrayAdapter adapter = new ArrayAdapter<>(
                this,
                android.R.layout.simple_spinner_item,
                speedLabels
        );
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

        speedSpinner.setAdapter(adapter);

        // Set default selection to Medium
        speedSpinner.setSelection(1);

        // Set spinner item selection listener
        speedSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView parent, View view, int position, long id) {
                currentSpeed = speedValues.get(position);
                if (snakeView != null) {
                    snakeView.setGameSpeed(currentSpeed);
                }

                // Show speed change feedback
                String speedName = speedLabels.get(position);
                showSpeedChangeMessage(speedName + " speed selected");
            }

            @Override
            public void onNothingSelected(AdapterView parent) {
                // Do nothing
            }
        });
    }

    private void showSpeedChangeMessage(String message) {
        // You can implement a toast or a small text display here
        // For now, we'll just log it
        System.out.println(message);

        // Optional: Show a small toast message
        // Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    private void setupButton(ImageButton button, final Snake.Direction direction) {
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (snakeView != null && !snakeView.isGameOver()) {
                    snakeView.setDirection(direction);
                    // Add button press animation
                    v.animate().scaleX(0.9f).scaleY(0.9f).setDuration(100)
                            .withEndAction(new Runnable() {
                                @Override
                                public void run() {
                                    v.animate().scaleX(1f).scaleY(1f).setDuration(100);
                                }
                            });
                }
            }
        });
    }

    public void updateScore(int score) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                scoreTextView.setText("Score: " + score);
                // Add score update animation
                scoreTextView.animate().scaleX(1.2f).scaleY(1.2f).setDuration(200)
                        .withEndAction(new Runnable() {
                            @Override
                            public void run() {
                                scoreTextView.animate().scaleX(1f).scaleY(1f).setDuration(200);
                            }
                        });
            }
        });
    }

    public void showGameOver() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                gameOverTextView.setVisibility(View.VISIBLE);
                // Add game over animation
                gameOverTextView.setScaleX(0.8f);
                gameOverTextView.setScaleY(0.8f);
                gameOverTextView.animate()
                        .scaleX(1f)
                        .scaleY(1f)
                        .setDuration(500)
                        .start();
            }
        });
    }

    private void restartGame() {
        gameOverTextView.setVisibility(View.INVISIBLE);
        if (snakeView != null) {
            snakeView.startGame(currentSpeed);
        }
        updateScore(0);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (snakeView != null) {
            snakeView.pauseGame();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (snakeView != null) {
            snakeView.resumeGame(currentSpeed);
        }
    }
}

    

📌 Paste this code in Food.java


package com.nahid.snakegame;

import android.graphics.Point;
import java.util.Random;

public class Food {
    private Point position;
    private Random random;
    private int blockSize;
    private int width;
    private int height;
    private int foodType;
    private int[] foodColors = {0xFFFF5252, 0xFFFFEB3B, 0xFF4CAF50, 0xFF2196F3, 0xFF9C27B0};
    private int[] foodSizes = {35, 40, 45, 50, 55};
    private int currentColor;
    private int currentSize;

    public Food(int blockSize, int width, int height) {
        this.blockSize = blockSize;
        this.width = width;
        this.height = height;
        random = new Random();
        position = new Point();
        currentColor = foodColors[0];
        currentSize = foodSizes[0];
    }

    public void spawn() {
        // Ensure we don't divide by zero and get positive bounds
        if (width <= blockSize * 3 || height <= blockSize * 3) {
            position.x = blockSize * 5;
            position.y = blockSize * 5;
            return;
        }

        // Generate random position within the game boundaries
        int maxX = (width / blockSize) - 2;
        int maxY = (height / blockSize) - 2;

        if (maxX <= 1) maxX = 2;
        if (maxY <= 1) maxY = 2;

        position.x = (random.nextInt(maxX) + 1) * blockSize;
        position.y = (random.nextInt(maxY) + 1) * blockSize;

        // Random food type and properties
        foodType = random.nextInt(5);
        currentColor = foodColors[foodType];
        currentSize = foodSizes[foodType];
    }

    public Point getPosition() {
        return position;
    }

    public int getFoodColor() {
        return currentColor;
    }

    public int getFoodSize() {
        return currentSize;
    }

    public int getFoodType() {
        return foodType;
    }

    public int getPoints() {
        // Different food types give different points
        return (foodType + 1) * 10;
    }
}
    

📌 Paste this code in Snake.java


package com.nahid.snakegame;

import android.graphics.Point;
import java.util.ArrayList;
import java.util.List;

public class Snake {
    public enum Direction {UP, DOWN, LEFT, RIGHT}

    private List body;
    private Direction direction;
    private int blockSize;
    private int width;
    private int height;
    private int snakeColor;
    private boolean isGrowing;
    private int growCounter;

    public Snake(int blockSize, int width, int height) {
        this.blockSize = blockSize;
        this.width = width;
        this.height = height;

        // Initialize snake with gradient colors
        snakeColor = 0xFF4CAF50;

        // Initialize snake with 3 segments
        body = new ArrayList<>();
        body.add(new Point(width / 2, height / 2)); // Head
        body.add(new Point(width / 2 - blockSize, height / 2));
        body.add(new Point(width / 2 - 2 * blockSize, height / 2));

        direction = Direction.RIGHT;
        isGrowing = false;
        growCounter = 0;
    }

    public void move() {
        // Move the snake by adding a new head
        Point newHead = new Point(getHead());

        switch (direction) {
            case UP:
                newHead.y -= blockSize;
                break;
            case DOWN:
                newHead.y += blockSize;
                break;
            case LEFT:
                newHead.x -= blockSize;
                break;
            case RIGHT:
                newHead.x += blockSize;
                break;
        }

        body.add(0, newHead); // Add new head

        // Handle growing animation
        if (isGrowing) {
            growCounter++;
            if (growCounter >= 3) { // Grow after 3 moves
                isGrowing = false;
                growCounter = 0;
            }
        } else {
            body.remove(body.size() - 1); // Remove tail
        }
    }

    public void grow() {
        isGrowing = true;
        growCounter = 0;
        // Change snake color slightly when growing
        snakeColor = 0xFF66BB6A;
    }

    public boolean checkCollision() {
        Point head = getHead();

        // Check if snake hits the wall (with some tolerance)
        if (head.x < 0 || head.y < 0 ||
                head.x >= width - blockSize || head.y >= height - blockSize) {
            return true;
        }

        // Check if snake hits itself (skip the head)
        for (int i = 1; i < body.size(); i++) {
            if (head.equals(body.get(i))) {
                return true;
            }
        }

        return false;
    }

    public boolean checkFoodCollision(Point foodPosition) {
        Point head = getHead();
        // Check if snake s head is within the food block with some tolerance
        return (Math.abs(head.x - foodPosition.x) < blockSize / 2 &&
                Math.abs(head.y - foodPosition.y) < blockSize / 2);
    }

    public void setDirection(Direction newDirection) {
        // Prevent 180-degree turns
        if ((direction == Direction.UP && newDirection == Direction.DOWN) ||
                (direction == Direction.DOWN && newDirection == Direction.UP) ||
                (direction == Direction.LEFT && newDirection == Direction.RIGHT) ||
                (direction == Direction.RIGHT && newDirection == Direction.LEFT)) {
            return;
        }

        direction = newDirection;
        // Reset color after direction change
        snakeColor = 0xFF4CAF50;
    }

    public Point getHead() {
        return body.get(0);
    }

    public Point getTail() {
        return body.get(body.size() - 1);
    }

    public List getBody() {
        return body;
    }

    public Direction getDirection() {
        return direction;
    }

    public int getSnakeColor() {
        return snakeColor;
    }

    public boolean isGrowing() {
        return isGrowing;
    }
}
          
          

📌 Paste this code in SnakeView.java


package com.nahid.snakegame;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class SnakeView extends View {
    private Snake snake;
    private Food food;
    private int score = 0;
    private boolean gameOver = false;
    private Paint snakePaint;
    private Paint foodPaint;
    private Paint backgroundPaint;
    private Paint eyePaint;
    private Paint gridPaint;
    private Paint textPaint;
    private int blockSize = 40;
    private Timer gameTimer;
    private boolean isInitialized = false;
    private ValueAnimator foodAnimator;
    private float foodScale = 1.0f;
    private int gameSpeed = 150; // Default speed in milliseconds (Medium)

    // Speed levels
    public static final int SPEED_EASY = 300;
    public static final int SPEED_MEDIUM = 150;
    public static final int SPEED_HARD = 75;

    public SnakeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaints();
        initFoodAnimation();
    }

    private void initPaints() {
        // Snake paint with gradient
        snakePaint = new Paint();
        snakePaint.setAntiAlias(true);
        snakePaint.setStyle(Paint.Style.FILL);

        // Food paint
        foodPaint = new Paint();
        foodPaint.setAntiAlias(true);
        foodPaint.setStyle(Paint.Style.FILL);

        // Background paint
        backgroundPaint = new Paint();
        backgroundPaint.setColor(0xFF1E1E1E);
        backgroundPaint.setStyle(Paint.Style.FILL);

        // Grid paint
        gridPaint = new Paint();
        gridPaint.setColor(0x33FFFFFF);
        gridPaint.setStrokeWidth(1);

        // Eye paint
        eyePaint = new Paint();
        eyePaint.setColor(Color.WHITE);
        eyePaint.setAntiAlias(true);

        // Text paint for debug info
        textPaint = new Paint();
        textPaint.setColor(Color.WHITE);
        textPaint.setTextSize(20);
        textPaint.setAntiAlias(true);
    }

    private void initFoodAnimation() {
        foodAnimator = ValueAnimator.ofFloat(0.8f, 1.2f);
        foodAnimator.setDuration(1000);
        foodAnimator.setRepeatMode(ValueAnimator.REVERSE);
        foodAnimator.setRepeatCount(ValueAnimator.INFINITE);
        foodAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        foodAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                foodScale = (float) animation.getAnimatedValue();
                postInvalidate(); // শুধুমাত্র food অংশ redraw করবে
            }
        });
    }

    public void setGameSpeed(int speed) {
        this.gameSpeed = speed;
        restartGameWithNewSpeed();
    }

    public int getGameSpeed() {
        return gameSpeed;
    }

    private void restartGameWithNewSpeed() {
        boolean wasRunning = (gameTimer != null && !gameOver);
        pauseGame();

        if (wasRunning) {
            resumeGame();
        }
    }

    public void startGame(int speed) {
        this.gameSpeed = speed;
        startGame();
    }

    public void startGame() {
        if (getWidth() <= 0 || getHeight() <= 0) {
            return;
        }

        snake = new Snake(blockSize, getWidth(), getHeight());
        food = new Food(blockSize, getWidth(), getHeight());
        food.spawn();

        score = 0;
        gameOver = false;
        isInitialized = true;
        foodScale = 1.0f;

        if (gameTimer != null) {
            gameTimer.cancel();
        }

        gameTimer = new Timer();
        gameTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (!gameOver && isInitialized) {
                    update();
                    postInvalidate();
                }
            }
        }, 0, gameSpeed); // Use the current game speed
    }

    private void update() {
        if (!isInitialized) return;

        snake.move();

        if (snake.checkFoodCollision(food.getPosition())) {
            int points = food.getPoints();
            snake.grow();
            food.spawn();
            score += points;

            // Food collection effect - Main thread এ run করতে হবে
            post(new Runnable() {
                @Override
                public void run() {
                    startFoodAnimation();
                }
            });

            if (getContext() instanceof MainActivity) {
                ((MainActivity) getContext()).updateScore(score);
            }
        }

        if (snake.checkCollision()) {
            gameOver = true;
            post(new Runnable() {
                @Override
                public void run() {
                    stopFoodAnimation();
                }
            });
            if (getContext() instanceof MainActivity) {
                ((MainActivity) getContext()).showGameOver();
            }
        }
    }

    public void startFoodAnimation() {
        if (foodAnimator != null && !foodAnimator.isStarted()) {
            foodAnimator.start();
        }
    }

    public void stopFoodAnimation() {
        if (foodAnimator != null && foodAnimator.isRunning()) {
            foodAnimator.cancel();
            foodScale = 1.0f;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // Draw background with grid pattern
        drawBackground(canvas);

        if (isInitialized && snake != null && food != null) {
            // Draw food with glowing effect
            drawFood(canvas);

            // Draw snake with realistic appearance
            drawSnake(canvas);

            // Draw debug information
            drawDebugInfo(canvas);
        }
    }

    private void drawBackground(Canvas canvas) {
        canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);

        // Draw grid lines
        for (int x = blockSize; x < getWidth(); x += blockSize) {
            canvas.drawLine(x, 0, x, getHeight(), gridPaint);
        }
        for (int y = blockSize; y < getHeight(); y += blockSize) {
            canvas.drawLine(0, y, getWidth(), y, gridPaint);
        }
    }

    private void drawFood(Canvas canvas) {
        Point foodPos = food.getPosition();
        int foodSize = food.getFoodSize();
        int foodColor = food.getFoodColor();

        // Calculate scaled food size
        float scaledSize = foodSize * foodScale;

        // Draw food with gradient and glow
        RadialGradient gradient = new RadialGradient(
                foodPos.x + foodSize/2f, foodPos.y + foodSize/2f, scaledSize/2f,
                foodColor, darkenColor(foodColor), Shader.TileMode.CLAMP
        );

        foodPaint.setShader(gradient);
        canvas.drawCircle(foodPos.x + foodSize/2f, foodPos.y + foodSize/2f,
                scaledSize/2f, foodPaint);

        // Draw food inner highlight
        foodPaint.setShader(null);
        foodPaint.setColor(lightenColor(foodColor));
        canvas.drawCircle(foodPos.x + foodSize/3f, foodPos.y + foodSize/3f,
                scaledSize/6f, foodPaint);
    }

    private void drawSnake(Canvas canvas) {
        List body = snake.getBody();
        int snakeColor = snake.getSnakeColor();

        // Draw snake body with gradient
        for (int i = 0; i < body.size(); i++) {
            Point segment = body.get(i);

            // Create gradient for each segment
            float[] hsv = new float[3];
            Color.colorToHSV(snakeColor, hsv);
            hsv[2] *= 0.9f; // Darken based on position
            int segmentColor = Color.HSVToColor(hsv);

            RadialGradient gradient = new RadialGradient(
                    segment.x + blockSize/2f, segment.y + blockSize/2f, blockSize/2f,
                    lightenColor(segmentColor), darkenColor(segmentColor), Shader.TileMode.CLAMP
            );

            snakePaint.setShader(gradient);
            canvas.drawRoundRect(
                    segment.x + 2, segment.y + 2,
                    segment.x + blockSize - 2, segment.y + blockSize - 2,
                    10, 10, snakePaint
            );
        }

        // Draw snake eyes on head
        Point head = snake.getHead();
        drawSnakeEyes(canvas, head, snake.getDirection());
    }

    private void drawSnakeEyes(Canvas canvas, Point head, Snake.Direction direction) {
        int eyeSize = blockSize / 4;
        int pupilSize = eyeSize / 2;

        int eyeX1, eyeY1, eyeX2, eyeY2;

        switch (direction) {
            case UP:
                eyeX1 = head.x + blockSize/3;
                eyeY1 = head.y + blockSize/3;
                eyeX2 = head.x + 2*blockSize/3;
                eyeY2 = head.y + blockSize/3;
                break;
            case DOWN:
                eyeX1 = head.x + blockSize/3;
                eyeY1 = head.y + 2*blockSize/3;
                eyeX2 = head.x + 2*blockSize/3;
                eyeY2 = head.y + 2*blockSize/3;
                break;
            case LEFT:
                eyeX1 = head.x + blockSize/3;
                eyeY1 = head.y + blockSize/3;
                eyeX2 = head.x + blockSize/3;
                eyeY2 = head.y + 2*blockSize/3;
                break;
            case RIGHT:
                eyeX1 = head.x + 2*blockSize/3;
                eyeY1 = head.y + blockSize/3;
                eyeX2 = head.x + 2*blockSize/3;
                eyeY2 = head.y + 2*blockSize/3;
                break;
            default:
                return;
        }

        // Draw eyes
        eyePaint.setColor(Color.WHITE);
        canvas.drawCircle(eyeX1, eyeY1, eyeSize, eyePaint);
        canvas.drawCircle(eyeX2, eyeY2, eyeSize, eyePaint);

        // Draw pupils
        eyePaint.setColor(Color.BLACK);
        canvas.drawCircle(eyeX1, eyeY1, pupilSize, eyePaint);
        canvas.drawCircle(eyeX2, eyeY2, pupilSize, eyePaint);
    }

    private void drawDebugInfo(Canvas canvas) {
        // Draw current speed information
        String speedText = "Speed: ";
        if (gameSpeed == SPEED_EASY) {
            speedText += "Easy (" + gameSpeed + "ms)";
        } else if (gameSpeed == SPEED_MEDIUM) {
            speedText += "Medium (" + gameSpeed + "ms)";
        } else if (gameSpeed == SPEED_HARD) {
            speedText += "Hard (" + gameSpeed + "ms)";
        } else {
            speedText += "Custom (" + gameSpeed + "ms)";
        }

        canvas.drawText(speedText, 10, 30, textPaint);
        canvas.drawText("Score: " + score, 10, 60, textPaint);
        canvas.drawText("Snake Size: " + snake.getBody().size(), 10, 90, textPaint);
    }

    private int darkenColor(int color) {
        float[] hsv = new float[3];
        Color.colorToHSV(color, hsv);
        hsv[2] *= 0.7f; // value component
        return Color.HSVToColor(hsv);
    }

    private int lightenColor(int color) {
        float[] hsv = new float[3];
        Color.colorToHSV(color, hsv);
        hsv[2] = Math.min(1.0f, hsv[2] * 1.3f);
        return Color.HSVToColor(hsv);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (w > 0 && h > 0) {
            startGame();
        }
    }

    public void setDirection(Snake.Direction direction) {
        if (snake != null) {
            snake.setDirection(direction);
        }
    }

    public boolean isGameOver() {
        return gameOver;
    }

    public void pauseGame() {
        if (gameTimer != null) {
            gameTimer.cancel();
            gameTimer = null;
        }
        stopFoodAnimation();
    }

    public void resumeGame(int speed) {
        this.gameSpeed = speed;
        resumeGame();
    }

    public void resumeGame() {
        if (!gameOver && isInitialized) {
            if (gameTimer != null) {
                gameTimer.cancel();
            }

            gameTimer = new Timer();
            gameTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    if (!gameOver && isInitialized) {
                        update();
                        postInvalidate();
                    }
                }
            }, 0, gameSpeed);
        }
    }

    public int getScore() {
        return score;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        pauseGame();
        if (foodAnimator != null) {
            foodAnimator.cancel();
        }
    }
}  

🎥 Watch the Video Tutorial

Share this post with your friends

See the next post
No one has commented on this post yet
Click here to comment
comment url