Need some help with OOP and clean coding

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
SudoCode
Citizen
Posts: 61
Joined: Fri May 04, 2012 7:05 pm

Need some help with OOP and clean coding

Post by SudoCode »

I'm pretty new to OOP but I understand the basic concepts. I'm just wondering if I'm using objects in an optimal manner in terms of making my code modular and easy to modify or add to. If anyone could take a look through it and give me a few pointers it would be appreciated.

Also, increasing the speed at which my player moves only seems to have an effect in specific increments (i.e. increasing Player.speed from 1 to 100 has no effect, but as soon as you increase it from 100 to 101, the player moves noticeably faster). I essentially copy-pasted it from an earlier version of this .love which wasn't using middleclass and it worked fine without it. Player.speed is set to 50 by default in the .love that is attached, and pressing left shift makes the player sprint, increasing movement speed by twenty percent, but not having any actual effect on movement.

Thanks for any help.
Attachments
New.love
(425.97 KiB) Downloaded 104 times
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Need some help with OOP and clean coding

Post by kikito »

Hi there!
I'm just wondering if I'm using objects in an optimal
Well, that's a bit of engineering's dirty secret. There is no such thing as Optimal Code. At least not in absolute terms. It is very rare that a piece of code is just "better" than another piece of code. It is usually better "according to certain criteria".

That's because good code is about tradeoffs. A simple example: you have a version of a function that does a simple thing (say, move the player around) in a very simple way. Then you have another option which is very flexible, but adds lots of complexity to the code. Which one is better?

Well, it depends. If you need a lot of flexibility in your code, then the flexible solution is better. But if you don't, then it's overkill.

Now, the conclusion would be: "get the requirements right, and you can get optimal code".

... well, actually, no. ;)

That misses one important characteristic of requirements. They change all the time. No matter how "set in stone" they are, reality always seems to find a way to need them changed.

This actually changes priorities. With that characteristic in mind, the important thing is not finding "the optimal code to satisfy the requirements". It's far more important to make code that is able to adapt to changing circumstances without a lot of pain. That is what clean code is about.

One of the most obvious ways to do that is to divide stuff up in smaller parts. Big classes and functions are more difficult to move around, change and reuse than small ones. When I say "big" I don't only mean "with lots of lines of code", I also mean "doing too many different things". Splitting them up into several parts is usually a good idea.

I say it is a good idea not because the resulting code will be simpler (it will not) but because it will be more adaptable to changes in requirements.

One particular example I see is the player class. It handles too many things: animations, keypresses, health. I see lots of trouble there when you want to change some things:
  • What happens when players demmand to be able to customize the key settings?
  • What happens when the player wants to use a joypad to move the player around
  • What if later on you decide that you want to use the mouse instead?
  • What if you need to create the player animations dynamically (say, show a helmet he's wearing)?
I would separate the player into several parts - I'd rather not see any mentions of "keypresses" on it. Make the player react to internal booleans (wannaGoUp, wannaGoDown, etc). Move the keypresses them to a different part; beholder.lua, which you already have included, could help you there. TLBind is an alternative (I have not used it myself, but people seem to like it). Similarly, move the Animations out of the player class - maybe make an AnimationSet class, and control the loading, updating and drawing from inside it.
I essentially copy-pasted it from an earlier version of this .love which wasn't using middleclass and it worked fine without it.
I suspect that your problems come from the fact that Player isn't really a middleclass class. I see this code:

Code: Select all

Player = class('Player', Entity)
Player = {
self = "",
map = test,
x = 0,
y = 0,
pic = love.graphics.newImage("assets/game/player.png"),
speed = 100,
currentHealth = 10,
maxHealth = 10,
currentMana = 10,
maxMana = 10,
currentStamina = 10,
maxStamina = 10,
facing = "down",
width = 64,
height = 64,
}
Right after Player is defined as a class, it's redefined as a common Lua table :megagrin:
When I write def I mean function.
Zeliarden
Party member
Posts: 139
Joined: Tue Feb 28, 2012 4:40 pm

Re: Need some help with OOP and clean coding

Post by Zeliarden »

Your sprint dosent work because of floor()
your movment is 1

btw here is a tips for draw range:

Code: Select all

Map:autoDrawRange(-(Player.x-(love.graphics.getWidth( )/2)), -(Player.y-(love.graphics.getHeight( )/2)), 1, -50)
User avatar
verilog
Citizen
Posts: 97
Joined: Thu Nov 03, 2011 3:15 am
Contact:

Re: Need some help with OOP and clean coding

Post by verilog »

Hello! I'd like to ask something somewhat related to SudoCode's original questions. This has been bugging me for sometime now, and I can't seem to find a clear answer so far.
I suspect that your problems come from the fact that Player isn't really a middleclass class
Suppose I have the following piece of code:

Code: Select all

player = class:new()

function player:init()
	self.x = 0	
	self.xMax = 0
	...
end

...
	
function player:update(dt)
	
	if self.x >= 0 and self.x < self.xMax then
		player:moveRight(dt)
	end

end
The “if-then” statement attempts to compare both values of self.x and self.xMax, however, lua will state that boths variables are nil inside the player:update function block, failing to execute the comparision.

Of course, if I change the code to this:

Code: Select all

player = class:new()

function player:init()
	player.x = 0	
	player.xMax = 0
        ...
end
...

function player:update(dt)
	
	if player.x >= 0 and player.x < player.xMax then
		player:moveRight(dt)
	end

end
The if statement evaluation will be successful. So, what's going on here? Is this a variable scope issue?
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: Need some help with OOP and clean coding

Post by bartbes »

Middleclass' initializer isn't called 'init', it's called 'initialize'.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Need some help with OOP and clean coding

Post by kikito »

No, you are mistaking the player class with the player instance.

The bit that you are missing is that, somewhere else, there have to be these other two lines:

Code: Select all

-- usually inside love.load
p = player:new()
and

Code: Select all

-- usually inside love.update
p = player:update(dt)
A class is a definition. It says how one or more "things" act. Once you have a class, you can use it to create things. These things you create are called instances. That's what the "new" and initialize methods are for.

You can have more than one instance of the same class. I usually name instances in lowercase, and classes in uppercase, to make the distinction more obvious:

Code: Select all

 -- class definition
Player = class('Player') -- notice the uppercase
function Player:initialize()
...
end
...
-- class instantiation (cration of an instance)
player = Player:new(...)
Instances use the methods defined by a class as if they were their own. In other words, if the Player class has a method called think, when you do player:think() - notice the lowercase - then "self" will be the player instance, not the player class.

I hope this clarifies it. If you have more questions, I suggest opening a different thread, otherwise it'll be too much off-topic.
When I write def I mean function.
SudoCode
Citizen
Posts: 61
Joined: Fri May 04, 2012 7:05 pm

Re: Need some help with OOP and clean coding

Post by SudoCode »

Zeliarden wrote:Your sprint dosent work because of floor()
your movment is 1

btw here is a tips for draw range:

Code: Select all

Map:autoDrawRange(-(Player.x-(love.graphics.getWidth( )/2)), -(Player.y-(love.graphics.getHeight( )/2)), 1, -50)
The problem with removing math.ceil and math.floor from the movement is now tiles have rough edges when you move.
Attachments
New.love
(425.87 KiB) Downloaded 68 times
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 58 guests