Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
80's style demos in Gideros - Page 2 — Gideros Forum

80's style demos in Gideros

2

Comments

  • piepie Member
    PaulH said:

    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... :smile:
  • PaulHPaulH Member
    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==0 then if dy<0 then return 180 else return 0 end elseif dy==0 then if dx<0 then return 270 else return 90 end end
    	result=deg(math.atan(dx/dy)) if dy<0 then return 180 + result elseif (dx<0) then return 360 + result end return result
    end
    for i=1,500 do 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/2 do s:lt(spts[i*2-1]*ssc,(spts[i*2]-3.5)*ssc) end
    for i=1,#spts/2 do s:lt(-spts[i*2-1]*ssc,(spts[i*2]-3.5)*ssc) end s:endPath() ship:ac(s)
    bhsz=50 local nl=1000 bh=S.new()
    for i=1,nl do local 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() do local ch=bh:getChildAt(i)
    ch:sr(r(0,360)) ch.life=ch.life * 0.97 if ch.life<0.05 then ch.life=r(90,100)/100 ch.a=r(0,3600)*0.1 end
    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) end end) 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)) if not go and d<(o.sz/2 + ship.sz/2) then bs() eg() end end
    function msg(t,p) if msgtf then msgtf:rfp() end if t then msgtf=TextField.new(null,t) msgtf:setTextColor(yel) stage:ac(msgtf) msgtf:setScale(4)
    	msgtf:sp((w-msgtf:getWidth()) / 2,70) if not p then tDC(3000,msg) end end end
    function tip(t,p) if tiptf then tiptf:rfp() end if t then tiptf=TextField.new(null,t) tiptf:setTextColor(yel) stage:ac(tiptf) tiptf:setScale(4)
    	if go then return end
    	local x=(w-tiptf:getWidth()) / 2 if p=="l" then x=50 elseif p=="r" then x=w-50-tiptf:getWidth() end
    	tiptf:sp(x,h-100) if not p then tDC(3000,tip) end end end
    function wfp() wting=1 msg("Tap to Play",1) end
    function sg()
    	go=null lvl=1 astdrt=1 grv=1 astdsz=1 astdtjf=0 sc=0
    	while astdlyr:getNumChildren()>0 do local 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() if not go then go=1 msg("Game Over",1) tDC(5000,wfp) if tiptf then tiptf:rfp() end end end
    function mv(o,g)
    	if o.clsp then
    		o.clsp=o.clsp * 0.9 o.vx,o.vy=0,0 local x,y=o:gp() o:sp(cx+(x-cx)*o.clsp,cy+(y-cy)*o.clsp)
    		o:setScale(o.clsp) if o.clsp<0.01 then o:rfp() end
    	end
    	o.vx=o.vx or 0 o.vy=o.vy or 0 local ox,oy=o:gp() if o.vr then o:sr(o:gr()+o.vr) end
    	local d=sqrt((ox-cx)*(ox-cx)+(oy-cy)*(oy-cy)) local ge=min(1/max(d,1),100) * 2 * grv
    	if g then local 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/2 then o.clsp=o.clsp or 1 if o==ship then eg() end end
    end
    function adex(side) -- Show the t from both nacelles
    	local ang=-ship:gr() + 35 if side=="l" then ang-=35*2 end local 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<=0 then s:rfp() else s:setAlpha(s.life/4)
    	s:setScale(min(5,1/s.life)) mv(s) end end )
    end
    function 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=5
    	for i=1,np do local 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,3 do 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 then return end
    	local rs,t=3,0 
    	if ltcs>0 then ship:sr(ship:gr()-rs) t=0.5 end if rtcs>0 then ship:sr(ship:gr()+rs) t=0.5 end
    	if (ltcs>0 or mtcs>0) and not go then adex("r") end if (rtcs>0 or mtcs>0) and not go then adex("l") end
    	if mtcs>0 then t=1 end
    	local sr,tf=ship:gr(),0.05 if not go then sc+=1 sc_tf:setText(string.format("Score: %d",sc)) end
    	if r(0,1000)<(astdrt + 1)*5 then
    		local 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,10 do 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<1 or x>w or y<1 or y>h then ship.vx,ship.vy =0,0 ship:sp(min(w,max(1,x)),min(h,max(1,y))) end
    	if sc%ppl==1 then lvl=floor(sc/ppl)+1 local nfi=(lvl+#dfs-1) % #dfs + 1 local txt="Level "..lvl if lvl>1 then txt=txt..": "..dfs[nfi] end msg(txt)
    		if nfi==1 then astdrt+=1 elseif nfi==2 then astdsz+=1 elseif nfi==3 then grv+=0.25 elseif nfi==4 then astdtjf += 0.2 end end
    end)
    ltcs,rtcs,mtcs=0,0,0
    function ntouches(zone,n) if zone=="l" then ltcs+=n elseif zone=="r" then rtcs+=n elseif zone=="m" then mtcs+=n end end
    function sz(event)
    	local zone if event.touch.x<w/3 then zone="l" elseif event.touch.x>w*2/3 then zone="r" else zone="m" end
    	if event.zone ~= zone then ntouches(event.zone,-1) event.zone=zone ntouches(event.zone,1) end
    end
    stage:ael(Event.TOUCHES_MOVE,sz)
    stage:ael(Event.TOUCHES_BEGIN,function (event) event.zone=null sz(event) if wting then wting=nil sg() end end )
    stage:ael(Event.TOUCHES_END,function (event) ntouches(event.zone,-1) end )

    Likes: pie

    +1 -1 (+1 / -0 )Share on Facebook
  • piepie Member
    @PaulH I am not sure how yet, but I was thinking about using base64 as hgy29 suggested: I guess it's possible to insert anything like that
  • MoKaLuxMoKaLux Member
    edited May 2023
    Imho these should be simpler? but is it even possible to make short "original" games without being a guru :s ?
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • PaulHPaulH Member
    edited May 2023
    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.
    SCREEN_W, SCREEN_H = 320,480
    function makeShape(p,fcol,px,lcol,scale)
    	local s=Shape.new()
    	s:setLineStyle(px or 0,lcol or 0)
    	s:setFillStyle(Shape.SOLID,fcol)
    	s:beginPath()
    	local sc=scale or 1
    	s:moveTo(p[1]*sc,p[2]*sc)
    	for i = 3, #p,2 do
    		s:lineTo(p[i]*sc,p[i+1]*sc)
    	end
    	s:endPath()
    	return s
    end
    local bg = makeShape({0,0,SCREEN_W,0,SCREEN_W,SCREEN_H,0,SCREEN_H,0,0}, 0xbbffee)
    stage:addChild(bg)
    LandScroll = gideros.class(Sprite)
    function LandScroll:init(speed,col,min_h,max_h,rugged,block_w)
    	self.speed, self.col, self.min_h, self.max_h, self.rugged, self.block_w=speed,col,min_h,max_h,rugged,block_w
    	self.right, self.height = -block_w, math.random(self.min_h, self.max_h)
    	while self.right < SCREEN_W + self.block_w do self:addBlock() end
    	self:addEventListener(Event.ENTER_FRAME,
    		function()
    			self.right-=self.speed*gspeed
    			for i = self:getNumChildren(), 1, -1 do
    				local ch = self:getChildAt(i)
    				local x, y = ch:getPosition()
    				ch:setPosition(x - self.speed*gspeed, y)
    				if x < 0 - block_w then
    					ch:removeFromParent()
    					self:addBlock()
    				end
    			end
    		end
    	)
    end
    function LandScroll:addBlock()
    	local new_h = math.min(self.max_h, math.max(self.min_h, self.height + math.random(-20, 20) * 0.001 * self.rugged))
    	local s=makeShape({0, SCREEN_H*(1-self.height),self.block_w+1,SCREEN_H*(1-new_h),self.block_w+1, SCREEN_H,0, SCREEN_H,0,0},self.col)
    	s:setAnchorPoint(0,1)
    	s:setPosition(self.right, SCREEN_H)
    	self.right+=self.block_w
    	self:addChild(s)
    	s.start_height, s.end_height = self.height, new_h
    	self.height = new_h
    end
    function LandScroll:getHeight(x)
    	local h = 0
    	for i = 1, self:getNumChildren() do
    		local ch = self:getChildAt(i)
    		local chx, chy = ch:getPosition()
    		if x >= chx and x <= chx + self.block_w then
    			h = ch.start_height + (ch.end_height - ch.start_height) * (x - chx) / self.block_w
    			break
    		end
    	end
    	return (1-h)*SCREEN_H
    end		
    stage:addChild(LandScroll.new(0.1,0xff9944,0.5, 0.8,2,10))
    stage:addChild(LandScroll.new(0.3,0xee8833,0.2, 0.7,1.5,20))
    t1=LandScroll.new(1,0x773322,0.05,0.6,5,30)
    stage:addChild(t1)
    plane=makeShape({0,0,3,0,6,3,7,4,15,4.5,18,4,20,2.2,23.5,2.5,24,4,18,4,17,5,20,6,24,6,26,5,24,4,26,5,30,5,31,5.5,30,6,30,3,30,8,
    	30,5,30,6,29,7.5,27,8,21,8.5,20,8.2,4,6,2,5.5,1.7,5,6.5,5,6.4,5.2,1.7,5,0,0},0x00ffff,1,0,1)
    plane:setAnchorPoint(0.75,0.5)
    stage:addChild(plane)
    px,py = SCREEN_W * 0.3, SCREEN_H * 0.2
    heat_tf=TextField.new(null,"HEAT")
    stage:addChild(heat_tf)
    heat_tf:setPosition(100, 50)
    heat_tf:setScale(3)
    needle=makeShape({2,0,4,50,0,50,2,0},0xff0000)
    needle:setPosition(132,55)
    stage:addChild(needle)
    needle:setAnchorPoint(0,1)
    heat,max_heat,climb=0,200,0
    gspeed,climb = 1,0
    function on_enter_frame()
    	if not game_over then
    		if in_y and heat < max_heat then
    			climb -= ((in_y > SCREEN_H * 0.5 and 1) or -1) * 0.01
    			heat+=(SCREEN_H - py)/ SCREEN_H * 2
    		else
    			heat=math.max(0, heat-1) climb-=0.01		
    		end
    		py = math.min(SCREEN_H, math.max(SCREEN_H * 0.1, py - climb))
    		plane:setPosition(px, py)
    		local h=t1:getHeight(px)
    		if py>h then py=h game_over=1 gspeed=0 end
    		plane:setRotation(climb*-30)
    		needle:setRotation(heat/max_heat*180-90)
    	end
    end
    stage:addEventListener(Event.ENTER_FRAME, on_enter_frame)
    stage:addEventListener(Event.MOUSE_DOWN, function(e) in_y = e.y end)
    stage:addEventListener(Event.MOUSE_MOVE, function(e) in_y = e.y end)
    stage:addEventListener(Event.MOUSE_UP, function() in_y = null end)

    Likes: hgy29, MoKaLux

    +1 -1 (+2 / -0 )Share on Facebook
  • piepie Member
    MoKaLux said:

    Imho these should be simpler? but is it even possible to make short "original" games without being a guru :s ?

    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 :smile: 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

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • hgy29hgy29 Maintainer
    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.

    Likes: MoKaLux, PaulH

    +1 -1 (+2 / -0 )Share on Facebook
  • piepie Member
    readable SIMON SAYS in 96 lines and 2985 chars
    application:setBackgroundColor(0x000000)
    math.randomseed(os.timer())
    s = {}
    sphrase = {}
    sstate = "idle"
    s_counter = 1
    usersay = {}
    colors = {0xFF0000, 0x0000FF, 0x00FF00, 0xFFFF00}
    start_time = os.timer()
    s_sector = Core.class(Sprite)
    sscale = {{5,5},{-5,5},{-5,-5},{5,-5}}
    function s_sector:init(i)
    self.gfx = Path2D.new()
    self.gfx:setSvgPath("M0.6,17.9c7.6,0,13.8,6.3,13.8,14.1L32,32C32,14.3,17.9,0,0.6,0V17.9z")
    self:addChild(self.gfx)
    self:setAnchorPoint(-0.01,1)
    self:setScale(sscale[i][1], sscale[i][2])
    self.i = i 
    self.gfx:setFillColor(colors[i],.5)
    	self:addEventListener(Event.ENTER_FRAME, function() 
    			local dt = os.timer()-start_time
    			if dt >0.5 and dt <0.6 then
    			self.gfx:setFillColor(colors[i],.5)
    			end
    		end)
    end
    for i=1,4 do
    	s[i] = s_sector.new(i)
    	stage:addChild(s[i])
    	s[i]:setPosition(200,250)
    end
    play = TextField.new(nil, "PLAY")
    play:setTextColor(0xFFFFFF)
    play:setScale(2)
    play:setPosition(20,20)
    stage:addChild(play)
    stage:addEventListener(Event.MOUSE_DOWN, function(e)
    										if sstate == "wait" then
    											for i in ipairs(s) do 
    												if s[i].gfx:hitTestPoint(e.x, e.y) then						
    													usersay[#usersay+1] = i
    													if sphrase[#usersay] == i then
    														s[i].gfx:setFillColor(colors[i],1)
    														if #usersay == #sphrase then
     
    															sstate = "add"
    														end
    													else
    														play:setText("YOU LOST AT LEVEL "..#sphrase.."!\nCLICK HERE TO TRY AGAIN")
    														sstate = "idle"
    													end
    												end
    											end
    										end
    										if play:hitTestPoint(e.x,e.y) then
    											play:setText("SIMON'S TURN")
    											sphrase = {}
    											usersay = {}
    											s_counter = 1
    											sstate = "add"
    										end
    									end)
    stage:addEventListener(Event.MOUSE_UP, function(e)
    										for i in ipairs(s) do 
    												if s[i]:hitTestPoint(e.x, e.y) then	
    													s[i].gfx:setFillColor(colors[i],.5)
    												end
    										end
    									end)
    stage:addEventListener(Event.ENTER_FRAME, function()
    										if sstate == "go" then
    											if  os.timer()-start_time >0.7 then
    													if s_counter <=#sphrase then
    														s[sphrase[s_counter]].gfx:setFillColor(colors[sphrase[s_counter]],1)
    														start_time = os.timer()	
    													end
     
    													if s_counter == #sphrase then
    														sstate = "wait"
    														play:setText("YOUR TURN")
    													else
    														sstate = "next"	
    														play:setText("SIMON'S TURN")
    													end
    											end	
    										elseif sstate == "add" then
    											sphrase[#sphrase+1]=math.random(4)
    											s_counter = 1
    											usersay = {}
    											sstate = "go"
    											start_time = os.timer()
    										elseif sstate == "next" then
    											s_counter = s_counter+1
    											sstate = "go"
    										end
    									end)

    +1 -1 (+4 / -0 )Share on Facebook
  • PaulHPaulH Member
    edited May 2023
    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:
    	local function to4Bytes(n) return n & 0xff,(n>>8) & 0xff, (n>>16) & 0xff,(n>>24) & 0xff end
    	local function sound_wave(p)
    		local n = math.random(-255*128,255*128)
    		return string.char((n>>8) & 0xff,n & 0xff)
    	end
    	local samples = 600
    	local total_file_length = 44+samples*2
    	local fl1, fl2, fl3, fl4 = to4Bytes(total_file_length)
    	local ds1, ds2, ds3, ds4 = to4Bytes(samples*2)
    	f=io.open("|D|noise.wav","wb")
    	f:write(string.char(82,73,70,70,fl1,fl2,fl3,fl4,87,65,86,69,102,109,116,32,16,0,0,0,1,0,1,0,112,23,0,0,
    		224,46,0,0,2,0,16,0,100,97,116,97,ds1,ds2,ds3,ds4))
    	for l = 1, samples do f:write(sound_wave(l)) end
    	f:close()
    	Sound.new("|D|noise.wav"):play()
    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:
    	local function 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 frequency
    	local function sound_wave(wave,p,freq,vol)
    		local sample_rate = 6000
    		local n = 0 -- Default to silence
    		if 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) end
    		local c2, c1 = (n>>8) & 0xff,n & 0xff
    		return string.char(c1,c2)
    	end
     
    	-- We're just dealing with a fixed format wav file:
    	local sample_rate = 6000
    	local samples = length*sample_rate
    	local data_size = samples * 2
    	local 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 overall
    		87,65,86,69,-- WAVE header
    		102,109,116,32,--fmt chunk marker w/ trailing null
    		16,0,0,0, -- Length of format data (16 bytes before this)
    		1,0, -- Wave type. 1 == PCM
    		1,0, -- Channels. 1 == mono
    		112,23,0,0,-- Sample rate (Hertz)
    		224,46,0,0, -- Bits per sample * channels * sample rate / 8
    		2,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 or 1)) 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, 7 do
    	for i = 1, #names do
    		local 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)
    	end
    end
     
    -- Given a string of 3 letter note names, play those notes in order:
    function play_sequence(seq)
    	for i = 1, string.len(seq), 3 do
    		local note = string.sub(seq, i, i+2)
    		if sounds[note] then
    			Timer.delayedCall(60 * (i-1), function()  sounds[note]:play() end)
    		end
    	end
    end
     
    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
    )

    Likes: MoKaLux, pie

    +1 -1 (+2 / -0 )Share on Facebook
  • hgy29hgy29 Maintainer
    Note that you can avoid using a real file in |D| by using Buffer class

    Likes: MoKaLux, keszegh, PaulH

    +1 -1 (+3 / -0 )Share on Facebook
  • PaulHPaulH Member
    Ah. Suspect there might be a way to do that. Thanks!
  • PaulHPaulH Member
    I'm trying to use the Buffer but getting "no such file" when I try to create the sound from it. Am I missing something?
    	local function to4Bytes(n) return n & 0xff,(n>>8) & 0xff, (n>>16) & 0xff,(n>>24) & 0xff end
    	local function sound_wave(p)
    		local n = math.random(-255*128,255*128)
    		return string.char((n>>8) & 0xff,n & 0xff)
    	end
    	local samples = 600
    	local total_file_length = 44+samples*2
    	local fl1, fl2, fl3, fl4 = to4Bytes(total_file_length)
    	local ds1, ds2, ds3, ds4 = to4Bytes(samples*2)
    	b=Buffer.new("|B|noise.wav")
    	b:set(string.char(82,73,70,70,fl1,fl2,fl3,fl4,87,65,86,69,102,109,116,32,16,0,0,0,1,0,1,0,112,23,0,0,
    		224,46,0,0,2,0,16,0,100,97,116,97,ds1,ds2,ds3,ds4))
    	for l = 1, samples do b:append(sound_wave(l)) end
    	Sound.new("|B|noise.wav"):play()
  • hgy29hgy29 Maintainer
    @PaulH, the Buffer name musn't include the '|B|' header, it is only present in actual file names.
  • hgy29hgy29 Maintainer
    Also, you can use string.encodeValue(n,"i") instead of to4Bytes() (but it will output a string directly)
  • piepie Member
    edited May 2023
    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]

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • PaulHPaulH Member
    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?
  • hgy29hgy29 Maintainer
    PaulH said:

    Thanks @hgy29. The buffer may come in really handy. I often create textures via code,

    Then you can also pass the texture data directly to Texture.new, as a string, bypassing file decoding completely

  • PaulHPaulH Member
    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==0 then nzz=0.1 end
       local 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 level
    	if (dx >= 0 and dx <= sw and dy >= 0 and dy <= sh and z >  0) then return true, dx, dy end
    	return false,dx,dy
    end
     
    local 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))
    	end
    	return s
    end
     
    function make3DLine(model, loc, rot, vloc, vrot)
    	local s=Shape.new() s:setLineStyle(1,0x00ff00) s:beginPath()
    	for i=1,#model do
    		local success,vis,dx,dy=pcall(get2DFrom3D, model[i],loc, rot,
    			vloc, vrot, sh/2,sw,sh)
    		if success then
    			if i == 1 then s:moveTo(dx,dy) else s:lineTo(dx,dy) end
    		end
    	end
    	s:endPath()
    	return s
    end
     
    vloc,vrot={0, 0.5, 0},{0, 0, 0}
    function make_object(z)
    	local m=pyramid
    	if math.random(0,9)<5 then m=cube end
    	local z = z or math.random(5,100)
    	return
    		{
    			model=m,
    			loc={math.random(-z,z),0,z},
    			rot={0,math.random(0,360),0}
    		}
    end
     
    local objects={}
    for i = 1, 10 do
    	table.insert(objects, make_object())
    end
     
    local 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 do
    			if 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]-= 1
    			if objects[i].loc[3] < 0 then
    				objects[i].shape:removeFromParent()
    				objects[i]=make_object(100)
    				collectgarbage()
    			end
     
    		end
    	end
    )

    Likes: pie

    +1 -1 (+1 / -0 )Share on Facebook
  • piepie Member
    edited May 2023
    PaulH said:

    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?

    It opens inside a gideros player window for me, and on Windows10 the content scales well.. I guess you are on another platform, right?
    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

    +1 -1 (+2 / -0 )Share on Facebook
  • PaulHPaulH Member
    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:
  • PaulHPaulH Member
    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.

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • hgy29hgy29 Maintainer
    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

    Likes: PaulH

    +1 -1 (+1 / -0 )Share on Facebook
  • PaulHPaulH Member
    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!

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • PaulHPaulH Member
    edited May 2023
    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==0 then nzz=0.1 end
       local 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 >= 0 and dx <= sw and dy >= 0 and dy <= sh and z >  0) then return true, dx, dy end
    	return false,dx,dy
    end
    local 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)) end
    	return s
    end
    function l3D(m, loc, rot, v)
    	local s=Shape.new() s:setLineStyle(3,gr) s:beginPath()
    	for i=1,#m do
    		local vis,dx,dy=get2D(m[i],loc, rot[3], v,sh/2)
    		if i == 1 then s:moveTo(dx,dy) else s:lineTo(dx,dy) end
    	end
    	s:endPath()
    	return s
    end
    v={0, 0.5, 0}
    function mkObj(z,x)
    	local m,y=pyramid,0
    	if math.random(0,9)<5 then m=cube y = 1 end
    	local z = z or math.random(100,200)
    	return { m=m, loc={(x or 0)+math.random(-50,50),y,z}, rot={0,math.random(0,360),0}}
    end
    function start()
    	o={}
    	v[1],v[3]=0,0
    	for i = 1, 10 do table.insert(o, mkObj()) end
    	speed,score,go=10,0,null
    	while stage:getNumChildren()>0 do 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, 50 do
    		x,y=x+math.random(50,70),y+math.random(-50,50)
    		s:lineTo(x,y)
    		if math.random(1,3)==1 then ox = x oy = y elseif ox and math.random(1,5)==2 then x = ox y = oy s:moveTo(x, y) end
    	end
    	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()
    		if not 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 do
    				if 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.5 then
    					if (math.abs(o[i].loc[1] - v[1])) < 1 then
    						go = 1
    						for j = 1, 9 do 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])
    				end
    			end
    			if score%500 == 0 then
    				table.insert(o, mkObj(v[3]))
    			end
    		end
    	end
    )
    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) end end )
    stage:addEventListener(Event.MOUSE_UP, function()steering = null end)
    +1 -1 (+5 / -0 )Share on Facebook
  • rrraptorrrraptor Member
    edited May 2023
    Tetris :smile:
    The visual part takes too many characters... When I use imgui for rendering, the code becomes smaller and cleaner (though still messy :smiley: ).
    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
    end
    for i=1,21 do BR[i]={} for j=1,10 do BR[i][j]={d=i>20 and 8 or 0,g=newTile()} end end
    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 or 20 do for j=1,10 do c(BR[i][j],j,i) end end end
    function forT(t,c) 
    	for y=1,#t do for x=1,#t[1] do 
    		if t[y][x]>0 then 
    			local r=c(x,y) 
    			if r~=nil then return r end 
    		end 
    	end end 
    end
    function over(t,tx,ty,td)
    	return forT(td,function(x,y)
    		local bv=t[ty+y-1][tx+x-1]
    		if not bv or bv.d>0 then return true end end)
    end
    function put(t,tx,ty,td,n) forT(td,function(x,y) t[ty+y-1][tx+x-1].d=n end) end
    function trMv(x,y,nb)
    	local tb={}
    	for i=1,21 do tb[i]={} for j=1,10 do tb[i][j]={d=BR[i][j].d} end end
    	put(tb,T.x,T.y,T.d,0)
    	if not over(tb,x,y,nb) then
    		put(BR,T.x,T.y,T.d,0)
    		put(BR,x,y,nb,T.i)
    		return true
    	end
    end
    function 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
    		return true 
    	end
    end
    stage:addEventListener("keyDown", function(e)
    	if e.keyCode==KC.SPACE and GS==0 then GS=1 end
    	if not CM then return end
    	if e.keyCode==KC.DOWN then TS=0.05
    	elseif e.keyCode==KC.UP then
    		local nb={}
    		for i=#T.d[1],1,-1 do
    			nb[i]={}
    			for j=1,#T.d do nb[i][j]=T.d[#T.d-j+1][i] end 
    		end
    		if trMv(T.x,T.y,nb) then T.d=nb end
    	elseif e.keyCode==KC.LEFT then mv(-1,0)
    	elseif e.keyCode==KC.RIGHT then mv(1,0) 
    	end
    end)
    stage:addEventListener("keyUp", function(e) if e.keyCode==KC.DOWN then TS=1 end end)
    stage:addEventListener("enterFrame", function(e)
    	TR+=e.deltaTime
    	if TR>0.4*TS then
    		TR=0
    		if GS==1 then
    			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=0 end)
    				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)
    +1 -1 (+6 / -0 )Share on Facebook
  • rrraptorrrraptor Member
    edited May 2023
    keszegh said:

    i did not expect a tetris in 3k. great thread.

    A bit more :smiley: I updated the code, now it is 3279 (114 lines) (before: 3317 (115))
  • piepie Member
    thank you guys for making me feel unable :D

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • keszeghkeszegh Member
    rrraptor said:

    keszegh said:

    i did not expect a tetris in 3k. great thread.

    A bit more :smiley: I updated the code, now it is 3279 (114 lines) (before: 3317 (115))
    you could separately share the imgui version, it could give ideas for us how to use the imgui graphics functions.
Sign In or Register to comment.