
- 在调度编辑器中模拟敌人移动,解决敌人静态显示的问题。
1. 解决方案概述
- 核心思路:通过记录敌人移动轨迹并在调度编辑器中展示这些轨迹,以实现敌人移动的可视化。
2. 轨迹记录与导入
- 轨迹记录:在敌人行为编辑器中,将敌人的移动路径保存为轨迹文件。
- 轨迹导入:在调度编辑器中导入这些轨迹文件,以便进一步处理和展示。
3. 轨迹处理与显示
- 轨迹处理:对导入的轨迹数据进行处理,以适应调度编辑器的显示需求。
- 轨迹显示:在调度编辑器中绘制敌人的移动轨迹,以直观展示敌人的移动模式。
4. 功能优化与细节调整
- 相对坐标调整:将轨迹点调整为相对于敌人生成位置的相对坐标,以确保轨迹的准确性。
- 轨迹长度限制:设置轨迹记录的最大帧数,以防止轨迹数据过大。
- UI优化:在编辑器UI中添加轨迹数量显示功能,提升用户体验。
5. 技术挑战与解决方案
- 三维数组处理:轨迹数据以三维数组形式存储,需特别处理以适应调度编辑器的二维显示需求。
- 敌人对象管理:优化敌人对象的生成和管理,确保轨迹与敌人对象正确关联。
pico-8 cartridge // http://www.pico-8.com
version 41
__lua__
--todo-- todo
-------
-- autosave
-- position goto
-- how do i fire multiple bulletsfunction _init()--- customize here ---#include shmup_brains.txt#include shmup_brains_meta.txt#include shmup_brains_trails.txtfile="shmup_brains.txt"filem="shmup_brains_meta.txt"arrname="brains"data=brains#include shmup_myspr.txt#include shmup_enlib.txt#include shmup_anilib.txt#include shmup_pats.txt------------------------ process trails --for i=1,#trails dolocal myt=trails[i]for j=1,#myt domyt[j]=split(myt[j],":")if #myt[j]!=2 thenmyt[j]={0,0}endendend-------------------debug={}msg={}_drw=draw_brain_upd=update_brainmenuitem(1,"export",export)reload(0x0,0x0,0x2000,"cowshmup.p8")curx=1cury=1scrolly=0scrollx=0selbrain=1cmdlist={"hed","wai","asp","got","fir","adr","clo","flw","lop"}execy=0scroll=0enemies={}buls={} muzz={}overlay=falseshowtrails=falseshowui=truenewtrails=trails[1]curtrails=trails[1]pspr={x=64,y=110}poke(0x5f2d, 1)t=0
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)endendfunction _update60()t+=1dokeys()mscroll=stat(36)scroll+=0.2scroll=scroll%16_upd()
endfunction dokeys()if stat(30) thenkey=stat(31)if key=="p" thenpoke(0x5f30,1)endelsekey=nilendend
-->8
--drawfunction draw_brain()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()circ(pspr.x,pspr.y,3,5)for e in all(enemies) dodrawobj(e)if overlay and e==protag thenlocal myang=e.angif e.spd<0 thenmyang-=-0.5endlocal ox=sin(myang)local oy=cos(myang)local ox1=e.x+ox*12local oy1=e.y+oy*12pset(e.x,e.y,11)line(ox1,oy1,ox1+ox*8*abs(e.spd),oy1+oy*8*abs(e.spd),11)endendfor s in all(buls) dodrawobj(s) end-- temp muzzle flashesfor m in all(muzz) dom.r-=1if m.en thencircfill(m.en.x,m.en.y,m.r,7)endif m.r<=0 thendel(muzz,m)endendif showtrails thenlocal selmeta=meta[selbrain]for t in all(curtrails) dopset(t[1]+selmeta[2],t[2]+selmeta[3],11)endendif showui thendrawmenu()line(1+scrollx,execy+scrolly,1+scrollx,execy+6+scrolly,11)end
endfunction draw_table()cls(2)--spr(0,0,0,16,16)drawmenu()--[[for i=1,#data dofor j=1,#data[i] dobgprint(data[i][j],2+18*j,2+8*i,7)endend]]
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 13bgprint(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_setup()scrolly=0refresh_setup()if btnp(⬆️) thencury-=1endif btnp(⬇️) thencury+=1endcury=mid(2,cury,#menu)curx=2if btnp(❎) thenlocal mymnu=menu[cury][curx]if mymnu.cmd=="meta" then_upd=upd_typetypetxt=tostr(mymnu.txt)typecur=#typetxt+1callback=enter_metaendendif btnp(🅾️) then_upd=update_brainrefresh_brain()returnendif data[selbrain] thenif #enemies==0 thenlocal selmeta=meta[selbrain]if enlib[selmeta[1]]!=nil thencurtrails=trails[selbrain]reseten()end enddobuls(buls)doenemies() elseenemies={}endendfunction update_brain()refresh_brain()pspr.x=stat(32)pspr.y=stat(33)if key=="1" thenoverlay= not overlayendif key=="2" thenshowtrails= not showtrailsendif key=="3" thenshowui= not showuiendif btnp(⬆️) thencury-=1endif btnp(⬇️) thencury+=1endcury=mid(1,cury,#menu)if cury==1 thenif btnp(⬅️) thenselbrain-=1enemies={}curtrails=trails[selbrain] or {}newtrails=curtrailsendif btnp(➡️) thenselbrain+=1enemies={}curtrails=trails[selbrain] or {}newtrails=curtrailsendselbrain=mid(1,selbrain,#data+1)elseif btnp(⬅️) thencurx-=1endif btnp(➡️) thencurx+=1endcurx=mid(1,curx,#menu[cury])end---scrollinglocal mymnu=menu[cury][curx]if mymnu.y+scrolly>100 thenscrolly-=4endif mymnu.y+scrolly<10 thenscrolly+=4endscrolly=min(0,scrolly)--interactionif btnp(❎) thenlocal mymnu=menu[cury][curx]if mymnu.cmd=="edit" then_upd=upd_typetypetxt=tostr(mymnu.txt)typecur=#typetxt+1callback=enter_brainelseif mymnu.cmd=="newline" thenadd(data[mymnu.cmdb],"wai",mymnu.cmdi)add(data[mymnu.cmdb],0,mymnu.cmdi+1)add(data[mymnu.cmdb],0,mymnu.cmdi+2)cury+=1curx=1elseif mymnu.cmd=="setup" thenrefresh_setup()_upd=update_setupreturnelseif mymnu.cmd=="newbrain" thenadd(data,{"wai",0,0})add(meta,{1,64,10})endreturn endif data[selbrain] thenif #enemies==0 thenlocal selmeta=meta[selbrain]if enlib[selmeta[1]]!=nil thentrails[selbrain]=newtrailscurtrails=trails[selbrain]reseten()endenddobuls(buls)doenemies()if protag and t%5==0 thenlocal selmeta=meta[selbrain]if t<450 thenadd(newtrails,{protag.x-selmeta[2],protag.y-selmeta[3]})elsetrails[selbrain]=newtrailscurtrails=newtrailsendendelseenemies={}end
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+1callback=enter_tableelseif 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-- enterpoke(0x5f30,1)callback()returnelseif 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 spacejam(n)local ret=""for i=1,n doret..=" "endreturn ret
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
endfunction onscreen(obj)if obj.x<-8 then return false endif obj.y<-8 then return false endif obj.x>136 then return false endif obj.y>136 then return false end return true
endfunction spread(val)return (rnd(2)-1)*val
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)local s="meta=split2d\""for i=1,#meta doif i>1 thens..="|"endfor j=1,#meta[i] doif j>1 thens..=","ends..=meta[i][j]endends..="\""printh(s,filem,true)local s="trails=split2d\""for i=1,#data doif i>1 thens..="|"endif trails[i]!=nil thenfor j=1,#trails[i] doif j>1 thens..=","ends..=trails[i][j][1]..":"..trails[i][j][2]endendends..="\""printh(s,"shmup_brains_trails.txt",true)add(msg,{txt="exported!",t=120})--debug[1]="exported!"
end
-->8
--uifunction refresh_setup()menu={}menui={}add(menu,{{txt="brain "..selbrain,w=" ",cmd="",x=3,y=3,c=13 }})local cap={"en:"," x:"," y:"}local selmeta=meta[selbrain]for i=1,3 dolocal lne={}add(lne,{txt=cap[i],w=" ",cmd="",x=3,y=3+i*6+2,c=13 })add(lne,{txt=selmeta[i],w=" ",cmd="meta",cmdy=i,x=3+12,y=3+i*6+2,c=13 })add(menu,lne)endendfunction refresh_brain()menu={}menui={}execy=-16if selbrain>#data then--empty brain slotadd(menu,{{txt="< new brain ",w=" ",cmd="newbrain",x=3,y=3,c=13 }}) returnendadd(menu,{{txt="< brain "..selbrain.." >",w=" ",cmd="head",x=3,y=3,c=13 }}) add(menu,{{txt="◆setup",w=" ",cmd="setup",x=3,y=3+8,c=13 }})local mybra=brains[selbrain]local ly=19for i=1,#mybra,3 doif enemies[1] thenlocal myen=enemies[1]if myen.brain==selbrain and myen.bri==i thenexecy=ly-9endendlocal lne={}add(lne,{txt=mybra[i],w=" ",cmd="edit",cmdi=i,cmdb=selbrain,x=3,y=ly,c=13 })local lx=3+14 for j=1,2 dolocal mytxt=tostr(mybra[i+j])add(lne,{txt=mytxt,w=spacejam(#mytxt),cmd="edit",cmdi=i+j,cmdb=selbrain,x=lx,y=ly,c=13 })lx+=#mytxt*4+2endif cury==#menu+1 thenadd(lne,{txt="+",w=" ",cmd="newline",cmdi=i+3,cmdb=selbrain,x=lx,y=ly,c=13 })endadd(menu,lne)ly+=8endif menu[cury] thenlocal mymnu=menu[cury][curx]if mymnu and mymnu.cmd=="edit" thenadd(menui,{{txt="i:"..mymnu.cmdi,w=" ",cmd="",x=3,y=120,c=15 }})endendif showtrails thenadd(menui,{{txt="trs:"..(trails[selbrain] and #trails[selbrain] or "nil"),w=" ",cmd="",x=102,y=3,c=3 }})end
endfunction refresh_table()menu={}menui={}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), }})
endfunction enter_table()local mymnu=menu[cury][curx]local typeval=typetxtif typeval==nil or typeval=="" 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=0end data[mymnu.cmdy][mymnu.cmdx]=typeval_upd=update_table
endfunction enter_brain()_upd=update_brainlocal mymnu=menu[cury][curx]local typeval=typetxtenemies={}if mymnu.cmdi%3==1 then--editing command entryif typeval=="" thendeli(data[mymnu.cmdb],mymnu.cmdi)deli(data[mymnu.cmdb],mymnu.cmdi)deli(data[mymnu.cmdb],mymnu.cmdi)if #data[mymnu.cmdb]==0 thendeli(data,mymnu.cmdb)deli(meta,mymnu.cmdb)add(msg,{txt="brain deleted!",t=120})endreturnelse local found=false for c in all(cmdlist) doif typeval==c thenfound=trueend endif not found thentypeval="wai"endendelse--editing parameterstypeval=tonum(typetxt)if typeval==nil thentypeval=0endenddata[mymnu.cmdb][mymnu.cmdi]=typevalendfunction enter_meta()_upd=update_setuplocal mymnu=menu[cury][curx]local typeval=tonum(typetxt)enemies={}if typeval==nil thentypeval=0endmeta[selbrain][mymnu.cmdy]=typevalend
-->8
--enemyfunction dobrain(e,depth)--★ remove robustnessif braincheck(e)==false then return endlocal depth=depth or 1if depth>100 thenif #msg>0 thenmsg[1].t=5elseadd(msg,{txt="infinite loop",t=5})endreturnend -- robustness code endlocal mybra=brains[e.brain]local quit=falseif e.bri<#mybra thenlocal cmd=mybra[e.bri]local par1=mybra[e.bri+1]local par2=mybra[e.bri+2]if cmd=="hed" then--set heading / speede.ang=par1e.spd=par2e.aspt=nile.flw=falseelseif cmd=="wai" then--wait x framese.wait=par1e.dist=par2quit=trueelseif cmd=="asp" then--animate speede.aspt=par1e.asps=par2elseif cmd=="adr" then--animate directione.adrt=par1e.adrs=par2e.flw=falseelseif cmd=="got" then--gotoe.brain=par1e.bri=par2-3elseif cmd=="lop" then--loope.loop=e.loop and e.loop+1 or 1if e.loop<par1 thene.bri=par2-3elsee.loop=0endelseif cmd=="fir" then--fire--- ★ robustness check ---if par1<1 or par1>#pats then par1=1end------------------patshoot(e,par1,par2)elseif cmd=="clo" then--clonefor i=1,par1 dolocal myclo=copylist(e)myclo.wait+=i*par2myclo.bri+=3add(enemies,myclo)endelseif cmd=="flw" then--followe.flw=truee.flws=par1--par2??else--★ extra robustnessreturnende.bri+=3if quit then return enddobrain(e,depth+1)end
endfunction doenemies()for e in all(enemies) doif e.wait>0 thene.wait-=1elseif e.dist<=0 thendobrain(e)endif e.flw thenlocal diff=atan2(pspr.y-e.y,pspr.x-e.x)-e.ang if abs(diff)>0.5 thendiff-=sgn(diff)ende.ang+=mid(-e.flws,diff,e.flws)if dist(pspr.x,pspr.y,e.x,e.y)<25 thene.flw=falseende.ang=e.ang%1endif e.aspt thene.spd+=e.aspsif abs(e.aspt-e.spd)<abs(e.asps) thene.spd=e.aspte.aspt=nilendendif e.adrt thene.ang+=e.adrsif abs(e.adrt-e.ang)<abs(e.adrs) thene.ang=e.adrte.adrt=nilendende.sx=sin(e.ang)*e.spde.sy=cos(e.ang)*e.spde.dist=max(0,e.dist-abs(e.spd))e.x+=e.sxe.y+=e.sye.age+=1if not onscreen(e) thendel(enemies,e)else dobulq(e)endend
endfunction reseten()t=0enemies={}local selmeta=meta[selbrain]spawnen(selmeta[1],selmeta[2],selmeta[3])protag=enemies[1]newtrails={}buls={}
endfunction spawnen(eni,enx,eny)local en=enlib[eni]add(enemies,{x=enx,y=eny,ani=anilib[en[1]],anis=en[2],sx=0,sy=0,ang=0,spd=0,brain=selbrain,bri=1,age=0,flash=0,hp=en[4],col=en[5],wait=0,dist=0,bulq={}})endfunction braincheck(e)if brains[e.brain]==nil thenif #msg>0 thenmsg[1].t=5elseadd(msg,{txt="bad brain "..e.brain,t=5})endreturn falseendlocal mybra=brains[e.brain]if e.bri<1 thenif #msg>0 thenmsg[1].t=5elseadd(msg,{txt="brain command index < 1",t=5})endreturn falseelseif e.bri<#mybra thenlocal cmd=mybra[e.bri]local found=falsefor c in all(cmdlist) doif c==cmd thenfound=trueendendif found==false thenif #msg>0 thenmsg[1].t=5elseadd(msg,{txt="bad command "..cmd,t=5})endreturn falseendendreturn true
end
--- tools function copylist(org)local ret={}for k, v in pairs(org) doret[k]=vendreturn ret
endfunction dist(x1,y1,x2,y2)local dx,dy=x2-x1,y2-y1return sqrt(dx*dx+dy*dy)
end
-->8
--patsfunction makepat(pat,pang)local mypat,ret=pats[pat],{}local patype,p2,p3,p4,p5,p6,p7,p8=unpack(mypat)if patype=="base" thenadd(ret,{age=0,x=0,y=0,ang=pang,spd=p2,ani=anilib[p3],anis=p4,col=p5,wait=0})elseif patype=="some" thenif rnd()<p3 thenret=makepat(p3,pang)endelseif patype=="sprd" thenfor i=p3-1,p4-1 dofor p in all(makepat(p2,pang)) dop.spd+=i*p6p.wait+=i*p7add(ret,p)if i>0 and p8>0 thenlocal copyp=copylist(p)copyp.ang+=i*-p5add(ret,copyp)endp.ang+=i*p5endendelseif patype=="brst" thenfor i=1,p3 dolocal rndw,rnds=flr(rnd(p6)),rnd(p5)for p in all(makepat(p2,pang+spread(p4))) dop.wait+=rndwp.spd+=rndsadd(ret,p)endendelseif patype=="comb" thenfor i=2,5 doif mypat[i]>0 thenfor p in all(makepat(mypat[i],pang)) doadd(ret,p)endendendendreturn ret
endfunction patshoot(en,pat,pang)if pang==-99 thenpang=atan2(pspr.y-en.y,pspr.x-en.x)endlocal mybuls=makepat(pat,pang)for b in all(mybuls) doadd(en.bulq,b)end
endfunction dobulq(en)local oldb=#bulsfor b in all(en.bulq) doif b.wait<=0 thenb.x+=en.xb.y+=en.yb.sx=sin(b.ang)*b.spdb.sy=cos(b.ang)*b.spdadd(buls,b)del(en.bulq,b)elseb.wait-=1endendif oldb!=#buls thenadd(muzz,{en=en,r=8})endendfunction 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
end
__gfx__
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__map__
0000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000