Brickout example error

I notice in the Brickout example that it is impossible to reset the game after you lose due to a syntax error. In the reset game function it has this code:

gameOver = false

But it should be :

gameover = false --no capital O there

Making that change allows it to work as intended. I am thinking this example may be way overdue for a rewrite. When this program was written, Codea did not yet have a text function and so it line draws all the numbers and letters. If the program author doesn’t want to do it, maybe I could be of service for the rewrite.

Thanks @Vega — I’ll integrate this fix.

If you are interested in enhancing or re-writing Brickout, I would be happy to include your updated version in Codea. I think direct-touch controls would help the game tremendously, too.

How are you thinking? Drag the paddle back and forth to move it? That seems like the most intuitive method.

By the way, I guess all of the samples that don’t have a name on them are by you, @Simeon? I wasn’t sure about that, but I probably should have guessed that.

I was thinking of doing a tilt version of brickout

All the example projects have listed Authors (readProjectInfo("Author")). I used that information to create this list on the wiki.

The example projects can ‘show off what Codea can do’ and/or ‘teach new users how to use Codea to do things’. Added functionality/complexity helps the first aim but may detract from the second aim - although good comments in the code may help with ‘teaching’. As I was learning about Codea, I was most impressed by the examples that cleverly used small amounts of easy to follow code to show off what Codea could do.

I hear you, @mpilgrim, but replacing all the text line-drawing with text calls will actually simplify the code quite a bit and make it shorter. I also think it could have some updated controls without adding a whole lot of complexity.

Swapping out the text was easy enough (though I did like the look of the line-like numbers so I’m not sure what font to use to get closest to that), I tried making the paddle move with gravity but it’s really hard to control if it is real gravity (ie acceleration is based on gravity) so I made it so that the tilt controls the speed. It is still quite tricky, but playable.

Also made it widescreen.

I see how it is, @Andrew_Stacey trying to steal my thunder. Well, I ALSO swapped out the text and made it widescreen and changed the controls to drag-style, not gravity/tilt. Here is the code for my current progress, if anyone wants to try it out and see how they like the drag. I think it works better than the way it is in the example.


------------------------
-- Main game functions --
-------------------------
supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)
blocks = {}
score = 0
lives = 3
ballIsMoving = false
gameover = false
won = false
instructions = true
level = 1
maxlevel = table.maxn(levels)

-- Use this function to perform your initial setup
function setup()
    ball = Ball()
    ball.vel.x = math.random(-3,3)
    bat = Bat()
    --makeBlocks()
    --print("Tap the bat to lanch the ball.")
    --print("Tap the far right side of the screen to move right, far left side to move left.")
    --print("When the game is over tap the middle of the screen to restart.")
end

-- create table of blocks from level array
function makeBlocks()
    local blockSize = vec2(math.floor(WIDTH/12),math.floor(WIDTH/24))
    for i = 1, 6 do
        c = getColorForRow(i)
        for j = 1, 10 do
            if levels[level][i][j] > 0 then
                table.insert(blocks,Block((j*(blockSize.x+2)+WIDTH/24),HEIGHT-(i * (blockSize.y+2)+WIDTH/24),c))
                
            end
        end
    end
end

-- get color for current row
function getColorForRow(row)
    colChanger = row * 35
    if level % 4 == 1 then
        c = color(colChanger,0,255,255)
    elseif level % 4 == 2 then
        c = color(255,colChanger,0,255)
    elseif level % 4 == 3 then
        c = color(255,0,colChanger,255)
    else
        c = color(0,255,colChanger,255)
    end
    return c
end

-- Stop ball and put it back on bat
function resetBall()
    ballIsMoving = false
    ball.pos.x = bat.pos.x
    ball.pos.y = 41
end

-- Reset game to original state
function resetGame()
    score = 0
    lives = 3
    level = 1
    blocks = {}
    makeBlocks()
    instructions = false
    gameover = false
    won = false
end

-- Level up
function nextLevel()
    score = score + 100 * lives * level
    --ball.vel.y = ball.vel.y + 0.5
    resetBall()
    if level < maxlevel then
        level = level + 1
        makeBlocks()
    else
        won = true
    end
end

-- Lose a life
function loseLife()
    resetBall()
    lives = lives - 1
    if lives == 0 then
        gameover = true
    end
end

-- This function gets called once every frame
function draw()
    background(0, 0, 0, 255)
    noSmooth()
    -- Update the ball
    if ballIsMoving then
        if ball:update() == false then
            loseLife()
        end
    else
        ball.pos.x = bat.pos.x
    end
    -- Check collision with the bat
    if bat:collide(ball) == false then
        -- Check collision with the blocks - no need to do this if ball has hit bat. 
        -- Still does a lot of unecessary checks
        for i = 1, table.maxn(blocks) do
            if blocks[i]:collide(ball) then
                table.remove(blocks, i)
                score = score + 100
                if table.maxn(blocks) == 0 then
                    nextLevel()
                end
                break
            end
        end
    end
    -- Draw game objects
    bat:draw()
    ball:draw()
    for i = 1, table.maxn(blocks) do
        blocks[i]:draw()
    end
    -- Draw score and lives
    pushStyle() --Start using temporary styles
        fill(255, 255, 255, 255)
        noStroke()
        text(score,40,HEIGHT-20)
        text("x " .. lives, WIDTH - 20, HEIGHT - 20)
        fill(253, 255, 0, 255)
        ellipse(WIDTH - 50, HEIGHT - 19, 20)
    popStyle()
    -- Display messages (Win, lose, or instructions)
    if gameover then
        pushStyle() --Start using temporary styles
            text("Game Over", WIDTH / 2, HEIGHT / 2)
            text("Score: " .. score, WIDTH / 2, HEIGHT / 2 - 40)
            text("Touch here to restart", WIDTH / 2, HEIGHT / 2 - 80)
        popStyle()  --End temporary styles
    elseif won then
        pushStyle() --Start using temporary styles
            text("Congratulations, all levels complete.", WIDTH / 2, HEIGHT / 2)
            text("Score: " .. score, WIDTH / 2, HEIGHT / 2 - 40)
            text("Touch here to restart", WIDTH / 2, HEIGHT / 2 - 80)
        popStyle()  --End temporary styles
    elseif instructions then
        pushStyle() --Start using temporary styles
            text("Tap the bat to launch the ball.", WIDTH / 2, HEIGHT / 2)
            text("Drag the bat to move it left and right.", WIDTH / 2, HEIGHT / 2 - 40)
            text("Touch here to Start game.", WIDTH / 2, HEIGHT / 2 - 80)
        popStyle()  --End temporary styles
    end
end

function touched(touch)
    if touch.state == BEGAN or
       touch.state == MOVING then
        if gameover == false and won == false and instructions == false then
            -- If bat is touched launch ball
            if touch.x < bat:right() + 120 and 
                touch.x > bat:left() - 120 and 
                touch.y < bat:top() + 80 and 
                touch.y > bat:bottom() - 10 then
                if ballIsMoving == false then
                    ballIsMoving = true
                else
                    if touch.x > bat:right() and bat:right() < WIDTH - math.floor(WIDTH/40) then
                        bat.pos.x = bat.pos.x + math.floor(WIDTH/20) --Move Right
                    elseif touch.x < bat:left() and bat:left() > math.floor(WIDTH/40) then
                        bat.pos.x = bat.pos.x - math.floor(WIDTH/20) --Move Left
                    end
                end
            end
        elseif gameover == true or won == true or instructions == true then
            -- If center of screen is touched start game
            if touch.y > 300 and touch.y < 448 and
            touch.x > 130 and touch.x < WIDTH - 130 then
                resetGame()
            end
        end
    end
end


----------------
-- Ball Class --
----------------

Ball = class()

function Ball:init()
    self.pos = vec2(WIDTH / 2, 41)
    self.radius = 10
    self.vel = vec2(0, 7)
end

function Ball:draw()
    fill(253, 255, 0, 255)
    noStroke()
    ellipse(self.pos.x, self.pos.y, 2 * self.radius)
end

function Ball:update()
    self.pos = self.pos + self.vel
    if (self.pos.x + self.radius) >= WIDTH then
        self.pos.x = WIDTH - self.radius
        self.vel.x = -self.vel.x
        sound(SOUND_JUMP)
    elseif (self.pos.x - self.radius) <= 0 then
        self.pos.x = self.radius
        self.vel.x = -self.vel.x
        sound(SOUND_JUMP)
    elseif (self.pos.y + self.radius) >= HEIGHT then
        self.pos.y = HEIGHT - self.radius
        self.vel.y = -self.vel.y
        sound(SOUND_JUMP)
    elseif (self.pos.y - self.radius) <= 0 then
        self.pos.y = self.radius
        self.vel.y = -self.vel.y
        sound(SOUND_EXPLODE)
        return false
    end
    return true
end

function Ball:left()
    return self.pos.x - self.radius
end

function Ball:right()
    return self.pos.x + self.radius
end

function Ball:top()
    return self.pos.y + self.radius
end

function Ball:bottom()
    return self.pos.y - self.radius
end


---------------
-- Bat Class --
---------------

Bat = class()

function Bat:init()
    self.pos = vec2(math.floor(WIDTH/2), math.floor(WIDTH/30)+1)
    self.size = vec2(math.floor(WIDTH/7), math.floor(WIDTH/30))
end

function Bat:draw()
    fill(167, 170, 186, 255)
    noStroke()
    rectMode(CENTER)
    ellipse(self.pos.x - self.size.x / 2 + 5, self.pos.y, math.floor(WIDTH/30))
    ellipse(self.pos.x + self.size.x / 2 - 5, self.pos.y, math.floor(WIDTH/30))
    rect(self.pos.x, self.pos.y, self.size.x, self.size.y)
end

function Bat:collide(ball)
    if ball:left() <= self:right() and
       ball:right() >= self:left() and
       ball:top() >= self:bottom() and
       ball:bottom() <= self:top() then
        sound(SOUND_JUMP)
        ball.vel.y = -ball.vel.y
        -- change the x velocity depending on where the ball hit the bat
        ball.pos.y = self:top() + ball.radius
        pos = ball.pos.x - self.pos.x
        ball.vel.x = pos / 10
        return true
    end
    return false
end

function Bat:left()
    return self.pos.x - self.size.x / 2
end

function Bat:right()
    return self.pos.x + self.size.x / 2
end

function Bat:top()
    return self.pos.y + self.size.y / 2
end

function Bat:bottom()
    return self.pos.y - self.size.y / 2
end

-----------------
-- Block Class --
-----------------

Block = class()

function Block:init(x, y, col)
    self.pos = vec2(x, y)
    self.size = vec2(math.floor(WIDTH/12),math.floor(WIDTH/24))
    self.color = col
end

function Block:draw()
    fill(self.color)
    noStroke()
    rectMode(CENTER)
    rect(self.pos.x, self.pos.y, self.size.x, self.size.y)
end

function Block:collide(ball)
    if ball:left() <= self:right() and
       ball:right() >= self:left() and
       ball:top() >= self:bottom() and
       ball:bottom() <= self:top() then
        sound(SOUND_BLIT)
        if ball.pos.y <= self:top() and ball.pos.y >= self:bottom() then
            ball.vel.x = -ball.vel.x
        else
            ball.vel.y = -ball.vel.y
        end
        return true
    end
    return false
end

function Block:left()
    return self.pos.x - self.size.x / 2
end

function Block:right()
    return self.pos.x + self.size.x / 2
end

function Block:top()
    return self.pos.y + self.size.y / 2
end

function Block:bottom()
    return self.pos.y - self.size.y / 2
end

------------------
-- Levels array --
------------------

levels = {
    
{{0,0,1,1,1,1,1,1,0,0},{0,1,1,0,0,0,0,1,1,0},{0,0,1,1,1,1,1,1,0,0},
{0,0,1,1,1,1,1,1,0,0},{0,1,1,0,0,0,0,1,1,0},{0,0,1,1,1,1,1,1,0,0}},
    
{{1,1,1,1,1,1,1,1,1,1},{1,0,0,1,0,0,1,0,0,1},{0,1,1,0,1,1,0,1,1,0},
{0,1,1,0,1,1,0,1,1,0},{1,0,1,1,0,0,1,0,0,1},{1,1,1,1,1,1,1,1,1,1}},
    
{{0,0,0,1,1,1,1,0,0,0},{0,0,1,1,1,1,1,1,0,0},{1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1},{0,1,1,1,1,1,1,1,1,0},{0,0,1,2,1,1,1,1,0,0}},
    
{{1,0,1,1,1,1,1,1,0,1},{1,1,1,0,1,1,0,1,1,1},{1,0,1,1,0,0,1,1,0,1},
{1,0,1,1,0,0,1,1,0,1},{1,1,1,0,1,1,0,1,1,1},{1,0,1,1,1,1,1,1,0,1}},
    
{{1,1,1,1,1,1,1,1,1,1},{1,0,1,1,0,0,1,1,0,1},{1,1,0,0,1,1,0,0,1,1},
{1,1,0,0,1,1,0,0,1,1},{1,0,1,1,0,0,1,1,0,1},{1,1,1,1,1,1,1,1,1,1}}
    
}

I am thinking maybe I should speed up the ball as the levels increase… What do you think?

I had a go updating this project this morning as well. I added gradient bevelled bricks, motion blur on the ball, drag controls and particle effects.

https://gist.github.com/3255183

Brickout Enhanced

If you guys want to make further changes I will be happy to include them.

Definitely not trying to steal thunder!

The big thing for me is tilt control. When my kids first tried brickout then they were trying to control the bat by tilting the ipad. I think that the fact you can pick up and move an ipad is what sets it apart as an experience from ordinary computers. So to be a truly ipad game, it ought to involve the ipad itsrlf as the controller.

Interesting that you still draw the numbers! I’ve got some ideas on reducing the collison tests - I’ll have a try-out tonight.

Don’t like drag. My fingers are so fat that I can’t see the paddle underneath.

But I do like the updated bricks, bat, ball, and effects.

I have a (minor) suggestion for Bat:draw(). Is it more or less efficient to make use of the lineCapMode(ROUND) pattern (applied in the RoundRect() function in the Sounds Plus example project)? For example:


function Bat:draw()
    stroke(167, 170, 186) -- Set the colour for the bat
    smooth()              -- Turn on for lineCapMode to work
    strokeWidth(24)       -- Set width of line
    lineCapMode(ROUND)    -- Set rounded ends
    line(self.pos.x - self.size.x / 2, self.pos.y,
        self.pos.x + self.size.x / 2, self.pos.y)
    noSmooth() -- Not clear to me why draw() sets noSmooth() repeatedly
end

May I ask what determines the fonts available in the font picker for font()? On my iPad, I am offered different weights etc of:


Academy Engraved
American Typewriter
Emoji
Arial (various versions of it)
Baskerville
Copperplate
Courier
Courier New
Didot
Futura
Georgia
Gill Sans
Helvetica Neue
Hoefler Text
Inconsolata
Marion
Marker Felt
Myriad Pro
Noteworthy
Optima
Palatino
Papyrus
Snell Roundhand
Times New Roman (various versions of it)
Verdana
Zapf Dingbats TC
Zapfino

That appears to be a subset of the fonts listed in the in-app reference for font().

@mpilgrem it’s the same — ROUND lineCapMode actually draws a squared-off line and two ellipses.

I did the bat this way so I could tweak the radius of the end caps by one pixel — I should really fix the issue in ROUND lines, though.

@Andrew_Stacey what if it asked you at the start if you want to use tilt or touch controls?

Edit: @mpilgrem That’s correct about the fonts, I only display a subset because there are so many fonts that would clutter up the list for most people. Most of them would not be useful to the majority of Codea users. (The fonts shown are just the available fonts in your version of iOS.)

Edit 2: @Andrew_Stacey I only left the numbers as-is because I was too lazy to ensure the layout worked well and select a nice font. I also didn’t mind the retro look.

For the numbers, using font("DBLCDTTempBlack") might preserve the retro feel of the original example project. See my recent discussion here (it is font number 49, in that example).

How about a 3D version?

He he just kidding. :slight_smile: I think it’s great there are lots of people willing and able to contribute like this in the community.

I was just playing, @Andrew_Stacey. :wink: I can imagine that playing with tilt might be fun, but I think touch may be the way to go on this one.

I think the new version looks nice, @Simeon. Some things I did on my initial version I do like better, like I changed the code so that it would work in full screen, put the instructions on screen and converted to text for the numbers, etc., but you have better implemented drag controls, converted to meshes on the blocks and made them look nice, added cool particle effects, etc.

So I will bow out here and feel content that I sparked the conversation that led to the neat new version of Brickout. Nice work.