Initializing a specific object with arbitrary parameters

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.
Mr. Strange
Party member
Posts: 101
Joined: Mon Aug 11, 2008 5:19 am

Initializing a specific object with arbitrary parameters

Post by Mr. Strange »

In my current project I have four classes of objects, all of which are sub-classes of "object"

* players
* minions
* enemies
* obstacles

objects have a variety of attributes, including health, xpos, ypos, xvel, yvel, name and so forth.

To keep track of everything, I have five lists - one which is the "object" list which holds everything, and four which are the list indicies for each class of object. For example, my object list might look like this:

Code: Select all

-- setup variables
object = {}
minion = {}
enemy = {}
obstacle = {}
-- these numbers come from a load level function
numberOfMinions = 3
numberOfEnemies = 5
numberOfObstacles = 15
numberofObjects = numberOfMinions + numberOfEnemies + numberOfObstacles

for i = 1, numberOfObjects do
     object[i] = {
          type = "none",
          health = 100,
          xpos = 0,
          ypos = 0,
          xvel = 0,
          yvel = 0,
          name = tostring(i),
     }
end

LoadMinions()
LoadEnemies()
LoadObstacles()
Now my level definition will hold the Load"X" functions, along with tables of the attributes. To start, my functions looked like this:

Code: Select all


function LoadMinions()
     minion[1].xpos = 50
     minion[1].ypos = 25
     minion[1].name = "minion1"
     minion[2].xpos = 49
     minion[2].ypos = 40
     minion[2].name = "minion2"
     minion[3].xpos = 59
     minion[3].ypos = 20
     minion[3].name = "minion3"
end

This works fine - I leverage the fact that I know how many of each thing will be initialized, so I just replace the information I want to replace directly. This allows me to initialize each thing with default values, and then specifically overwrite the values I need changed. I get that because I am modifying the elements explicitly.

However, it does require a lot of typing in the load functions. I'd much rather have a table of minions that looks like this:

Code: Select all

setupminion = {}
setupminion[1] = {x = 50, y = 25,}
setupminion[2] = {x = 49, y = 40,}
setupminion[3] = {x = 59, y = 20,}
Now I'm not touching the minion table explicitly, but you can see how I might easily initialize 3 minions into the object list from this data. I can procedurally create names, and replace the default xpos and ypos with the data from x and y.

But here is the problem - I don't see an easy way to do the same thing given a list with arbitrary parameters from the object list. So instead of just x and y, I might want to specify x, y, and health for one minion, and x,y, vel for another. Once the list of data in each element of "setupminion" is unknown, I lose the simplicity which makes this implementation much keener.

To sum up - mangling the initialized data gives me perfect power, at the cost of changeability and ease of understanding. Initializing from smaller tables gives me the clarity and tunability I want, but at the cost of some flexibility. I want both. How can I get it?

and no, I don't want to check each table in "setupminion" to see if each possible parameter exists, and if so overwrite the proper data in "minion."

--Mr. Strange
User avatar
rude
Administrator
Posts: 1052
Joined: Mon Feb 04, 2008 3:58 pm
Location: Oslo, Norway

Re: Initializing a specific object with arbitrary parameters

Post by rude »

This might be what you didn't want, but here's how I would to it. (Warning: untested)

Code: Select all

--
-- Object "superclass"
--

Object = {}

function Object:new(p)
	local o = {}
	setmetatable(o, self)
	self.__index = self
	
	-- Default values.
	o.x = 0
	o.y = 0
	o.name = "noname"
	
	-- Overwrite.
	for i,v in pairs(p) do o[i] = v end
	
	return o
end


--
-- Minion "subclass"
--

Minion = Object:new{}

function Minion:new(p)
	local o = {}
	setmetatable(o, self)
	self.__index = self
	
	-- Minion-specific default values.
	o.x = math.random(0, 800)
	o.y = math.random(0, 600)
	o.name = "Minion"
	
	-- Overwrite.
	for i,v in pairs(p) do o[i] = v end
	
	return o
end

-- Object creation
minions = {
	Minion:new{ x = 13, y = 37 },
	Minion:new{ x = 19, y = 84, name = "Minion1" },
}
EDIT: Instead of looping through the parameter table, you can also do this:

Code: Select all

	-- Minion-specific default values.
	o.x = p.x or math.random(0, 800)
	o.y = p.y or math.random(0, 600)
	o.name = p.name or "Minion"
Mr. Strange
Party member
Posts: 101
Joined: Mon Aug 11, 2008 5:19 am

Re: Initializing a specific object with arbitrary parameters

Post by Mr. Strange »

Well I'll admit - I really don't follow your example. I'm not really clear on how metatables work, or how to use ipairs.

So I guess I'll hit the books and learn those things - and then I'm sure I'll be grateful for your assistance!

--Mr. Strange
User avatar
rude
Administrator
Posts: 1052
Joined: Mon Feb 04, 2008 3:58 pm
Location: Oslo, Norway

Re: Initializing a specific object with arbitrary parameters

Post by rude »

Sure, but:
Mr. Strange wrote:how to use ipairs.
In the above case you need to use pairs(), not ipairs().
Mr. Strange
Party member
Posts: 101
Joined: Mon Aug 11, 2008 5:19 am

Re: Initializing a specific object with arbitrary parameters

Post by Mr. Strange »

Alright, I've increased my knowledge set. I believe I understand pairs and ipairs now.

Here is the sticking point for me. When you have a table with elements with named indicies (as opposed to number keys) how can you assign them after pull them via pairs?

If I call:

Code: Select all

for i,v in pairs(p) do
     obj[i] = v
end
that works great when the i values are integers, but not so great if they are not integers. I tried this:

Code: Select all

for i,v in pairs(p) do
   obj.i = v
end
and it blew up on me. Should that type of construction work?

--Mr. Strange
Mr. Strange
Party member
Posts: 101
Joined: Mon Aug 11, 2008 5:19 am

Re: Initializing a specific object with arbitrary parameters

Post by Mr. Strange »

Alright, I answered my own question through experimentation. However, I still don't really understand the syntactic complexities of lua...

calling "obj.i = v" did not work, but "obj = v" _did_ work.

However, if I typed out the expected value for i ("health") then:

"obj.health = v" _did_ work and "obj[health] = v" did not work.

So, apparently there are subtle things at work here that I don't understand.

However, I now have a functioning loader app, so that's cool.

--Mr. Strange
surtic
Citizen
Posts: 74
Joined: Sat Jul 12, 2008 12:18 am

Re: Initializing a specific object with arbitrary parameters

Post by surtic »

The confusing thing is that a.x is not equivalent to a[x] but to a["x"].

Lua expects a value in the brackets. If you write a[x], it sees x (which is not a value, but apparently a variable name), and it tries to evaluate it. In most cases, x is not defined, so its value is nil. So Lua sees a[nil]. But you can't assign to a nil index, so instead it shouts at you.

For the same reason, if i is a variable and you are accessing a.i, you are in fact accessing a["i"], which is the same index for every value of i (i the variable is not the same as "i" the string).

I hope this helps. If not - ask some more...
Mr. Strange
Party member
Posts: 101
Joined: Mon Aug 11, 2008 5:19 am

Re: Initializing a specific object with arbitrary parameters

Post by Mr. Strange »

Alright, my loader is working in general, but now I have a problem which appears to be love-specific.

I'm pulling lots of data from one table to another using pairs. But when I try to pull attributes assigned to images made with love.graphics.newImage("foo") the thing blows up on me again.

I can assign variables to my image foo directly with no trouble - the problems seems to be in passing that data through pairs.

--Mr. Strange
Mr. Strange
Party member
Posts: 101
Joined: Mon Aug 11, 2008 5:19 am

Re: Initializing a specific object with arbitrary parameters

Post by Mr. Strange »

In case my previous post was not totally clear, I have some sample code here:

Code: Select all

pic1 = love.graphics.newImage("/data/images/test_image1.png")
pic2 = love.graphics.newImage("/data/images/test_image2.png")

minion = {}

minion[1] = {
	x = 0,
	y = 0,
	name = "minion1",
	pic = pic1,
	}

minionData = {
	pic = pic2,
	}

function ApplyParams(id, p)
	for i,v in pairs(p) do
			minion[i] = v
		end
	end
end

ApplyParams(1, minionData)

This example seems a bit simplistic, because I've stripped out lots of other parameters, and random stuff. I initialize a minion with default parameters, including "pic = pic1", and then I overload some number of parameters, including pic. I can do this overload explicitly, but trying to pass the pic data through pairs seems to break things. Since the pic object is a love.graphics object, I suppose that maybe this is to be expected. Anyway, I'm hoping someone has some insight for me, so I don't have to iterate through every object and assign pictures manually.

love rules!

--Mr. Strange
User avatar
rude
Administrator
Posts: 1052
Joined: Mon Feb 04, 2008 3:58 pm
Location: Oslo, Norway

Re: Initializing a specific object with arbitrary parameters

Post by rude »

Mr. Strange wrote:I suppose that maybe this is to be expected
No it's not. Precisely how does it "blow up"?
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot] and 3 guests