attempt to index global 'player' (a nil value)

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
David10000
Prole
Posts: 5
Joined: Tue Nov 08, 2016 3:23 pm

attempt to index global 'player' (a nil value)

Post by David10000 »

Hi,

Brief introduction: I've been learning to code for the last few months and have done a few tutorials and made a few games, I'm at the stage where I'm trying to create my own game (barebones at the moment and using parts of tutorials, goature among others) and it was working until I put in a menu now it comes up with "states/openworld/main.lua:156 attempt to index global 'player' (a nil value)". It's really confusing that it worked without the menu but nothing is wrong with the menu. I thought I solved this problem but it's come back and I'm really stuck, my best guess is something is wrong with function player:update but it seems ok to me. Any advice would be greatly appreciated. Full Code Below.

Code: Select all

local AdvTiledLoader = require("AdvTiledLoader.Loader")
require("camera")

function love.load()
	love.graphics.setBackgroundColor( 220, 220, 255 )
	AdvTiledLoader.path = "states/openworld/maps/"
	map = AdvTiledLoader.load("beginning.tmx")
	map:setDrawRange(0, 0, map.width * map.tileWidth, map.height * map.tileHeight)
	
	camera:setBounds(0, 0, map.width * map.tileWidth - love.graphics.getWidth(), map.height * map.tileHeight - love.graphics.getHeight() )

	playerL = love.graphics.newImage("textures/playerL.png")
	playerD = love.graphics.newImage("textures/playerD.png")
	playerR = love.graphics.newImage("textures/playerR.png")
	playerU = love.graphics.newImage("textures/playerU.png")
	
	world = 	{
				ground = 512,
				}
	player = 	{
				x = 94,
				y = 3100,
				x_vel = 0,
				y_vel = 0,
				speed = 256,
				state = "",
				h = 50,
				w = 50,
				standing = false,
				image = love.graphics.newImage("textures/playerR.png")
				}
			self = player	
	
	
	
function player:update(dt)

		local halfX = self.w / 2
		local halfY = self.h / 2		
		
		self.x_vel = math.clamp(self.x_vel, -self.speed, self.speed)
		self.y_vel = math.clamp(self.y_vel, -self.speed, self.speed)
		
		local nextY = self.y + (self.y_vel*dt)
		if self.y_vel < 0 then
			if not (self:isColliding(map, self.x - halfX, nextY - halfY))
				and not (self:isColliding(map, self.x + halfX - 1, nextY - halfY)) then
				self.y = nextY
				self.standing = false
			else
				self.y = nextY + map.tileHeight - ((nextY - halfY) % map.tileHeight)
				self:collide("ceiling")
			end
		end
		if self.y_vel > 0 then
			if not (self:isColliding(map, self.x-halfX, nextY + halfY))
				and not(self:isColliding(map, self.x + halfX - 1, nextY + halfY)) then
					self.y = nextY
					self.standing = false
			else
				self.y = nextY - ((nextY + halfY) % map.tileHeight)
				self:collide("floor")
			end
		end
		
		local nextX = self.x + (self.x_vel * dt)
		if self.x_vel > 0 then
			if not(self:isColliding(map, nextX + halfX, self.y - halfY))
				and not(self:isColliding(map, nextX + halfX, self.y + halfY - 1)) then
				self.x = nextX
			else
				self.x = nextX - ((nextX + halfX) % map.tileWidth)
			end
		elseif self.x_vel < 0 then
			if not(self:isColliding(map, nextX - halfX, self.y - halfY))
				and not(self:isColliding(map, nextX - halfX, self.y + halfY - 1)) then
				self.x = nextX
			else
				self.x = nextX + map.tileWidth - ((nextX - halfX) % map.tileWidth)
			end
		end
		
		self.state = self:getState()
	end
	
	function player:collide(event)
		if event == "floor" then
			self.y_vel = 0
			self.standing = true
		end
		if event == "ceiling" then
			self.y_vel = 0
		end
	end
	
	function player:isColliding(map, x, y)
		local layer = map.tl ["Solid"]
		local tileX, tileY = math.floor(x / map.tileWidth), math.floor(y / map.tileHeight)
		local tile = layer.tileData (tileX, tileY)
		return not(tile == nil)
	end
	
	function player:getState()
		local tempState = ""
		if self.standing then
			if self.x_vel > 0 then
				tempState = "right"
			elseif self.x_vel < 0 then
				tempState = "left"
			else
				tempState = "stand"
			end
		end
		if self.y_vel > 0 then
			tempState = "fall"
		elseif self.y_vel < 0 then
			tempState = "jump"
		end
		return tempState
	end
end
function love.draw()
	camera:set()
	
	love.graphics.draw(player.image, player.x ,player.y)
	love.graphics.setColor( 255, 255, 255 )
	map:draw()
	
	camera:unset()
end

function love.update(dt)
	if dt > 0.05 then
		dt = 0.05
	end
	if love.keyboard.isDown("d") then
		self.x_vel = self.speed
		player.image = playerR
		
	elseif love.keyboard.isDown("a") then
		self.x_vel = -1 * self.speed
		player.image = playerL
		
	end
	if love.keyboard.isDown("w") then
		self.y_vel = -1 * self.speed
		player.image = playerU
		
	elseif love.keyboard.isDown("s") then
		self.y_vel = 1 * self.speed
		player.image = playerD
	end
	
	
	player:update(dt)
	
	camera:setPosition( player.x - (love.graphics.getWidth()/2), player.y - (love.graphics.getHeight()/2))
end

function love.keyreleased(key)
	if (key == "a") or (key == "d") then
		player.x_vel = 0
	end
	if (key == "w") or (key == "s") then
		player.y_vel = 0
	end
end
lines 150 - 158 below (so it's easier to find the problem)

Code: Select all

elseif love.keyboard.isDown("s") then
		self.y_vel = 1 * self.speed
		player.image = playerD
	end
	
	
	player:update(dt)
	
	camera:setPosition( player.x - (love.graphics.getWidth()/2), player.y - (love.graphics.getHeight()/2))
	
And here's the code for the menu (straight from goature)

Code: Select all

function clearLoveCallBacks()
	love.draw = nil
	love.joystickpressed = nil
	love.joystickreleased = nil
	love.keypressed = nil
	love.keyreleased = nil
	--love.load = nil
	love.mousepressed = nil
	love.mousereleased = nil
	love.update = nil
end

state = {}
function loadState(name)
	state = {}
	
	clearLoveCallBacks()
	local path = "states/" .. name
	require (path .. "/main")
	load()
end
function load()
	loadState("menu")
end

function love.load()
	loadState("menu")
end

function love.draw()
end

function love.update(dt)
end

function love.focus(bool)
end

function love.keypressed(key, unicode)
end

function love.keyreleased(key)
end

function love.mousepressed(x, y)
end

function love.mousereleased(x, y)
end

function love.quit()
end

function love.draw()
end
Thank you in advance for any help you can offer

Dave
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: attempt to index global 'player' (a nil value)

Post by raidho36 »

Firstly, using globals is not a good idea anyway. Here you have it defined in one place but then it's erased elsewhere, and since it's globally available (read: could be changed from anywhere) I'd not trivial to pinpoint the problem. Which is your situation exactly.

I suspect the state loader alters entire global namespace and that destroys global variables that were created in a different state. Or they're not yet defined as of moment you use them.
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: attempt to index global 'player' (a nil value)

Post by zorg »

For the possible reason that you don't really know what you're doing, which i'm not saying as an insult, mind you, you are defining a global variable called self in your full code, and assigning player (, another global, ) to it.

The menu system also uses a global called self, so basically whenever you use self in your code expecting it to be your player, it's actually the one for the menu system.

Edit:

Now, you don't need to replace self from any player:whatever functions, since due to the colon (:), self is a hidden local parameter in those, which will work fine; my guess is that your issues are here:

Code: Select all

  -- love.update
  if love.keyboard.isDown("d") then
      self.x_vel = self.speed -- this should be player.x_vel, since self here is already the menu system's own self global... which is equally bad, but whatever.
      player.image = playerR
      
   elseif love.keyboard.isDown("a") then
      self.x_vel = -1 * self.speed -- same here as above.
      player.image = playerL
      
   end
   if love.keyboard.isDown("w") then
      self.y_vel = -1 * self.speed -- same here as above.
      player.image = playerU
      
   elseif love.keyboard.isDown("s") then
      self.y_vel = 1 * self.speed -- same here as above.
      player.image = playerD
   end
   
Though i would remove the self = player line from love.load.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
David10000
Prole
Posts: 5
Joined: Tue Nov 08, 2016 3:23 pm

Re: attempt to index global 'player' (a nil value)

Post by David10000 »

"Firstly, using globals is not a good idea anyway. Here you have it defined in one place but then it's erased elsewhere, and since it's globally available (read: could be changed from anywhere) I'd not trivial to pinpoint the problem. Which is your situation exactly.

I suspect the state loader alters entire global namespace and that destroys global variables that were created in a different state. Or they're not yet defined as of moment you use them."

Sounds promising, I read that you should use local variables but haven't really bothered implementing it. I'll change the variables to local and see how it goes, thanks for your help.

"For the possible reason that you don't really know what you're doing, which i'm not saying as an insult, mind you, you are defining a global variable called self in your full code, and assigning player (, another global, ) to it."

Gee, thanks for the encouragement. I already thought of that and changed self to player and thought that would be the end of it, however that didn't work, so I reverted back to goatures self. Which worked. (I couldn't actually find where he declared the variable self. Confusing. I not sure that he did, I did the tutorial months ago and looked at it again, can't find self declared anywhere. Really confusing because as far as I know that shouldn't work but it seemed to, so I thought if it's not broken, don't fix it, now it's broken, now I'm fixing it).

"The menu system also uses a global called self, so basically whenever you use self in your code expecting it to be your player, it's actually the one for the menu system."

That sounds more useful, I'll look into that.

Edit: Where in the menu does it have a global named self? I had a look and couldn't see it. Used find to search it too, still couldn't see it
Last edited by David10000 on Thu Nov 10, 2016 4:38 pm, edited 1 time in total.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: attempt to index global 'player' (a nil value)

Post by raidho36 »

Self is an automatically declared local in colon notation functions, which refers to the object upon which it was called. You could otherwise use dot notation function with first argument named "self" and pass the same object into it as first argument - that's functionally identical.
David10000
Prole
Posts: 5
Joined: Tue Nov 08, 2016 3:23 pm

Re: attempt to index global 'player' (a nil value)

Post by David10000 »

raidho36 wrote:Self is an automatically declared local in colon notation functions, which refers to the object upon which it was called. You could otherwise use dot notation function with first argument named "self" and pass the same object into it as first argument - that's functionally identical.
So self:update(dt) or self.update(dt) declare the variable self, that I didn't know, I thought you needed to use something like self = 0 or local self = 0 to declare it as a variable. That makes much more sense :) and explains why it worked to begin with. Thanks.

Can either of you tell me where in the menu it refers to self? Or is it because I'm using self in main.lua it is also declared in the menu but not used. I really can't see it.
David10000
Prole
Posts: 5
Joined: Tue Nov 08, 2016 3:23 pm

Re: attempt to index global 'player' (a nil value)

Post by David10000 »

zorg wrote:For the possible reason that you don't really know what you're doing, which i'm not saying as an insult, mind you, you are defining a global variable called self in your full code, and assigning player (, another global, ) to it.

The menu system also uses a global called self, so basically whenever you use self in your code expecting it to be your player, it's actually the one for the menu system.

Edit:

Now, you don't need to replace self from any player:whatever functions, since due to the colon (:), self is a hidden local parameter in those, which will work fine; my guess is that your issues are here:

Code: Select all

  -- love.update
  if love.keyboard.isDown("d") then
      self.x_vel = self.speed -- this should be player.x_vel, since self here is already the menu system's own self global... which is equally bad, but whatever.
      player.image = playerR
      
   elseif love.keyboard.isDown("a") then
      self.x_vel = -1 * self.speed -- same here as above.
      player.image = playerL
      
   end
   if love.keyboard.isDown("w") then
      self.y_vel = -1 * self.speed -- same here as above.
      player.image = playerU
      
   elseif love.keyboard.isDown("s") then
      self.y_vel = 1 * self.speed -- same here as above.
      player.image = playerD
   end
   
Though i would remove the self = player line from love.load.
I did this before and didn't have self = player originally that's something I stuck in to give player a value and hopefully get around this, did it again just now, it still doesn't work. Thanks for trying though.

Code: Select all

function clearLoveCallBacks()
	love.draw = nil
	love.joystickpressed = nil
	love.joystickreleased = nil
	love.keypressed = nil
	love.keyreleased = nil
	--love.load = nil
	love.mousepressed = nil
	love.mousereleased = nil
	love.update = nil
end

state = {}
function loadState(name)
	state = {}	
	clearLoveCallBacks()
	local path = "states/" .. name
	require (path .. "/main")
	load()
end
function load()
	loadState("menu")
end

function love.load()
	loadState("menu")
end

function love.draw()
end

function love.update(dt)
end

function love.focus(bool)
end

function love.keypressed(key, unicode)
end

function love.keyreleased(key)
end

function love.mousepressed(x, y)
end

function love.mousereleased(x, y)
end

function love.quit()
end

function love.draw()
end
Where in the menu is self declared? I can't see it in the above.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: attempt to index global 'player' (a nil value)

Post by raidho36 »

Function needs to know upon which object to operate - it can use globals or upvalues but most often it is passes as an argument. In oop code, functions are fields of a table, and you usually want the function operate that table. So you must use dot notation to access the function to call it, since it's a table field, and then pass the same table into the function as an argument. That's not very convenient, and it's a redundant code. So the function-specific colon notation was devised to solve that problem. When a function is invoked with colon notation, the table from which it is called is automatically passed in as the first argument. Function can also be declared with colon notation, then it implicitly inserts "self" as first argument. So when you use colon notation declared function via colon notation, the table from which it's called implicitly becomes "self".

Code: Select all

function table.dot ( )
  --this function will not know what object to operate upon
end

function table.dot ( self )
  --this function will use whatever you pass into it as "self"
  --it doesn't have to spell "self", it could be "this" or "me" or any other valid variable name
end

function table:colon ( )
  --this function has first argument set as "self", other arguments go after that
  --from inside this function it is same as the function above
  --you can't choose this variable to have a different name
end

--this will call the function to act upon a different table
table.dot ( othertable )

--this will call the function to act upon the same table
table.dot ( table )

--this will pass the table as the first argument
--since it has first argument declared as "self", this is ok
table:dot ( )

--this is same as above, except this function uses implicit "self"
table:colon ( )
David10000
Prole
Posts: 5
Joined: Tue Nov 08, 2016 3:23 pm

Re: attempt to index global 'player' (a nil value)

Post by David10000 »

Thanks for the explanation raidho, I didn't know you could do that in lua, I'm having another look at the manual (and other resources) to find out about what else I've missed. I tried putting a menu in goature's 2d platformer and it comes up with the same problem. It works on it's own but when I try giving it a menu, player = nil. For some reason this makes me feel better :).

For now I'm lost but I'll keep trying anyway
Post Reply

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 6 guests