模式编辑器的新功能
现在,我们有了这个能力,可以像这样创建子弹的扩散效果。通常它看起来是这样的,但现在我们可以创建10个子弹,然后效果就变成了这样。我们可以创建出这些美丽的扩散效果,这真是太强大了!
扩散效果的进一步探索
而且,你们知道吗?当我们有这样的小扩散效果时,我们还可以导出它,然后创建一个扩散的扩散效果。这意味着我们可以引用那个扩散效果,然后再创建一个基于它的新扩散效果。我们之前讨论过想要实现哪些功能,现在看起来,几乎任何效果都是可能的。
例如,这里有一个看起来很不错的子弹模式,我们有一个安全区域。所以,有很多可能性。现在,我们可以将这些不同的模式修饰符叠加在一起,它们的效果会相互叠加,让子弹的数量和角度都发生变化,是不是很酷?
快速发射问题的讨论
接下来,我想讨论一下快速发射的问题。目前,当我们创建一个模式时,子弹会立即被创建并飞出去。如果我们想要实现快速发射的效果,就不能让子弹立即出现。有两种方法可以实现快速发射:一种是先创建所有子弹,然后在延迟后一起发射;另一种是创建一个带有内置定时器的对象,让它定时生成子弹,就像粒子效果中的爆炸一样。
我倾向于使用第一种方法,即创建一个事件来生成所有子弹,并给每个子弹添加一个内置的等待定时器。这样,子弹就不会立即出现,而是会在延迟后发射。但这意味着我们需要一个子弹队列,每个敌人都有自己的队列,而不是全局队列。
实现子弹队列
现在,我们来实现这个子弹队列。首先,我们不添加任何延迟,只是将子弹添加到敌人的子弹队列中。然后,我们会有一个函数来处理这个队列,将子弹发射出去。
在update
函数中,当我们创建敌人并射击时,我们会将子弹添加到敌人的子弹队列中,然后处理这个队列,将子弹发射出去。但是,我遇到了一个问题,子弹发射得太快了!原来是因为我忘记删除已经发射的子弹了。修复这个问题后,我们就可以实现快速发射的效果了。
快速发射效果的实现
现在,我们来创建一个新的模式类型,叫做"快速"。我们将有一个源模式、子弹数量和延迟时间。在创建子弹时,我们会保持原始子弹的角度,但会给每个子弹添加一个等待时间。当等待时间结束时,子弹就会被发射出去。
但是,我又遇到了一个问题:当敌人移动时,子弹会在敌人原来的位置生成,而不是在敌人当前的位置。为了解决这个问题,我们需要在处理子弹队列时,将子弹的位置设置为敌人的当前位置,并计算发射角度。
散射效果的改进
接下来,我们来改进散射效果。目前,散射效果只能修改子弹的角度,但我们可以让它也能修改子弹的速度和延迟时间。这样,我们就可以创建一个更强大的散射效果了。
为了实现这个功能,我们需要给每个子弹添加一个速度属性和一个等待时间属性。在创建子弹时,我们可以随机修改这些属性,从而得到不同的散射效果。
爆炸效果的实现
最后,我们来实现一个爆炸效果,我称之为"爆发"。爆发效果将参考一个源模式,并创建多个子弹,但会给每个子弹添加一个随机的角度偏移、速度偏移和延迟时间。这样,我们就可以得到一个类似于霰弹枪的效果了。
为了实现这个功能,我们需要创建一个新的模式修饰符,并在创建子弹时应用随机偏移量。同时,我们还需要一个辅助函数来生成随机的正负值,以便让子弹在两个方向上散射。

pico-8 cartridge // http://www.pico-8.com
version 41
__lua__
-- todo
------------------
-- spread
-- - both sides / mirror?
-- assumptions
------------------
-- bullets don't change direction
-- bullets don't change speed
-- pattern goals
------------------
-- 4 spread
-- 6 spread of spreads
-- 7 turning rapid fire (spiral)
-- 8 chunker spread
-- 9 labirinth
function _init()
--- customize here ---
#include shmup_pats.txt
file="shmup_pats.txt"
arrname="pats"
data=pats
#include shmup_myspr.txt
#include shmup_anilib.txt
----------------------
debug={}
msg={}
_drw=draw_pats
_upd=update_pats
menuitem(1,"export",export)
reload(0x0,0x0,0x2000,"cowshmup.p8")
curx=1
cury=1
scrolly=0
scrollx=0
scroll=0
pspr={
x=64,
y=110
}
enspr={
x=64,
y=64,
bulq={}
}
buls={}
poke(0x5f2d, 1)
selpat=1
fireang=-99
end
function _draw()
_drw()
if #msg>0 then
bgprint(msg[1].txt,64-#msg[1].txt*2,80,14)
msg[1].t-=1
if msg[1].t<=0 then
deli(msg,1)
end
end
-- debug --
cursor(4,4)
color(8)
for txt in all(debug) do
print(txt)
end
end
function _update60()
dokeys()
domouse()
mscroll=stat(36)
scroll+=0.2
scroll=scroll%16
_upd()
end
function dokeys()
if stat(30) then
key=stat(31)
if key=="p" then
poke(0x5f30,1)
end
else
key=nil
end
end
function domouse()
local oldmousex=mousex
local oldmousey=mousey
mousex=stat(32)
mousey=stat(33)
mousemove=false
if mousex!=oldmousex or oldmousey!=mousey then
mousemove=true
end
if stat(34)==0 then
clkwait=false
end
clkl=false
clkr=false
if not clkwait then
if stat(34)==1 then
clkl=true
clkwait=true
elseif stat(34)==2 then
clkr=true
clkwait=true
end
end
end
-->8
--draw
function draw_pats()
cls(13)
if flr(scroll)%2==0 then
fillp(0b0000111100001111.1)
else
fillp(0b1111000011110000.1)
end
for i=0,7 do
line(i*16,0,i*16,128,5)
end
fillp(▥)
for i=-1,7 do
line(0,i*16+scroll,128,i*16+scroll,5)
end
fillp()
--enemy
line(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)
--player
circ(pspr.x,pspr.y,3,5)
for s in all(buls) do
drawobj(s)
end
drawmenu()
end
function draw_table()
cls(2)
--spr(0,0,0,16,16)
drawmemnu()
end
function drawmenu()
if menu then
for i=1,#menu do
for j=1,#menu[i] do
local mymnu=menu[i][j]
local c=mymnu.c or 13
if i==cury and j==curx then
c=7
if _upd==upd_type then
c=0
end
end
bgprint(mymnu.w,mymnu.x+scrollx,mymnu.y+scrolly,13)
bgprint(mymnu.txt,mymnu.x+scrollx,mymnu.y+scrolly,c)
end
end
end
if menui then
for i=1,#menui do
for j=1,#menui[i] do
local mymnui=menui[i][j]
local c=mymnui.c or 13
if i==cury and j==curx then
c=7
if _upd==upd_type then
c=0
end
end
bgprint(mymnui.w,mymnui.x,mymnui.y,13)
bgprint(mymnui.txt,mymnui.x,mymnui.y,c)
end
end
end
if _upd==upd_type then
local 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 then
txt_cur="\^i"..txt_cur.."\^-i"
end
local txt=txt_bef..txt_cur..txt_aft
bgprint(txt,mymnu.x+scrollx,mymnu.y+scrolly,7)
end
end
-->8
--update
function update_pats()
if key=="1" then
if fireang==-99 then
fireang=0
else
fireang=-99
end
end
refresh_pats()
enspr.x=mousex
enspr.y=mousey
if clkl and selpat<=#pats then
patshoot(enspr,selpat,fireang)
end
dobulq(enspr)
dobuls(buls)
if btnp(⬆️) then
cury-=1
end
if btnp(⬇️) then
cury+=1
end
cury=mid(1,cury,#menu)
if cury==1 then
curx=1
if btnp(⬅️) then
selpat-=1
end
if btnp(➡️) then
selpat+=1
end
selpat=mid(1,selpat,#pats+1)
elseif cury==2 then
curx=1
elseif cury==#menu then
curx=1
else
curx=2
end
if btnp(❎) then
local mymnu=menu[cury][curx]
if mymnu.cmd=="patedit" then
_upd=upd_type
typetxt=tostr(mymnu.txt)
typecur=#typetxt+1
callback=enter_pat
return
elseif mymnu.cmd=="newpat" then
add(pats,newpat("base"))
return
elseif mymnu.cmd=="delpat" then
deli(pats,selpat)
add(msg,{txt="pat deleted!",t=120})
end
end
end
function update_table()
refresh_table()
if btnp(⬆️) then
cury-=1
end
if btnp(⬇️) then
cury+=1
end
cury=(cury-1)%#menu+1
cury-=mscroll
cury=mid(1,cury,#menu)
if btnp(⬅️) then
curx-=1
end
if btnp(➡️) then
curx+=1
end
if cury<#menu then
curx=(curx-2)%(#menu[cury]-1)+2
else
curx=1
end
local mymnu=menu[cury][curx]
if mymnu.y+scrolly>110 then
scrolly-=4
end
if mymnu.y+scrolly<10 then
scrolly+=4
end
scrolly=min(0,scrolly)
if mymnu.x+scrollx>110 then
scrollx-=2
end
if mymnu.x+scrollx<20 then
scrollx+=2
end
scrollx=min(0,scrollx)
if btnp(❎) then
local mymnu=menu[cury][curx]
if mymnu.cmd=="edit" then
_upd=upd_type
typetxt=tostr(mymnu.txt)
typecur=#typetxt+1
callback=enter_table
elseif mymnu.cmd=="newline" then
add(data,{0})
elseif mymnu.cmd=="newcell" then
add(data[mymnu.cmdy],0)
end
end
end
function upd_type()
if key then
if key=="\r" then
-- enter
poke(0x5f30,1)
callback()
return
elseif key=="\b" then
--backspace
if typecur>1 then
if typecur>#typetxt then
typetxt=sub(typetxt,1,#typetxt-1)
else
local txt_bef=sub(typetxt,1,typecur-2)
local txt_aft=sub(typetxt,typecur)
typetxt=txt_bef..txt_aft
end
typecur-=1
end
else
if typecur>#typetxt then
typetxt..=key
else
local txt_bef=sub(typetxt,1,typecur-1)
local txt_aft=sub(typetxt,typecur)
typetxt=txt_bef..key..txt_aft
end
typecur+=1
end
end
if btnp(⬅️) then
typecur-=1
end
if btnp(➡️) then
typecur+=1
end
typecur=mid(1,typecur,#typetxt+1)
end
-->8
--tools
function bgprint(txt,x,y,c)
print("\#0"..txt,x,y,c)
end
function spacejam(n)
local ret=""
for i=1,n do
ret..=" "
end
return ret
end
function split2d(s)
local arr=split(s,"|",false)
for k, v in pairs(arr) do
arr[k] = split(v)
end
return arr
end
function mysgn(v)
return v==0 and 0 or sgn(v)
end
function rndrange(low,high)
return flr(rnd(high+1-low)+low)
end
function 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 then
sspr(_x,_y,_w,_h,sx-_ox+_w,sy-_oy,_w,_h,true)
end
if _nx then
mspr(_nx,sx,sy)
end
end
function cyc(age,arr,anis)
local anis=anis or 1
return arr[(age\anis-1)%#arr+1]
end
function drawobj(obj)
mspr(cyc(obj.age,obj.ani,obj.anis),obj.x,obj.y)
--★
if coldebug and obj.col then
msprc(obj.col,obj.x,obj.y)
end
end
-->8
--i/o
function export()
local s=arrname.."=split2d\""
for i=1,#data do
if i>1 then
s..="|"
end
for j=1,#data[i] do
if j>1 then
s..=","
end
s..=data[i][j]
end
end
s..="\""
printh(s,file,true)
add(msg,{txt="exported!",t=120})
--debug[1]="exported!"
end
-->8
--ui
function refresh_pats()
menu={}
if selpat>#pats then
add(menu,{
{
txt="< new pat ",
w=" ",
cmd="newpat",
x=4,
y=4,
c=13
}
})
return
end
local mypat=pats[selpat]
add(menu,{
{
txt="< pat "..selpat.." >",
w=" ",
cmd="pat",
x=4,
y=4,
c=13
}
})
add(menu,{
{
txt=mypat[1],
w=" ",
cmd="patedit",
cmdy=selpat,
cmdx=1,
x=4,
y=12,
c=13
}
})
local mycap={}
if mypat[1]=="base" then
mycap={
"spd :",
"ani :",
"anis:",
"col :"
}
elseif mypat[1]=="some" then
mycap={
"src :",
"perc:"
}
elseif mypat[1]=="sprd" then
mycap={
"src :",
"num :",
"ang :"
}
elseif mypat[1]=="rapd" then
mycap={
"src :",
"num :",
"time:"
}
elseif mypat[1]=="brst" then
mycap={
"src :",
"num :",
"ang :",
"spd :",
"time:"
}
else
for i=2,#mypat do
add(mycap,"p"..i..":")
end
end
for i=2,#mypat do
add(menu,{
{
txt=mycap[i-1],
w=" ",
cmd="",
cmdy=selpat,
cmdx=i,
x=4,
y=6+i*7,
c=13
},
{
txt=mypat[i],
w=spacejam(#tostr(mypat[i])),
cmd="patedit",
cmdy=selpat,
cmdx=i,
x=24,
y=6+i*7,
c=13
}
})
end
add(menu,{
{
txt="delete",
w=" ",
cmd="delpat",
cmdy=selpat,
x=4,
y=6+#mypat*7+9,
c=13
}
})
end
function refresh_table()
menu={}
for i=1,#data do
local lne={}
local linemax=#data[i]
if i==cury then
linemax+=1
end
add(lne,{
txt=i,
w=" ",
cmd="",
x=4,
y=-4+8*i,
c=2
})
for j=1,linemax do
if j==#data[i]+1 then
add(lne,{
txt="+",
w=" ",
cmd="newcell",
cmdy=i,
x=-10+14*(j+1),
y=-4+8*i,
})
else
add(lne,{
txt=data[i][j],
cmd="edit",
cmdx=j,
cmdy=i,
x=-10+14*(j+1),
y=-4+8*i,
w=" "
})
end
end
add(menu,lne)
end
add(menu,{{
txt=" + ",
w=" ",
cmd="newline",
x=4,
y=-4+8*(#data+1),
}})
end
function newpat(typ)
if typ=="base" then
return {
"base",
1,
11,
3,
40
}
elseif typ=="some" then
return {
"some",
1,
0.5
}
elseif typ=="sprd" then
return {
"sprd",
1,
1,
0.5
}
elseif typ=="rapd" then
return {
"rapd",
1,
1,
2
}
elseif typ=="brst" then
return {
"brst",
1,
1,
0.5,
0.5,
5
}
else
return {
typ
}
end
end
function enter_pat()
local mymnu=menu[cury][curx]
local typeval=typetxt
if mymnu.cmdx==1 then
--tricky!!
if data[mymnu.cmdy][mymnu.cmdx]!=typetxt and typetxt!="" then
data[mymnu.cmdy]=newpat(typetxt)
end
else
typeval=tonum(typeval)
if typeval==nil then
typeval=0
end
data[mymnu.cmdy][mymnu.cmdx]=tonum(typeval)
end
_upd=update_pats
end
function enter_table()
local mymnu=menu[cury][curx]
local typeval=typetxt
if typeval==nil or typeval=="" then
if mymnu.cmdx==#data[mymnu.cmdy] and typetxt=="" then
--delete cell
deli(data[mymnu.cmdy],mymnu.cmdx)
if mymnu.cmdx==1 then
deli(data,mymnu.cmdy)
end
_upd=update_table
return
end
typeval=0
end
data[mymnu.cmdy][mymnu.cmdx]=typeval
_upd=update_table
end
-->8
--pats
function dobuls(arr)
for s in all(arr) do
s.age+=1
s.x+=s.sx
s.y+=s.sy
if s.y<-16 or s.y>130 then
del(arr,s)
end
end
end
function patshoot(en,pat,pang)
if pang==-99 then
pang=atan2(pspr.y-en.y,pspr.x-en.x)
end
local mybuls=makepat(pat,pang)
for b in all(mybuls) do
add(en.bulq,b)
end
end
function dobulq(en)
for b in all(en.bulq) do
if b.wait<=0 then
b.x+=en.x
b.y+=en.y
b.sx=sin(b.ang)*b.spd
b.sy=cos(b.ang)*b.spd
add(buls,b)
del(en.bulq,b)
else
b.wait-=1
end
end
end
function makepat(pat,pang)
local mypat=pats[pat]
local patype=mypat[1]
local ret={}
if patype=="base" then
add(ret,{
age=0,
x=0,
y=0,
ang=pang,
spd=mypat[2],
ani=anilib[mypat[3]],
anis=mypat[4],
col=mypat[5],
wait=0
})
elseif patype=="some" then
if rnd()<mypat[3] then
ret=makepat(mypat[2],pang)
end
elseif patype=="sprd" then
for i=1,mypat[3] do
local nxpat=makepat(mypat[2],pang+((i-1)*mypat[4]))
for p in all(nxpat) do
add(ret,p)
end
end
elseif patype=="rapd" then
for i=1,mypat[3] do
local nxpat=makepat(mypat[2],pang)
for p in all(nxpat) do
p.wait+=mypat[4]*(i-1)
add(ret,p)
end
end
elseif patype=="brst" then
for i=1,mypat[3] do
local nxpat=makepat(mypat[2],pang+spread(mypat[4]))
local rndw=flr(rnd(mypat[6]))
local rnds=rnd(mypat[5])
for p in all(nxpat) do
p.wait+=rndw
p.spd+=rnds
add(ret,p)
end
end
end
return ret
end
function spread(val)
return (rnd(2)-1)*val
end
__gfx__
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__map__
0000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000