What am I doing wrong here?
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
Re: What am I doing wrong here?
Thanks for the feedback. I have worked on some other games and engines in the past just nothing in lua yet. one that I worked on was VB and everything was stored in SQL, mostly due to the fact that it was an Orpg and the info needed to be called from the server to the clients and back again to store info. I will keep dinking around, so far I'm liking Lua.. loving love and from all of the posts I have seen this seems like a really cool community.
"We do not stop playing because we grow old. We grow old because we stop playing." -- Unknown
- kikito
- Inner party member
- Posts: 3153
- Joined: Sat Oct 03, 2009 5:22 pm
- Location: Madrid, Spain
- Contact:
Re: What am I doing wrong here?
Hi Jasoco,
Using any of the existing OOP libraries will help you make what you want more easily.
Allow me to present how you could do that with middlecass. The other libraries are similar, with changes in the syntax.
First, if you are making classes, you don't need to add "type" - you can call your class Enemy instead of "enemyType":
I've put empty update & draw methods (functions in classes are usually called like that, 'methods'). These ensure that no matter what type of enemy you are working with, if you call "update", you will get no errors.
initialize would be your "onLoad" function, and delete could be your "onDeath". I've taken the liberty of adding a couple parameters to initialize, and using them to set up the enemy's x and y.
You can create your first enemy already, using Enemy:new(x,y)
And you can call billy:draw() and billy:update(dt). However, since they are empty functions, this will not do anything.
Now your Blob enemy type can be a subclass of Enemy. This is were the magic begins:
First, I've changed the initialize method so it changes the initial hp of blobs to 2. But I'm still using the Enemy.initialize method for the "common Enemy creation parts" - in this case, setting up x and y.
Then I have overridden the default Enemy.draw implementation, which did nothing, with a Blob-specific one, which draws each blob on the coordinates it has (self.x, self.y).
You can create a Blob enemy by using Blob:new()
It is also worth noting that timmy will also have inherited from Enemy the default update() and delete() methods; you could have overriden them if you wanted to.
This is completely extensible. For example, you could create a "FireBlob", as a subclass of Blob, with a different image, hp and update function. Or you could create a totally different Enemy subclass, called "Orc". An then add a "ShamanOrc". And so on.
This is with middleclass alone; with middleclass-extras you can do more stuff - for example automating the addition/deletion of all enemies to an "enemies" table using Apply. But I hope this gives you a taste.
Using any of the existing OOP libraries will help you make what you want more easily.
Allow me to present how you could do that with middlecass. The other libraries are similar, with changes in the syntax.
First, if you are making classes, you don't need to add "type" - you can call your class Enemy instead of "enemyType":
Code: Select all
require 'middleclass.init'
Enemy = class('Enemy')
function Enemy:initialize(x,y)
self.hp = 1
self.x = x
self.y = y
end
function Enemy:update(dt) end
function Enemy:draw() end
function Enemy:delete() end
initialize would be your "onLoad" function, and delete could be your "onDeath". I've taken the liberty of adding a couple parameters to initialize, and using them to set up the enemy's x and y.
You can create your first enemy already, using Enemy:new(x,y)
Code: Select all
local billy = Enemy:new(100,200)
Now your Blob enemy type can be a subclass of Enemy. This is were the magic begins:
Code: Select all
Blob = class('Blob', Enemy) -- note the Enemy mention here
function Blob:initialize(x,y)
Enemy.initialize(self, x, y)
self.hp = 2
end
function Blob:draw()
-- Assuming that blobImage is loaded somewhere else
love.graphics.draw(blobImage, self.x, self.y)
end
Then I have overridden the default Enemy.draw implementation, which did nothing, with a Blob-specific one, which draws each blob on the coordinates it has (self.x, self.y).
You can create a Blob enemy by using Blob:new()
Code: Select all
local timmy = Blob:new(300, 400)
timmy:draw() -- draws it on 300, 400
timmy.x, timmy.y = 100, 200
timmy:draw() -- draws on 100, 200
This is completely extensible. For example, you could create a "FireBlob", as a subclass of Blob, with a different image, hp and update function. Or you could create a totally different Enemy subclass, called "Orc". An then add a "ShamanOrc". And so on.
This is with middleclass alone; with middleclass-extras you can do more stuff - for example automating the addition/deletion of all enemies to an "enemies" table using Apply. But I hope this gives you a taste.
When I write def I mean function.
- BlackBulletIV
- Inner party member
- Posts: 1261
- Joined: Wed Dec 29, 2010 8:19 pm
- Location: Queensland, Australia
- Contact:
Re: What am I doing wrong here?
Oh that's good. By the sounds of things I thought you were another one of those people completely new to game development wanted to make this awesome RPG thing. Well good luck then.Wulfie wrote:Thanks for the feedback. I have worked on some other games and engines in the past just nothing in lua yet. one that I worked on was VB and everything was stored in SQL, mostly due to the fact that it was an Orpg and the info needed to be called from the server to the clients and back again to store info. I will keep dinking around, so far I'm liking Lua.. loving love and from all of the posts I have seen this seems like a really cool community.
Re: What am I doing wrong here?
Awesome Kikito, this example definitely helped me out in understanding how middleclass works.
"We do not stop playing because we grow old. We grow old because we stop playing." -- Unknown
- Jasoco
- Inner party member
- Posts: 3727
- Joined: Mon Jun 22, 2009 9:35 am
- Location: Pennsylvania, USA
- Contact:
Re: What am I doing wrong here?
Yeah! I think I am going to look into that. You make it look so easy.
- rhezalouis
- Party member
- Posts: 100
- Joined: Mon Dec 07, 2009 10:27 am
- Location: Indonesia
- Contact:
[Response] Class and Inheritance in Lua
Hi Jasoco,
Objective: Create an enemy/blob class which would define the parameters and functions for new enemies.
Terms Listing --> Implementation Sample:
Code2:
I think that explains it all. Please CMIIW. And ask again if you're still confused.
Regards,
RhezaLouis
I guess the problem to understand this feature is because there are several terms to address the same thing which create lots of confusion.Jasoco wrote:But that's what I'm trying to do. Create an enemy from a set of enemy types. Each type needs to have its own functions for stuff. I don't know how to use metatables. Even after reading the official documentation on Lua.org.
Let me try to explain the concept of Prototyping/OOP-Class-Emulation in Lua along with an example; I will use several terms in parallel to show you the equivalency:Jasoco wrote:I want to be able to create enemies from enemy types so that when I say enemy[#enemy+1] = enemyType["blob"] it transfers all the variables from the enemy type into the enemy. And I want each type to have its own onLoad(), update(), draw() and onDeath() functions attached that are different for each one.
Objective: Create an enemy/blob class which would define the parameters and functions for new enemies.
Terms Listing --> Implementation Sample:
- Prototype == Class--> blobClass == blobPrototype
- Instance == Child == Descendant ~= Clone --> blob1 == enemy[1]
Note that the term clone used by Robin refers to instances with same parameters at all time.Robin wrote:you will have an army of clones
- First you create a table that would collect the functions accessible by each blob instance you will be creating:
It's simply a table! It is called prototype, because it is the blueprint for the new blob instances.
Code: Select all
blobPrototype = {}
- Next, you have to define the behaviour of this blueprint that would be copied by its descendant/instance. Here's the reset function:
A colon is used instead of period to enable the self parameter. This self variable allows each instance to have its own properties because it is dynamically filled, dependent to the caller ( Such a useful feature!).E.g.
Code: Select all
function blobPrototype:reset(pHealth, pLevel) self.HP = pHealth or self.HP or 1 self.level = pLevel or self.level or 1 self.age = 0 end
Compare the previous function with the following static code:Code: Select all
blobPrototype:reset() --> self = blobPrototype blob1:reset() --> self = blob1;
The function would always change the same HP, level, and age variable: those in the blobPrototype table.Code: Select all
--Do not use this code-- function blobPrototype.reset(pHealth, pLevel) blobPrototype.HP = pHealth or self.HP or 1 blobPrototype.level = pLevel or self.level or 1 blobPrototype.age = 0 end
- Similarly, you could define more functions/behaviour for the prototype :
Code: Select all
function blobPrototype:update(dt) self.age = self.age + (dt or 0) end function blobPrototype:draw() print("There is a ".. self.age .. "-year(s)-old level" .. self.level .. " blob with " .. self.HP .. " HP") end
- Next, create a constructor function to create a new instance based on the blobPrototype. The new instance is basically another table that is made aware about its prototype. This relationship between the prototype and its instances is defined in another table. That table called the instance's metatable. Let's see how to assign a table as a metatable for the new instance:Everytime we call this constructor, a new table would be created and 'stored' in the variable blobInstance. If you drop the local modifier, each call would refer to the same global variable that 'store' a single table.
Code: Select all
function newBlob(pHealth, pLevel) local blobInstance = {} setmetatable(blobInstance, {__index = blobPrototype}) return blobInstance end
Reading the next line from the right, the constructor then create a new anonymous table with one member:This anonymous table is then set as the metatable for blobInstance table. The member of the metatable is called metamethod. [Metatable|Metamethod?] [__index?]Code: Select all
--Pseudo Code-- key = "__index" value = blobPrototype --the prototype table
"setmetatable(blobInstance, {__index = blobPrototype})" could be translated into: "When accessing an index that is nil in blobInstance, look for it in blobPrototype." I'll explain more on this at the example. - Usage Example:blob1 has no function called update, so Lua would redirect it to the __index metamethod entry (i.e. blobPrototype); and thus, calling update function from blobPrototype but the self is set to the original caller i.e.. blob1. So, blob1:draw() is equivalent to:
Code: Select all
blob1 = newBlob(3, 2) blob2 = newBlob() blob1:update(1) blob2:update(1) blob1:draw() --> There is a 1-year(s)-old level2 blob with 3 HP blob2:draw() --> There is a 1-year(s)-old level1 blob with 1 HP
Code: Select all
--Pseudo Code-- blobPrototype.draw(self = blob1)
Code2:
- We would modify only the constructor to open new features to this prototype:The constructor function now is embedded in the prototype, thus, enabling the use of self. Then, change the metamethod __index value to self so that every instance of this prototype could be a prototype as well. This is called Inheritance in OOP.
Code: Select all
function blobPrototype:new(pHealth, pLevel) local blobInstance = {} setmetatable(blobInstance, {__index = self}) blobInstance:reset(pHealth, pLevel) return blobInstance end
- Usage Example:When we call enemy[2].draw(), the code is translated twice:
Code: Select all
enemy = {} enemy[1] = blobPrototype:new(3, 2) enemy[2] = enemy[1]:new() --inherit the values of enemy[1] --update for i, ithEnemy in ipairs(enemy) do ithEnemy:update(1) end --draw for i, ithEnemy in ipairs(enemy) do ithEnemy:draw() end
Code: Select all
--Pseudo Code-- enemy[1].draw(self = enemy[2]) blobPrototype.draw(self = enemy[2])
I think that explains it all. Please CMIIW. And ask again if you're still confused.
Regards,
RhezaLouis
- Attachments
-
- oopEmulation.zip
- Lua Code
- (895 Bytes) Downloaded 85 times
Aargh, I am wasting my posts! My citizenshiiiip...
- Jasoco
- Inner party member
- Posts: 3727
- Joined: Mon Jun 22, 2009 9:35 am
- Location: Pennsylvania, USA
- Contact:
Re: What am I doing wrong here?
It worked! You have changed my coding ways. Thank you!
Seriously, this is going to revolutionize how I create my games from now on. And will probably let me continue working on my Adventure engine that I had stopped working on for a while because I couldn't figure out a good method to use for enemies and their behaviors. Now I can make them all classes like I wanted to and just create new() ones with their own behaviors and everything with a lot less code and confusion. I still have to rewrite some of that engine anyway.
Seriously, this is going to revolutionize how I create my games from now on. And will probably let me continue working on my Adventure engine that I had stopped working on for a while because I couldn't figure out a good method to use for enemies and their behaviors. Now I can make them all classes like I wanted to and just create new() ones with their own behaviors and everything with a lot less code and confusion. I still have to rewrite some of that engine anyway.
- nevon
- Commander of the Circuloids
- Posts: 938
- Joined: Thu Feb 14, 2008 8:25 pm
- Location: Stockholm, Sweden
- Contact:
Re: What am I doing wrong here?
If you have access and opportunity, I would highly recommend that you attend/audit an object orientation class at some kind of educational institution - preferably one that's language agnostic and deals specifically with object orientation. While you can most certainly learn it on your own, attending a few lectures on the basic theory has been very helpful for me at least.Jasoco wrote:Seriously, this is going to revolutionize how I create my games from now on. And will probably let me continue working on my Adventure engine that I had stopped working on for a while because I couldn't figure out a good method to use for enemies and their behaviors. Now I can make them all classes like I wanted to and just create new() ones with their own behaviors and everything with a lot less code and confusion. I still have to rewrite some of that engine anyway.
That said, I find it amazing that you were able to create your adventure game engine without knowing OOP. That must have been hell.
- bartbes
- Sex machine
- Posts: 4946
- Joined: Fri Aug 29, 2008 10:35 am
- Location: The Netherlands
- Contact:
Re: What am I doing wrong here?
Hah, like the class you got? I heard nothing but complaints..nevon wrote:I would highly recommend that you attend/audit an object orientation class at some kind of educational institution
- nevon
- Commander of the Circuloids
- Posts: 938
- Joined: Thu Feb 14, 2008 8:25 pm
- Location: Stockholm, Sweden
- Contact:
Re: What am I doing wrong here?
That's because my teachers were incompetent. Also, I already knew how to program, and I already knew about object orientation. But I see how it could be useful to someone who has programming experience, but doesn't know anything about OO (if the teachers don't suck, that is).bartbes wrote:Hah, like the class you got? I heard nothing but complaints..nevon wrote:I would highly recommend that you attend/audit an object orientation class at some kind of educational institution
Who is online
Users browsing this forum: Ahrefs [Bot], Semrush [Bot] and 10 guests