commit 406cd63527884bc470d0bb1d36f7839c3282e4ae Author: Howard T Date: Tue Mar 28 14:52:59 2017 +0800 First commit diff --git a/mazemap.mat b/mazemap.mat new file mode 100644 index 0000000..5e333e9 Binary files /dev/null and b/mazemap.mat differ diff --git a/snek_editor_help.m b/snek_editor_help.m new file mode 100644 index 0000000..282a060 --- /dev/null +++ b/snek_editor_help.m @@ -0,0 +1,90 @@ +function snek_editor_help() +scrsz = get(0,'ScreenSize'); +helpwin = figure('ToolBar','none','Name','SNEK Editor Help',... + 'NumberTitle','off','MenuBar','none',... + 'Resize','off','Visible','off','Color',[170 255 211]/255,... + 'Position',[scrsz(3)/8,scrsz(4)/8,min(scrsz(3:4)/1.2),min(scrsz(3:4)/1.5)]); +tgroup = uitabgroup('Parent', helpwin); +tab1 = uitab('Parent', tgroup, 'Title', 'Introduction'); +tab1_text = uicontrol('Style','text','Units','Normalized','Position',[0 0 1 1],'FontSize',10,'Parent',tab1); +tab2 = uitab('Parent', tgroup, 'Title', 'Map Packs'); +tab2_text = uicontrol('Style','text','Units','Normalized','Position',[0 0 1 1],'FontSize',10,'Parent',tab2); +tab3 = uitab('Parent', tgroup, 'Title', 'Backups and Rolling Back'); +tab3_text = uicontrol('Style','text','Units','Normalized','Position',[0 0 1 1],'FontSize',10,'Parent',tab3); +tab4 = uitab('Parent', tgroup, 'Title', 'Map Listings'); +tab4_text = uicontrol('Style','text','Units','Normalized','Position',[0 0 1 1],'FontSize',10,'Parent',tab4); +tab5 = uitab('Parent', tgroup, 'Title', 'Map Editing'); +tab5_text = uicontrol('Style','text','Units','Normalized','Position',[0 0 1 1],'FontSize',10,'Parent',tab5); +tab6 = uitab('Parent', tgroup, 'Title', 'Player Editing'); +tab6_text = uicontrol('Style','text','Units','Normalized','Position',[0 0 1 1],'FontSize',10,'Parent',tab6); + +intro_text = { 'This editor and help is created by EN YI','',... + 'Hello! Welcome to the SNEK map editor!',... + 'This editor allows you to create maps for the snake game for MATLAB created by me.',... + 'You can use the tabs to navigate to the section you want help on.','',... + 'By having the editor, you also have the source code yourself.',... + 'So, you may modify the code to your heart delights',... + 'Just remember to credit me for the original code','',... + 'Please report any problems with the editor to me',... + 'Please also state the method to replicate the error',... + 'Any suggestions/improvements are also welcomed, unless otherwise stated ','',... + 'You can contact me by emailing to randomsity1@gmail.com',... + 'Happy editing!','','',... + 'Credits: ',... + 'Mathworks for creating MATLAB',... + 'and Google for most of my MATLAB problems'}; +map_pack_text = { 'By default, you should have a file called "mazemap.mat".',... + 'That is the default file the snake game will load.','',... + 'When first opened, the editor will look empty as no file is loaded.',... + 'Press the "Change Map Pack" on the top to load a map pack.',... + 'When a pack is loaded, two extra files is created.',... + 'backup_ and old_',... + 'Make sure you have the files in the same directory as the editor','',... + 'You may also press on the "New Map Pack" on the top to create a new pack pack',... + 'A list will show you names of other mat files so you don''t replace them when creating a new file',... + 'Enter the new file name and press okay.',... + 'On the top right corner is the pack name. You can rename this anytime.',... + 'The new name will be used the next time you save.'}; +map_file_text = { 'There is two files when you load a file:',... + 'backup_ and old_',... + 'Both will allow you to roll back changes you made to your map pack','',... + 'backup_ will revert changes to the last time you saved',... + 'You can do so by pressing the "Reset changes" button at the bootom left corner','',... + 'old_ will revert changes to the moment you opened the editor',... + 'You have to load this yourself with the "Change Map Pack" button','',... + 'Keep these in mind when trying to reset any changes'}; +map_list_text = { 'Below the pack name shows all the maps contained within the pack.','',... + 'Click on one of the map name to display the map','',... + 'Press on the UP and DOWN buttons to sort the order of the map up and down respectively','',... + 'Press on the "New" button to add a new map at the end of the list','',... + 'Press on the "Delete" button to delete the selected map','',... + 'Press on the "Save" button to save ALL changes in the map pack','',... + 'You may edit the map name, which is below the "Save button"','',... + 'Changes are applied once you click out of the box'}; +map_edit_text = { 'The current map is displayed next to the map list',... + 'You can change the map name, width, and height in their respective boxes','',... + 'Left click to add a tile. Right Click to remove a tile','',... + 'Middle click to add a "nospawn" tile, where fruits cannot spawn in',... + 'They look slightly less black','',... + 'You can drag along the map to continuously add/remove tile',... + 'You cannot draw tiles over the snake','',... + 'You can also resize the map, by edit its width and height',... + 'Note that any tiles outside of the map wil be removed when resizing.','',... + 'Below the width and height boxes shows the coordinates of the mouse on the map.'}; + +player_edit_text = {'The speed slider changes the speed of the snake in this map, from 1 to 25','',... + 'Click on the "Redefine player position" button to edit the snake starting position',... + 'This works just like adding tiles, but you must make sure the snake is continuous.',... + 'The red tiles indicate the snake''s head','',... + 'Click on the "Redefine player position" button again to stop edit the player position','',... + 'The "Red","Green","Blue" sliders adjust the colour of the snake''s body','',... + 'Click on the "Reset Colour" to restore the snake to its default colour','',... + 'Click on the "Reset Speed" to reset the speed of the snake',''}; +set(tab1_text,'String',intro_text); +set(tab2_text,'String',map_pack_text); +set(tab3_text,'String',map_file_text); +set(tab4_text,'String',map_list_text); +set(tab5_text,'String',map_edit_text); +set(tab6_text,'String',player_edit_text); +set(helpwin,'Visible','on'); +end \ No newline at end of file diff --git a/snek_editor_viper.m b/snek_editor_viper.m new file mode 100644 index 0000000..d10f9d4 --- /dev/null +++ b/snek_editor_viper.m @@ -0,0 +1,556 @@ +%% Information +% Original author: En Yi +% A map editor for the snake game created by the same author +% Have fun! +% Anyone can modify it, just need to give credits to the original author + +function snek_editor_viper() +%% Define variables +file_name = '';map = {};map_name = {};speed = 0; +num_of_map = 0;current_map = 1;selected_map = []; +player_body = {};selected_body=[];colours = {};selected_colour = []; +prev_X = 0;prev_Y = 0;edit_enable=0; +default_colours = [0.7 0.2 0.5]; +%% Prepare the window +%Create the window +scrsz = get(0,'ScreenSize'); +min_dim = min(scrsz(3:4)/1.2); +win = figure('ToolBar',... + 'none','Name','SNEK Map Editor','NumberTitle','off','MenuBar','none',... + 'Resize','off','Visible','off','Color',[170 255 211]/255,... + 'Position',[scrsz(3)/8,scrsz(4)/8,min_dim,min_dim],... + 'WindowButtonMotionFcn',@display_mouse_pos); +%Prepare the block units +blk = 33; +blocks = min_dim/blk; +%% Prepare the UI components +%Create the map list +map_listbox = uicontrol('Style','listbox','Position',[1,19,6,11]*blocks,... + 'String',map_name,'Max',1,'Min',1,'Callback',@show_map); +%Create the map grid +map_panel = uipanel('Position',[8,7,23,23]/blk); +map_axes = axes('Parent',map_panel); +%UI to handle map packs +mappack_name_text = uicontrol('Style','text','Position',[1,31.5,7,1]*blocks,... + 'String','Pack Name'); +mappack_name_edit = uicontrol('Style','edit','Position',[1,30.5,7,1]*blocks); +change_mappack_button = uicontrol('Style','pushbutton','Position',[9,31,6,1.5]*blocks,... + 'String','Change Map Pack','Callback',@change_mappack); +create_mappack_button = uicontrol('Style','pushbutton','Position',[16,31,6,1.5]*blocks,... + 'String','New Map Pack','Callback',@new_mappack); +%Buttons to sort the map order +sort_up_button = uicontrol('Style','pushbutton','Position',[1,17,2.5,2]*blocks,... + 'String','UP','Callback',{@sort_map,-1}); +sort_down_button = uicontrol('Style','pushbutton','Position',[4,17,2.5,2]*blocks,... + 'String','DOWN','Callback',{@sort_map,1}); +%Buttons to create and delete maps, and save the current changes to the pack +new_button = uicontrol('Style','pushbutton','Position',[1,15,4,2]*blocks,... + 'String','New','Callback',@new_map); +delete_button = uicontrol('Style','pushbutton','Position',[1,13,4,2]*blocks,... + 'String','Delete','Callback',@delete_map); +save_button = uicontrol('Style','pushbutton','Position',[1,11,4,2]*blocks,... + 'String','Save','Callback',@save_map,'Interruptible','off','BusyAction','cancel'); +%UI to change the map name +map_name_text = uicontrol('Style','text','Position',[1,9,5,1]*blocks,... + 'String','Map Name'); +map_name_edit = uicontrol('Style','edit','Position',[1,8,5,1]*blocks,... + 'Callback',@edit_map_name); +%UI to change the map size +map_width_text = uicontrol('Style','text','Position',[1,6,2.5,1]*blocks,... + 'String','Width'); +map_width_edit = uicontrol('Style','edit','Position',[4,6,2,1]*blocks,... + 'Callback',@resize_map); +map_width_text = uicontrol('Style','text','Position',[1,5,2.5,1]*blocks,... + 'String','Height'); +map_height_edit = uicontrol('Style','edit','Position',[4,5,2,1]*blocks,... + 'Callback',@resize_map); +%UI to reset the map changes +reset_button = uicontrol('Style','pushbutton','Position',[1,1,5,2]*blocks,... + 'String','Reset changes','Callback',@reset_changes); +%UI to change the speed of the game +speed_text = uicontrol('Style','text','Position',[7,5.5,2.5,1]*blocks,... + 'String','Speed'); +speed_slider = uicontrol('Style','slider','Position',[10,5.5,9,1]*blocks,'value',12.5,... + 'String','Speed','Callback',@change_speed,'Min',1,'Max',25,'SliderStep',[0.1 1]/24); +speed_value_text = uicontrol('Style','text','Position',[19,5.5,2.5,1]*blocks,... + 'HorizontalAlignment','center'); +%UI for the editor manual +help_button = uicontrol('Style','pushbutton','Position',[28,1,5,2]*blocks,... + 'String','Need Help?','Callback',@open_manual); +%UI for displaying the version +version_text = uicontrol('Style','text','Position',[26.5,-1,7,2]*blocks,... + 'String','V1.1 Made by En Yi','BackgroundColor',[170 255 211]/255); +%UI for displaying the mouse position +mouse_xpos_text = uicontrol('Style','text','Position',[1,3.5,2.5,1]*blocks,... + 'HorizontalAlignment','center'); +mouse_ypos_text = uicontrol('Style','text','Position',[4,3.5,2.5,1]*blocks,... + 'HorizontalAlignment','center'); +redefine_player_toggle = uicontrol('Style','togglebutton','Position',[22,5.5,8,1.5]*blocks,... + 'String','Redefine player position'); +%UI for snake colour +colour_red_text = uicontrol('Style','text','Position',[7,4,2.5,1]*blocks,... + 'String','Red'); +colour_red_slider = uicontrol('Style','slider','Position',[10,4,9,1]*blocks,... + 'String','Speed','Callback',@change_colour,'Min',0,'Max',1,'SliderStep',[0.01 0.1]); +red_value_text = uicontrol('Style','text','Position',[19,4,2.5,1]*blocks,... + 'HorizontalAlignment','center'); +colour_green_text = uicontrol('Style','text','Position',[7,2.5,2.5,1]*blocks,... + 'String','Green'); +colour_green_slider = uicontrol('Style','slider','Position',[10,2.5,9,1]*blocks,... + 'String','Speed','Callback',@change_colour,'Min',0,'Max',1,'SliderStep',[0.01 0.1]); +green_value_text = uicontrol('Style','text','Position',[19,2.5,2.5,1]*blocks,... + 'HorizontalAlignment','center'); +colour_blue_text = uicontrol('Style','text','Position',[7,1,2.5,1]*blocks,... + 'String','Blue'); +colour_blue_slider = uicontrol('Style','slider','Position',[10,1,9,1]*blocks,... + 'String','Speed','Callback',@change_colour,'Min',0,'Max',1,'SliderStep',[0.01 0.1]); +blue_value_text = uicontrol('Style','text','Position',[19,1,2.5,1]*blocks,... + 'HorizontalAlignment','center'); +%UI to reset values to default +colour_reset_button = uicontrol('Style','pushbutton','Position',[22,3,5,2]*blocks,... + 'String','Reset Colours','Callback',@reset_colours); +reset_speed_button = uicontrol('Style','pushbutton','Position',[22,1,5,2]*blocks,... + 'String','Reset Speed','Callback',@reset_speed); +%Disable UI until user loads a map pack +disenable(0); +set(change_mappack_button,'Enable','On'); +set(create_mappack_button,'Enable','On'); +set(help_button,'Enable','On'); +set(win,'Visible','on'); +%% Callback functions + %Function to display a map and its info + function show_map(src,~) + %Store the current map chagnes + map{current_map} = selected_map; + player_body{current_map} = selected_body; + colours{current_map} = selected_colour; + %Get the selected map info + map_n = get(src,'Value'); + selected_map = map{map_n}; + selected_body = player_body{map_n}; + selected_colour = colours{map_n}; + current_map = map_n; + %Display everything + refresh_map(); + update_maplist(); + update_map_info(); + end + %Function to start the dragging function in editting + function drag_edit_enable(~,~) + if (edit_enable==0) + %Store the current callback of the figure + props.WindowButtonMotionFcn = get(win,'WindowButtonMotionFcn'); + props.WindowButtonUpFcn = get(win,'WindowButtonUpFcn'); + setappdata(win,'TestGuiCallbacks',props); + %Set the dragging edit callback and disable drag callback + set(win,'WindowButtonMotionFcn',@edit_map); + set(win,'WindowButtonUpFcn',@drag_edit_disable); + %Set the editting variables + prev_X = 0;prev_Y = 0;edit_enable=1; + %Edit the current tile + edit_map(); + end + end + %Function to stop the dragging function in editting + function drag_edit_disable(~,~) + if (edit_enable==1) + %Reset the original callbacks of the figure + props = getappdata(win,'TestGuiCallbacks'); + set(win,props); + %Set the editting variables + prev_X = 0;prev_Y = 0;edit_enable=0; + end + end + %Function to edit the tile of a map + function edit_map(~,~) + %Check if the user is defining player position + player_redf = get(redefine_player_toggle,'value'); + %Check which mouse button is pressed + if(~player_redf) + edit_type = 1; + switch(get(win,'selectiontype')) + case 'normal' + edit_type = 1;%Add tile + case 'alt' + edit_type = 0;%Remove tile + case 'extend' + edit_type = 0.1;%Add nospawn tile + end + end + %Get the mouse position + [X,Y] = display_mouse_pos(); + %Only edit a tile if the user move away from the current tile + %and stay within the map + map_sz = size(selected_map); + if (X>0 && X<=map_sz(2) && Y>0 && Y<=map_sz(1) && (X ~= prev_X || Y ~= prev_Y)) + %Map tile editing + if(~player_redf) + if ~(any((Y == selected_body(2,:)) & (X==selected_body(1,:)))) + selected_map(Y,X,:) = edit_type; + end + else + %Player position editing + %Only add to player if it is continuous, + %otherwise reset the player position + if (selected_map(Y,X)~=1) + if (( abs(X- selected_body(1,1))+abs(Y- selected_body(2,1)) == 1)&&... + ~any(X==selected_body(1,:) & Y==selected_body(2,:)) ) + selected_body = [[X;Y] selected_body]; + else + selected_body = [X;Y]; + end + end + end + end + %Store the previous mouse position + prev_X = X;prev_Y = Y; + refresh_map(); + end + %Function to add a new map + function new_map(~,~) + %Create the new map+infos + new_mat = zeros(30,30,3); + new_body = [15 14 13; 15 15 15]; + new_colour = default_colours; + %Append the new map+infos + map = [map new_mat]; + num_of_map = length(map); + map_name = [map_name sprintf('map_%d',num_of_map)]; + speed = [speed 12.5]; + player_body = [player_body new_body]; + colours = [colours new_colour]; + %Set to display the new map + selected_map = new_mat; + selected_body = new_body; + selected_colour = new_colour; + current_map = num_of_map; + set(map_listbox,'value',num_of_map); + refresh_map(); + update_maplist(); + update_map_info(); + end + %Function to delete a map + function delete_map(~,~) + %Only delete if there is more than one map + if(num_of_map ~=1) + map_num = get(map_listbox,'Value'); + map = [map(1:map_num-1) map(map_num+1:end)]; + map_name = [map_name(1:map_num-1) map_name(map_num+1:end)]; + speed = [speed(1:map_num-1) speed(map_num+1:end)]; + player_body = [player_body(1:map_num-1) player_body(map_num+1:end)]; + colours = [colours(1:map_num-1) colours(map_num+1:end)]; + num_of_map = length(map); + %Shift the map if the deleted map is not the first one + if (map_num>num_of_map) + map_num = map_num - 1; + end + set(map_listbox,'Value',map_num); + selected_map = map{map_num}; + selected_body = player_body{map_num}; + selected_colour = colours{map_num}; + current_map = map_num; + refresh_map(); + update_maplist(); + update_map_info(); + end + end + %Function to save all the changes + function save_map(~,~) + try + file_name = get(mappack_name_edit,'String'); + map{current_map} = selected_map; + player_body{current_map} = selected_body; + colours{current_map} = selected_colour; + save(file_name,'map','map_name','speed','player_body','colours'); + save(['backup_' file_name],'map','map_name','speed','player_body','colours'); + catch + savebox = errordlg('Error occured while saving','modal'); + uiwait(savebox); + return + end + savebox = msgbox('Save Complete','modal'); + uiwait(savebox); + end + %Function to move a map up or down + function sort_map(~,~,dir) + map_num = get(map_listbox,'Value'); + %Move down the map, if the map is not the last map + if (dir==1) + if (map_num ~= num_of_map) + map = move_list(map,map_num,1); + map_name = move_list(map_name,map_num,1); + speed = move_list(speed,map_num,1); + player_body = move_list(player_body,map_num,1); + colours = move_list(colours,map_num,1); + current_map = map_num+1; + set(map_listbox,'Value',current_map); + end + %Move up the map, if it is not the first + elseif (dir == -1) + if (map_num ~= 1) + map = move_list(map,map_num,-1); + map_name = move_list(map_name,map_num,-1); + speed = move_list(speed,map_num,-1); + player_body = move_list(player_body,map_num,-1); + colours = move_list(colours,map_num,-1); + current_map = map_num-1; + set(map_listbox,'Value',current_map); + end + end + %Update everything + refresh_map(); + update_maplist(); + end + %Function to change the map name + function edit_map_name(src,~) + map_name{current_map} = get(src,'String'); + update_maplist(); + end + %Function change the speed of the snake + function change_speed(src,~) + spd_val = get(src,'Value'); + spd_val = round(spd_val*10)/10; + speed(current_map) = spd_val; + update_map_info(); + end + %Function change the size of the map + function resize_map(~,~) + %Get the new dimension + new_width = str2double(get(map_width_edit,'String')); + new_height = str2double(get(map_height_edit,'String')); + map_sz = size(selected_map); + %Do the width first + %If it is enlarging, append the extra columns + if (new_width>map_sz(2)) + selected_map = [selected_map zeros(map_sz(1),new_width-map_sz(2),3)]; + else + %If it is shrinking, delete the extra columns + selected_map = selected_map(:,1:new_width,:); + end + %Redo for height + map_sz = size(selected_map); + if (new_height>map_sz(1)) + selected_map = [selected_map; zeros(new_height-map_sz(1),map_sz(2),3)]; + else + selected_map = selected_map(1:new_height,:,:); + end + map_sz = size(selected_map); + selected_body = floor([map_sz(2);map_sz(1)]/2+1); + refresh_map(); + end + %Function to reset the changes + function reset_changes(~,~) + load_map_pack(['backup_' file_name]) + refresh_map(); + update_maplist(); + update_map_info(); + end + %Function to open the manual + function open_manual(~,~) + snek_editor_help(); + end + %Function to obtain and display the mouse position + function [X,Y] = display_mouse_pos(~,~) + map_sz = size(selected_map); + mpos = get(map_axes,'CurrentPoint'); + X = round(mpos(1,1)); + Y = round(mpos(1,2)); + if (X>0 && X<=map_sz(2) && Y>0 && Y<=map_sz(1)) + set(mouse_xpos_text,'String',num2str(X)); + set(mouse_ypos_text,'String',num2str(Y)); + end + end + %Function to change the colour of the snake body + function change_colour(~,~) + new_colour(1) = get(colour_red_slider,'value'); + new_colour(2) = get(colour_green_slider,'value'); + new_colour(3) = get(colour_blue_slider,'value'); + selected_colour = round(new_colour*100)/100; + refresh_map(); + update_map_info(); + end + %Function to reset the colours + function reset_colours(~,~) + selected_colour = default_colours; + refresh_map(); + update_map_info(); + end + %Function to reset the speed + function reset_speed(~,~) + speed(current_map) = 12.5; + refresh_map(); + update_map_info(); + end +%% Non Callback functions + %Function to refresh the display + function refresh_map() + map_sz = size(selected_map); + snake_pos = zeros(map_sz); + if ~isempty(selected_body) + snake_pos(selected_body(2,1),selected_body(1,1),1) = 1; + for i = 2:length(selected_body(1,:)) + snake_pos(selected_body(2,i),selected_body(1,i),:) = selected_colour; + end + end + image(selected_map+snake_pos,'Parent',map_axes,'PickableParts','none'); + set(map_axes,'PickableParts','all','ButtonDownFcn',@drag_edit_enable); + set(map_axes,'Xcolor',[1 1 1],'xtick',(1:1:map_sz(2))+0.5,'XTickLabel',[]); + set(map_axes,'Ycolor',[1 1 1],'ytick',(1:1:map_sz(1))+0.5,'YTickLabel',[]); + newsize = [0 0 map_sz(2) map_sz(1)]/max(map_sz(1:2)); + newsize = newsize+[0.5-newsize(3:4)/2 0 0]; + set(map_axes,'Position',newsize); + grid on; + end + %Function to update the map list + function update_maplist() + set(map_listbox,'String',map_name,'Value',current_map); + end + %Function to update the UI to display the map info + function update_map_info() + set(map_name_edit,'String',map_name{current_map}); + map_sz = size(map{current_map}); + set(map_width_edit,'String',map_sz(2)); + set(map_height_edit,'String',map_sz(1)); + set(speed_slider,'Value',speed(current_map)); + set(speed_value_text,'String',num2str(speed(current_map))); + set(colour_red_slider,'value',selected_colour(1)); + set(red_value_text,'String',num2str(selected_colour(1))); + set(colour_green_slider,'value',selected_colour(2)); + set(green_value_text,'String',num2str(selected_colour(2))); + set(colour_blue_slider,'value',selected_colour(3)); + set(blue_value_text,'String',num2str(selected_colour(3))); + end + %Function to load a map pack + function load_map_pack(fname) + try + mzmp = load(fname); + catch + loadbox = errordlg('File might not exist within the directory','modal'); + uiwait(loadbox); + return + end + try + map = mzmp.map; + map_name = mzmp.map_name; + speed = mzmp.speed; + player_body = mzmp.player_body; + colours = mzmp.colours; + num_of_map = length(map); + current_map = 1; + selected_map = map{current_map}; + selected_body = player_body{current_map}; + selected_colour = colours{current_map}; + file_name = strrep(fname,'old_',''); + file_name = strrep(file_name,'backup_',''); + save(['backup_' file_name],'map','map_name','speed','player_body','colours'); + save(['old_' file_name],'map','map_name','speed','player_body','colours'); + set(mappack_name_edit,'String',file_name); + catch + loadbox = errordlg('File is corrupted','modal'); + uiwait(loadbox); + return + end + disenable(1); + end + %Function to move a item in a list + function[list] = move_list(list,val,increment) + temp_list = list(val+increment); + list(val+increment) = list(val); + list(val) = temp_list; + end + %Function to enable/disable UIs + function disenable(type) + if (type == 0) + disabling = findobj(win,'Parent',win,'-not','Type','uipanel'); + set(disabling,'Enable','Off'); + else + enabling = findobj(win,'Parent',win,'-not','Type','uipanel'); + set(enabling,'Enable','On'); + end + end +%% Functions involving dialog boxes + function new_mappack(~,~) + winpos = get(win,'Position'); + new_filename = 0;file_confirm = 0; + newfile = dialog('Name','Create a Map Pack','Position',[winpos(1:2)+winpos(3:4)/4 winpos(3:4)/1.5]); + matfiles = what; + matfiles = matfiles.mat; + uicontrol('Style','text','Units','normalized',... + 'Position',[0.05 0.9 0.4 0.05],'String','Available files') + uicontrol('Style','listbox','Units','normalized',... + 'Position',[0.1 0.3 0.7 0.6],... + 'String',matfiles); + uicontrol('Style','text','Units','normalized',... + 'Position',[0.05 0.15 0.3 0.1],... + 'String','File Name'); + new_filename_edit = uicontrol('Style','edit','Units','normalized',... + 'Position',[0.3 0.175 0.6 0.075]... + ); + uicontrol('Style','pushbutton','Units','normalized',... + 'Position',[0.1 0.05 0.3 0.1],... + 'String','Okay','Callback',@confirm_file); + uicontrol('Style','pushbutton','Units','normalized',... + 'Position',[0.6 0.05 0.3 0.1],... + 'String','Cancel','Callback',@cancel_file); + function confirm_file(~,~) + fname = get(new_filename_edit,'String'); + if(isempty(fname)); + return + end + new_filename = fname; + file_confirm = 1; + close(newfile); + end + function cancel_file(~,~) + close(newfile); + end + uiwait(newfile); + + if(file_confirm) + map = {};map_name = {};speed = [];player_body = {};colours = {}; + new_map();new_map();new_map(); + current_map =1; + selected_map = map{current_map}; + selected_body = player_body{current_map}; + save(new_filename,'map','map_name','speed','player_body','colours'); + load_map_pack(new_filename); + refresh_map(); + update_maplist(); + update_map_info(); + end + end + function change_mappack(~,~) + winpos = get(win,'Position'); + change_filename = 0;change_confirm = 0; + changefile = dialog('Name','Select a Map Pack','Position',[winpos(1:2)+winpos(3:4)/4 winpos(3:4)/1.5]); + matfiles = what; + matfiles = matfiles.mat; + file_listbox = uicontrol('Style','listbox','Units','normalized',... + 'Position',[0.1 0.1 0.5 0.85],... + 'String',matfiles); + uicontrol('Style','pushbutton','Units','normalized',... + 'Position',[0.7 0.7 0.2 0.2],... + 'String','Okay','Callback',@confirm_change); + uicontrol('Style','pushbutton','Units','normalized',... + 'Position',[0.7 0.5 0.2 0.2],... + 'String','Cancel','Callback',@cancel_change); + function confirm_change(~,~) + fnum = get(file_listbox,'value'); + change_filename = matfiles{fnum}; + change_filename = strrep(change_filename,'.mat',''); + change_confirm = 1; + close(changefile); + end + function cancel_change(~,~) + close(changefile); + end + uiwait(changefile); + + if(change_confirm) + load_map_pack(change_filename); + refresh_map(); + update_maplist(); + update_map_info(); + end + end +end \ No newline at end of file diff --git a/snek_final.m b/snek_final.m new file mode 100644 index 0000000..2f8090c --- /dev/null +++ b/snek_final.m @@ -0,0 +1,388 @@ +%% Information +% Original author: En Yi +% A snake game on MATLAB, very self-explanatory +% Have fun! +% Anyone can modify it, just need to give credits to the original author +function snek_final() +%% Creating the game window +clc;clear; +%Default Constants +default_updatetime = 0.08; +default_ftsize = 10; +%Create the update timer, used for continuously updating the game screen +update_t = timer('ExecutionMode','fixedSpacing','Period',default_updatetime,'TimerFcn',@update_screen); +%Create the window +scrsz = get(0,'ScreenSize'); +start_dim = min(scrsz(3)/1.5,scrsz(4)/1.5);%Used for rescaling +win = figure('KeyPressFcn',@key_check,'DeleteFcn',@delete_t,'ToolBar',... + 'none','Name','SNEK','NumberTitle','off','MenuBar','none',... + 'Resize','off','Visible','off','Color',[170 255 211]/255,... + 'Position',[[scrsz(3),scrsz(4)]/4.5 start_dim start_dim]); +%For displaying instructions and score +load_map_button = uicontrol('Style','pushbutton','Units','Normalized',... + 'Position',[0.01 0.01 0.2 0.08],'FontSize',default_ftsize,... + 'Callback',@select_mappack,'String','Change Pack'); +console_text = uicontrol('Style','text','Units','Normalized',... + 'Position',[0.25 0.01 0.7 0.08],'FontSize',default_ftsize); +%Just to display the version and author +uicontrol('Style','text','Units','Normalized','Position',[24,28,6,2]/30,... + 'String','V1.0 Made by En Yi','BackgroundColor',[170 255 211]/255); +%Create the axes for drawing the game +disp_axes = axes('Parent',win); +%graze = 0; + +%% Load the map levels and define game variables +%Prepare the map +width = 30;height = 30; +map_number = 0;n_of_maps = 0; +maze_map = {};map_name = {};speed = [];map = zeros(width,height,3); +start_body={};player_body = [];player_len = 1;move_dir = 2; +body_colour = {};selected_colour = [];fruit_colour = [0.5 0.5 0]; +load_map_pack('mazemap') +%Define the fruit fruit_values+bonus and player score +fruit_value = 0; +score = 0; +max_value = 60; +fruit_x = -1;fruit_y = -1; +%Create the sounds for collecting a fruit and losing the game +Fs = 10000; +t = 0:1/Fs:.02; +y = sin(2*pi*4000*t); +beep = audioplayer(y, Fs); +t = 0:1/Fs:0.1; +x = sawtooth(2*pi*60*t); +x = [x zeros(1,5) x]; +gover = audioplayer(x, Fs); +%Define the variable to check whether the player is playing the game +game_on = 0; +%Update the screen and display controls +update_screen(); +set(win,'Visible','on') +set(console_text,'String',{'WASD or Arrows key to select map','Space to start'}); +%% Callback functions + %Function to update the game screen + function update_screen(~,~) + scr_mat = map; %Put in the current map + if(game_on ==1) %If the player is playing + isdead = move(scr_mat); %Move the player and check whether the player is dead + if(isdead) %If so + stop(update_t); %Stop continuously updating the screen + set(console_text,'String',{'Game Over. Press R to restart.',... + sprintf('Final Score : %d\n',floor(score/10))} ); + play(gover); + return %Stop the game + end + end + %Put in the player body after updating + body_sz = size(player_body); + scr_mat(player_body(2,1),player_body(1,1),1) = 1; + for n=2:body_sz(2) + scr_mat(player_body(2,n),player_body(1,n),:) = selected_colour; + end + %Spawn the fruit if it is collected + if(fruit_x == -1) + scr_mat = spawn_fruit(scr_mat); + fruit_value = max_value; %Set the fruit fruit_values + end + %Put in the fruit + scr_mat(fruit_y,fruit_x,:) = fruit_colour; + %Set focus on the game window + figure(win); + %Draw the game screen + image(scr_mat,'Parent',disp_axes); + set(disp_axes,'XTickLabel',[],'YTickLabel',[]); + if (map_number>0) + title(map_name{map_number}); + else + title('Open'); + end + + if(game_on ==0) %If the player is not playing + %Readjust the window screen to fit the aspect ratio of the level + current_winpos = get(win,'Position'); + topright = current_winpos(2)+current_winpos(4); + new_winpos = [0 0 width height]/max([0 0 width height])... + *start_dim; + new_winpos = new_winpos+[current_winpos(1) topright-new_winpos(4) 0 0]; + set(win,'Position',new_winpos); + end + end + %Function to check the key presses + function key_check(~,evtdata) + %disp(evtdata.Key); + if(game_on == 1) + %When the game is running + %Direction keys change the direction of the snake + %The inputs are recorded up to 3 key presses for updating + n = length(move_dir)+1; + if (n<4) + if (move_dir(end)~=2 && move_dir(end)~=1) + switch(evtdata.Key) + case 'leftarrow' + move_dir(n) = 1; + case 'rightarrow' + move_dir(n) = 2; + case 'a' + move_dir(n) = 1; + case 'd' + move_dir(n) = 2; + end + elseif (move_dir(end)~=3 && move_dir(end)~=4) + switch(evtdata.Key) + case 'uparrow' + move_dir(n) = 3; + case 'downarrow' + move_dir(n) = 4; + case 'w' + move_dir(n) = 3; + case 's' + move_dir(n) = 4; + end + end + end + %Press R to try again. restarting the screen + if(strcmp(evtdata.Key,'r')) + restart(); + end + else + %When the game is not running + %Direction keys change the map + if(strcmp(evtdata.Key,'leftarrow') ||strcmp(evtdata.Key,'rightarrow')||... + strcmp(evtdata.Key,'a') ||strcmp(evtdata.Key,'d')) + switch(evtdata.Key) + case 'leftarrow' + map_number = map_number -1; + case 'rightarrow' + map_number = map_number +1; + case 'a' + map_number = map_number -1; + case 'd' + map_number = map_number +1; + end + if map_number>n_of_maps + map_number = 0; + elseif map_number<0 + map_number = n_of_maps; + end + restart(); + end + end + %Space to start the game + if(strcmp(evtdata.Key,'space')) + if(game_on == 0) + set(load_map_button,'Enable','off'); + drawnow; + start(update_t); + game_on =1; + set(console_text,'String',{sprintf('Score : %d\n',floor(score/10));} ); + end + end + end +%% Non Callback functions + %Function to update the player coordinates and check is it dead + function[isdead] = move(scr_mat) + isdead = 0; + player_newx = player_body(1,1);player_newy = player_body(2,1); + %Check the next key press if there is one, otherwise last key press + if(length(move_dir)>1) + move_dir = move_dir(2:end); + end + switch(move_dir(1)) + case 1 + player_newx = player_newx - 1; + case 2 + player_newx = player_newx + 1; + case 3 + player_newy = player_newy - 1; + case 4 + player_newy = player_newy + 1; + end + %Wrap the player around the edge of the map + if player_newx>width + player_newx = 1; + end + if player_newx<1 + player_newx = width; + end + if player_newy>height + player_newy = 1; + end + if player_newy<1 + player_newy = height; + end + %Check if the head of the player intersect with the body + xcheck = (player_body(1,2:end)==player_newx); + ycheck = (player_body(2,2:end)==player_newy); + if(sum(scr_mat(player_newy,player_newx,:))>2.9 ||any(xcheck & ycheck)) +% if(graze <1) +% graze = graze+1; +% else + isdead = 1; + return + %end + else + %graze = 0; + if (player_len >1) + player_body = [[player_newx;player_newy] player_body(:,1:end-1)]; + else + player_body = [player_newx;player_newy]; + end + end + %Check if the player head intersect with the fruit + if(player_body(1,1) == fruit_x && player_body(2,1) == fruit_y) + fruit_x = -1; + play(beep); + player_len = player_len+1; + player_body = [player_body player_body(:,end)]; + score = score + fruit_value; + set(console_text,'String',{sprintf('Score : %d',floor(score/10)),... + sprintf('+%.1f',fruit_value/10)} ); + else + %Decrease the fruit value if it is not collected + if fruit_value>10 + fruit_value = fruit_value -1; + else + fruit_value = 10; + end + end + end + %Function to spawn the fruit + function [scr_mat] = spawn_fruit(scr_mat) + comp_mat = sum(scr_mat,3); + [empty_y,empty_x] = find(comp_mat==0); + fruit_pos = floor(rand(1)*length(empty_x))+1; + fruit_x = empty_x(fruit_pos);fruit_y = empty_y(fruit_pos); + end + %Function to restart the game + function restart() + stop(update_t); %Stop continuously updating the game + game_on = 0; %Indicate the game is not running + map_load(map_number); %Load the selected map + fruit_x = -1; %Remove the fruit + fruit_value = 1; %Reset the fruit value + score = 0; %Reset the score + update_screen(); %Update the screen + set(console_text,'String',{'WASD or Arrows key to select map','Space to start'}); + set(load_map_button,'Enable','on'); + end + %Function to load the selected map + function map_load(num) + if(num>0) + map = maze_map{num}; + map_sz = size(maze_map{num}); + map_sz_norm = map_sz/max(map_sz); + width = map_sz(2); + height = map_sz(1); + set(update_t,'Period',round(1000/speed(num))/1000); + max_value = ceil(60*map_sz(2)*map_sz(1)/900*speed(num)/12.5); + set(console_text,'FontSize',round(default_ftsize*map_sz_norm(2)*map_sz_norm(1))); + player_body = start_body{num}; + selected_colour = body_colour{num}; + player_len = length(player_body(1,:)); + if (player_len>1) + if(player_body(1,1)==player_body(1,2)) + if(player_body(2,1)>player_body(2,2)) + move_dir = 4; + else + move_dir = 3; + end + else + if(player_body(1,1)>player_body(1,2)) + move_dir = 2; + else + move_dir = 1; + end + end + else + move_dir = 2; + end + else + width = 30; + height = 30; + map = zeros(height,width,3); + set(update_t,'Period',default_updatetime); + max_value = 60; + set(console_text,'FontSize',default_ftsize); + player_body = zeros(2,3); %Reset the player body + selected_colour = [0.7 0.2 0.5]; + player_headx = round(30/2); + player_heady = round(30/2); + player_len = 3; + for n=1:player_len + player_body(1,n) = player_headx-n+1; + player_body(2,n) = player_heady; + end + move_dir = 2; + end + end + function load_map_pack(fname) + try + mzmp = load(fname); + maze_map = mzmp.map; + n_of_maps = length(maze_map); + map_name = mzmp.map_name; + speed = mzmp.speed; + start_body = mzmp.player_body; + body_colour = mzmp.colours; + map_number = 0; + map_load(map_number); + catch + errorbox = errordlg('mazemap.mat might not exist or not compatible within the directory. No levels loaded','modal'); + uiwait(errorbox); + maze_map = {}; + n_of_maps = 0; + map_number = 0; +% player_body = zeros(2,3); %Reset the player body +% player_headx = round(30/2); +% player_heady = round(30/2); +% player_len = 3; +% for n=1:player_len +% player_body(1,n) = player_headx-n+1; +% player_body(2,n) = player_heady; +% end + mapload(0); + end + end + %Function to delete the timer when the game is closed + function delete_t(~,~) + stop(update_t); + delete(update_t); + end + function select_mappack(~,~) + winpos = get(win,'Position'); + change_pack = 0;change_confirm = 0; + changepack = dialog('Name','Select a Map Pack','Position',[winpos(1:2)+winpos(3:4)/4 winpos(3:4)/1.5]); + matfiles = what; + matfiles = matfiles.mat; + file_listbox = uicontrol('Style','listbox','Units','normalized',... + 'Position',[0.1 0.1 0.5 0.85],... + 'String',matfiles); + uicontrol('Style','pushbutton','Units','normalized',... + 'Position',[0.7 0.7 0.2 0.2],... + 'String','Okay','Callback',@confirm_change); + uicontrol('Style','pushbutton','Units','normalized',... + 'Position',[0.7 0.5 0.2 0.2],... + 'String','Cancel','Callback',@cancel_change); + function confirm_change(~,~) + fnum = get(file_listbox,'value'); + change_pack = matfiles{fnum}; + change_pack = strrep(change_pack,'.mat',''); + change_confirm = 1; + close(changepack); + end + function cancel_change(~,~) + close(changepack); + end + uiwait(changepack); + + if(change_confirm) + load_map_pack(change_pack); + restart(); + confirm = msgbox('Load Complete','modal'); + uiwait(confirm); + end + set(load_map_button,'Enable','off') + drawnow; + set(load_map_button,'Enable','on') + end +end \ No newline at end of file