Question regarding how to make a game similar to snake in MATLAB.
    5 views (last 30 days)
  
       Show older comments
    
Currently, my snake game spawns two players as 1x1 rectangles. The script is functional in the sense that each player can move and eat apples which increases their collision count. The game stops when one player reaches a set collision count. I can't figure out how to implement a logic in my code that will allow each player to grow and move like a snake each time they collide with the apple. Can anyone help me fix this issue? Here is my code:
clc
clear
close all
% Create a new figure
figure('WindowState','maximized');
hold on
% Create a nested loop to plot lines for the grid
for i = 0:13
    for j = 0:13
        % Plot horizontal lines
        plot([0 13], [i i], 'k', 'LineWidth', 1.25);
        axis off
        % Plot vertical lines
        plot([j j], [0 13], 'k', 'LineWidth', 1.25);
        axis off
    end
end
% Set the axis limits
xlim([0 13]);
ylim([0 13]);
%Set aspect ratio to equal
axis square;
% Spawn a 1x1 black square in the top left corner of the grid
rect = rectangle('Position', [0, 12, 1, 1], 'FaceColor', 'black');
% Spawn a 1x1 red square randomly in the middle of the grid
red_rect = rectangle('Position', [6, 6, 1, 1], 'FaceColor', 'red');
% Spawn a 1x1 blue square in the bottom right corner of the grid
blue_rect = rectangle('Position', [12, 0, 1, 1], 'FaceColor', 'blue');
% Create a counter for collisions
global collision_count
global collision_count1
collision_count = 0;
collision_count1 = 0;
% Boolean value to determine if the game ends or not
global x
x = 1;
% Create a text object to display the collision count
text_obj = text(0, 13.5, ['Player 1 Score: ', num2str(collision_count)], 'Color', 'black');
text_obj1 = text(0, -0.5, ['Player 2 Score: ', num2str(collision_count)], 'Color', 'black');
% Set the callback function for the key presses of both players
set(gcf, 'KeyPressFcn', {@moveBothPlayer, rect, red_rect, blue_rect, text_obj, text_obj1});
% Define the callback function for the players
function moveBothPlayer(~, event, rect, red_rect, blue_rect, text_obj, text_obj1)
    % Leave the function when the game ends
    global collision_count
    global collision_count1
    global x
    % Leave the function when the game is determined to be over
    if (collision_count == 5 || collision_count1 == 5) && x == 0
        return
    end
    if (collision_count || collision_count1 ~= 5) && x == 1
        % Get the current/previous position of each square
        pos = get(rect, 'Position');
        blue_pos = get(blue_rect, 'Position');
        prev_pos = pos;
        prev_blue_pos = blue_pos;
        % Check for 'w' key press
        if strcmp(event.Key, 'w')
            pos(2) = pos(2) + 1;
        % Check for 'a' key press
        elseif strcmp(event.Key, 'a')
            pos(1) = pos(1) - 1;
        % Check for 's' key press
        elseif strcmp(event.Key, 's')
            pos(2) = pos(2) - 1;
        % Check for 'd' key press
        elseif strcmp(event.Key, 'd')
            pos(1) = pos(1) + 1;
        end
        % Check if the black square leaves the grid
        if pos(1) > 12
            pos(1) = 0;
        elseif pos(1) < 0
            pos(1) = 12;
        end
        if pos(2) > 12
            pos(2) = 0;
        elseif pos(2) < 0
            pos(2) = 12;
        end
        % Check for 'uparrow' key press
        if strcmp(event.Key, 'uparrow')
            blue_pos(2) = blue_pos(2) + 1;
        % Check for 'leftarrow' press
        elseif strcmp(event.Key, 'leftarrow')
            blue_pos(1) = blue_pos(1) - 1;
        % Check for 'downarrow' press
        elseif strcmp(event.Key, 'downarrow')
            blue_pos(2) = blue_pos(2) - 1;
        % Check for 'rightarrow' press
        elseif strcmp(event.Key, 'rightarrow')
            blue_pos(1) = blue_pos(1) + 1;
        end
        % Check if the blue square leaves the grid
        if blue_pos(1) > 12
            blue_pos(1) = 0;
        elseif blue_pos(1) < 0
            blue_pos(1) = 12;
        end
        if blue_pos(2) > 12
            blue_pos(2) = 0;
        elseif blue_pos(2) < 0
            blue_pos(2) = 12;
        end
        % Check if a players position equals the position of the apple
        red_pos = get(red_rect, 'Position');
        if pos(1) == red_pos(1) && pos(2) == red_pos(2)
            collision_count = collision_count + 1;
            set(text_obj, 'String', ['Player 1 Score: ', num2str(collision_count)]);
            set(red_rect, 'Position', [randi([0,12]), randi([0,12]), 1, 1]);
        elseif blue_pos(1) == red_pos(1) && blue_pos(2) == red_pos(2)
            collision_count1 = collision_count1 + 1;
            set(text_obj1, 'String', ['Player 2 Score: ', num2str(collision_count1)]);
            set(red_rect, 'Position', [randi([0,12]), randi([0,12]), 1, 1]);
        end
        % Update the position of Player 1
        set(rect, 'Position', pos);
        % Update the position of Player 2
        set(blue_rect, 'Position', blue_pos);
    end
    % Prompting the users to play another game
    if collision_count == 5
        answer = questdlg( 'Player 1 Wins! Would you like to play again?', ...
        'Choices', 'Yes','No','Yes');
        % Handle response
        switch answer
            case 'Yes'
                collision_count = 0;
                collision_count1 = 0;
                set(text_obj, 'String', ['Player 1 Score: ', num2str(collision_count)]);
                set(red_rect, 'Position', [6, 6, 1, 1]);
                set(rect, 'Position', [0, 12, 1, 1]);
                set(blue_rect, 'Position', [12, 0, 1, 1]);
                x = 1;
            case 'No'
                annotation('textbox', 'String', 'Thank you for playing!', 'BackgroundColor',...
                    'w', 'EdgeColor', 'black', 'Position', [0.468 0.5 0.1 0.1],...
                    'HorizontalAlignment', 'center', 'LineWidth', 1.5);
                delete(red_rect);
                delete(rect);
                delete(blue_rect);
                x = 0;
                return
        end
    elseif collision_count1 == 5
        answer = questdlg('Player 2 Wins! Would you like to play again?', ...
        'Choices', 'Yes','No','Yes');
        % Handle response
        switch answer
            case 'Yes'
                collision_count = 0;
                collision_count1 = 0;
                set(text_obj1, 'String', ['Player 2 Score: ', num2str(collision_count1)]);
                set(red_rect, 'Position', [6, 6, 1, 1]);
                set(rect, 'Position', [0, 12, 1, 1]);
                set(blue_rect, 'Position', [12, 0, 1, 1]);
                x = 1;
            case 'No'
                annotation('textbox', 'String', 'Thank you for playing!', 'BackgroundColor',...
                'w', 'EdgeColor', 'black', 'Position', [0.468 0.5 0.1 0.1],...
                'HorizontalAlignment', 'center', 'LineWidth', 1.5);
            delete(red_rect);
            delete(rect);
            delete(blue_rect);
                x = 0;
                return
        end
    end
end
0 Comments
Accepted Answer
  Chris
      
 on 30 Jan 2023
        
      Edited: Chris
      
 on 30 Jan 2023
  
      You could introduce a "grow" bit for each snake. Set it to false initially.
grow = false;
If a snake collides with an apple, set it to true.
Whenever a move occurs, check that bit. If it's true, add a new rectangle element to the snake and reset the bit before moving:
rect(end+1) = rect(end);
grow = false;
Finally, increment the position of the other rectangles. The tail segment stays put for this move, but gets updated in subsequent moves.
Next, you'll need to remember where the snake turns and increment the positions differently after they reach that square.
This program is getting large enough that it might be helpful to break it out into numerous small functions that could exist in their own file (or after the end of the program) and are easy to comprehend--like, as an extreme example, a "move_rect(rect,x,y)" to change the position of a single rectangle. Then you could call that function repeatedly to update each rectangle in a snake. Small functions tend to make things easier to debug as you continue building out your code.
function rect = move_rect(rect,x,y)
    rect.Position(1:2) = rect.Position(1:2) + [x, y];
end
If you have lots of time, this could also be a perfect opportunity to learn some object-oriented programming. You could create a "snake" class, with properties like an Mx2 "Position" array, with an x and y value for each of the M segments, and a "Turns" array containing each of the snake's current turning positions. Then you would need a move() method, a grow() method, checkCollision(), etc. blue_snake and black_snake would each be objects, or instances, of the snake class.
More Answers (0)
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!
