I'm having trouble getting the code tag to mark it up correctly. Any tips?
I see I'm way, way over the 3K character goal, almost triple that. Now I've got it down to around 6k, but I'll never get it to 3K without cutting into the gameplay.
May I ask how could you squeeze sounds in? I have another small project where that might come in handy...
I was thinking there was a method to play a simple tone without a sound file resource, but evidently not, so it would get trickier. One could generate a wav file on the fly by writing out the file header (https://docs.fileformat.com/audio/wav/) and then populate the file procedurally. One could easily produce sine wave or sawtooth tones, or white noise.
I'll try posting the code again. Taking out the exhaust effects and the on-screen instructions I'm down to about 5k. But here's the version with those included:
j1,j2,w,h=application:getLogicalBounds() cx,cy=w/2,h/2 ppl=2000
at=" Asteroids" dfs={"More"..at,"Bigger"..at,"Higher Gravity","More Varied"..at.." Trajectories"}
S=Sprite Sh=Shape
tDC=Timer.delayedCall
S.gp=S.getPosition S.sp=S.setPosition S.gr=S.getRotation S.sr=S.setRotation Sh.sls=Sh.setLineStyle Sh.mt=Sh.moveTo Sh.lt=Sh.lineTo S.ac=S.addChild S.ael=S.addEventListener
S.rfp=S.removeFromParent
ef,floor,wht,yel,sqrt,min,max,r,deg,rad,sin,cos=Event.ENTER_FRAME,math.floor,0xffffff,0xffff00,math.sqrt,math.min,math.max,math.random,math.deg,math.rad,math.sin,math.cos
application:setBackgroundColor(0x000010)function abp(x1,y1,x2,y2)local dy,dx=y2-y1,x2-x1 if dx==0thenif dy<0thenreturn180elsereturn0endelseif dy==0thenif dx<0thenreturn270elsereturn90endend
result=deg(math.atan(dx/dy))if dy<0thenreturn180 + result elseif(dx<0)thenreturn360 + result endreturn result
endfor i=1,500do s=Sh.new() s:sls(2,wht) s:mt(0,0) s:lt(0,0) s:endPath() s:sp(r(0,w),r(0,h)) s:setScale(r(1,2)) s:setAlpha(r(10,50)/100) stage:ac(s)end
spts={0,2,0.3,2.5,0.6,2,0,0.6,0,0,1,2,1,4,2,5,2,3,3,3,3,7,2,7,2,6,1,5,1,6,0,5,0,0}
ship=S.new() s=Sh.new() s:sls(2,yel) s:setFillStyle(Sh.SOLID,0x4040ff) s:beginPath() s:mt(0,0) ssc=12 ship.sz=7*ssc ship.name="ship"for i=1,#spts/2do s:lt(spts[i*2-1]*ssc,(spts[i*2]-3.5)*ssc)endfor i=1,#spts/2do s:lt(-spts[i*2-1]*ssc,(spts[i*2]-3.5)*ssc)end s:endPath() ship:ac(s)
bhsz=50local nl=1000 bh=S.new()for i=1,nl dolocal s=Sh.new() bh:ac(s)local b=r(100,200) s:sls(3,wht) s:beginPath()local l=r(0,1000)/1000 s.life=l*l
s.a=r(0,3600)*0.1 s:mt(0,0) s:lt(0,0) s:endPath() s:sp(r(1,bhsz),r(1,bhsz))end
bh:ael(ef,function()for i=1,bh:getNumChildren()dolocal ch=bh:getChildAt(i)
ch:sr(r(0,360)) ch.life=ch.life *0.97if ch.life<0.05then ch.life=r(90,100)/100 ch.a=r(0,3600)*0.1end
ch:sp(ch.life *sin(rad(ch.a))* bhsz,ch.life *cos(rad(ch.a))* bhsz) ch:setAlpha(1-ch.life)local br=(ch.life - 0.2)*2
ch:setColorTransform(br,br,br)endend) stage:ac(bh) bh:sp(cx,cy)
exlyr=S.new() stage:ac(exlyr) astdlyr=S.new() stage:ac(astdlyr)
sc=0 sc_tf=TextField.new() sc_tf:setScale(3) sc_tf:sp(50,50) sc_tf:setTextColor(yel) stage:ac(sc_tf)function cc(o)local x,y=o:gp()local sx,sy=ship:gp()local d=sqrt((x-sx)*(x-sx)+(y-sy)*(y-sy))ifnot go and d<(o.sz/2 + ship.sz/2)then bs() eg()endendfunction msg(t,p)if msgtf then msgtf:rfp()endif t then msgtf=TextField.new(null,t) msgtf:setTextColor(yel) stage:ac(msgtf) msgtf:setScale(4)
msgtf:sp((w-msgtf:getWidth())/2,70)ifnot p then tDC(3000,msg)endendendfunction tip(t,p)if tiptf then tiptf:rfp()endif t then tiptf=TextField.new(null,t) tiptf:setTextColor(yel) stage:ac(tiptf) tiptf:setScale(4)if go thenreturnendlocal x=(w-tiptf:getWidth())/2if p=="l"then x=50elseif p=="r"then x=w-50-tiptf:getWidth()end
tiptf:sp(x,h-100)ifnot p then tDC(3000,tip)endendendfunction wfp() wting=1 msg("Tap to Play",1)endfunction sg()
go=null lvl=1 astdrt=1 grv=1 astdsz=1 astdtjf=0 sc=0while astdlyr:getNumChildren()>0dolocal c=astdlyr:getChildAt(1) c:removeAllListeners() c:rfp()end
ship.vx,ship.vy=0,0 ship:sp(w*0.1,h*0.1) ship:sr(-45) stage:ac(ship) ship:setScale(1) ship.clsp=null
tDC(1000,function()tip("Touch To Turn","l")end) tDC(5000,function()tip("Touch To Turn","r")end)
tDC(9000,function()tip("Touch To Burn")end) tDC(13000,function()tip("Avoid the Black Hole")end)
tDC(17000,function()tip("Avoid the "..at)end)end
wfp()function eg()ifnot go then go=1 msg("Game Over",1) tDC(5000,wfp)if tiptf then tiptf:rfp()endendendfunction mv(o,g)if o.clsp then
o.clsp=o.clsp *0.9 o.vx,o.vy=0,0local x,y=o:gp() o:sp(cx+(x-cx)*o.clsp,cy+(y-cy)*o.clsp)
o:setScale(o.clsp)if o.clsp<0.01then o:rfp()endend
o.vx=o.vx or0 o.vy=o.vy or0local ox,oy=o:gp()if o.vr then o:sr(o:gr()+o.vr)endlocal d=sqrt((ox-cx)*(ox-cx)+(oy-cy)*(oy-cy))local ge=min(1/max(d,1),100)*2* grv
if g thenlocal a=rad(abp(ox,oy,cx,cy)) o.vx+=sin(a)*ge o.vy+=cos(a)*ge end
df=0.995 o.vx,o.vy=o.vx*df,o.vy*df o:sp(ox+o.vx,oy+o.vy)if o.sz and d<o.sz/2 + bhsz/2then o.clsp=o.clsp or1if o==ship then eg()endendendfunction adex(side)-- Show the t from both nacelleslocal ang=-ship:gr() + 35if side=="l"then ang-=35*2endlocal sx,sy,l=ship:gp()local ex,ey,s=sx + ship.sz*0.63*sin(rad(ang)),sy + ship.sz*0.63*cos(rad(ang)),Sh.new()
s:sls(2,0xff5000) s:mt(-5,0) s:lt(5,0) s:endPath() s:sp(ex,ey) s:sr(ship:gr()) exlyr:ac(s)
s.life,s.vx,s.vy=1,ship.vx,ship.vy
s:ael(ef,function() s.life-=0.02*r(0.9,1)if s.life<=0then s:rfp()else s:setAlpha(s.life/4)
s:setScale(min(5,1/s.life)) mv(s)endend)endfunction bs()local sx,sy=ship:gp()local x,y,w,h=ship:getBounds(ship)local rt=RenderTarget.new(w,h)
ship:sp(0-x,0-y) rt:draw(ship) ship:sp(sx,sy)local np=5for i=1,np dolocal s=Sh.new() s:sls(0,0xff0000) s:setFillStyle(Sh.TEXTURE,rt)local x1,y1=r(1,w),r(1,h)
s:mt(sx,sy)for j=1,3do s:lt(r(1,w),r(1,h))end s:lt(x1,y1) s:endPath() astdlyr:ac(s) s.sz=h s:sp(sx+x,sy+y)
s.vx,s.vy,s.vr=ship.vx+r(-10,10)*0.1,ship.vy+r(-10,10)*0.1,r(-10,10)*0.1 s:ael(ef,function()mv(s,1)end)end
ship:rfp()end
stage:ael(ef,
function()if wting thenreturnendlocal rs,t=3,0if ltcs>0then ship:sr(ship:gr()-rs) t=0.5endif rtcs>0then ship:sr(ship:gr()+rs) t=0.5endif(ltcs>0or mtcs>0)andnot go then adex("r")endif(rtcs>0or mtcs>0)andnot go then adex("l")endif mtcs>0then t=1endlocal sr,tf=ship:gr(),0.05ifnot go then sc+=1 sc_tf:setText(string.format("Score: %d",sc))endif r(0,1000)<(astdrt + 1)*5thenlocal a,s=r(0,3600)*0.1,Sh.new() s.sz=astdsz *0.4* r(50,100) s.vx,s.vy=r(-100,100)* astdtjf *0.1,
r(-100,100)* astdtjf *0.1 s.vr=r(-10,10)*0.1 s:sls(2,wht) s:setFillStyle(Sh.SOLID,0xbbbbbb) s:beginPath() s:mt(0,s.sz/2)for a=10,360,10do s:lt(sin(rad(a))*s.sz/2+r(-10,10)*s.sz/200,cos(rad(a))*s.sz/2 + r(-10,10)*s.sz/200)end s:lt(0,s.sz/2) s:endPath()
astdlyr:ac(s) s:sp(w/2+sin(rad(a))*max(w,h)*0.6,h/2+cos(rad(a))*max(w,h)*0.6) s:ael(ef,function()mv(s,1) cc(s)end)end ship.vx+=sin(rad(sr))*t*tf ship.vy-=cos(rad(sr))*t*tf mv(ship,1)local x,y=ship:gp()if x<1or x>w or y<1or y>h then ship.vx,ship.vy =0,0 ship:sp(min(w,max(1,x)),min(h,max(1,y)))endif sc%ppl==1then lvl=floor(sc/ppl)+1local nfi=(lvl+#dfs-1)% #dfs + 1local txt="Level "..lvl if lvl>1then txt=txt..": "..dfs[nfi]end msg(txt)if nfi==1then astdrt+=1elseif nfi==2then astdsz+=1elseif nfi==3then grv+=0.25elseif nfi==4then astdtjf +=0.2endendend)
ltcs,rtcs,mtcs=0,0,0function ntouches(zone,n)if zone=="l"then ltcs+=n elseif zone=="r"then rtcs+=n elseif zone=="m"then mtcs+=n endendfunction sz(event)local zone if event.touch.x<w/3then zone="l"elseif event.touch.x>w*2/3then zone="r"else zone="m"endif event.zone ~= zone then ntouches(event.zone,-1) event.zone=zone ntouches(event.zone,1)endend
stage:ael(Event.TOUCHES_MOVE,sz)
stage:ael(Event.TOUCHES_BEGIN,function(event) event.zone=null sz(event)if wting then wting=nil sg()endend)
stage:ael(Event.TOUCHES_END,function(event) ntouches(event.zone,-1)end)
Since my first crack at this was way too complicated to get down to close to 3k, even making the code insanely unreadable, I gave it another shot. This one is 97 lines, 2995 non-whitespace characters, 3,316 with spaces. I could cut it down further at the expense of readability.
This one is a side-scrolling flight game. You're flying an airplane over rugged terrain, but the engine is overheating. As you climb the heat increases, both with rate of climb and with altitude. As you glide it cools down. If it overheats too much, you lose power.
Graphically it's a 3 layer scrolling landscape, with the mountains you need to clear in the foreground, and two more distant layers of mountains in the background.
If I had space to spare I'd love to add power-ups, like clouds you could fly through to cool the engine.
Imho these should be simpler? but is it even possible to make short "original" games without being a guru ?
yes it is possible, look at my contributions I am not 1/1000 as smart as hgy, vitalitymobile or paulh and I still made something.. much more basic than their projects but (I think) still worth playing once, and easier to follow for the newbie coming to gideros stick to what you already know and find a simple game mechanic to exploit: instead of focusing on Catan, think about how tic-tac-toe is made of 2 rules and it's one of the most played games out there
Nice ones @PaulH. That's exciting to see everyone involved. About number characters/lines/readability, I think we could make several categories at some point. In those 80's magazines, there often where longer programs too, sometimes spanning several issues of the magazine.
On the topic of producing sound without resources, I've played with that today and it's not too bad. Without comments one could produce a wave file in just a few lines. This code creates a short white noise file and plays it:
Expanding on that, the code below demonstrates making musical notes with a sine or sawtooth wave, and includes comments that explain the .wav file header. It's just a proof of concept. Currently the white noise option ignores the frequency parameter. It also abuses Beethoven.
If people want a resource-free system for basic sounds, this could be expanded upon and incorporated into Gideros. I could imagine creating a sound by passing an optional table of parameters into Sound.new(), or a new SoundBuilder class, specifying the waveform (sine, sawtooth, square, etc.), length and frequency, potentially frequency and volume curves for rising and falling tones, fading in and out, and so on. And perhaps a mechanism for combining those created sounds, either in sequence or mixed. For basic games that could easily create explosions, sci-fi sounds, and simple music.
I'm clearly going down a rabbit hole here. None of this is entirely necessary. There's lots of software that can create and edit sound files. Then again, the same is true of graphics. If it's a notable feature of Gideros that it can create graphics without image files or other added resources, perhaps it would make sense to support some basic sounds the same way. Just a thought...
Anyway, here's the sound and music demo:
function make_wave(file_name, length, wave, freq, vol)-- We'll need to write some 32 bit values in binary. Helper function for that:localfunction to4Bytes(n)return n & 0xff,(n>>8)& 0xff, (n>>16)& 0xff,(n>>24)& 0xff end-- For a given point (sample number) in a wav file, return the 16 bit value for the specified sound wave and frequencylocalfunction sound_wave(wave,p,freq,vol)local sample_rate =6000local n =0-- Default to silenceif wave=="noise"then n =math.random(-255*128* vol,255*128*vol)elseif wave=="sine"then n =math.floor(math.sin(p/sample_rate * freq *math.rad(180))*256* vol *127)elseif wave=="sawtooth"then n =math.floor((freq * p / sample_rate)*256* vol *256)endlocal c2, c1 =(n>>8)& 0xff,n & 0xff
returnstring.char(c1,c2)end-- We're just dealing with a fixed format wav file:local sample_rate =6000local samples = length*sample_rate
local data_size = samples *2local total_file_length =44 + data_size
local fl1, fl2, fl3, fl4 = to4Bytes(total_file_length)local ds1, ds2, ds3, ds4 = to4Bytes(data_size)
f=io.open(file_name,"wb")-- Write the WAV file header:
f:write(string.char(82,73,70,70,--RIFF
fl1, fl2, fl3, fl4,-- File size overall87,65,86,69,-- WAVE header102,109,116,32,--fmt chunk marker w/ trailing null16,0,0,0, -- Length of format data (16 bytes before this)1,0, -- Wave type. 1 == PCM1,0, -- Channels. 1 == mono112,23,0,0,-- Sample rate (Hertz)224,46,0,0, -- Bits per sample * channels * sample rate / 82,0,
16,0, -- Bytes per sample - this is 16 100,97,116,97, -- "data" - chunk header
ds1,ds2,ds3,ds4-- size of data section))-- Write the data:for l =1, samples do f:write(sound_wave(wave,l,freq,vol or1))end
f:close()end-- Demo some white noise:
make_wave("|D|noise.wav", 1, "noise")-- 0.1 second sine wave at 256 Herz (middle C) at full volume
s=Sound.new("|D|noise.wav")
s:play()-- Then one note with a sawtooth wave:
Timer.delayedCall(2000,
function()
make_wave("|D|note.wav", 2, "sawtooth", 262, 1)-- 0.1 second sine wave at 256 Herz (middle C) at full volume
s=Sound.new("|D|note.wav")
s:play()end)-- Make wave files for the chromatic scale.-- The frequencies in Hertz of the notes in octave zero:
notes ={16.35,17.32,18.35,19.45,20.60,21.83,23.12,24.5,25.96,27.50,29.14,30.87}-- 3 letter names for those notes:
names={"C0n","C0s","D0n","D0s","E0n","F0n","F0s","G0n","G0s","A0n","A0s","B0n"}-- Compute the ratio of each note to the C:local note_multipliers ={}for i =1, #notes do note_multipliers[i]= notes[i]/notes[1]end-- Make a sound for each note in several octaves:local sounds ={}for octave =3, 7dofor i =1, #names dolocal original_note = names[i]local octave_note =string.gsub(original_note, "0", octave)local file_name ="|D|note_" .. octave_note .. ".wav"-- Compute the frequency for the note by applying the ratio of that note over-- C, and the apply the octave multiplier. Each octave is double the frequency-- of the one below it:local fr =math.floor(notes[1]* note_multipliers[i]*math.pow(2, octave))-- We'll make each note 0.2 seconds, using a sine wave
make_wave(file_name, 0.2, "sine", fr, 1)
sounds[octave_note]= Sound.new(file_name)endend-- Given a string of 3 letter note names, play those notes in order:function play_sequence(seq)for i =1, string.len(seq), 3dolocal note =string.sub(seq, i, i+2)if sounds[note]then
Timer.delayedCall(60*(i-1), function() sounds[note]:play()end)endendend
Timer.delayedCall(5000,
function()
fur_elise ="E4nD4sE4nD4sE4nB3nD4nC4nA3n C3nE3nA3nB3n E3nG3sB3nC4n E3nE4nD4sE4nD4sE4nB3nD4nC4nA3n C3nE3nA3nB3n E3nC4nB3nA3n"
play_sequence(fur_elise)-- To play an octave higher:
Timer.delayedCall(9000, function() play_sequence(string.gsub(string.gsub(fur_elise,"4","5"),"3","4"))end)end)
We already have something to play generated sounds with gideros: GFSXR plugin, and we also have a sound editor embedded in Addons menu, but for this short games purpose in pure gideros lua your solution is brilliant @PaulH [edit: it works like a charm, now I just need to find some time to understand what you are doing exactly and embed sounds into simon]
Thanks @hgy29. The buffer may come in really handy. I often create textures via code, and.
And thanks @pie. I'd never looked at GFSXR before. It looks really interesting, but when I open it it's scaled very small, too small to read any of the text. I can resize the window but the contents don't scale. Any help?
Yeah, that sounds great. I'll definitely be doing that in the future.
I tried one other 80s style experiment, making a wireframe 3D style like Battle Zone. It's incomplete but moves through a field of wireframe cubes and pyramids, but after a few seconds the player crashes and so far I haven't figured out why.
Here's the code:
local j1,j2,sw,sh=application:getLogicalBounds()-- Get the screen size
application:setBackgroundColor(0)function get2DFrom3D(point, loc, rot, vloc, vrot, h, sw, sh)-- add scale
px,py,pz=point[1],point[2],point[3]
rx,ry,rz=rot[1],rot[2],rot[3]
vx,vy,vz=vloc[1],vloc[2],vloc[3]
locx,locy,locz=loc[1],loc[2],loc[3]local x = vx-(px*math.cos(math.rad(ry))+pz*math.sin(math.rad(ry)))+locx
local z = vz-(pz*math.cos(math.rad(ry))-px*math.sin(math.rad(ry)))+locz
local y = vy-py+locy -- only rotating on vertical axis so far - no impact on vertical position.local nzz=z if z==0then nzz=0.1endlocal dx =(sw/2) + math.floor(1.3*((x /(math.abs(nzz)))* sw))-- -- 90 degree field?local dy = h + math.floor(1.3*((y /(math.abs(nzz)))* sw))-- -- Assumes top of screen is looking levelif(dx >=0and dx <= sw and dy >=0and dy <= sh and z >0)thenreturntrue, dx, dy endreturnfalse,dx,dy
endlocal cube={{{-1,-1,-1},{1,-1,-1},{1,-1,1},{-1,-1,1},{-1,-1,-1}},{{-1,1,-1},{1,1,-1},{1,1,1},{-1,1,1},{-1,1,-1}},
{{-1,1,-1},{-1,-1,-1}},{{-1,1,1},{-1,-1,1}},{{1,1,-1},{1,-1,-1}},{{1,1,1},{1,-1,1}}}local pyramid={{{-1,0,-1},{1,0,-1},{1,0,1},{-1,0,1},{-1,0,-1}},{{-1,0,-1},{0,1,0}},{{-1,0,1},{0,1,0}},{{1,0,1},{0,1,0}},{{1,0,-1},{0,1,0}},}function make3DShape(model, loc, rot,vloc,vrot)local s=Sprite.new()for i =1, #model do
s:addChild(make3DLine(model[i],loc,rot,vloc,vrot))endreturn s
endfunction make3DLine(model, loc, rot, vloc, vrot)local s=Shape.new() s:setLineStyle(1,0x00ff00) s:beginPath()for i=1,#model dolocal success,vis,dx,dy=pcall(get2DFrom3D, model[i],loc, rot,
vloc, vrot, sh/2,sw,sh)if success thenif i ==1then s:moveTo(dx,dy)else s:lineTo(dx,dy)endendend
s:endPath()return s
end
vloc,vrot={0, 0.5, 0},{0, 0, 0}function make_object(z)local m=pyramid
ifmath.random(0,9)<5then m=cube endlocal z = z ormath.random(5,100)return{
model=m,
loc={math.random(-z,z),0,z},
rot={0,math.random(0,360),0}}endlocal objects={}for i =1, 10dotable.insert(objects, make_object())endlocal hrt=RenderTarget.new(sw,1)
hrt:clear(0x00ff00)
horizon=Bitmap.new(hrt)
horizon:setPosition(0,sh/2)
stage:addChild(horizon)
stage:addEventListener(Event.ENTER_FRAME,
function()for i =1, #objects doif objects[i].shape then objects[i].shape:removeFromParent()end
objects[i].shape=make3DShape(objects[i].model, objects[i].loc, objects[i].rot, vloc, vrot)
stage:addChild(objects[i].shape)
objects[i].loc[3]-=1if objects[i].loc[3]<0then
objects[i].shape:removeFromParent()
objects[i]=make_object(100)collectgarbage()endendend)
I'm also on Windows 10. For me it opens in a player, but the UI is small and centered in the window. The menu bar on the player is hidden, and alt-M displays it, but the menu is grayed out, so I can't adjust the zoom:
The player crash in the 3D code seems to happen when drawing an object very close to the viewer, perhaps resulting in a nearly infinitely long line being drawn. Changing this line; if objects[i].loc[3] < 0 then to if objects[i].loc[3] < 1 then solves it.
For 3D wireframe rendering, one could use Mesh with primitive set as lines. Mesh accepts z coordinates, so in a viewport with a 3D projection Gideros can take care of all computations
That's interesting. I had assumed the Mesh class was part of the 3D library, not standard Gideros. I thought doing the math for a wireframe 3D engine might be a fun challenge, but there's no need to reinvent something that works. Thanks!
I've really got to get back to work on my main projects, but I didn't want to leave the wireframe game half finished, so I fixed a few things, added scoring, etc. It's just a driving game, dodging cubes and pyramids as they get more numerous and you steadily accelerate until you hit one. Touch and hold, sliding left and right to steer. Here it is in playable form in 99 lines, just under 3,000 characters:
j1,j2,sw,sh=application:getLogicalBounds()
application:setBackgroundColor(0)
gr=0x00ff00
function get2D(p, l, ry, v,h)
px,py,pz,locx,locy,locz=p[1],p[2],p[3],l[1],l[2],l[3]
vx,vy,vz=v[1],v[2],v[3]local x,y,z = vx-(px*math.cos(math.rad(ry))+pz*math.sin(math.rad(ry)))-locx,vy-py-locy,
vz-(pz*math.cos(math.rad(ry))-px*math.sin(math.rad(ry)))-locz
local nzz=z if z==0then nzz=0.1endlocal dx =(sw/2) + math.floor(1.3*((x /(math.abs(nzz)))* sw))local dy = h + math.floor(1.3*((y /(math.abs(nzz)))* sw))if(dx >=0and dx <= sw and dy >=0and dy <= sh and z >0)thenreturntrue, dx, dy endreturnfalse,dx,dy
endlocal cube={{{-1,-1,-1},{1,-1,-1},{1,-1,1},{-1,-1,1},{-1,-1,-1}},{{-1,1,-1},{1,1,-1},{1,1,1},{-1,1,1},{-1,1,-1}},
{{-1,1,-1},{-1,-1,-1}},{{-1,1,1},{-1,-1,1}},{{1,1,-1},{1,-1,-1}},{{1,1,1},{1,-1,1}}}local pyramid={{{-1,0,-1},{1,0,-1},{1,0,1},{-1,0,1},{-1,0,-1}},{{-1,0,-1},{0,2,0}},{{-1,0,1},{0,2,0}},{{1,0,1},{0,2,0}},{{1,0,-1},{0,2,0}},}function mkShp(m, loc, rot,v)local s=Sprite.new()for i =1, #m do s:addChild(l3D(m[i],loc,rot,v))endreturn s
endfunction l3D(m, loc, rot, v)local s=Shape.new() s:setLineStyle(3,gr) s:beginPath()for i=1,#m dolocal vis,dx,dy=get2D(m[i],loc, rot[3], v,sh/2)if i ==1then s:moveTo(dx,dy)else s:lineTo(dx,dy)endend
s:endPath()return s
end
v={0, 0.5, 0}function mkObj(z,x)local m,y=pyramid,0ifmath.random(0,9)<5then m=cube y =1endlocal z = z ormath.random(100,200)return{ m=m, loc={(x or0)+math.random(-50,50),y,z}, rot={0,math.random(0,360),0}}endfunction start()
o={}
v[1],v[3]=0,0for i =1, 10dotable.insert(o, mkObj())end
speed,score,go=10,0,null
while stage:getNumChildren()>0do stage:removeChildAt(1)end
stage:addChild(stf)
stage:addChild(h)end
go =true
ttp="Touch to Play"
stf = TextField.new(null,ttp)
stage:addChild(stf)
stf:setTextColor(0xff0000)
stf:setScale(6) stf:setPosition(50,100)local hrt=RenderTarget.new(sw,1)
hrt:clear(gr)
h=Bitmap.new(hrt)
h:setPosition(0,sh/2)function cf()local x,y,ox,oy =0, 0, null, null
local s=Shape.new() s:setLineStyle(2,gr) s:moveTo(x,y)for i =1, 50do
x,y=x+math.random(50,70),y+math.random(-50,50)
s:lineTo(x,y)ifmath.random(1,3)==1then ox = x oy = y elseif ox andmath.random(1,5)==2then x = ox y = oy s:moveTo(x, y)endend
s:endPath()
s:setPosition(sw/2,sh/2) s:setRotation(math.random(0,360))
stage:addChildAt(s,1)end
stage:addEventListener(Event.ENTER_FRAME,
function()ifnot go then
speed,score,v[3]=speed+0.05,score+1,v[3]+0.01*speed
if steering then v[1]-=steering *0.005* speed end
stf:setText(string.format("Score: %d", score))for i =1, #o doif o[i].shape then o[i].shape:removeFromParent()end
o[i].shape=mkShp(o[i].m, o[i].loc, o[i].rot, v)
stage:addChildAt(o[i].shape,2)if o[i].loc[3]< v[3] + 0.5thenif(math.abs(o[i].loc[1] - v[1]))<1then
go =1for j =1, 9do cf()end
stf:setText(stf:getText() .. " - Game Over - " .. ttp)end
o[i].shape:removeFromParent()
o[i]=mkObj(v[3]+math.random(150,200),v[1])endendif score%500 ==0thentable.insert(o, mkObj(v[3]))endendend)function steer(e) steering =(e.x-sw/2)/(sw/2)end
stage:addEventListener(Event.MOUSE_MOVE, steer)
stage:addEventListener(Event.MOUSE_DOWN, function(e)if go then start()else steer(e)endend)
stage:addEventListener(Event.MOUSE_UP, function()steering = null end)
Tetris The visual part takes too many characters... When I use imgui for rendering, the code becomes smaller and cleaner (though still messy ).
application:setBackgroundColor(0xcfcfcf)
GS,SC,MSC,TR,TS,CM,KC,BR,ST=0,0,0,0,1,false,KeyCode,{},{[0]=0,40,100,300,1200}local rt=RenderTarget.new(32,32,false,true)
rt:clear(0x505050, 1)
rt:clear(0xffffff,1,2,2,28,28)function newTile()local px=Pixel.new(rt,32,32)
px:setVisible(false)
stage:addChild(px)return px
endfor i=1,21do BR[i]={}for j=1,10do BR[i][j]={d=i>20and8or0,g=newTile()}endend
INF=TextField.new(nil,"Press \'Space\' to start")
INF:setPosition(20,20)
INF:setScale(2)
stage:addChild(INF)
TES={{0x00F0F0,{{0,0,0,0},{1,1,1,1},{0,0,0,0},{0,0,0,0}}}, {0x0000F0,{{1,0,0},{1,1,1},{0,0,0}}},
{0xF0A000,{{0,0,1},{1,1,1},{0,0,0}}}, {0xF0F000,{{1,1},{1,1}}},
{0x00F000,{{0,1,1},{1,1,0},{0,0,0}}}, {0xA000F0,{{0,1,0},{1,1,1},{0,0,0}}},
{0xF00000,{{1,1,0},{0,1,1},{0,0,0}}}, {0x323232}}
T={i=0,x=4,y=1,d=0}function forB(c,m)for i=1,m or20dofor j=1,10do c(BR[i][j],j,i)endendendfunction forT(t,c)for y=1,#t dofor x=1,#t[1]doif t[y][x]>0thenlocal r=c(x,y)if r~=nilthenreturn r endendendendendfunction over(t,tx,ty,td)return forT(td,function(x,y)local bv=t[ty+y-1][tx+x-1]ifnot bv or bv.d>0thenreturntrueendend)endfunction put(t,tx,ty,td,n) forT(td,function(x,y) t[ty+y-1][tx+x-1].d=n end)endfunction trMv(x,y,nb)local tb={}for i=1,21do tb[i]={}for j=1,10do tb[i][j]={d=BR[i][j].d}endend
put(tb,T.x,T.y,T.d,0)ifnot over(tb,x,y,nb)then
put(BR,T.x,T.y,T.d,0)
put(BR,x,y,nb,T.i)returntrueendendfunction mv(dx, dy)local nx,ny=T.x+dx,T.y+dy
if trMv(nx,ny,T.d)then
T.x,T.y=nx,ny
returntrueendend
stage:addEventListener("keyDown", function(e)if e.keyCode==KC.SPACE and GS==0then GS=1endifnot CM thenreturnendif e.keyCode==KC.DOWN then TS=0.05elseif e.keyCode==KC.UP thenlocal nb={}for i=#T.d[1],1,-1do
nb[i]={}for j=1,#T.d do nb[i][j]=T.d[#T.d-j+1][i]endendif trMv(T.x,T.y,nb)then T.d=nb endelseif e.keyCode==KC.LEFT then mv(-1,0)elseif e.keyCode==KC.RIGHT then mv(1,0)endend)
stage:addEventListener("keyUp", function(e)if e.keyCode==KC.DOWN then TS=1endend)
stage:addEventListener("enterFrame", function(e)
TR+=e.deltaTime
if TR>0.4*TS then
TR=0if GS==1then
T.x,T.y,T.i=4,1,math.random(1, 7)
T.d=TES[T.i][2]if over(BR,T.x,T.y,T.d)then
forB(function(v) v.d=0end)
GS,SC,CM=0,0,false
INF:setText(`Scores: {SC} Max: {MSC}\nPress \'Space\' to restart`)
else
put(BR,T.x,T.y,T.d,T.i)
GS,CM=2,true
INF:setText(`Scores: {SC} Max: {MSC}`)
end
elseif GS==2 then
if not mv(0,1) then
local r=0
for y=1,20 do
local c=0
for x=1,10 do if BR[y][x].d>0 then c+=1 end end
if c == 10 then
local newRow={}
for j,o in ipairs(BR[y]) do stage:removeChild(o.g) newRow[j]={d=0,g=newTile()} end
table.remove(BR,y)
table.insert(BR,1,newRow)
r+=1
end
end
SC+=ST[r]
MSC=MSC<>SC
INF:setText(`Scores: {SC} | Max: {MSC}`)
GS,CM=1,false
end
end
end
forB(function(v,x,y)
v.g:setVisible(v.d > 0)
v.g:setPosition((x-1)*32,(y-1)*32+40)
v.g:setColor(v.d>0 and TES[v.d][1] or 0,1)
end,21)
end)
Comments
May I ask how could you squeeze sounds in? I have another small project where that might come in handy...
I'll try posting the code again. Taking out the exhaust effects and the on-screen instructions I'm down to about 5k. But here's the version with those included:
Likes: pie
This one is a side-scrolling flight game. You're flying an airplane over rugged terrain, but the engine is overheating. As you climb the heat increases, both with rate of climb and with altitude. As you glide it cools down. If it overheats too much, you lose power.
Graphically it's a 3 layer scrolling landscape, with the mountains you need to clear in the foreground, and two more distant layers of mountains in the background.
If I had space to spare I'd love to add power-ups, like clouds you could fly through to cool the engine.
Likes: hgy29, MoKaLux
Likes: pie, vitalitymobile, hgy29
Likes: MoKaLux
About number characters/lines/readability, I think we could make several categories at some point. In those 80's magazines, there often where longer programs too, sometimes spanning several issues of the magazine.
Likes: MoKaLux, PaulH
Likes: MoKaLux, PaulH, hgy29, vitalitymobile
If people want a resource-free system for basic sounds, this could be expanded upon and incorporated into Gideros. I could imagine creating a sound by passing an optional table of parameters into Sound.new(), or a new SoundBuilder class, specifying the waveform (sine, sawtooth, square, etc.), length and frequency, potentially frequency and volume curves for rising and falling tones, fading in and out, and so on. And perhaps a mechanism for combining those created sounds, either in sequence or mixed. For basic games that could easily create explosions, sci-fi sounds, and simple music.
I'm clearly going down a rabbit hole here. None of this is entirely necessary. There's lots of software that can create and edit sound files. Then again, the same is true of graphics. If it's a notable feature of Gideros that it can create graphics without image files or other added resources, perhaps it would make sense to support some basic sounds the same way. Just a thought...
Anyway, here's the sound and music demo:
Likes: MoKaLux, pie
Likes: MoKaLux, keszegh, PaulH
[edit: it works like a charm, now I just need to find some time to understand what you are doing exactly and embed sounds into simon]
Likes: MoKaLux
And thanks @pie. I'd never looked at GFSXR before. It looks really interesting, but when I open it it's scaled very small, too small to read any of the text. I can resize the window but the contents don't scale. Any help?
I tried one other 80s style experiment, making a wireframe 3D style like Battle Zone. It's incomplete but moves through a field of wireframe cubes and pyramids, but after a few seconds the player crashes and so far I haven't figured out why.
Here's the code:
Likes: pie
GSFXR source is here for you to run directly in gideros though (exclude studio.lua from execution) https://github.com/gideros/gideros/tree/7aba140cb3ccfc92c8df451f4e3a48c585556af5/studio_addons/GSFXREditor
Likes: PaulH, MoKaLux
if objects[i].loc[3] < 0 then
to
if objects[i].loc[3] < 1 then
solves it.
Likes: MoKaLux
Likes: PaulH
Likes: MoKaLux
Likes: hgy29, MoKaLux, vitalitymobile, pie, keszegh
The visual part takes too many characters... When I use imgui for rendering, the code becomes smaller and cleaner (though still messy ).
Likes: keszegh, pie, MoKaLux, hgy29, PaulH, MobAmuse
Fragmenter - animated loop machine and IKONOMIKON - the memory game
Likes: MoKaLux
Fragmenter - animated loop machine and IKONOMIKON - the memory game