WiiLab Example Code - Pong
Note: MATLAB R2007a or greater is required for this code to run correctly. The program uses the Wiimote class, along with the UND function set EG111-H.
Annotations
This program is a Pong simulator that allows two players to play a game of Pong using the Wiimote's Y-axis acceleration to move the paddles. The game requires two Wiimotes to play. There is no one-player version yet, though I hope to finish and upload it soon.
One thing to note is that this demo game is different from the other demos because it uses the Wiimote class directly instead of through the global functions. This is because you can only use one Wiimote when using the global functions.
Program Breakdown
The program is fairly large so I have broken it down into its vital blocks to describe each in detail. The complete file is attached at the bottom of the page.
Initialization
The program starts by connecting to the Wiimotes and making sure there are enough to play. I create two instances of the Wiimote class, wiimote1 and wiimote2, and try to connect to two Wiimotes. If we were unable to connect to one of the Wiimotes, for any reason, then we disconnect the Wiimotes and run the DisconnectAllWiimotes method to clear the connected Wiimotes' variables of the WiimoteCOM in case some Wiimotes were incorrectly disconnected from earlier applications and their connection is still believed to be active.
% Create an instance of the wiimote and connect it
wiimote1 = Wiimote();
wiimote2 = Wiimote();
wiimote1.Connect();
wiimote2.Connect();
if(wiimote1.isConnected <= 0 || wiimote2.isConnected <= 0)
% There were not enough available Wiimotes
% Disconnect all wiimotes to initialize new wiimotes
wiimote1.Disconnect();
wiimote2.Disconnect();
wiimote1.DisconnectAllWiimotes();
% Attempt to reconnect
wiimote1.Connect();
wiimote2.Connect();
end
After the Wiimotes are connected we are going to initialize the game window. This involves the following:
- Creating the main window
- Setting the background color to Black
- Drawing the Center Line (an array of rectangles)
- Drawing the initial player scores
- Drawing the users' paddles and ball
% Initialize Window
% -----------------------------------------------------------------
% Init window
initializeWindow(0);
% set background color
set(gca,'Color','black');
% Draw Center Line
for i = 0 : 19
drawRectangle(149, (5 + i*15), 2, 5, [.7 .7 .7]);
end
% Draw score
p1_score = 0;
p2_score = 0;
p1_score_disp = writeText(80,280,'0');
set( p1_score_disp, 'FontWeight', 'bold', ...
'FontSize', 64, 'FontName', 'OCR A Extended', ...
'Color', [.7 .7 .7]);
p2_score_disp = writeText(180,280,'0');
set( p2_score_disp, 'FontWeight', 'bold', ...
'FontSize', 64, 'FontName', 'OCR A Extended', ...
'Color', [.7 .7 .7]);
% Draw user paddles
player1_Paddle = drawRectangle(5, 135, paddleSize_x * 2, paddleSize_y * 2, 'white');
player2_Paddle = drawRectangle(290, 135, paddleSize_x * 2, paddleSize_y * 2, 'white');
% Draw ball
ball = drawRectangle((150 - ballSize), (150 - ballSize), (ballSize * 2), (ballSize * 2), 'white');
needNewBall = false;
% Initialize ball speed
ydir = (rand * 2) - 1;
xdir = ballSpeed;
% -----------------------------------------------------------------
Main Loop
For the Pong program, our main loop will execute until one of two things happens: either "HOME" is pressed on the Wiimote or one of the players reaches a score of 11.
Inside the loop the following things are done:
- Update the Wiimote state. The UpdateWiimoteState method of the Wiimote class will get both the button and acceleration values of the Wiimote, so it is the perfect choice for our use here. It is also possible to get either one separately if that is needed.
- Move the paddles based on the Y-axis acceleration of each Wiimote. (For details on how this is done see below)
- Move the ball. (For details on how this is done see below)
- Check if someone has won.
- Redraw and Pause. This is one of the most important pieces of any program like this. You need to make sure that you have a pause(xx) at the end of every loop that uses the Wiimote or EG111-H functions. MATLAB will not execute any callback functions until it reaches a pause(xx). If the pause is omitted you will begin to experience unusual behavior from the Wiimote class.
while (~wiimote1.Buttons(9) && ~gameover)
% Get the wiimote status
wiimote1.UpdateWiimoteState();
wiimote2.UpdateWiimoteState();
% Move the paddle with Accel mapped directly to velocity
% player 1
%--------------------------------------------------------------
%--------------------------------------------------------------
% player 2
%--------------------------------------------------------------
%--------------------------------------------------------------
% Move the ball
%---------------------------------------------------------------
%---------------------------------------------------------------
% make a new ball if needed
% check if someone has won
if (p1_score == 11)
winner = 1;
gameover = true;
elseif (p2_score == 11)
winner = 2;
gameover = true;
end
% Redraw the screen and pause
redraw();
pause(0.01);
end % while
Move Paddle
The movement of each player's paddle is controlled by the Y-axis velocity of the Wiimote.
I have included checks in the code to make sure the paddle does not go off the screen. The pseudocode is below:
- if the paddle will end up inside the bounds * then move the paddle as normal
- if the paddle will end up above the upper bound * then move the paddle the rest of the distance to the boundary and no further
- if the paddle will end up below the lower bound * then move the paddle the rest of the distance to the boundary and no further
% Move the paddle with Accel mapped directly to velocity
% player 1
%--------------------------------------------------------------
vely1 = -1 * wiimote1.Accel(2) * velDamping;
[x_p1 y_p1] = getCenter(player1_Paddle);
if( ((y_p1 + paddleSize_y) + vely1 < 300) && ((y_p1 - paddleSize_y) + vely1 > 0) )
yMove(player1_Paddle, vely1);
elseif ( ((y_p1 + paddleSize_y) + vely1 > 300) && ((300 - (y_p1 + paddleSize_y)) > 0) )
yMove(player1_Paddle, (300 - (y_p1 + paddleSize_y)));
elseif ( ((y_p1 - paddleSize_y) + vely1 < 0) && (0 - (y_p1 - paddleSize_y) < 0) )
yMove(player1_Paddle, (0 - (y_p1 - paddleSize_y)));
end
%--------------------------------------------------------------
Move Ball
This section of code moves the ball. Moving the ball is fairly simple; all we do is store the current x and y velocity (direction) of the ball. Before we move the ball every time we need to check if the ball is about to hit a wall or a paddle. This is accomplished by finding the center of the ball and comparing it to the positions of the paddles and the walls. If they overlap all that needs to be done is negate the direction that would hit the wall.
The Move Ball section also checks if a player has scored. Just like determining if the ball needs to change direction we have to check if the ball has gone past one of the player's paddles. If so, we need to reset the game, create a new ball, and increment the other player's score.
% Move the ball
%---------------------------------------------------------------
[x_ball y_ball] = getCenter(ball);
% if it is against the upper or lower wall switch its ydir
if( ((y_ball + ballSize) > 300) || ((y_ball - ballSize) < 0) )
ydir = -1 * ydir;
end
% If the ball is behind player 1's line
if( ((x_ball + ballSize) > 290) )
if( ((y_ball - ballSize) < y_p2 + paddleSize_y) ...
&& ((y_ball + ballSize) > y_p2 - paddleSize_y) )
xdir = -1 * xdir;
ydir = vely2 * (.5 * velDamping);
else
p1_score = p1_score + 1;
set(p1_score_disp, 'string', p1_score);
needNewBall = true;
end
% Actually move the ball
xMove(ball, xdir);
yMove(ball, ydir);
%--------------------------------------------------------------
Raw Source Code
The source code is also available as a .m file download at the bottom of the page.
% Pong - Recreation using the Nintendo Wiimote as a controller
%
% Author: Jordan Brindza, University of Notre Dame
% add the path containing simple graphics funcions
addpath .\EG111-H
% add the path containing the wiimote functions
addpath .\WiimoteFunctions
% Constants
velDamping = 1; % # <=1 to scale the incoming accel to vel
ballSize = 2; % # indicating the ball size (1/2 the actual size)
paddleSize_x = 2.5; % # indicating the x paddle size (1/2 of the actual size)
paddleSize_y = 10; % # indicating the y paddle size (1/2 of the actual size)
ballSpeed = 2; % # indicating the total speed of the ball
gameover = false;
% Disconnect all wiimotes to initialize new wiimotes
wiimote = Wiimote();
wiimote.DisconnectAllWiimotes();
% Create an instance of the wiimote and connect it
wiimote1 = Wiimote();
wiimote2 = Wiimote();
wiimote1.Connect();
wiimote2.Connect();
% If two wiimotes were found
if(wiimote1.isConnected > 0 && wiimote2.isConnected > 0)
% Initialize Window
% -----------------------------------------------------------------
% Init window
initializeWindow(0);
% set background color
set(gca,'Color','black');
% Draw Center Line
for i = 0 : 19
drawRectangle(149, (5 + i*15), 2, 5, [.7 .7 .7]);
end
% Draw score
p1_score = 0;
p2_score = 0;
p1_score_disp = writeText(80,280,'0');
set( p1_score_disp, 'FontWeight', 'bold', ...
'FontSize', 64, 'FontName', 'OCR A Extended', ...
'Color', [.7 .7 .7]);
p2_score_disp = writeText(180,280,'0');
set( p2_score_disp, 'FontWeight', 'bold', ...
'FontSize', 64, 'FontName', 'OCR A Extended', ...
'Color', [.7 .7 .7]);
% Draw user paddles
player1_Paddle = drawRectangle(5, 135, paddleSize_x * 2, paddleSize_y * 2, 'white');
player2_Paddle = drawRectangle(290, 135, paddleSize_x * 2, paddleSize_y * 2, 'white');
% Draw ball
ball = drawRectangle((150 - ballSize), (150 - ballSize), (ballSize * 2), (ballSize * 2), 'white');
needNewBall = false;
% Initialize ball speed
ydir = (rand * 2) - 1;
xdir = ballSpeed;
% -----------------------------------------------------------------
% While "Home" is not pressed
wiimote1.UpdateWiimoteState();
wiimote2.UpdateWiimoteState();
while (~wiimote1.Buttons(9) && ~gameover)
% Get the wiimote status
wiimote1.UpdateWiimoteState();
wiimote2.UpdateWiimoteState();
% Move the paddle with Accel mapped directly to velocity
% player 1
%--------------------------------------------------------------
vely1 = -1 * wiimote1.Accel(2) * velDamping;
[x_p1 y_p1] = getCenter(player1_Paddle);
if( ((y_p1 + paddleSize_y) + vely1 < 300) && ((y_p1 - paddleSize_y) + vely1 > 0) )
yMove(player1_Paddle, vely1);
elseif ( ((y_p1 + paddleSize_y) + vely1 > 300) && ((300 - (y_p1 + paddleSize_y)) > 0) )
yMove(player1_Paddle, (300 - (y_p1 + paddleSize_y)));
elseif ( ((y_p1 - paddleSize_y) + vely1 < 0) && (0 - (y_p1 - paddleSize_y) < 0) )
yMove(player1_Paddle, (0 - (y_p1 - paddleSize_y)));
end
%--------------------------------------------------------------
% Move the paddle with Accel mapped directly to velocity
% player 2
%--------------------------------------------------------------
vely2 = -1 * wiimote2.Accel(2) * velDamping;
[x_p2 y_p2] = getCenter(player2_Paddle);
if( ((y_p2 + paddleSize_y) + vely2 < 300) && ((y_p2 - paddleSize_y) + vely2 > 0) )
yMove(player2_Paddle, vely2);
elseif ( ((y_p2 + paddleSize_y) + vely2 > 300) && ((300 - (y_p2 + paddleSize_y)) > 0) )
yMove(player2_Paddle, (300 - (y_p2 + paddleSize_y)));
elseif ( ((y_p2 - paddleSize_y) + vely2 < 0) && (0 - (y_p2 - paddleSize_y) < 0) )
yMove(player2_Paddle, (0 - (y_p2 - paddleSize_y)));
end
%--------------------------------------------------------------
% Move the ball
%---------------------------------------------------------------
[x_ball y_ball] = getCenter(ball);
% if it is against the upper or lower wall switch its ydir
if( ((y_ball + ballSize) > 300) || ((y_ball - ballSize) < 0) )
ydir = -1 * ydir;
end
% If the ball is behind player 1's line
if( ((x_ball + ballSize) > 290) )
if( ((y_ball - ballSize) < y_p2 + paddleSize_y) ...
&& ((y_ball + ballSize) > y_p2 - paddleSize_y) )
xdir = -1 * xdir;
ydir = vely2 * (.5 * velDamping);
else
p1_score = p1_score + 1;
set(p1_score_disp, 'string', p1_score);
needNewBall = true;
end
% If the ball is behind player 2's line
elseif( ((x_ball - ballSize) < 10) )
if( ((y_ball - ballSize) < y_p1 + paddleSize_y) ...
&& ((y_ball + ballSize) > y_p1 - paddleSize_y) )
xdir = -1 * xdir;
ydir = vely1 * (.5 * velDamping);
else
p2_score = p2_score + 1;
set(p2_score_disp, 'string', p2_score);
needNewBall = true;
end
end
% Actually move the ball
xMove(ball, xdir);
yMove(ball, ydir);
%---------------------------------------------------------------
% make a new ball if needed
if(needNewBall)
% make the new ball
hide(ball);
ball = drawRectangle((150 - ballSize), (150 - ballSize), (ballSize * 2), (ballSize * 2), 'white');
% set a random y dir
ydir = (rand * 2) - 1; % number between -5 and 5
% reset the paddles back to the center
hide(player1_Paddle);
hide(player2_Paddle);
player1_Paddle = drawRectangle(5, 135, paddleSize_x * 2, paddleSize_y * 2, 'white');
player2_Paddle = drawRectangle(290, 135, paddleSize_x * 2, paddleSize_y * 2, 'white');
needNewBall = false;
% pause
pause(1);
end
% check if someone has won
if (p1_score == 11)
winner = 1;
gameover = true;
elseif (p2_score == 11)
winner = 2;
gameover = true;
end
% Redraw the screen and pause
redraw();
pause(0.01);
end % while
else
disp('Not enough wiimotes available to play');
end
if(gameover)
winDisp = writeText(65,100,['Player ' int2str(winner) ' Wins!']);
set( winDisp, 'FontWeight', 'bold', ...
'FontSize', 32, 'FontName', 'OCR A Extended', ...
'Color', 'red');
end
pause(5);
% Disconnect the Wiimote
if(wiimote1.isConnected > 0)
wiimote1.Disconnect();
end
if(wiimote2.isConnected > 0)
wiimote2.Disconnect();
end
--
JordanBrindza - 02 Jul 2008