50 Line Challenge

@Simeon - I usually depth sort 3D objects, yes. I also find it helps to discard transparent pixels sometimes.

The interesting thing about this is that I made two versions - one with fake 3D, and the other using 3D. The fake one (this one) looks way more realistic, because for some reason, 3D objects don’t seem to draw at all when they are far away, but suddenly jump onto the screen when they are quite big, creating a disjointed and unrealistic effect.

[EDIT] modified to add sound and a faster startup


displayMode(FULLSCREEN) -- 50 lines challenge  - copyright JMV38-2013
local rnd, floor, balls = math.random, math.floor, {}
local xt,yt,started, timeLeft,finished, total, rmin, Nballs ; local ball = class()
function ball:init()
    self.i = #balls + 1 ; balls[self.i] = self ; self:restart()
end
function ball:touching(otherBall)
    local dx,dy = (self.x-otherBall.x), (self.y-otherBall.y)
    local d2,r = dx*dx + dy*dy, (self.r + (otherBall.r or 0) )
    return (d2 < r*r)
end
function ball:restart(confirmed)
    self.r = 10; self.dt = rnd(100)/10 + 20; self.timeout = ElapsedTime + self.dt
    self.x, self.y = rnd(WIDTH-200)+100, rnd(HEIGHT-200)+100
    self.dr =  200/60/self.dt; self.c = color(rnd(127)+128, rnd(127)+128, rnd(127)+128)
end
function ball:draw()
    if self.r<rmin then fill(255, 0, 0, 255) else fill(self.c) end
    ellipse(self.x,self.y,self.r); self.r = self.r + self.dr
    if ElapsedTime>self.timeout then self:restart() end
end
function ball:score(a,ps) total = total + floor( a*self.r*self.r ) 
    if a<0 then if ps then sound(SOUND_BLIT, 48967) end else sound(SOUND_JUMP, 15197) end
end
function setup()
    xt,yt,started, timeLeft,finished,rmin,Nballs = WIDTH/2,HEIGHT/2,false, 30,false,30,20
    ellipseMode(RADIUS) textWrapWidth(WIDTH/1.5) 
    fill(255) fontSize(30) font("AmericanTypewriter-Bold") 
    total = "Touch the colored bubbles to score, bigger bubbles make bigger score. "..
    " Score decreases when you touch red ones or when 2 bubbles touch each other. You have 30s."
end
function finish() balls = {}; fontSize(100); yt = HEIGHT/2; finished =true end
function start()  started = true ; total = 0 ; xt = WIDTH/2; yt = HEIGHT -100; timeLeft=30
    for i=1,Nballs do tween.delay(i/3,ball) end end
function touched(touch)
    if finished then return elseif started then
        for i,b in ipairs(balls) do if b:touching(touch) then local a = 1
        if b.r<rmin then a=-1 end b:score(a, (a<0)) b:restart() end end
    else start() end
end
function draw()
    background(40, 40, 50)
    for i,b in ipairs(balls) do b:draw() end
    for i = 1, Nballs-1 do for j = i+1, Nballs do local b1,b2 = balls[i],balls[j]
if b1 and b2 and b1:touching(b2) then b1:score(-1,true) b2:score(-1) b1:restart() b2:restart() end
    end end
    fill(255) text(tostring(total),xt,yt) text(tostring(floor(timeLeft)),xt,100)    
    if timeLeft < 1 then finish() end
    if started and not finished then timeLeft = timeLeft - DeltaTime end
end

I think this format (50 lines) is really an interesting exercise. It is surprising how much can be made within it.

@Ignatz that sounds like they are being frustum culled — see the perspective() function documentation:

http://twolivesleft.com/Codea/Reference/Graphics.html#perspective

When setting up the perspective a default near and far clipping plane are assumed. These are two planes perpendicular to the viewpoint. Any geometry outside of these planes is culled.

The default far plane is set based on the height of the view frustum (i.e., HEIGHT by default). So if you use a larger distance for your far clipping plane, then your objects should be able to draw further away.

Thanks, @Simeon, I should have thought of that…

Air code is so awesome. I am copying and pasting your various projects in there and seeing it run on my iPad. Amazing! My two kids thought the bubble popping app was fun. I liked the asteroid app. Param Breaker was completely unexpected.

Assuming my server is operational, to get the images, this won’t crash codea…
It’s a little ship that you remote control and fly around in space to your hearts content.

Any ideas on a more elegant way to share image resources are welcome.

Enjoy!

`
-- Space50, Doh! Just realized comments don't count as lines.....
-- Ah well, you didn't want to know what was going on anyway, right?
function setup()
    p1Angle = 0;p1ThrustAngle = 0;p1Ship = vec2(400,200);p1Speed = vec2(0,0)
    p1ThrustSpeed = .07;p1TopSpeed = 3;thrustButP1 = vec4(250,0,350,100)
   leftImage = image(100,100); rightImage=image(100,100);thrustImage = image(100,100)
shipImage = image(100,100);rocketsImage = image(100,100)
    http.request("http://www.mrscience101.com/images/left.png", getLeftImage)
    http.request("http://www.mrscience101.com/images/right.png", getRightImage)
    http.request("http://www.mrscience101.com/images/thrust.png", getThrustImage)
    http.request("http://www.mrscience101.com/images/ship.png", getShipImage)
    http.request("http://www.mrscience101.com/images/rockets.png", getRocketsImage)
    touches = {}; end
function touched(touch)
    if touch.state == ENDED then touches[touch.id] = nil
    else touches[touch.id] = touch;end;end
function draw()
    background(0,0,0);stroke(0, 0, 0, 255);fill(0, 0, 0, 255);
    for k,touch in pairs(touches) do
        xTouch = touch.x; yTouch = touch.y
        CheckIfThrustButtonPushed(touch)
if touch.x < 200 then
    if touch.y < 200 then
        angleX = touch.x - 100;angleY = touch.y - 100
        if angleX <0 then p1Angle = p1Angle + 3;end
        if angleX> 0 then p1Angle = p1Angle - 3;end;end;end;end;
pushMatrix(); p1Ship.x = p1Ship.x + p1Speed.x; p1Ship.y = p1Ship.y + p1Speed.y
    if p1Ship.x < -16 then p1Ship.x = WIDTH +16;end
    if p1Ship.x > WIDTH + 16 then p1Ship.x = -16;end    
    if p1Ship.y > HEIGHT + 16 then p1Ship.y = 100-16;end
    if p1Ship.y < 100-16 then p1Ship.y = HEIGHT + 16; end
    translate(p1Ship.x,p1Ship.y);rotate(p1Angle)
    if shipThrust == true then sprite(rocketsImage, 0,0,32)
    else  sprite(shipImage,0,0,32); end; popMatrix()
sprite(leftImage,50,50);sprite(rightImage, 150, 50);sprite(thrustImage, 300, 50)
shipThrust = false;end
function CheckIfThrustButtonPushed(touch)
    if touch.x > thrustButP1.x and touch.x < thrustButP1.z then
        if touch.y> thrustButP1.y and touch.y < thrustButP1.w then
            shipThrust = true
            p1ThrustAngle = math.rad(p1Angle+90)
            p1Speed.x = p1Speed.x + p1ThrustSpeed * math.cos(p1ThrustAngle)
            p1Speed.y = p1Speed.y + p1ThrustSpeed * math.sin(p1ThrustAngle)
            if p1Speed.x > p1TopSpeed then p1Speed.x = p1TopSpeed;end
            if p1Speed.x < -p1TopSpeed then p1Speed.x = -p1TopSpeed;end
            if p1Speed.y > p1TopSpeed then  p1Speed.y = p1TopSpeed; end
            if p1Speed.y < -p1TopSpeed then p1Speed.y = -p1TopSpeed;end;end;end;end  
function getLeftImage( theImage)leftImage= theImage;end
function getRightImage( theImage)rightImage= theImage;end
function getThrustImage( theImage)thrustImage= theImage;end
function getShipImage( theImage)shipImage= theImage;end
function getRocketsImage( theImage)rocketsImage= theImage;end
`

Hehe my nooby fail…

-- conors text printer

-- Use this function to perform your initial setup
function setup()
    print("Hello World!")
    parameter.text("Title", "TEXT WRITER")
    parameter.text("TEXT", "HELLO WORLD")
    parameter.number("fontsize", 0, 100, 40 )
    parameter.number("X", 0, WIDTH, WIDTH/2)
    parameter.number("Y", 0, HEIGHT, 450)
    parameter.number("fontsize2", 20, 80, 20)
    parameter.number("AmountofRed", 0, 250, 250)
    parameter.number("AmountofGreen", 0, 250, 250)
    parameter.number("AmountofBlue", 0, 250, 250)
    parameter.number("AmountofOpacity", 0, 250, 250)
end

-- This function gets called once every frame
function draw()
    background(0, 0, 0, 255)
    fill(AmountofRed, AmountofGreen, AmountofBlue, AmountofOpacity)
    fontSize(fontsize)
    font("Noteworthy-Light")
    textWrapWidth(WIDTH)
    
    text(TEXT, X, Y)
    
    fill(172, 170, 170, 255)
    fontSize(fontsize2)
    font("AmericanTypewriter-Bold")
    textWrapWidth(WIDTH)

    text(Title, WIDTH/2, 700)
    
    parameter.number("fontsize", 20, 80, 40 )
    
    fill(127, 127, 127, 255)
    fontSize(30)
    font("Baskerville-Italic")
    text("Created by [removed for reasons]", WIDTH/4, 50)
end

Yes I know my surname’s awesome.
No I don’t sign autographs
Only joking I’m not that vain my surname isn’t awesome
??? ???

@TheRogueBatcher It’s not bad, actually! Keep learning! :)>-

@TheRogueBatcher Looks cool, but I have to say, the parameter.number for the font size should be in setup, and RGBA goes from 0-255, not 0-250.

@Prynok
Thank you and I will
@SkyTheCoder
The RGBA error I understand but the location of the parameter is irrelevant. It still does the same thing. Until it actually has a critical fault I don’t see the problem. Also what do you mean the parameter should be in setup. It’s already there

-- conors text printer

-- Use this function to perform your initial setup
function setup()
    print("Hello World!")
    parameter.text("Title", "TEXT WRITER")
    parameter.text("TEXT", "HELLO WORLD")
    parameter.number("fontsize", 0, 100, 40 )
    parameter.number("X", 0, WIDTH, WIDTH/2)
    parameter.number("Y", 0, HEIGHT, 450)
    parameter.number("fontsize2", 20, 80, 20)
    parameter.number("AmountofRed", 0, 250, 250)
    parameter.number("AmountofGreen", 0, 250, 250)
    parameter.number("AmountofBlue", 0, 250, 250)
    parameter.number("AmountofOpacity", 0, 250, 250)
end

It’s on line 8 see?

@TheRogueBatcher I meant, in draw it says parameter.number("fontsize", 20, 80, 40).

@TheRogueBatcher, yes, but you also have one for the same variable in draw for no good reason.

EDIT: @SkyTheCoder beat me to it.

Here’s another program I found in my projects to post here.


displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)

function setup()
    hr,min,sec={},{},{}
end

function draw()
    background(40, 40, 50)
    fill(255)
    t = os.date("*t")
    fontSize(40)
    text("Binary Clock",WIDTH/2,HEIGHT-75)
    str=string.format("%02d:%02d:%02d",t.hour,t.min,t.sec)
    text(str,WIDTH/2,HEIGHT-160)
    text("32   16     8      4     2     1",570,480)
    createTabs(t.hour,t.min,t.sec)
    fill(255, 0, 0, 255)
    text("Hours",220,400)
    for z=1,6 do
        size=5
        if hr[z]==1 then size=50 end
        ellipse(300+80*z,400,size)
    end   
    fill(0, 255, 19, 255)
    text("Minutes",220,300)
    for z=1,6 do
        size=5
        if min[z]==1 then size=50 end
        ellipse(300+80*z,300,size)
    end    
    fill(6, 0, 255, 255)
    text("Seconds",220,200)
    for z=1,6 do
        size=5
        if sec[z]==1 then size=50 end
        ellipse(300+80*z,200,size)
    end
end

function createTabs(h,m,s)
    for z=1,6 do
        p2=2^(6-z)
        hr[z],min[z],sec[z]=0,0,0
        if h>=p2 then hr[z]=1 h=h-p2 end
        if m>=p2 then min[z]=1 m=m-p2 end
        if s>=p2 then sec[z]=1 s=s-p2 end
    end
end

@JakAttak OOOOH that one! I forgot to remove that! It was a prototype experiment for a top secret organisation involving the apocalypse, werewolves, witty one-liners and a lot of pirates!
NAWW not really I just forgot to remove it from my original plan
@SkyTheCoder
When you write just make sure you are explaining what your talking about.

@TheRogueBatcher At first, I only saw the one in draw.

I do explain what I’m talking about, you’re not reading it right…

ok, guys, we’re all on the same side here, let’s keep adding 50-liners

One for the kids. One of these balls deletes any other ball it touches. Spot which ball it is, and touch it. You get a point for each of the other balls you save from being deleted.

displayMode(FULLSCREEN)
function setup()
    physics.gravity(0,0)
    start()
end

function start()
    balls={}
    if high==nil then high,score,total=0,0,0 else score=0 end 
    for i=1,100 do  --create some random balls
        local s=math.random(20,40)
        local c=color(math.random(0,255),math.random(0,255),math.random(0,255))
        local b=physics.body(CIRCLE,s/2)  
        b.x,b.y=math.random(100,WIDTH-100),math.random(100,HEIGHT-100)
        b.linearVelocity=vec2(math.random(-300,300),math.random(-300,300)) 
        b.restitution=1
        b.info=i
        balls[i]={b=b,c=c,s=s,i=i}
    end
    killer=balls[math.random(1,100)]
    killer.b.info="k" 
    if edges then return end
    edges={physics.body(EDGE,vec2(1,1),vec2(WIDTH,1)),physics.body(EDGE,vec2(1,1),vec2(1,HEIGHT)),
    physics.body(EDGE,vec2(1,HEIGHT),vec2(WIDTH,HEIGHT)),physics.body(EDGE,vec2(WIDTH,1),vec2(WIDTH,HEIGHT))}
    fill(43, 48, 138, 255) fontSize=24 textMode(CORNER)
end

function draw()
    background(220) 
    for i,b in pairs(balls) do fill(b.c) ellipse(b.b.x,b.b.y,b.s) end
    fill(0) text("High score "..high,50,HEIGHT-100) 
    text("Total score "..total,50,HEIGHT-125) text("Time "..math.floor(ElapsedTime),50,HEIGHT-150)
end

function collide(c)
    if ElapsedTime<2 or c.bodyA==nil or c.bodyB==nil then return end
    if c.bodyA.info=="k" then kill(c.bodyB.info) elseif c.bodyB.info=="k" then kill(c.bodyA.info) end  
end

function kill(u)
    for i,b in pairs(balls) do
        if b.i==u then b.b:destroy() table.remove(balls,i) break end
    end
end

function touched(t)
    if t.state==BEGAN then
        if vec2(t.x,t.y):dist(vec2(killer.b.x,killer.b.y))<killer.s then 
            score=table.maxn(balls)-1 total=total+score if score>high then high=score end
            for i=1,#balls do balls[i].b:destroy() end  
            collectgarbage() 
            start()  
        end
    end
end

@Ignatz this game is great! I modified it a little bit so:

  • the killer ball is not always white (more difficult to spot).
  • you lose points when the killer ball kills a ball.
  • you lose points if you touch the wrong ball
displayMode(FULLSCREEN)
function setup()
    physics.gravity(0,0)
    start()
end

function start()
    balls={}
    if high==nil then high,score,total=0,0,0 else score=0 end 
    for i=1,100 do  --create some random balls
        local s=math.random(20,40)
        local c=color(math.random(0,255),math.random(0,255),math.random(0,255))
        local b=physics.body(CIRCLE,s/2)  
        b.x,b.y=math.random(100,WIDTH-100),math.random(100,HEIGHT-100)
        b.linearVelocity=vec2(math.random(-300,300),math.random(-300,300)) 
        b.restitution=1
        b.info=i
        balls[i]={b=b,c=c,s=s,i=i}
    end
    killer=balls[math.random(1,100)]
    killer.b.info="k" -- killer.c=color(255)
    if edges then return end
    edges={physics.body(EDGE,vec2(1,1),vec2(WIDTH,1)),physics.body(EDGE,vec2(1,1),vec2(1,HEIGHT)),
    physics.body(EDGE,vec2(1,HEIGHT),vec2(WIDTH,HEIGHT)),physics.body(EDGE,vec2(WIDTH,1),vec2(WIDTH,HEIGHT))}
    fill(43, 48, 138, 255) fontSize(24) textMode(CORNER)
end

function draw()
    background(220) 
    for i,b in pairs(balls) do fill(b.c) ellipse(b.b.x,b.b.y,b.s) end
    fill(0) text("High score "..high,50,HEIGHT-100) 
    text("Total score "..total,50,HEIGHT-125) text("Time "..math.floor(ElapsedTime),50,HEIGHT-150)
end

function collide(c)
    if ElapsedTime<2 or c.bodyA==nil or c.bodyB==nil then return end
    if c.bodyA.info=="k" then kill(c.bodyB.info) elseif c.bodyB.info=="k" then kill(c.bodyA.info) end  
end

function kill(u)
    for i,b in pairs(balls) do
        if b.i==u then b.b:destroy() table.remove(balls,i) total = total - 3 break end
    end
end

function touched(t)
    if t.state==BEGAN then
        if vec2(t.x,t.y):dist(vec2(killer.b.x,killer.b.y))<killer.s then 
            score=table.maxn(balls)-1 total=total+score if score>high then high=score end
            for i=1,#balls do balls[i].b:destroy() end  
            collectgarbage() 
            start()  
        else total = total - 5
        end
    end
end

ah, my mistake, I made it white for testing. I’ve changed it now above. :">

I thought I already had it so you only got points for the balls left over…