## middleclass & extras: middleclass 3.0 is out!

Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Contact:

### Re: middleclass & extras: middleclass 2.0 is out!

TechnoCat wrote:I'm guessing because with invoke you can pass it a variable string that gets invoked.
Sorry, I may not have clearly stated what my point was.
For instance, when I create a class, I do know what are its properties and methods, because i am the one who define them.
It is true that whith Lua tables, these two results in the same way (

Code: Select all

-- assuming class.f = function(class,...) bla bla end
class:f(...)
--same as
class['f'](class,...)

That's the basic concept Invoker mixin relies on, I guess.
But, to me, it don't know why I would rather write class:invoke('f',...) instead of class:f(...) which is simpler.

I am not saying Invoker is pointless. Far from that.
I am just asking if there are cases where Invoker is a nice solution.

Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

### Re: middleclass & extras: middleclass 2.0 is out!

It can replace:

Code: Select all

if func == 'f' then
class:f(...)
elseif func == 'g' then
class:g(...)
elseif func == 'h' then
class:h(...)
end
by

Code: Select all

class:invoke(func, ...)
although I guess
class[func](...)
would work just as well. Unless invoke does something extra?

kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Contact:

### Re: middleclass & extras: middleclass 2.0 is out!

The reason Invoker is there is to avoid one extra if.

I've found myself often wanting to be able to parse a list of (potentially heterogeneous) collection of instances, and wanting to call a method on them. "Draw all Enemies", "update all Entities", "pause all Entities not related with the Pause menu".

Sometimes I knew there was a common method name that I could call ("update", or "draw"). Other times, I wanted to use a function - probably an anonymous function. For example, to pause all the entities not related with a menu, I might want to do this:

Code: Select all

function(instance)
instance:pause()
end
end
I found out that I was doing "if type(method) == 'string' then method = instance[method] end" a bit too often, so I created the Invoker mixin to put it there.
Roland_Yonaba wrote:For instance, when I create a class, I do know what are its properties and methods, because i am the one who define them.
The case is simple when you phrase it like that.

Invoke is to be used with classes that you have *not* built. At least not in the context of what you are writing. This is useful when you are creating a library, and you don't really know what classes - or methods - it will be used with

The most obvious example (and the reason why I created the Invoker mixin in the first place) is the Apply mixin.

This mixin basically "insterts itself" between "new" and "initialize", and stores references to the newly created instances in a private _instances variable. Then you can do YourClass:apply(methodNameOrFunction, param1, param2 ...) and that will parse _instances applying the method to the lib. And you can pass a method name or a function, and it will work.

I hope this clarifies it a bit.
When I write def I mean function.

Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Contact:

### Re: middleclass & extras: middleclass 2.0 is out!

A lot, thanks!

jasonisop
Citizen
Posts: 92
Joined: Thu Jun 21, 2012 1:35 am

### Issues with a class

Sorry double post
Last edited by jasonisop on Fri Jun 22, 2012 1:15 pm, edited 2 times in total.

jasonisop
Citizen
Posts: 92
Joined: Thu Jun 21, 2012 1:35 am

### Having an issue with a class ive made

I posted on this last night here but the post didnt show.

So my issue is this, I have an Enemy class that runs a timed function to look and see if a player is next to it. If the player is next to the Enemy and the Enemy is facing the player it deals it a point of damage. I create two instances of the Enemy class and place them on my map, but only one of the Enemies seems to be running the timed function and when it it damages the player it deals 2 damage instead of the one.

I am new to lua so im sure my code is not quite right and might be a bit messy and redundant in places.

here is the Enemy class. And a .love file for the whole project.

Code: Select all

-- Our Enemy class
require ('luascripts/middleclass')

Enemy = class('Enemy')

function temps()
Enemy:checkForPlayer()
end

function Enemy:initialize(enemyType,enemyImage,enemyWidth,enemyHeight,enemyFacing,enemySpeed,enemyTileX,enemyTileY)
--public vars
self.cron = require 'luascripts/cron'

self.type = enemyType -- can be used later for things like insect,human ect...
self.image = love.graphics.newImage("images/".. enemyImage)
self.imageName = enemyImage
self.width = enemyWidth
self.height = enemyHeight
self.x = 0
self.y = 0
self.facing = enemyFacing
self.animating = false
self.speed = enemySpeed or 150
self.tileX = enemyTileX or 0
self.tileY = enemyTileY or 0

self.noWalk = false
Enemy:setTileX(self.tileX)
Enemy:setTileY(self.tileY)
Enemy:setFacing(self.facing)

self.id = self.cron.every(1, temps)
--private vars
self._anim8 = require ('luascripts/anim8')
self._g =  self._anim8.newGrid(self.width, self.height, self.image:getWidth(), self.image:getHeight())
self._animation = self._anim8.newAnimation('loop', self._g('1-3,1'), 0.1)
end

--- getters and setters ---
function Enemy:setEnemyType(paramType)
self.type = paramType
end
function Enemy:getEnemyType()
return self.type
end

function Enemy:setImage(paramImage)
self.image = love.graphics.newImage("images/".. paramImage)
end
function Enemy:getImage()
return self.image
end

function Enemy:getnoWalk()
return self.noWalk
end

function Enemy:setFacing(paramFacing)
self.facing = paramFacing
end
function Enemy:getFacing()
return self.facing
end

function Enemy:setSpeed(paramSpeed)
self.speed = paramSpeed
end
function Enemy:getSpeed()
return self.speed
end

function Enemy:setX(paramX)
self.x = paramX
end
function Enemy:getX()
return self.x
end

function Enemy:setY(paramY)
self.y = paramY
end
function Enemy:getY()
return self.y
end

function Enemy:setTileX(paramTileX)
self.tileX = paramTileX
end
function Enemy:getTileX()
return self.tileX
end

function Enemy:setTileY(paramTileY)
self.tileY = paramTileY
end
function Enemy:getTileY()
return self.tileY
end
--- end of getters and setters ---

--this will turn animations on or off
function Enemy:animate(paramAnimate)
self.animate = paramAnimate
end

--sets Enemy location on the map
function Enemy:Location(Px,Py,paramFacing)
Enemy:setFacing(paramFacing)

self.tileX = Px
self.tileY = Py
--32 is tile size
self.x = self.tileX * 32
self.y = self.tileY * 32
Enemy:setTileX(self.tileX)
Enemy:setTileY(self.tileY)
Enemy:setFacing(paramFacing)
end

--moves the enemy to the tile if it can might add a return false so you can error fix
function Enemy:moveTile(x,y)

-- Grab the tile
tile = global.layer.tileData(self.tileX + x, self.tileY + y)

-- If the tile doesn't exist or is an obstacle then exit the function
if tile == nil then return end
if tile.properties.obstacle then return end
-- Otherwise change the guy's tile
self.tileX = self.tileX + x
self.tileY = self.tileY + y

end

function Enemy:checkTile(x,y)
-- Grab the tile
tile = global.layer.tileData(tileX+x, tileY+y)

-- If the tile doesn't exist or is an obstacle then exit the function
if tile == nil then
self.noWalk = true
return
end

if tile.properties.obstacle then
self.noWalk = true
return
end

self.noWalk = false
end

--might want to rename this to setAnimation
function Enemy:setDirection(parmDirection,paramAnimation)

Enemy:setFacing(parmDirection)
if paramAnimation =="walk" then
--	Enemy:setImage(self.imageName)

if 	 	self.facing == "up" then	self._animation = self._anim8.newAnimation('loop', self._g('1-3,1'), 0.1)
elseif  self.facing  == "left" then self._animation = self._anim8.newAnimation('loop', self._g('1-3,2'), 0.1)
elseif  self.facing  == "down" then self._animation = self._anim8.newAnimation('loop', self._g('1-3,3'), 0.1)
elseif  self.facing  == "right" then self._animation = self._anim8.newAnimation('loop', self._g('1-3,4'), 0.1)
end
end
if paramAnimation =="stand" then
--	Enemy:setImage(self.imageName)
if 	 	self.facing == "up" then	self._animation = self._anim8.newAnimation('loop', self._g('1-3,1'), 0.1)
elseif  self.facing  == "left" then self._animation = self._anim8.newAnimation('loop', self._g('1-3,2'), 0.1)
elseif  self.facing  == "down" then self._animation = self._anim8.newAnimation('loop', self._g('1-3,3'), 0.1)
elseif  self.facing  == "right" then self._animation = self._anim8.newAnimation('loop', self._g('1-3,4'), 0.1)
end
end

end

function Enemy:checkForPlayer()

local tempX = 0
local tempY = 0

if 	 	Enemy:getFacing() == "up" then		tempY = -1
elseif  Enemy:getFacing()  == "down" then 	tempY = 1
elseif  Enemy:getFacing()  == "left" then	tempX = -1
elseif  Enemy:getFacing()  == "right" then 	tempX = 1
end

if	player.getTileX() ==  	Enemy:getTileX() + tempX and player.getTileY()  ==  Enemy:getTileY()+ tempY  then
--iif	player.getTileX() ==  	self.tileX + tempX and player.getTileY()  == self.tileY + tempY  then
global.player_Health = global.player_Health - 1
else
global.player_Health = 10
end

end

function Enemy:update(dt)
self.cron.update(dt)
self._animation:update(dt)
end

function Enemy:draw()
self._animation:draw(self.image, self.x, self.y )
end

return Enemy
Attachments
Game.love

kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Contact:

### Re: middleclass & extras: middleclass 2.0 is out!

Hi there! I've decided to release middleclass 3.0.

The functionality stays in parity with 2.0, but I have decided to finally hide all those global variables, which is the correct thing to do when you create a lua library.

Old way:

Code: Select all

require 'middleclass'

MyClass = class('MyClass')
assert(subclassOf(Object, MyClass))
assert(instanceOf(Object, MyClass:new()))
New way:

Code: Select all

local class = require 'middleclass'
local Object = class.Object

MyClass = class('MyClass')
assert(MyClass:isSubclassOf(Object))
assert(MyClass:new():isInstanceOf(Object))
Those are the main changes. You can see a list of all the changes in the changelog. I have also written a detailed update guide.

Regards!

EDIT: I just noticed the old unanswered message from jasonisop. I must have received it while I was on holidays last year and I didn't see it. If this is still an issue, jasonisop, let me know.

EDIT2: Also, I haven't tried it, but I suspect that Stateful works with middleclass 3.0 without any modifications.
When I write def I mean function.

ejmr
Party member
Posts: 302
Joined: Fri Jun 01, 2012 7:45 am
Location: South Carolina, U.S.A.
Contact:

### Re: middleclass & extras: middleclass 3.0 is out!

Thank you for the updates, especially hiding the global variables. That was the only minor thing I didn't like. So that change alone makes this awesome library even better.
ejmr :: Programming and Game-Dev Blog, GitHub

kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Contact:

### Re: middleclass & extras: middleclass 3.0 is out!

middleclass was my learning project in Lua. When I started it I quite didn't understand what where the best practices. I've got a few of them down:
• Never create global variables. That is just bad in Lua.
• Always return a table (it can be a callable table). The reason for returning a table instead of a function is:
• Include a __VERSION field in the table you return. That way it's very easy to check which version of the lib you are using during runtime. You can add other fields like __DESCRIPTION, __URL and __LICENSE too.
• Use dot (.) instead of colon (:) in your root object (do myLib.foo(), not myLib:foo())
• Don't store state in your library if you can avoid it. Instead, provide creators/handlers for the things that have state, and return them to the user. One possible exception to this rule is caching stuff. But if you can, also export the "cache creator" and let the state live in the user's program.
• Make as little public methods as you can. Check inputs (param types, etc) on those methods only.
• If you need to split your lib into several files, use the PATH trick to require files inside your lib.
Those are the main ones, but I'm still learning. I'm plan to slowly go over my existing libs and apply these rules to them. But at the same time, I really want to finish bump 2.0. So we'll see.
When I write def I mean function.

Nixola
Inner party member
Posts: 1940
Joined: Tue Dec 06, 2011 7:11 pm
Location: Italy

### Re: middleclass & extras: middleclass 3.0 is out!

I fear the path trick doesn't work if someone uses lf.load instead of require. There should be no reason for that, but you can never be too sure
lf = love.filesystem
ls = love.sound
la = love.audio
lp = love.physics
li = love.image
lg = love.graphics

### Who is online

Users browsing this forum: Google [Bot] and 7 guests