制作一款打飞机游戏52:子弹模式
到目前为止,我们已经完成了很多工作:
. 整合动画系统:动画系统已经存在并整合完成。
. 统一碰撞系统:我们使用了统一碰撞盒逻辑。
. 数据库系统:涵盖了HP、Brain、动画、碰撞盒等内容,一切都被数据库系统管理。
不过,还有一些工作需要完善,比如当前的计划生成系统(或称为编辑器)没有良好的预览功能,特别是与“大脑”(敌人的行为系统)相关的部分还没有很好地链接起来。
子弹模式系统的规划
接下来,我们要专注于子弹模式系统。我的思路是先定义一些基本行为,然后开发一个支持这些行为的系统。以下是我计划实现的一些基本功能:
. 基础射击:敌人向玩家射击单发子弹。
. 连发:敌人快速连续射击多发子弹。
. 散射:子弹不仅在时间上分散,而且在空间(角度)上也分散。
. 复杂模式:如螺旋射击、多重散射(子弹的散射中再包含散射)等。
实现策略
为了实现这些功能,我打算采用递归模式系统。这意味着我们将有一系列基础模式和模式修饰符,可以将它们叠加在一起以创建复杂的子弹模式。例如:
- 基础模式:发射单发子弹。
- 修饰符:将单发子弹变成连发,或将连发子弹散射开来。
通过这种方式,我们可以组合出各种复杂的子弹模式。
假设与限制
为了让系统更加可控,我做了一些假设和限制:
. 子弹方向不变:一旦子弹被发射,它的方向就不会改变。
. 子弹速度不变:子弹将以恒定速度飞行,不会加速或减速。
这些限制有助于简化系统,使我们能够更专注于创造有趣的效果。
编辑器的开发
为了编辑和测试这些子弹模式,我们需要开发一个新的编辑器——模式编辑器(Pat Edit)。在这个编辑器中,我们可以定义和预览各种子弹模式。
目前,我已经开始搭建编辑器的基础框架,并计划添加一些基本功能,比如用鼠标控制模拟敌人的移动,并通过点击鼠标来发射子弹模式。
. 创建子弹动画:设计三种不同大小的子弹(小、中、大),并为它们创建动画。
. 实验子弹模式:尝试实现上述子弹模式,看看哪些系统工作得最好,哪些需要调整。
pico-8 cartridge // http://www.pico-8.com
version 41
__lua__
-- todo
------------------
-- move simiulated enemy with mouse
-- fire on click-- assumptions
------------------
-- bullets don't change direction
-- bullets don't change speed-- pattern goals
------------------
-- 1 static
-- 2 aimed
-- 3 rapid fire
-- 4 spread
-- 5 burst
-- 6 spread of spreads
-- 7 turning rapid fire (spiral)
-- 8 chunker spread
-- 9 labirinthfunction _init()--- customize here ---#include shmup_pats.txtfile="shmup_pats.txt"arrname="pats"data=pats#include shmup_myspr.txt#include shmup_anilib.txt----------------------debug={}msg={}_drw=draw_pats_upd=update_patsmenuitem(1,"export",export)reload(0x0,0x0,0x2000,"cowshmup.p8")curx=1cury=1scrolly=0scrollx=0scroll=0pspr={x=64,y=110}enspr={x=64,y=64}buls={} poke(0x5f2d, 1)
endfunction _draw()_drw()if #msg>0 thenbgprint(msg[1].txt,64-#msg[1].txt*2,80,14)msg[1].t-=1if msg[1].t<=0 thendeli(msg,1)end end-- debug --cursor(4,4)color(8)for txt in all(debug) doprint(txt)end
endfunction _update60()dokeys()domouse()mscroll=stat(36)scroll+=0.2scroll=scroll%16_upd()
endfunction dokeys()if stat(30) thenkey=stat(31)if key=="p" thenpoke(0x5f30,1)endelsekey=nilendendfunction domouse()local oldmousex=mousexlocal oldmousey=mouseymousex=stat(32)mousey=stat(33)mousemove=falseif mousex!=oldmousex or oldmousey!=mousey thenmousemove=trueendif stat(34)==0 thenclkwait=falseendclkl=falseclkr=falseif not clkwait thenif stat(34)==1 thenclkl=trueclkwait=trueelseif stat(34)==2 thenclkr=trueclkwait=true endendend
-->8
--drawfunction draw_pats()cls(13)if flr(scroll)%2==0 thenfillp(0b0000111100001111.1)elsefillp(0b1111000011110000.1)endfor i=0,7 doline(i*16,0,i*16,128,5)endfillp(▥)for i=-1,7 doline(0,i*16+scroll,128,i*16+scroll,5)endfillp()--enemyline(enspr.x-2,enspr.y-2,enspr.x+2,enspr.y+2,5)line(enspr.x-2,enspr.y+2,enspr.x+2,enspr.y-2,5)--playercirc(pspr.x,pspr.y,3,5)for s in all(buls) dodrawobj(s) enddrawmenu()endfunction draw_table()cls(2)--spr(0,0,0,16,16)drawmemnu()
endfunction drawmenu()if menu thenfor i=1,#menu dofor j=1,#menu[i] dolocal mymnu=menu[i][j]local c=mymnu.c or 13if i==cury and j==curx thenc=7if _upd==upd_type thenc=0endendbgprint(mymnu.w,mymnu.x+scrollx,mymnu.y+scrolly,13) bgprint(mymnu.txt,mymnu.x+scrollx,mymnu.y+scrolly,c) endendendif menui thenfor i=1,#menui dofor j=1,#menui[i] dolocal mymnui=menui[i][j]local c=mymnui.c or 13if i==cury and j==curx thenc=7if _upd==upd_type thenc=0endend bgprint(mymnui.w,mymnui.x,mymnui.y,13) bgprint(mymnui.txt,mymnui.x,mymnui.y,c) endendendif _upd==upd_type thenlocal mymnu=menu[cury][curx]local txt_bef=sub(typetxt,1,typecur-1)local txt_cur=sub(typetxt,typecur,typecur)local txt_aft=sub(typetxt,typecur+1)txt_cur=txt_cur=="" and " " or txt_cur if (time()*2)%1<0.5 thentxt_cur="\^i"..txt_cur.."\^-i"endlocal txt=txt_bef..txt_cur..txt_aftbgprint(txt,mymnu.x+scrollx,mymnu.y+scrolly,7)end
end-->8
--updatefunction update_pats()refresh_pats()enspr.x=mousexenspr.y=mouseyif clkl thenpatshoot(enspr,1)end dobuls(buls)
endfunction update_table()refresh_table()if btnp(⬆️) thencury-=1endif btnp(⬇️) thencury+=1endcury=(cury-1)%#menu+1cury-=mscrollcury=mid(1,cury,#menu)if btnp(⬅️) thencurx-=1endif btnp(➡️) thencurx+=1endif cury<#menu thencurx=(curx-2)%(#menu[cury]-1)+2elsecurx=1endlocal mymnu=menu[cury][curx]if mymnu.y+scrolly>110 thenscrolly-=4endif mymnu.y+scrolly<10 thenscrolly+=4endscrolly=min(0,scrolly)if mymnu.x+scrollx>110 thenscrollx-=2endif mymnu.x+scrollx<20 thenscrollx+=2endscrollx=min(0,scrollx)if btnp(❎) thenlocal mymnu=menu[cury][curx]if mymnu.cmd=="edit" then_upd=upd_typetypetxt=tostr(mymnu.txt)typecur=#typetxt+1elseif mymnu.cmd=="newline" thenadd(data,{0}) elseif mymnu.cmd=="newcell" thenadd(data[mymnu.cmdy],0)endend
endfunction upd_type()if key thenif key=="\r" then-- enterlocal mymnu=menu[cury][curx]poke(0x5f30,1)local typeval=tonum(typetxt)if typeval==nil thenif mymnu.cmdx==#data[mymnu.cmdy] and typetxt=="" then--delete celldeli(data[mymnu.cmdy],mymnu.cmdx)if mymnu.cmdx==1 thendeli(data,mymnu.cmdy)end_upd=update_tablereturnend typeval=0enddata[mymnu.cmdy][mymnu.cmdx]=typeval_upd=update_tablereturnelseif key=="\b" then--backspaceif typecur>1 thenif typecur>#typetxt thentypetxt=sub(typetxt,1,#typetxt-1)elselocal txt_bef=sub(typetxt,1,typecur-2)local txt_aft=sub(typetxt,typecur)typetxt=txt_bef..txt_aftendtypecur-=1endelseif typecur>#typetxt thentypetxt..=keyelselocal txt_bef=sub(typetxt,1,typecur-1)local txt_aft=sub(typetxt,typecur)typetxt=txt_bef..key..txt_aftendtypecur+=1endendif btnp(⬅️) thentypecur-=1endif btnp(➡️) thentypecur+=1endtypecur=mid(1,typecur,#typetxt+1)
end
-->8
--toolsfunction bgprint(txt,x,y,c)print("\#0"..txt,x,y,c)
endfunction split2d(s)local arr=split(s,"|",false)for k, v in pairs(arr) doarr[k] = split(v)endreturn arr
endfunction mysgn(v) return v==0 and 0 or sgn(v)
endfunction rndrange(low,high)return flr(rnd(high+1-low)+low)
endfunction mspr(si,sx,sy)local _x,_y,_w,_h,_ox,_oy,_fx,_nx=unpack(myspr[si])sspr(_x,_y,_w,_h,sx-_ox,sy-_oy,_w,_h,_fx==1)if _fx==2 thensspr(_x,_y,_w,_h,sx-_ox+_w,sy-_oy,_w,_h,true)endif _nx thenmspr(_nx,sx,sy)end
endfunction cyc(age,arr,anis)local anis=anis or 1return arr[(age\anis-1)%#arr+1]
endfunction drawobj(obj)mspr(cyc(obj.age,obj.ani,obj.anis),obj.x,obj.y)--★if coldebug and obj.col thenmsprc(obj.col,obj.x,obj.y)end
end
-->8
--i/o
function export()local s=arrname.."=split2d\""for i=1,#data doif i>1 thens..="|"endfor j=1,#data[i] doif j>1 thens..=","ends..=data[i][j]endends..="\""printh(s,file,true)add(msg,{txt="exported!",t=120})--debug[1]="exported!"
end
-->8
--uifunction refresh_pats()menu={}
endfunction refresh_table()menu={}for i=1,#data dolocal lne={}local linemax=#data[i]if i==cury thenlinemax+=1 endadd(lne,{txt=i,w=" ",cmd="",x=4,y=-4+8*i,c=2 })for j=1,linemax doif j==#data[i]+1 thenadd(lne,{txt="+",w=" ",cmd="newcell",cmdy=i,x=-10+14*(j+1),y=-4+8*i, })elseadd(lne,{txt=data[i][j],cmd="edit",cmdx=j,cmdy=i,x=-10+14*(j+1),y=-4+8*i,w=" "})endendadd(menu,lne)endadd(menu,{{txt=" + ",w=" ",cmd="newline",x=4,y=-4+8*(#data+1), }})
end
-->8
--patsfunction dobuls(arr)for s in all(arr) dos.age+=1s.x+=s.sxs.y+=s.syif s.y<-16 or s.y>130 thendel(arr,s)endend
endfunction patshoot(en,pat)add(buls,{x=en.x,y=en.y,sx=0,sy=1,ani=anilib[6],anis=1,age=0})
end
__gfx__
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__map__
0000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000