制作一款打飞机游戏40:点选敌人

当前UI状态

目前,我们的编辑器界面有一侧边栏时间线,用户可以通过鼠标或键盘与时间线进行交互。我们还提供了一个预览界面和UI覆盖层,用于显示不同敌人的生成位置等信息。

问题与挑战

我们最初采用的是一种在现代UI设计中非常常见的范式,即"所见即所得"(WYSIWYG)。然而,在编辑游戏日程时,我们遇到了一个问题:我们正在编辑的内容(敌人的生成事件)并不总是在屏幕上可见。为了解决这个问题,我们添加了一个额外的时间线UI,用于显示生成事件。但这并不是我们想要的最终解决方案。

改进思路

我们希望能够恢复一些"所见即所得"的感觉,让用户能够直接点击并移动屏幕上的敌人。但这样做会带来一个挑战:如何准确判断鼠标点击的是哪个敌人?

实现步骤
  1. 解决鼠标悬停问题‌:首先,我们添加了一个变量来检测鼠标是否移动,从而避免在鼠标悬停在按钮上时无法移动选择的问题。
  2. 数据驱动UI‌:接着,我们改变了直接从数据绘制UI的方式,而是先根据数据生成敌人精灵列表,然后再绘制这些精灵。这样做的好处是我们可以更容易地与这些精灵进行交互。
  3. 实现敌人点击交互‌:通过遍历敌人精灵列表,我们实现了点击敌人时的交互效果,比如显示敌人的生成位置。
  4. 创建下拉菜单‌:当用户点击敌人时,我们希望显示一个下拉菜单,提供编辑敌人的选项(如改变类型、移动、复制、删除等)。我们创建了一个新的更新和绘制函数来处理下拉菜单的显示和交互。
下拉菜单的实现细节
  • 位置计算‌:下拉菜单的位置需要根据点击的敌人位置来计算,以确保它不会出现在屏幕之外。

  • 交互逻辑‌:我们添加了鼠标和键盘的交互逻辑,使用户可以通过点击或按键来选择下拉菜单中的选项。

    pico-8 cartridge // http://www.pico-8.com
    version 41
    lua
    --show cursor
    --move the cursor
    --backspace

    function _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.txt

    debug={}
    msg={}

    _drw=draw_map
    _upd=update_map

    menuitem(1,"export",export)

    reload(0x0,0x0,0x2000,"cowshmup.p8")
    reload(0x1000, 0x1000, 0x2000, "cowshmup.p8")

    curx=1
    cury=1
    scrolly=0
    scrollx=0

    enemies={}

    scroll=0
    xscroll=0
    poke(0x5f2d, 1)

    selsched=nil
    t=0
    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()
    t+=1
    dokeys()
    domouse()
    mscroll=stat(36)

    _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
    clkl=true
    clkwait=true
    end
    end

    end
    -->8
    --draw

    function draw_drop()
    drawbg()

    drawmenu()
    drawcur(mousex,mousey)
    end

    function 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
    ]]
    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
    end

    function 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)
    end

    function drawbg()
    cls(2)
    for i=1,#mapsegs do
    local segnum=mapsegs[i]
    local sx=segnum\418
    local sy=segnum%4
    8
    map(sx,sy,xscroll,scroll-((i-2)*64),18,8)
    end

    camera(-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
    end

    camera()
    end
    -->8
    --update

    function 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
    end

    function 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
    end

    if btnp(⬆️) then
    scroll+=1
    curx=2
    end

    scroll=max(0,scroll)

    if btnp(⬅️) then
    curx-=1
    end

    if btnp(➡️) then
    curx+=1
    end
    curx=mid(2,curx,#menu[cury])

    if btnp(❎) then
    dobutton(menu[cury][curx])
    return
    end

    selsched=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.y

    复制代码
       refresh_drop()
       _drw=draw_drop
       _upd=update_drop     
      end
     end
    end

    end

    if key=="t" then
    _drw=draw_table
    _upd=update_table
    refresh_table()
    return
    end

    end

    function update_table()
    refresh_table()

    if key=="m" then
    _drw=draw_map
    _upd=update_map
    refresh_map()
    return
    end

    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
    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
    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
    end

    复制代码
     data[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
    --tools

    function bgprint(txt,x,y,c)
    print("#0"..txt,x,y,c)
    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 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 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}))
    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

    function cyc(age,arr,anis)
    local anis=anis or 1
    return arr[(age\anis-1)%#arr+1]
    end

    function sortsched()
    if #sched<2 then return end

    repeat
    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==false

    end

    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
    end

    function spwnlst(scr)
    local ret={}
    for s in all(sched) do
    if s[1]==scr then
    add(ret,s)
    end
    end
    return ret
    end

    function mousecol(b)
    local wid=#b.w*4-1

    if 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 end

    return true
    end

    function 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=mousey

    local 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-1

    if 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 end

    return 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
    end

    s..="""
    printh(s,file,true)
    add(msg,{txt="exported!",t=120})
    --debug[1]="exported!"
    end
    -->8
    --ui

    function 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
    end

    add(lne,{
    txt="+",
    w=" ",
    cmd="adden",
    x=uix,
    y=60,
    c=13
    })

    add(menu,lne)
    end

    function 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
    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+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),
    }})
    end

    function 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

相关推荐
AI黑客10 小时前
恶心的win11更新DIY 设置win11更新为100年
人工智能·游戏·微信·everything·火绒安全
与己斗其乐无穷13 小时前
小游戏(2)扫雷游戏
游戏
上海云盾商务经理杨杨14 小时前
2025年游戏行业DDoS攻防指南:智能防御体系构建与实战策略
服务器·安全·游戏·ddos
Sui_Network15 小时前
Sui 上线两周年,掀起增长「海啸」
人工智能·物联网·游戏·web3·区块链·智能合约
eve杭1 天前
游戏代码C
c语言·人工智能·python·游戏·ai
烟水寻常1 天前
UE5 GAS开发P47 游戏标签
游戏·ue5
Mu先生Ai世界3 天前
探寻适用工具:AI+3D 平台与工具的关键能力及选型考量 (AI+3D 产品经理笔记 S2E03)
人工智能·游戏·3d·ai·aigc·产品经理·vr
windwind20005 天前
(转)角色与动画的性能优化 | UnrealFest演讲干货
大数据·游戏·青少年编程·性能优化·创业创新
北冥没有鱼啊5 天前
UE 材质 条纹循环发光
游戏·ue5·游戏引擎·ue4·材质