当前UI状态
目前,我们的编辑器界面有一侧边栏时间线,用户可以通过鼠标或键盘与时间线进行交互。我们还提供了一个预览界面和UI覆盖层,用于显示不同敌人的生成位置等信息。
问题与挑战
我们最初采用的是一种在现代UI设计中非常常见的范式,即"所见即所得"(WYSIWYG)。然而,在编辑游戏日程时,我们遇到了一个问题:我们正在编辑的内容(敌人的生成事件)并不总是在屏幕上可见。为了解决这个问题,我们添加了一个额外的时间线UI,用于显示生成事件。但这并不是我们想要的最终解决方案。
改进思路
我们希望能够恢复一些"所见即所得"的感觉,让用户能够直接点击并移动屏幕上的敌人。但这样做会带来一个挑战:如何准确判断鼠标点击的是哪个敌人?
实现步骤
- 解决鼠标悬停问题:首先,我们添加了一个变量来检测鼠标是否移动,从而避免在鼠标悬停在按钮上时无法移动选择的问题。
- 数据驱动UI:接着,我们改变了直接从数据绘制UI的方式,而是先根据数据生成敌人精灵列表,然后再绘制这些精灵。这样做的好处是我们可以更容易地与这些精灵进行交互。
- 实现敌人点击交互:通过遍历敌人精灵列表,我们实现了点击敌人时的交互效果,比如显示敌人的生成位置。
- 创建下拉菜单:当用户点击敌人时,我们希望显示一个下拉菜单,提供编辑敌人的选项(如改变类型、移动、复制、删除等)。我们创建了一个新的更新和绘制函数来处理下拉菜单的显示和交互。
下拉菜单的实现细节
-
位置计算:下拉菜单的位置需要根据点击的敌人位置来计算,以确保它不会出现在屏幕之外。
-
交互逻辑:我们添加了鼠标和键盘的交互逻辑,使用户可以通过点击或按键来选择下拉菜单中的选项。
pico-8 cartridge // http://www.pico-8.com
version 41
lua
--show cursor
--move the cursor
--backspacefunction _init()
--- customize here ---
#include shmup_sched.txt
file="shmup_sched.txt"
arrname="sched"
data=sched
#include shmup_mapsegs.txt
#include shmup_enlib.txt
#include shmup_anilib.txt
#include shmup_myspr.txtdebug={}
msg={}_drw=draw_map
_upd=update_mapmenuitem(1,"export",export)
reload(0x0,0x0,0x2000,"cowshmup.p8")
reload(0x1000, 0x1000, 0x2000, "cowshmup.p8")curx=1
cury=1
scrolly=0
scrollx=0enemies={}
scroll=0
xscroll=0
poke(0x5f2d, 1)selsched=nil
t=0
endfunction _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
endfunction _update60()
t+=1
dokeys()
domouse()
mscroll=stat(36)_upd()
endfunction dokeys()
if stat(30) then
key=stat(31)
if key=="p" then
poke(0x5f30,1)
end
else
key=nil
endend
function domouse()
local oldmousex=mousex
local oldmousey=mouseymousex=stat(32)
mousey=stat(33)mousemove=false
if mousex!=oldmousex or oldmousey!=mousey then
mousemove=true
endif 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
clkl=true
clkwait=true
end
endend
-->8
--drawfunction draw_drop()
drawbg()drawmenu()
drawcur(mousex,mousey)
endfunction draw_map()
drawbg()-- timeline
for i=0,21 do
local iscr=scroll+(20-i)-10
local ens=spwnlst(iscr)if i==10 or iscr%5==0 then print(" "..tostrn(iscr,4),0,i*6,7) print("-",21,i*6,7) end if #ens>0 then local s="" local uix=26 for j=1,#ens do print(tostrn(ens[j][2],2,"0"),uix,i*6,7) uix+=10 end end if iscr<0 then break end
end
drawmenu()
drawcur(mousex,mousey)--debug[1]=scroll
end
function draw_table()
cls(2)
--spr(0,0,0,16,16)drawmenu()
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
--[[
for i=1,#data do
for j=1,#data[i] do
bgprint(data[i][j],2+18j,2+8i,7)
end
end
]]
endfunction 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
endbgprint(mymnu.w,mymnu.x+scrollx,mymnu.y+scrolly,13) bgprint(mymnu.txt,mymnu.x+scrollx,mymnu.y+scrolly,c) end end
end
endfunction drawcur(cx,cy)
local col=rnd({6,7})
line(cx,cy-1,cx,cy-2,col)
line(cx,cy+1,cx,cy+2,col)
line(cx-1,cy,cx-2,cy,col)
line(cx+1,cy,cx+2,cy,col)
endfunction drawbg()
cls(2)
for i=1,#mapsegs do
local segnum=mapsegs[i]
local sx=segnum\418
local sy=segnum%48
map(sx,sy,xscroll,scroll-((i-2)*64),18,8)
endcamera(-xscroll,0)
genens()-- draw enemies
if selsched then
local col=11+16*12
fillp(flr(▥))
line(0,selsched[4],127,selsched[4],col)
fillp(flr(▤))
line(selsched[3],0,selsched[3],127,col)line(selsched[3],selsched[4],selsched[3]-128,selsched[4]+128,col) line(selsched[3],selsched[4],selsched[3]+128,selsched[4]+128,col) line(selsched[3],selsched[4],selsched[3]-128,selsched[4]-128,col) line(selsched[3],selsched[4],selsched[3]+128,selsched[4]-128,col) fillp()
end
for en in all(enemies) do
mspr(en.s,en.x,en.y)
if en.sched==selsched then
rect(en.x-8,en.y-8,en.x+9,en.y+9,rnd({6,7}))
end
endcamera()
end
-->8
--updatefunction update_drop()
refresh_drop()if btnp(⬆️) then
cury-=1
end
if btnp(⬇️) then
cury+=1
end
cury=mid(1,cury,#menu)if btnp(⬅️) then
curx-=1
end
if btnp(➡️) then
curx+=1
end
curx=mid(1,curx,#menu[cury])-- mouse button control
local mousehit=false
if mousemove or clkl then
for my=1,#menu do
for mx=1,#menu[my] do
if mousecol(menu[my][mx]) then
curx=mx
cury=my
if clkl then
dobutton(menu[cury][curx])
end
end
end
end
end
endfunction update_map()
refresh_map()scroll+=mscroll*8
xscroll=mid(0,(mousex-10)/108,1)-0.0625
cury=1
if btnp(⬇️) then
scroll-=1
curx=2
endif btnp(⬆️) then
scroll+=1
curx=2
endscroll=max(0,scroll)
if btnp(⬅️) then
curx-=1
endif btnp(➡️) then
curx+=1
end
curx=mid(2,curx,#menu[cury])if btnp(❎) then
dobutton(menu[cury][curx])
return
endselsched=menu[cury][curx].cmdsch
-- mouse button control
local mousehit=false
if mousemove or clkl then
for my=1,#menu do
for mx=1,#menu[my] do
if mousecol(menu[my][mx]) then
curx=mx
cury=my
selsched=menu[cury][curx].cmdsch
mousehit=true
if clkl then
dobutton(menu[cury][curx])
return
end
end
end
end
end
if not mousehit then
for en in all(enemies) do
if col3(en) then
selsched=en.sched
mousehit=true
if clkl then
dropx=en.x+xscroll
dropy=en.yrefresh_drop() _drw=draw_drop _upd=update_drop end end end
end
if key=="t" then
_drw=draw_table
_upd=update_table
refresh_table()
return
endend
function update_table()
refresh_table()if key=="m" then
_drw=draw_map
_upd=update_map
refresh_map()
return
endif 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
elseif mymnu.cmd=="newline" then
add(data,{0})
elseif mymnu.cmd=="newcell" then
add(data[mymnu.cmdy],0)
end
end
endfunction upd_type()
if key then
if key=="\r" then
-- enter
local mymnu=menu[cury][curx]
poke(0x5f30,1)
local typeval=tonum(typetxt)
if typeval==nil 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
enddata[mymnu.cmdy][mymnu.cmdx]=typeval _upd=update_table 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
--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) do
arr[k] = split(v)
end
return arr
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 then
sspr(_x,_y,_w,_h,sx-_ox+_w,sy-_oy,_w,_h,true)
endif _nx then
mspr(_nx,sx,sy)
end
endfunction msprc(si,sx,sy)
local _x,_y,_w,_h,_ox,_oy,_fx,_nx=unpack(myspr[si])
rect(sx-_ox,sy-_oy,sx-_ox+_w-1,sy-_oy+_h-1,rnd({8,14,15}))
endfunction 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
endfunction cyc(age,arr,anis)
local anis=anis or 1
return arr[(age\anis-1)%#arr+1]
endfunction sortsched()
if #sched<2 then return endrepeat
local switch=false
for i=1,#sched-1 do
if sched[i][1]>sched[i+1][1] then
sched[i],sched[i+1]=sched[i+1],sched[i]
switch=true
end
end
until switch==falseend
function tostrn(v,l,ch)
local ch=ch or " "
local sv=tostr(v)
if #sv<l then
local diff=l-#sv
for i=1,diff do
sv=ch..sv
end
end
return sv
endfunction spwnlst(scr)
local ret={}
for s in all(sched) do
if s[1]==scr then
add(ret,s)
end
end
return ret
endfunction mousecol(b)
local wid=#b.w*4-1if mousex<b.x-1 then return false end
if mousey<b.y-1 then return false end
if mousex>b.x+wid then return false end
if mousey>b.y+5 then return false endreturn true
endfunction col3(ob)
local _bx,_by,_bw,_bh,_box,_boy,_bfx=unpack(myspr[ob.col])local a_left=mousex-xscroll
local a_top=mousey
local a_right=mousex-xscroll
local a_bottom=mouseylocal b_left=flr(ob.x)-_box
local b_top=flr(ob.y)-_boy
local b_right=b_left+_bw-1
local b_bottom=b_top+_bh-1if a_top>b_bottom then return false end
if b_top>a_bottom then return false end
if a_left>b_right then return false end
if b_left>a_right then return false endreturn true
end
-->8
--i/o
function export()
sortsched()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
ends..="""
printh(s,file,true)
add(msg,{txt="exported!",t=120})
--debug[1]="exported!"
end
-->8
--uifunction refresh_drop()
menu={}
add(menu,{{
txt="type",
w=" ",
cmd="",
x=dropx,
y=dropy,
c=13
}})
add(menu,{{
txt="move",
w=" ",
cmd="",
x=dropx,
y=dropy+6,
c=13
}})
add(menu,{{
txt="copy",
w=" ",
cmd="",
x=dropx,
y=dropy+12,
c=13
}})
add(menu,{{
txt="delete",
w=" ",
cmd="",
x=dropx,
y=dropy+18,
c=13
}})end
function refresh_map()
menu={}
local lne={}
add(lne,{
txt=tostrn(scroll,4),
w=" ",
cmd="",
x=4,
y=60,
c=6
})local uix=26
local ens=spwnlst(scroll)
if #ens>0 then
for i=1,#ens do
add(lne,{
txt=tostrn(ens[i][2],2,"0"),
w=" ",
cmd="editen",
cmdsch=ens[i],
x=uix,
y=60,
c=13
})
uix+=10
end
endadd(lne,{
txt="+",
w=" ",
cmd="adden",
x=uix,
y=60,
c=13
})add(menu,lne)
endfunction genens()
enemies={}
for sch in all(sched) do
local schx=sch[3]
local schy=sch[4]+scroll-sch[1]local en=enlib[sch[2] ] local ani=anilib[en[1] ] add(enemies,{ x=schx, y=schy, s=cyc(t,ani,en[2]), sched=sch, col=en[5] })
end
endfunction 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+8i,
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+8i,
})
else
add(lne,{
txt=data[i][j],
cmd="edit",
cmdx=j,
cmdy=i,
x=-10+14(j+1),
y=-4+8i,
w=" "
})
end
end
add(menu,lne)
end
add(menu,{{
txt=" + ",
w=" ",
cmd="newline",
x=4,
y=-4+8(#data+1),
}})
endfunction dobutton(b)
if b.cmd=="adden" then
local sch={}
sch[1]=scroll
sch[2]=1
sch[3]=64
sch[4]=8
add(sched,sch)
elseif b.cmd=="editen" then--dropx=mid(2,b.cmdsch[3],128-25) --dropy=mid(2,b.cmdsch[4],128-30) dropx=b.x dropy=b.y refresh_drop() _drw=draw_drop _upd=update_drop
end
end
gfx
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
map
0000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000