在TIC-80中尝试索引一个空值。

huangapple go评论70阅读模式
英文:

Attempt to index a nil value in TIC-80

问题

我有两个物体正在发生碰撞,当它们碰撞时,会导致一个错误。以下是错误信息:

[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

以下是代码的其余部分:(请注意,这部分是HTML和JavaScript代码,不需要翻译)

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

    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

<details>
<summary>英文:</summary>

I have two objects that are colliding, and when they do collide, it causes an error. Here is the error:

attempt to index a nil value (local 'sprite1')


I am checking every single frame for a collision between the player and the list of enemies (index of each one).

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


Here is the rest of the code:
&lt;!-- begin snippet: js hide: false console: true babel: false --&gt;
&lt;!-- language: lang-html --&gt;
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=&quot;left&quot;
end
if btnp(3) then
dir=&quot;right&quot;
end
if btnp(0) then
dir=&quot;up&quot;
end
if btnp(1) then
dir=&quot;down&quot;
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(&quot;KILLS: &quot;..kills,10,20,12)
print(&quot;TURN: &quot;..turn,100,10,12)
print(&quot;MP: &quot;..mp,10,30,12)
print(&quot;AP: &quot;..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(&quot;Press z to end turn&quot;,63,120,12)
end	
end
--player functions
function playermove()
if mp&gt;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&gt;144 then
player.y=-8
elseif player.y&lt;-8 then
player.y=144
elseif player.x&gt;248 then
player.x=-8
elseif player.x&lt;-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==&quot;left&quot; then
if mget(player.x-7,player.y)==112 then
return true
else
return false
end
end	
if dir==&quot;right&quot; then
if mget(player.x+9,player.y)==112 then
return true
else
return false
end
end	
if dir==&quot;up&quot; then
if mget(player.x,player.y-9)==112 then
return true
else
return false
end
end
if dir==&quot;down&quot; 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 &gt;= sprite2Left and sprite1Left &lt;= sprite2Right and sprite1Bottom &gt;= sprite2Top and sprite1Top &lt;= sprite2Bottom then
return true
else
return false
end
end
&lt;!-- end snippet --&gt;
I think it might be that the collision code is wrong somehow, although I am not sure.
</details>
# 答案1
**得分**: 1
不要在迭代过程中移除敌人。如果你杀死了某人,它会崩溃,因为最后一个敌人已经不存在。
而是要么
* 倒序迭代,`for #enemy_weak, 1, -1 do`,因为这样你只会移动已经处理过的敌人。
* 使用表和 `pairs`。在遍历表时,将现有条目设置为 `nil` 而不是使用 `table.remove` 是可以的,不会导致任何位移。
顺便说一下,`true` 的值就是 `true`,因此在大多数情况下不需要在你的条件中使用 `== true`。
<details>
<summary>英文:</summary>
Do not remove enemies while iterating. If you kill someone, it will crash because the last enemy no longer exists.
Instead either
* Iterate backwards, `for #enemy_weak, 1, -1 do`, because then you only shift enemies who are already processed.
* Use tables and `pairs`. Setting an existing entry to `nil` instead of `table.remove` while traversing a table is fine and will not shift anything.
Btw, `true` evaluates to `true`, thus ` == true` in your condition is in most cases not required.
</details>

huangapple
  • 本文由 发表于 2023年6月19日 13:15:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76503781.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定