You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TIC-80中尝试索引nil值错误排查求助

问题排查:TIC-80碰撞检测触发nil值错误

错误信息

[string "function _init()..."]:178: 
attempt to index a nil value (local 'sprite1')

相关碰撞检测代码

for I=1,#enemy_weak do
 if collision(enemy_weak[I],player)==true then
  playerattack(I)
 end
end

完整代码

function _init()
    --essential_variables
    mp=5
    ap=3
    turn=1
    kills=0
    dir=nil
    
    --nonessential_variables
    end_turn_text=false
    mspr=272
    
    --tables
    player={
        x=120,
     y=48,
        hp=3,
        sprite=256,
    }
    
    enemy_weak={
        x=-5,
        y=-5,
        hp=1,
        sprite=288,
 }
 
end

--initialize game
_init()

function TIC()
--update    
    --turn_control
    if mp==0 then
        end_turn_text=true
    end
    
    if btnp(4) then
            end_turn_text=false
            turn=turn+1
            mp=3
    end
    
    --misc
    mx,my,md=mouse()
    if md==true then
        mspr=273
    else
        mspr=272
    end
    
    player_mouse={
    x=mx-7,
  y=my-4,
  down=md,
  sprite=mspr
    }
    
    if btnp(2) then
        dir="left"
    end
    if btnp(3) then
        dir="right"
    end
    if btnp(0) then
        dir="up"
    end
    if btnp(1) then
        dir="down"
    end
--draw/print
    
    cls(0)
    poke(0x3FFB,1)
    --map
    map(0,0,30,17,0,0,0)
    
    --enemy
    
    spawnenemy(120,80)

    --player
    
    playermove()
    spr(player.sprite,player.x,player.y,0)
    playerloop()
    
    --mouse
    spr(mspr,grid(mx-2),grid(my-2),0)
    for I=1,#enemy_weak do
        if collision(enemy_weak[I],player_mouse)==true then
            mspr=274
        else
            mspr=272
        end
    end
    
        --player_attack
    for I=1,#enemy_weak do
        if collision(enemy_weak[I],player)==true then
            --playerattack(I)
        end
    end
    
    --text
    
    print("KILLS: "..kills,10,20,12)
    print("TURN: "..turn,100,10,12)
    print("MP: "..mp,10,30,12)
    print("AP: "..ap,10,40,12)
    print(detect_wall_collision(),5,5)
    print(dir,50,10)
    print(player.x,150,10)
    print(player.y,150,20)
    if end_turn_text==true then
        print("Press z to end turn",63,120,12)
    end 
end

--player functions

function playermove()
    if mp>0 then
        if btnp(2)and not detect_wall_collision() then
            player.x=player.x-8
            mp=mp-1
        end
        if btnp(3)and detect_wall_collision()==false then
            player.x=player.x+8
            mp=mp-1
        end
        if btnp(0)and detect_wall_collision()==false  then
            player.y=player.y-8
            mp=mp-1
        end
        if btnp(1)and  detect_wall_collision()==false then
            player.y=player.y+8
            mp=mp-1
        end
    end
end

function playerloop()
    if player.y>144 then
        player.y=-8
    elseif player.y<-8 then
        player.y=144
    elseif player.x>248 then
        player.x=-8
    elseif player.x<-8 then
        player.x=248
    end
end

function playerattack(enemyid)
    kills=kills+1
    table.remove(enemy_weak,enemyid)    
end

function inventory()

end

function detect_wall_collision()
    if dir=="left" then
        if mget(player.x-7,player.y)==112 then
            return true
        else
         return false
        end
    end 
    if dir=="right" then
        if mget(player.x+9,player.y)==112 then
            return true
        else
        return false
        end
    end 
    if dir=="up" then
        if mget(player.x,player.y-9)==112 then
            return true
        else
        return false
        end
    end
    if dir=="down" then
        if mget(player.x,player.y+9)==112 then
            return true
        else
        return false
        end
    end
end

--enemy
function spawnenemy(x,y)
    local enemy={}
    enemy.hp=1
    enemy.sprite=288
    enemy.x=grid(x)
    enemy.y=grid(y)
    spr(enemy.sprite,enemy.x,enemy.y,0)
    table.insert(enemy_weak,enemy)
end

--other
function grid(x)
    return math.floor((x)/8)*8
end

function collision(sprite1, sprite2)
-- Calculate the bounding boxes for each sprite
 local sprite1Left = sprite1.x
 local sprite1Right = sprite1.x+4
 local sprite1Top = sprite1.y
 local sprite1Bottom = sprite1.y+4

 local sprite2Left = sprite2.x
 local sprite2Right = sprite2.x+5
 local sprite2Top = sprite2.y
 local sprite2Bottom = sprite2.y+4

 -- Check for overlap
 if sprite1Right >= sprite2Left and sprite1Left <= sprite2Right and sprite1Bottom >= sprite2Top and sprite1Top <= sprite2Bottom then
  return true
 else
  return false
 end
end

问题原因与修复方案

1. 核心问题:enemy_weak初始化错误

在_init()中,你将enemy_weak定义为一个单个敌人对象(包含x、y等键值对),但后续又用table.insert()往里面添加数组元素,导致表结构混乱:既有非数组的键值对,又有数组索引。当用#enemy_weak获取数组长度时,会得到错误的数值,遍历到无效索引时,enemy_weak[I]会返回nil,传给collision函数后就触发了"index a nil value"错误。

修复:
将_init()中的enemy_weak改为空数组:

enemy_weak={} -- 空数组,专门用于存放敌人对象

2. 次要问题:敌人无限生成

在TIC()函数的每帧循环里都调用spawnenemy(120,80),会不断往enemy_weak中添加敌人,不仅占用性能,还会加剧表结构的混乱。

修复:
调整敌人生成时机,比如只在回合结束时生成:

if btnp(4) then
    end_turn_text=false
    turn=turn+1
    mp=3
    spawnenemy(120,80) -- 移到此处,每次结束回合生成一个敌人
end

3. 可选优化:碰撞检测循环(针对后续恢复删除逻辑)

如果后续恢复playerattack(I)的调用,建议倒序遍历敌人数组,避免删除元素后索引错乱导致漏检:

for I=#enemy_weak,1,-1 do
    if collision(enemy_weak[I],player)==true then
        playerattack(I)
    end
end

内容的提问来源于stack exchange,提问作者Sam Cao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 14:44:55