Since beginning my game development journey, I’ve been working through the CS50 Introduction to Game Development course from Harvard (highly recommended). Earlier this month, I was working through lecture 4 (Mario) and everything had been going decently. Then I hit a pretty big snag and learned an important lesson. In Lua, the order in which you require files is important.
About an hour into the lecture, Colton talks about tile collisions and the concept of entities. I followed along and deferred the Player behavior to the appropriate Player state machine. The Entity class had been set up and most Player parameters had been passed.
But I was getting an error immediately upon entering the Play State:
src/states/game/PlayState.lua:31: attempt to call method 'changeState' (a nil value)
And for about 3 days, I couldn’t quite make sense of this one.
Tracing the Issue
Here is the line in question. Immediately above, I’ve created the Player object.
self.player = Player({
x = 0,
y = 0,
width = 16,
height = 20,
texture = 'green-alien',
stateMachine = StateMachine {
['idle'] = function() return PlayerIdleState(self.player) end,
['walking'] = function() return PlayerWalkingState(self.player) end,
['jump'] = function() return PlayerJumpState(self.player, self.gravityAmount) end,
['falling'] = function() return PlayerFallingState(self.player, self.gravityAmount) end
},
map = self.tileMap,
level = self.level
})
self.player:changeState('falling') -- This is the error line.
The Player object on init()
creates an Entity object, passing itself and all parameters.
Player = Class {__includes = Entity}
function Player:init(params)
Entity.init(self, params)
self.score = 0
end
function Player:update(dt)
Entity.update(self, dt)
end
function Player:render()
Entity.render(self)
end
And here is the Entity class with the appropriate :changeState()
method.
Entity = Class {}
function Entity:init(params)
self.x = params.x
self.y = params.y
self.dx = 0
self.dy = 0
self.width = params.width
self.height = params.height
self.texture = params.texture
self.stateMachine = params.stateMachine
self.direction = 'left'
self.map = params.map
self.level = params.level
end
function Entity:changeState(state, params)
self.stateMachine:change(state, params)
end
Method names are all correct. Player State changes from previous repo pushes matched the distro code. Even spelling errors were minimal. For the life of me, I couldn’t figure out where I had gone wrong on this one.
The Solution
I did eventually determine the solution. And it was absolutely not what I expected.
Notice in the Player class I had {__includes = Entity}
. A single Dependencies.lua file holds all Class requirements. (did not show all files, for simplicity).
-- External libraries
push = require 'lib/push'
Class = require 'lib/class'
Timer = require 'lib/knife.timer'
-- Utility items
require 'src/constants'
require 'src/StateMachine'
require 'src/Util'
require 'src/Assets'
-- Entities
require 'src/Animation'
require 'src/Entity'
require 'src/Player'
require 'src/Snail'
Originally, I had the Player class placed before the Entity class. Player class tries to include the Entity class before Entity class is required in Dependencies.lua. Moving require 'src/Entity'
above require 'src/Player'
in my Dependencies.lua file corrected the issue.
After figuring this out, I swapped it back just to make sure that’s all it was. And sure enough it was.
So I learned an important lesson. When you use require in Lua, the order in which you require files is important. Nowhere near where I thought the issue would be. But figured it out. Lesson learned, yeah?