In my last blog entry, I showed how we can use object-oriented programming (OOP) to set up an analysis of a truss. My journey into OOP all started a few weeks ago when my eleven-year-old had to do a project for school and wanted to learn how to code video games. Simple enough, I thought.
A Google search led me to an app called Codea that allows you to code simple 2D video games on an iPad for a mere $15. It comes with some sample games already coded for you. This seemed like a good route for us to take because it was simple and low cost. Codea is in the language of Lua. There isn’t much support out there, but I was able to find some useful tutorials on the web. I decided start simple by creating a jazzed up version of 1-player pong.
The idea of Color Pong is that the ball bounces off the left, top, and right sides of the screen, and the player controls a paddle at the bottom. The objective is to keep the ball from crossing the bottom edge of the screen (like the original Pong game). To add some flair to the game, I wanted the ball to change color everytime it hits a surface. I also threw in a sprite of a cat that my five year old helped draw.
From a programming standpoint, the game has two objects: the ball and the paddle, which would give me some experience with OOP. The screen is divided into pixels, and the origin is located at the bottom-left corner of the screen. Global variables WIDTH and HEIGHT give the dimensions of the screen, independent of the resolution on the device. We’ll see how to use these in a bit.
The ball is a circle that is characterized by its diameter (or size), its x and y coordinates, and its velocity, which has x and y components. The units on these variables are in pixels. We also attach the score to the ball so that points are added when the ball hits the paddle. We create a class and initialize instances using the following code.
Ball = class() function Ball:init(size,x,y,xSpeed,ySpeed,r,g,b,score) self.size=size self.x=x self.y=y self.xSpeed=xSpeed self.ySpeed=ySpeed self.r=r self.g=g self.b=b self.score=score end
There are several functions related to the ball that we want to define in the class. The first defines the random color assigned to the ball when it hits a surface:
function Ball:color() self.r=math.random(256) self.g=math.random(256) self.b=math.random(256) end
Next we define three functions that describe what to do when the ball hits the left (or west), top (or north), and right (or east) walls. I call these bounceW, bounceN, and bounceE, respectively. For bounceW, for example, we say that when the x-coordinate of the ball minus its radius is less than or equal to zero, the ball bounces.
Note that I am using “if (self.x – self.size/2) <= 0” instead of “if (self.x – self.size) == 0” because the ball travels in frames and the speed can be an odd number that does not neatly divide into WIDTH. I use this approach throughout the code.
Bouncing on the left wall means that the y-component of velocity stays the same but the x-component shifts directions. Thus, self.xSpeed = -self.xSpeed. Upon bouncing, the color changes (i.e., we call on self:color()).
We do something similar for the top and right walls. The code is:
function Ball:bounceN() --Bounce off North wall if(self.y >= HEIGHT-self.size/2) then self.ySpeed = self.ySpeed * -1 self:color() end end function Ball:bounceE() -- Bounce off the East wall if(self.x >= WIDTH-self.size/2) then self.xSpeed = self.xSpeed * -1 self:color() end end function Ball:bounceW() -- Bounce off the West wall if(self.x <= self.size/2) then self.xSpeed = self.xSpeed * -1 self:color() end end
Similarly, we define a function bounceS that takes the position and dimensions of the paddle and determines when ball comes in contact with the paddle. When it does, it changes direction, it changes color, and scores points. The code is as follows:
function Ball:bounceS(paddlex, paddley, width, thick) --Ball touches the paddle if (self.y - self.size/2) <= paddley+thick … and (self.y - self.size/2)>paddley then if self.x>=paddlex and self.x<=paddlex… + width then self.ySpeed = self.ySpeed * -1 self:color() self.score=self.score+10 end end return score end
Lastly, the draw function draws an ellipse with diameter equal to self.size at coordinates self.x and self.y. The ellipse is filled with the random RGB values determined above.
function Ball:draw() fill(self.r,self.g,self.b) ellipse(self.x,self.y,self.size,self.size) end
The paddle is defined by its x- and y-coordinates, its width and its thickness. The y-coordinate and the width and thickness of the paddle are fixed. The x-coordinate of the paddle follows the touch measured by the iPad screen. I will explain this below. To create the class and initialize the instance, we use the following:
Paddle = class() function Paddle:init(x,y,width,thickness) self.x = x self.y = y self.width = width self.thickness = thickness end
The only function is the draw function, which draws a rectangle at the position and with the dimensions of the paddle. The paddle is shaded yellow.
function Paddle:draw() fill(254, 255, 0) rect(self.x,self.y,self.width,self.thickness) end
The Main Program
In the main program we need to define two functions: setup and draw. The setup function is where we define the initial inputs to the code. In our case, we will initialize the ball and paddle instances by specifying their dimensions, initial coordinates, etc. In the following, we set ball.size = 100, ball.x and ball.y so the ball is at the top-center of the screen, ball.xSpeed and ball.ySpeed to be random numbers between 3 and 8, RGB each to 256 so the ball is white, and ball.score = 0. For the paddle, we position it at the bottom-center, and we give it paddle.width = 250 and paddle.thickness = 20. We also initialize a variable called taps.
function setup() ball=Ball(100,WIDTH/2,HEIGHT-100,math.random(3,8),… math.random(-8,-3),256,256,256,0) paddle=Paddle(WIDTH/2-125,50,250,20) taps=0 end
The draw function refreshes every frame. Objects are drawn in the order they appear in the draw function. Therefore, we draw the background first; then we place the ball and paddle on the screen; then we draw the sprite on the ball. Doing it in this order means the sprite will be in front of the ball, and the ball and paddle will be in front of the background.
The ball is drawn such that it doesn’t move until the screen is tapped. We do this using the variable CurrentTouch.tapCount, which measures every time the screen is touch. Once touched, then we update the x and y coordinates of the ball based on the assigned speed. To start the ball on touch, we use the following:
if CurrentTouch.tapCount > 0 then taps=taps+1 end if taps>0 then ball.x = ball.x + ball.xSpeed ball.y = ball.y + ball.ySpeed end
We also use the touch screen to control the paddle. We say that the x coordinate of the paddle matches the x coordinate of the touch while the touch is moving on the screen. In code, we have:
if CurrentTouch.state == MOVING then paddle.x=CurrentTouch.x-paddle.width/2 end
Then, we call on the bounce functions for the ball instance and we draw the ball and paddle. For the finishing touch, we draw the cat sprite. The complete code for the main program is as follows:
--Color Pong -- Use this function to perform your initial setup function setup() ball=Ball(100,WIDTH/2,HEIGHT-100,math.random(3,8),… math.random(-8,-3),256,256,256,0) paddle=Paddle(WIDTH/2-125,50,250,20) taps=0 end -- This function gets called once every frame function draw() -- This sets a dark background color background(40, 40, 50) -- This sets the line thickness strokeWidth(2) text("Color Pong Game",WIDTH-125,HEIGHT-50) text("Score:",125,HEIGHT-50) text(ball.score,200,HEIGHT-50) --Start ball movement on touch if CurrentTouch.tapCount > 0 then taps=taps+1 end -- Set the x and y coordinates for the ball if taps>0 then ball.x = ball.x + ball.xSpeed ball.y = ball.y + ball.ySpeed end --Move the paddle based on touch if CurrentTouch.state == MOVING then paddle.x=CurrentTouch.x-paddle.width/2 end --If the ball touches the wall, it bounces and changes color ball:bounceW() ball:bounceN() ball:bounceE() --If the ball touches the paddle, it bounces and… changes color ball:bounceS(paddle.x, paddle.y, paddle.width, … paddle.thickness) --Draw the ball and fill with color r, g, b ball:draw() sprite(asset.documents.Dropbox.kitty,ball.x,ball.y,75,75) --Draw the paddle and fill with yellow paddle:draw() end
Putting it all together
Running the code in Codea, we can actually play the game. Below is a sample video of the game in action.
You can see that the game runs smoothly and does everything as it is expected to.
This was a fun little exploration into coding video games. It was a practical application to OOP that allowed me to teach my child some things about coding, like the RGB color palette, x and y coordinates, and the basic structure of an OOP code. My child now wants me to sell the game in the App Store, lol!