Professional Documents
Culture Documents
Super Mario MATLAB
Super Mario MATLAB
% History
% -------
% Date Updater Modification
% ---- ------- ------------
% Dec.30,2011 M. Zhang wrote it
% Jan.13,2012 M. Zhang converted to 32-color as used in FC
% Dec.29,2012 M. Zhang added sprites for skidding/jumping
% Jan.02,2013 M. Zhang added full support for walking/skidding
% and added a variable monitor
% Jan 11,2013 M. Zhang added primitive collision detection
% Jan 19,2013 M. Zhang added comments for subfunction ShowMario
% Feb 25,2013 M. Zhang added JUMPING (from standing position only)
% and LANDING; thus UPGRADED to 0.3.
% Feb 28,2013 M. Zhang added JUMPING (from walking and sprinting)
% and manipulation in the air.
% UPGRADED to 0.36
% Mar 01,2013 M. Zhang added JUMPING (from skidding)
% and refined air manipulation to
% better emulate the behavior of the
% Mario as in the original NES game.
% The dynamics of Mario is now fully
% programmed (without interaction
% with other objects)
% UPGRADED to 0.4
% Mar 02,2013 M. Zhang added COLLISION DETECTION for
% landing (only when Mario goes downwards);
% implemented correct handling for
% drawing and coll.det. when Mario
% approaches or goes out of the canvas
% boundary.
% UPGRADED to 0.45
% Mar 02,2013 M. Zhang VER 0.45 ARCHIVED
% Mar 02,2013 M. Zhang cleared obsolete functions,
% UPGRADED to 0.46
% Mar 02,2013 M. Zhang added FREE FALLING (from a ledge)
% UPGRADED to 0.55
% Mar 02,2013 M. Zhang VER 0.55 ARCHIVED
% Mar 03,2013 M. Zhang added COLLISION DETECTION (partially)
% during the rising phase.
% UPGRADED to 0.57
% Mar 03,2013 M. Zhang VER 0.57 ARCHIVED
% Mar 04,2013 M. Zhang completed COLLISION DETECTION for
% the rising phase.
% Upgraded to VER 0.58
% Mar 04,2013 M. Zhang VER 0.58 ARCHIVED
% Mar 23,2013 M. Zhang Fixed all noticeable bugs on collision
% detection. Now collision detection
% is (presumably) fully functional
% Attempted to use hitPoints instead
% of hit boxes but did not succeed.
% UPGRADED to 0.70
% Mar 23,2013 M. Zhang VER 0.70 ARCHIVED
% Containing the 'hitpoint' code
% Mar 24,2013 M. Zhang Cleaned up a bit
% Now handled collision and drawing
% when Mario reaches the edge of the
% stage
% Added several text messages
% UPGRADED to 0.90
% Mar 24,2013 M. Zhang VER 0.90 ARCHIVED
% Apr 19,2013 M. Zhang Changed Renderer from OpenGL to
% Painter, thus increased drawing
% speed on slow computers
% UPGRADED to 0.91
% Apr 19,2013 M. Zhang VER 0.91 ARCHIVED
% Apr 20,2013 M. Zhang Added Music!
% Commented out an output statement
% that causes error when
% Mario jumps outside the upper
% border
% UPGRADED to 1.00
% Apr 20,2013 M. Zhang Ver 1.00 ARCHIVED
% Apr 20,2013 M. Zhang * Added a try - catch block
% so that the window and audioplayer
% will be deleted upon error
% * Corrected a bug that occurs when
% Mario is ABOUT to land on the ground
% from an absolute horizontal falling
% * UPGRADED to 1.10
% Apr 20,2013 M. Zhang Ver 1.10 ARCHIVED
% ---- ------- ------------
% Copyright (C) Stellari Studio, 2011-2013
% Mingjing Zhang @ Vision & Media Lab, Simon Fraser University, Canada
%% Variable Declaration
try
MarioVer = '1.10'; % Last updated Apr 20, 2013
MainAxesSize = []; % The size of the main axes, same as GAME_RESOLUTION
GAME_RESOLUTION = []; % The resolution of the game screen, fixed at 240x256
FPS = []; % Frames-per-second, ideally over 60
nUpdatesPerSec = []; % Number of event updates per second, must be 60
FRAME_DURATION = []; % The duration of one single frame, ideally less than 1/60
MAX_FRAME_SKIP = []; % The maximum of frame skips allowed if the game runs
sluggishly
DEFAULT_FRAME_SKIP = []; %
MainFigureInitPos = []; % The initial position of the main figure
MainFigureSize = []; % The size of the figure
MainAxesInitPos = []; % The initial position of the axes IN the figure
CyclePalFrames = []; %
CyclePalIndices = [];
% Handles
MainFigureHdl = [];
MainAxesHdl = [];
CurrentStageBkgdHandle = [];
CurrentBrickHandle = [];
% Keyboard-related variables
KeyStatus = [];
LastKeyStatus = [];
LastFrameKeyStatus = [];
KeyNames = [];
KeyFuncs = [];
KeyLRStatus = 0;
KeyAccelStatus = 0;
KeyJumpAvailFlag = true;
% Collision Flags
ThisCollision = 0;
CloseReq = false;
key = [];
%% Initialization
initVariable;
initWindow;
allmusic = [];
if playSound
if exist('mario_music.mat','file')
allmusic = load('mario_music.mat');
else
warning('STL_MARIO_MAIN(): mario_music.mat not found. Music disabled');
playSound = false;
end
end
%% Load StageList
% 'StageList' is a cell array whose elements are the names (string) of the stage
% variables.
% 'StatSpriteLib' is a 16x16x3xN array containing the graphical data of each
% static sprite
% 'pal_all' contains the palette
StageList = [];
StatSpriteLib = [];
pal_all = [];
if exist('MarioData.mat','file')
load('MarioData.mat','MarioSprite');
else
error('STL_MARIO_MAIN(): MarioData.mat not found.');
end
themePlayer = [];
%% Main Game Cycle
for i_stage = 1:length(StageList)
%% Load the current stage
StageTemp = load('mario_stages.mat', StageList{i_stage});
CurrentStage = StageTemp.(StageList{i_stage});
%% Music Handling
if playSound
[CurrentStage.music CurrentStage.fs] = makeMusic();
if ~isempty(themePlayer)
delete(themePlayer);
end
% Add a single line at the bottom; This is very important to handle out
% of boundary pixels/collisions
curSpriteSize = CurrentStage.spriteSize;
curStageSize = curMapSize(1:2).*curSpriteSize(1:2);
end
end
end
set(CurrentStageBkgdHandle, 'CData', CurrentStageBkgd, 'Visible', 'on');
colormap(pal_all([CurrentStage.stageBGPal,CurrentStage.stageSPPal],:));
%% Main Game Loop for each stage
% Here, number of 'Frame' actually means the number of times the world
% gets updated. So named to keep consistent with the former game:
% Stellaria
cycle_total_frames = cumsum(CyclePalFrames);
frame_updated = false; % Whether the world is actually updated
if ShowFPS
fps_text_handle = text(10,10, 'FPS:60.0');
var_text_handle = text(10,20, 'Var = '); % Display a variable
total_frame_update = 0;
end
prompt_text_handle = text(30,80,'','Color',[1 1 1],...
'FontSize',24,...
'HorizontalAlignment','left');
% Show Mario
Mario.Visible = true;
if playSound; themePlayer.play(); end
stageStartTime = tic;
c = stageStartTime;
FPS_lastTime = toc(stageStartTime);
terminateFlag = false;
while 1
loops = 0;
curTime = toc(stageStartTime);
colormap_updated = false;
while (curTime >= ((CurrentFrameNo) * FRAME_DURATION) && loops <
MAX_FRAME_SKIP)
%% Process Mario
if ~terminateFlag
processMario;
end
if colormap_updated
set(MainFigureHdl,'colormap',pal_all([CurrentStage.stageBGPal
CurrentStage.stageSPPal],:));
end
drawnow;
% b = toc(a);
% lastTime = c;
c = toc(stageStartTime);
frame_updated = false;
if ShowFPS
total_frame_update = total_frame_update + 1;
% set(fps_text_handle,
'String',sprintf('%.2f',1./b),'Position', [newXLim(1)+10,10]);
varname = 'Mario.Direction';%'Mario.curFrame';
if mod(total_frame_update,SHOWFPS_FRAMES) == 0 % If time to update
fps
set(fps_text_handle, 'String',sprintf('FPS:
%.2f',SHOWFPS_FRAMES./(c-FPS_lastTime)));
FPS_lastTime = toc(stageStartTime);
end
set(var_text_handle, 'String', sprintf('%s = %.2f', varname,
eval(varname)));
end
% CloseReq = true;
elseif Mario.Pos(1) > curStageSize(2) - 400
set(prompt_text_handle,'String', 'Please leave on the right side of
the stage','FontSize',16);
end
end
if CloseReq
delete(MainFigureHdl);
if exist('themePlayer','var') &&
strcmp(get(themePlayer,'Type'),'audioplayer')
themePlayer.stop();
delete(themePlayer);
end
clear all;
return;
end
end
end
catch err
delete(MainFigureHdl);
if exist('themePlayer','var') && strcmp(get(themePlayer,'Type'),'audioplayer')
themePlayer.stop();
delete(themePlayer);
end
rethrow(err);
% return
end
%% ---------------- Regular Subfunctions ----------------------------------
%% Initializations
function initVariable()
% initVariable - initialize variables
% a = [3 4]
% b =[5 6 7];
% a = b([1 2 3]);
MainAxesSize = [256 240];
GAME_RESOLUTION = MainAxesSize;
FPS = 60;
% nUpdatesPerSec = 60;
SPRITE_PAL_SIZE = 4;
MSPRITE_OFFSET = 16;
FRAME_DURATION = 1./FPS;
MAX_FRAME_SKIP = 5;
% DEFAULT_FRAME_SKIP = 2;
MainFigureSize = MainAxesSize .* 2;
MainFigureInitPos = [300 50];
MainAxesInitPos = [0 0];
KeyNames = {'w','s','a','d','j','k','return','space'};
% KeyFuncs = {'up','down','left','right','sprint','jump','start','select'};
KeyStatus = false(1, length(KeyNames));
LastKeyStatus = KeyStatus;
LastFrameKeyStatus = KeyStatus;
ShowFPS = true;
CyclePalFrames = [24 8 8 8];
CyclePalIndices = [40 24 8 24];
COLLISION.TOP = 1;
COLLISION.LEFT = [2 3];
COLLISION.RIGHT = [4 5];
COLLISION.BOTTOM = [6 7];
% -------------------------
CONST_STOP_SKIDDING_V = 0.6;
CONST_NFRAME_SPRINT_DEACCX = 10;
sprint_deaccx_counter = CONST_NFRAME_SPRINT_DEACCX;
%% Mario Initialization
% Objects
Mario.bodyState = BODY_STAT.SMALL; % 1: small, 2: big, 3: fiery
Mario.Direction = DIRECTION.RIGHT; % 1: right, -1: left
Mario.curAction = ACTION.STANDING; % 1: standing, 2: walking, 3: jumping,
4: skidding
Mario.curFrame = 1; % 1-3 for walking and 1 for all other cases
Mario.curFrameLim = 1;
Mario.nextAction = ACTION.STANDING;
Mario.nextFrame = 1;
Mario.nextFrameLim = 1;
Mario.nextDirection = Mario.Direction;
Mario.onGround = true; % true if standing on the ground; false otherwise
Mario.nextOnGround = true;
Mario.motionStat = MOTION_STAT.NOTHING; % 1: normal walking 2: sprinting,
3: jumping, 4: falling
Mario.nextMotionStat = Mario.motionStat;
Mario.palset0 = 0;
Mario.Pos = [40 176]; % X = 40 ,Y = 176
Mario.NextPos = Mario.Pos;
Mario.Vxy = [0 0];
Mario.NextVxy = Mario.Vxy;
Mario.Axy = [0 0];
Mario.Gravity = 0;
Mario.pixelsy = 2:33;
Mario.pixelsx = 1:16;
Mario.Handle = 0; % Image handle for Mario (may not be used)
Mario.Visible = true; % Visibility
% Mario.WalkFrameRepeatCount = 6;
% Mario.WalkFrameRepeatLim = 7;
Mario.curWalkCycle = Mario.walkCycle(1);
Mario.corr = [0 0]; % position cor5rection on x and y
Mario.airSpeedLimitIdx = 1;
Mario.velocityXChanged = false;
Mario.vxFlipCounter = 0;
function initWindow()
% initWindow - initialize the main window, axes and image objects
MainFigureHdl = figure('Name', ['Mario MAT ' MarioVer], ...
'NumberTitle' ,'off', ...
'Units', 'pixels', ...
'Position', [MainFigureInitPos, MainFigureSize], ...
'MenuBar', 'figure', ...
'Renderer', 'Painter',...\
'UserData', 'stl_mario_main',...
'KeyPressFcn', @stl_KeyDown,...
'KeyReleaseFcn', @stl_KeyUp,...
'CloseRequestFcn', @stl_CloseReqFcn);
MainAxesHdl = axes('Parent', MainFigureHdl, ...
'Units', 'normalized',...
'Position', [MainAxesInitPos, 1-MainAxesInitPos.*2], ...
'color', [0 0 0], ...
'XLim', [0 MainAxesSize(1)]-0.5, ...
'YLim', [0 MainAxesSize(2)]-0.5, ...
'YDir', 'reverse', ...
'NextPlot', 'add', ...
'Visible', 'on', ...
'XTick',[], ...
'YTick',[]);
CurrentStageBkgdHandle = image(0, 0, [],...
'Parent', MainAxesHdl,...
'Visible', 'off');
CurrentBrickHandle = image(0, 0, [], ...
'Parent', MainAxesHdl,...
'Visible', 'off');
Mario.Handle = image(0, 0, [], ...
'Parent', MainAxesHdl, ...
'Visible', 'off');
end
%% Game logics
%% Collision Detection
function [hitStat] = detCollision()
hitStat = false(1,4);
if Mario.bodyState == BODY_STAT.SMALL;
nVertBlocks = 2;
MarioMatPos = Mario.Pos([2 1])./curSpriteSize + [2 1]; % in [y x], 1-
indexed
MarioNextMatPos = Mario.NextPos([2 1])./curSpriteSize + [2 1];
MarioLocalIndsRow = floor(MarioNextMatPos(1)) + [0 1]; %
floor(MarioNextMatPos(1))+1];
else
nVertBlocks = 3;
MarioMatPos = Mario.Pos([2 1])./curSpriteSize + [1 1]; % in [y x], 1-
indexed
MarioNextMatPos = Mario.NextPos([2 1])./curSpriteSize + [1 1];
MarioLocalIndsRow = floor(MarioNextMatPos(1)) + [0 1 2];
end
MarioLocalIndsCol = [floor(MarioNextMatPos(2)), ceil(MarioNextMatPos(2))];
nHoriBlocks = 2;
ColOverlap = floor(ColOverlap(ones(1,nVertBlocks),:));
RowOverlap = floor(RowOverlap(:,ones(1,nHoriBlocks)));
thisSide = MarioLocalEnv(1,:);
marioTopCollide = thisSide & RowOverlap(1,:) >= 2 & ColOverlap(1,:) >=
[8+1e-10 8];
if Mario.Vxy(2) < 0
% If Mario is jumping up
% As for the block on the left, Mario must has MORE THAN 8 pixels under
% it to be able to hit it; For the right one, EXACTLY 8 pixels are
% enough
if any(marioTopCollide)
% which block is hit
ThisMovedBlock = [MarioLocalIndsRow(1)
MarioLocalIndsCol(marioTopCollide)];
% Mario.NextPos(2) = Mario.Pos(2); % Do not bounce
Mario back %[MarioLocalIndsRow(1)-1].*curSpriteSize(1);
Mario.NextVxy(2) = 0;
Mario.nextMotionStat = MOTION_STAT.JUMPING.DOWN;
hitStat(HIT_STAT.UP) = 1;
else
hitStat(HIT_STAT.UP) = 0;
% if ~all(MarioLocalEnv(1,:)) %marioLeftCollide &&
marioRightCollide
if marioLeftCollide % If any collision with objects horizontally,
% then Mario is not bounced back immediately.
% the solid block(s) would slowly push Mario outward
if Mario.Vxy(1) <= 0
Mario.NextVxy(1) = 0;
Mario.NextPos(1) = Mario.NextPos(1) +
min(1,ColOverlap(1,1)-2);
hitStat(HIT_STAT.LEFT) = true;
hitStat(HIT_STAT.RIGHT) = false;
end
elseif marioRightCollide
if Mario.Vxy(1) >= 0
Mario.NextVxy(1) = 0;
Mario.NextPos(1) = Mario.NextPos(1) -
min(1,ColOverlap(1,end)-2);
hitStat(HIT_STAT.LEFT) = false;
hitStat(HIT_STAT.RIGHT) = true;
end
else
hitStat(HIT_STAT.LEFT) = false;
hitStat(HIT_STAT.RIGHT) = false;
end
end
end
% ThisCollision = 1;
hitStat(HIT_STAT.DOWN) = 1;
marioLeftCollide = any(MarioLocalEnv(1:end-1,1) & ColOverlap(1:end-
1,1) > 2);
marioRightCollide = any(MarioLocalEnv(1:end-1,end) &
ColOverlap(1:end-1,end) > 2);
marioOverallCollide = double(marioBottomCollide) +
double([marioLeftCollide marioRightCollide]);
if any(marioRightCollide)
% if ~any(marioRightCollide(1:end-1)|
marioRightSlightCollide(1:end-1))
% Mario.NextPos(2) = Mario.NextPos(2) - RowOverlap(end,2);
% hitStat(HIT_STAT.DOWN) = true;
% %
% else
Mario.NextVxy(1) = 0;
Mario.NextPos(1) = Mario.NextPos(1) -
min(1,ColOverlap(1,end)-2);
hitStat(HIT_STAT.LEFT) = false;
hitStat(HIT_STAT.RIGHT) = true;
if marioBottomCollide(1)
hitStat(HIT_STAT.DOWN) = true;
else
hitStat(HIT_STAT.DOWN) = false;
end
% end
elseif any(marioLeftCollide)
Mario.NextVxy(1) = 0;
Mario.NextPos(1) = Mario.NextPos(1) + min(1,ColOverlap(1,1)-2);
hitStat(HIT_STAT.LEFT) = true;
hitStat(HIT_STAT.RIGHT) = false;
if marioBottomCollide(2)
hitStat(HIT_STAT.DOWN) = true;
else
hitStat(HIT_STAT.DOWN) = false;
end
end
elseif any(thisSide)
% If Mario is not supported by anything under his feet, but there is
something in its local surroundings
hitStat(HIT_STAT.DOWN) = false;
if all(MarioLocalEnv(1,:)) %marioRightCollide &&
marioLeftCollide
% I really hope something like this will never
% happen
% hitStat(HIT_STAT.UP) = 1;
elseif any(marioRightCollide)
if ~any(marioTopCollide)
if Mario.NextVxy(1) >= 0
Mario.NextVxy(1) = 0;
end
Mario.NextPos(1) = Mario.NextPos(1) -
min(1,ColOverlap(1,end)-2);
hitStat(HIT_STAT.LEFT) = false;
hitStat(HIT_STAT.RIGHT) = true;
hitStat(HIT_STAT.DOWN) = false;
end
elseif any(marioLeftCollide)
if ~any(marioTopCollide)
if Mario.NextVxy(1) <= 0
Mario.NextVxy(1) = 0;
end
Mario.NextPos(1) = Mario.NextPos(1) +
min(1,ColOverlap(1,1)-2);
hitStat(HIT_STAT.LEFT) = false;
hitStat(HIT_STAT.RIGHT) = true;
hitStat(HIT_STAT.DOWN) = false;
end
end
end
end
% ThisCollision = marioRightCollide;
end
%% Process Mario
function processMario()
% Update Position/Velocity
if ~Mario.onGround && Mario.Vxy(1)*Mario.NextVxy(1) <= 0 &&
Mario.Vxy(1)~=Mario.NextVxy(1)
Mario.velocityXChanged = true;
Mario.vxFlipCounter = Mario.vxFlipCounter + 1;
else
Mario.velocityXChanged = false;
end
Mario.Vxy = Mario.NextVxy;
Mario.Pos = Mario.NextPos;
% Update Status
Mario.onGround = Mario.nextOnGround;
Mario.curAction = Mario.nextAction; % STANDING/WALKING/SKIDDING
Mario.curFrame = Mario.nextFrame; %
Mario.curFrameLim = Mario.nextFrameLim;
Mario.Direction = Mario.nextDirection;
Mario.motionStat = Mario.nextMotionStat;
% Keyboard Processing
% Left and Right Status
KeyLRStatus = KeyStatus(4) - KeyStatus(3);
LastKeyLRStatus = LastFrameKeyStatus(4) - LastFrameKeyStatus(3);
KeyAccelStatus = KeyStatus(5);
LastKeyAccelStatus = LastFrameKeyStatus(5);
KeyJumpStatus = KeyStatus(6);
if Mario.onGround
if ~KeyJumpStatus
KeyJumpAvailFlag = true; % If Mario's on the ground and the jump
key was released
end
if Mario.curAction == ACTION.STANDING
if KeyJumpStatus && KeyJumpAvailFlag % If Jump Key is pressed,
then jump
% and after landing, the user must release the jump key
% in order to jump again.
Mario.minvy = 0;
Mario.Axy(2) = 0;
Mario.Gravity = Mario.ACCELY_UP(1);
Mario.accay = Mario.Gravity;
Mario.accay_carry = 0;
Mario.minvy_carry = 0;
Mario.nextAction = ACTION.JUMPING; % Let's jump!
Mario.nextOnGround = false;
Mario.nextMotionStat = MOTION_STAT.JUMPING.UP;
Mario.jumpInitStat = 1; % speed less than 0x10
Mario.UPGravity = Mario.ACCELY_UP(Mario.jumpInitStat);
Mario.DOWNGravity = Mario.ACCELY_DOWN(Mario.jumpInitStat);
Mario.Vxy(2) = -Mario.VY_MAX(Mario.jumpInitStat);
Mario.airSpeedLimitIdx = 2;
if KeyLRStatus~=0 % If jumping and left or right is pressed
% That's not going to change Mario's direction
% And the acceleration key doesn't work in the air
Mario.Axy(1) = Mario.ACCELX(1).*Mario.Direction; %
Accelerating
end
Mario.nextFrame = 1;
KeyJumpAvailFlag = false;
Mario.vxFlipCounter = 0;
else
if KeyLRStatus~=0 % If not jumping but left or right is
pressed
Mario.nextAction = ACTION.WALKING; % Start walking
Mario.nextDirection = KeyLRStatus;
Mario.Axy(1) =
Mario.ACCELX(1+KeyAccelStatus).*Mario.nextDirection; % Accelerating
Mario.nextFrame = 1; % Start from the first
frame
Mario.nextFrameLim = 3; % 3 Frames in the
walking cycle
Mario.walkCounter = 6; % The first movement
lasts only 1 frame (7-6)
end
end
elseif Mario.curAction == ACTION.WALKING
if KeyJumpStatus && KeyJumpAvailFlag
Mario.minvy = 0;
Mario.Axy(2) = 0;
if abs(Mario.Vxy(1)) <= Mario.gravityThreshold(1)+0.001
Mario.jumpInitStat = 1; % speed less than 0x10
Mario.airSpeedLimitIdx = 1;
else
Mario.jumpInitStat = 3;
Mario.airSpeedLimitIdx = 2;
end
Mario.UPGravity = Mario.ACCELY_UP(Mario.jumpInitStat);
Mario.DOWNGravity = Mario.ACCELY_DOWN(Mario.jumpInitStat);
Mario.Vxy(2) = -Mario.VY_MAX(Mario.jumpInitStat);
Mario.Gravity = Mario.UPGravity;
Mario.accay = Mario.Gravity;
Mario.accay_carry = 0;
Mario.minvy_carry = 0;
Mario.nextAction = ACTION.JUMPING; % Let's jump!
Mario.nextOnGround = false;
Mario.nextMotionStat = MOTION_STAT.JUMPING.UP;
if KeyLRStatus~=0 % If jumping and left or right is pressed
% That's not going to change Mario's direction
% i.e. if Mario started with facing right, then he
% will always face right in the air.
% And the acceleration key doesn't work in the air
Mario.Axy(1) = Mario.ACCELX(1).*Mario.Direction; %
Accelerating
end
Mario.nextFrame = 1;
KeyJumpAvailFlag = false;
Mario.vxFlipCounter = 0;
else
if KeyLRStatus~=0 % If not jumping but left or right is
pressed
if KeyAccelStatus % If this button is pressed
sprint_deaccx_counter = CONST_NFRAME_SPRINT_DEACCX;
else
sprint_deaccx_counter = max(sprint_deaccx_counter-1,0);
end
if KeyLRStatus == Mario.Direction % If the key is of the
same direction as Mario goes
% disp((KeyAccelStatus &&
sprint_deaccx_counter >0));
% disp(Mario.Axy(1));
Mario.Axy(1) = Mario.ACCELX(1+(KeyAccelStatus |
sprint_deaccx_counter >0) ).*Mario.Direction; % then accelerate Mario
% And there is no need to change the status
elseif KeyLRStatus == -Mario.Direction % If the key is of
the opposite direction
% disp(Mario.Vxy(1))
if abs(Mario.Vxy(1)) < CONST_STOP_SKIDDING_V % If the
Velocity is low
Mario.nextDirection = -Mario.Direction;
Mario.Vxy(1) =
Mario.ACCELX(1).*Mario.nextDirection; % Then immediately turn around
Mario.Axy(1) =
Mario.ACCELX(1).*Mario.nextDirection; % And accelerate in that direction
Mario.nextFrame = 1; % Start from
the first frame
Mario.walkCounter = 6;
else
if abs(Mario.Vxy(1))<=1.5
deaccel_skid = Mario.DEACCX_SKID(1).* ...
abs(Mario.Vxy(1)) + Mario.DEACCX_SKID(2);
else
deaccel_skid = Mario.DEACCX_SKID(3);
end
Mario.Axy(1) = -deaccel_skid.*Mario.Direction; %
then deaccelerate Mario
Mario.nextAction = ACTION.SKIDDING; % Start
skidding
Mario.nextFrame = 1;
Mario.nextFrameLim = 1;
end
end
else % If no left or right key is pressed
% then start deaccelerating
if LastKeyLRStatus == Mario.Direction
if abs(Mario.Vxy(1)) >= Mario.speedThresholdsDown(2)
deaccel_ind = 2;
else
deaccel_ind = 1;
end
end
Mario.Axy(1) =
-Mario.DEACCELX(deaccel_ind).*Mario.Direction;
% if the velocity is going to be zero or minus in
% the next frame, then stop Mario
if abs(Mario.Axy(1) + Mario.Vxy(1))<1e-3 ...
|| sign((Mario.Axy(1) + Mario.Vxy(1))*Mario.Vxy(1))
< 1 ...
|| (KeyAccelStatus &&
abs(Mario.Vxy(1))<Mario.speedThresholdsDown(1))
% disp(Mario.Vxy(1));
Mario.nextAction = ACTION.STANDING; % Stop walking
% disp(Mario.nextAction);
Mario.Vxy(1) = 0;
Mario.Axy(1) = 0;
% Mario.Axy(1) =
-Mario.Vxy(1); % Make sure the velocity is 0 on the next frame
Mario.nextFrame = 1; % Start from the
first frame
Mario.nextFrameLim = 1; % only 1 frame in
the 'standing cycle'
end
end
end
elseif Mario.curAction == ACTION.SKIDDING
% disp(Mario.Vxy(1));
if KeyJumpStatus && KeyJumpAvailFlag
Mario.minvy = 0;
Mario.Axy(2) = 0;
Mario.accay_carry = 0;
Mario.minvy_carry = 0;
Mario.nextAction = ACTION.JUMPING; % Let's jump!
Mario.nextOnGround = false;
Mario.nextMotionStat = MOTION_STAT.JUMPING.UP;
Mario.nextDirection = -Mario.Direction;
else
if KeyLRStatus~=0 % If not jumping but left or right is
pressed
if KeyLRStatus == Mario.Direction % If the key is of the
same direction as Mario proceeds in
% Back to walking
Mario.nextAction = ACTION.WALKING; % Start walking
Mario.nextDirection = KeyLRStatus;
Mario.Axy(1) =
Mario.ACCELX(1+KeyAccelStatus).*Mario.nextDirection; % Accelerating
Mario.nextFrame = 1; % Start from the
first frame
Mario.nextFrameLim = 3; % 3 Frames in the
walking cycle
Mario.walkCounter = 6;
elseif KeyLRStatus == -Mario.Direction % If the key is of
the opposite direction
if abs(Mario.Vxy(1)) < CONST_STOP_SKIDDING_V % If the
Velocity is low
Mario.nextAction = ACTION.WALKING; % Start walking
Mario.nextDirection = -Mario.Direction;
Mario.Vxy(1) =
Mario.ACCELX(1).*Mario.nextDirection; % Then immediately turn around
Mario.Axy(1) =
Mario.ACCELX(1).*Mario.nextDirection; % And accelerate in that direction
Mario.nextFrame = 1; % Start from
the first frame
Mario.walkCounter = 6;
else
% No need to change
end
end
else
if abs(Mario.Vxy(1)) < CONST_STOP_SKIDDING_V % If the
Velocity is low
Mario.nextAction = ACTION.WALKING; % Start walking
Mario.nextDirection = -Mario.Direction;
Mario.Vxy(1) = Mario.ACCELX(1).*Mario.Direction;
% Then immediately turn around but still
% goes in the same direction
Mario.Axy(1) = Mario.ACCELX(1).*Mario.nextDirection; %
And accelerate in that direction
Mario.nextFrame = 1; % Start from the
first frame
Mario.walkCounter = 6;
else
end
end
end
end
end
else % If initial speed is higher than 0x18
if Mario.Vxy(1)*Mario.Direction > 0 % If Mario has forward
speed
Mario.Axy(1) = Mario.ACCELX(2).*KeyLRStatus;
else % If the speed is negative
Mario.Axy(1) =
Mario.JUMP_BACK_ACCELX(Mario.jumpInitStat).*KeyLRStatus;
% Mario.jumpInitStat = 2;
end
if Mario.vxFlipCounter >= 2;
% If Mario changes its speed direction twice in
% the air, then it will be switch back to
% jumpstat 2.
Mario.jumpInitStat = 2;
end
if abs(Mario.Vxy(1)) <= Mario.gravityThreshold(2)
% If the speed ever falls below 18h, even just
% for a split second, then the speed limit
% cannot go back to 28h
Mario.airSpeedLimitIdx = 1;
end
end
else % If no button is pressed, then maintain the old speed
Mario.Axy(1) = 0;
end
end
end
% WALKING NEEDS SPECIAL PROCESSING
if Mario.nextAction == ACTION.WALKING
Mario.walkCounter = Mario.walkCounter + 1; % Update Walk Counter
% Still count even when
%% If a new frame cycle starts
if Mario.onGround && Mario.walkCounter > Mario.curWalkCycle
Mario.walkCounter = 1; % Restarts the frame cycle
Mario.nextFrame = Mario.curFrame + 1; % Go to the next frame
Mario.nextFrameLim = 3;
% Update WalkCounter if necessary
if sign(Mario.Axy(1)) == Mario.Direction % If accelerating
if abs(Mario.Vxy(1)) < Mario.speedThresholdsUp(1)
Mario.curWalkCycle = Mario.walkCycle(1);
elseif abs(Mario.Vxy(1)) < Mario.speedThresholdsUp(2)
Mario.curWalkCycle = Mario.walkCycle(2);
else
Mario.curWalkCycle = Mario.walkCycle(3);
end
elseif sign(Mario.Axy(1)) == -Mario.Direction
if abs(Mario.Vxy(1)) < Mario.speedThresholdsDown(1)
Mario.curWalkCycle = Mario.walkCycle(1);
elseif abs(Mario.Vxy(1)) < Mario.speedThresholdsDown(2)
Mario.curWalkCycle = Mario.walkCycle(2);
else
Mario.curWalkCycle = Mario.walkCycle(3);
end
end
end
end
Mario.NextVxy = Mario.Vxy + Mario.Axy;
if Mario.onGround
Mario.NextVxy(1) = valchop(Mario.NextVxy(1), Mario.VX_MAX(1+
(KeyAccelStatus | sprint_deaccx_counter >0)));
else
Mario.NextVxy(1) = valchop(Mario.NextVxy(1),
Mario.VX_MAX(Mario.airSpeedLimitIdx));
end
% Mario.nextFrame = Mario.curFrame;
Mario.UPGravity = Mario.ACCELY_UP(Mario.jumpInitStat);
Mario.DOWNGravity = Mario.ACCELY_DOWN(Mario.jumpInitStat);
Mario.nextVxy(2) = 0;
Mario.Gravity = Mario.DOWNGravity;
Mario.accay = 0; %Mario.Gravity;
Mario.accay_carry = 0;
Mario.minvy_carry = 0;
Mario.nextOnGround = false;
Mario.nextMotionStat = MOTION_STAT.JUMPING.FALL;
if Mario.curAction == ACTION.SKIDDING
Mario.nextDirection = -Mario.Direction;
else
Mario.nextDirection = Mario.Direction;
end
end
end
if hitStat(HIT_STAT.UP)
Mario.nextMotionStat = MOTION_STAT.JUMPING.DOWN;
Mario.nextVxy(2) = 0;
end
LastFrameKeyStatus = KeyStatus;
% if KeyAccelStatus
% % fprintf('%.3f,%.3f,%s,%s,%d\n',Mario.NextPos(1), Mario.NextPos(2),
dec2hex(floor(Mario.NextPos(1))), dec2hex(floor(Mario.NextPos(2))),Mario.onGround);
% end
end
function showMario()
if Mario.Visible
% Take a small piece out of the big canvas that is exactly as
% large as the Mario Sprite.
marioBkg = CurrentStageBkgd(vertRange(vertValidIndex),
horiRange(horiValidIndex),:);
if Mario.Direction == DIRECTION.RIGHT
curMarioData =
MarioSprite(Mario.curAction).CData(vertValidIndex,horiValidIndex,Mario.curFrame);
curMarioAlpha =
MarioSprite(Mario.curAction).AlphaData(vertValidIndex,horiValidIndex,Mario.curFrame
);
else
curMarioData =
MarioSprite(Mario.curAction).CData(vertValidIndex,horiValidIndex(end:-
1:1),Mario.curFrame);
curMarioAlpha =
MarioSprite(Mario.curAction).AlphaData(vertValidIndex,horiValidIndex(end:-
1:1),Mario.curFrame);
curMarioData = curMarioData(:,end:-1:1);
curMarioAlpha = curMarioAlpha(:,end:-1:1);
end
% Reverse the Mario sprite if he is facing left
end
%% Callback functions
function stl_KeyUp(hObject, eventdata, handles)
LastKeyStatus = KeyStatus;
key = get(hObject,'CurrentKey');
KeyStatus = (~strcmp(key, KeyNames) & LastKeyStatus);
end
function stl_KeyDown(hObject, eventdata, handles)
LastKeyStatus = KeyStatus;
key = get(hObject,'CurrentKey');
if keynum == 2
tone=rand(1,length(tt));
return;
end
if keynum == 0
return;
end
basefreq=440*2^((keynum-49)/12);
freqs = [1 3 5 7 9]*basefreq;
res_coeffs = [.75 .65 .5 .222 .12 1]; % the last number is not used
for i = 1:min(numel(freqs),numel(res_coeffs)) % Use the smaller length
tone = tone + res_coeffs(i) * sin( 2*pi*freqs(i) * tt);
end
end