Hello guys!
I've seen LÖVE projects that:
A) Uses globals everywhere, which are easy to access from anywhere in the project, only need to include files once (e.g. in main.lua). Mari0 is large enough and does this if I recall corretly.
B) Uses a couple of globals that are tables with useful variables and functions.
C) Uses no globals, locally includes files as they are needed. This is what I do now.
D) Uses no globals, passes objects as references. This is what I used to do in C++, and as I see Bump2 does this.
So what do you suggest the best way is to organize my code?
Thanks in advance!
Organizing your project
Re: Organizing your project
If you prefer to do less typing, A and B. If you prefer to have your scripts' dependencies more concise, C and D.
I asked about this a little bit ago, and kikito gives a good answer: viewtopic.php?f=5&t=78086#p168852
I asked about this a little bit ago, and kikito gives a good answer: viewtopic.php?f=5&t=78086#p168852
Re: Organizing your project
This is something that I've been struggling with also. I'm working on a little learning project and I'm having difficulty avoiding global variables but I suspect it's just the way that I have structured my game.
What I would really like, is to see some high quality example projects that I can dig through well organized source code. I like this framework and the community because I can dig through .love files and everyone is willing to help. However, many of the projects people post here are incomplete or quick and dirty games done for jams. In some ways this is the nature of the framework (do it yourself / prototyping) and even Lua itself but I'd like some great, clean examples of how to structure and organize things. Wikis and tutorials are great for initial learning and understanding concepts but I'd like to work towards larger projects.
What I would really like, is to see some high quality example projects that I can dig through well organized source code. I like this framework and the community because I can dig through .love files and everyone is willing to help. However, many of the projects people post here are incomplete or quick and dirty games done for jams. In some ways this is the nature of the framework (do it yourself / prototyping) and even Lua itself but I'd like some great, clean examples of how to structure and organize things. Wikis and tutorials are great for initial learning and understanding concepts but I'd like to work towards larger projects.
Re: Organizing your project
IMHO the best way to go is probably going with Globals for your classes, and locals for all of your instance variables. If you can pull off all-locals, just because you're that awesome, then go for it. But it's probably best to just load all of your code before or during love.load, and not have to worry about it.
Re: Organizing your project
I do mostly A and B - maybe I shouldn't but I do.
A quick sample of my workflow:
It is then required by main.lua so, akin to working with HTML, everything is referenced in one place.
This way, say I want a timer within the defeat screen (think Street Fighter's continue screen), I could easily do
And if I wanted a timer for the actual fight, I would just as easily do, in the fight.lua or whatever you wanna call:
The downside is, like is said in Kikito's post that was linked, it is not immediately clear what is used where - any semantic or argument changes I did to this timer would need to be redone across multiple files.
The upside is that I can define a new local timer for each game state and I basically always have it written the same way.
That is, for me, semantically better than doing something like this:
TL, DR:
The less I have to type, the better. More lines of code means I'm more likely to make mistakes.
I like referencing things from a single file - I can, at a glance, know every module I'm using and it is easier to disable something across multiple files.
It is not as safe as a local require but I'm not too worried about perfect sandboxing.
A quick sample of my workflow:
Code: Select all
--timer.lua
local timer = {}
timer.new = function()
--code
end
return timer
Code: Select all
--main.lua
timer = require "core/timer"
Code: Select all
--continueScreen.lua
local timer = timer.new(10)
Code: Select all
--fightScreen.lua
local timer = timer.new(99)
The upside is that I can define a new local timer for each game state and I basically always have it written the same way.
Code: Select all
--continueScreen.lua
local timer = timer.new(10)
timer.set(20)
Code: Select all
--continueScreen.lua
local timer = require "core/timer"
local continueTimer = timer.new(10)
continueTimer.set(20)
The less I have to type, the better. More lines of code means I'm more likely to make mistakes.
I like referencing things from a single file - I can, at a glance, know every module I'm using and it is easier to disable something across multiple files.
It is not as safe as a local require but I'm not too worried about perfect sandboxing.
Re: Organizing your project
C
if i see a variable and can't find its declaration in the same file, i ain't gonna be happy
sandbox/dsl function environments are an exception of course (the other being the standard lib, obviously)
if i see a variable and can't find its declaration in the same file, i ain't gonna be happy
sandbox/dsl function environments are an exception of course (the other being the standard lib, obviously)
Re: Organizing your project
Depends on what I'm building. If it's a gamejam, where I work on a project for a short time with high intensity, I'll just global everything.
If it's a project I'll work on for a longer time but in short bursts, I'll use more locals.
The reason for is that if you use globals, only a small part of the project's structure is captured in your code; the rest of it is in your head. This is fine as long as you will complete the project before that structure has time to decay, but it's terrible if you want to work on the project for a longer time.
Using a lot of globals is kind of like working with an editor that will occasionally delete your comments and rename classes you haven't touched in a while; sure, everything will continue to work but it'll be confusing as hell after a while. You'll want to capture your structure in a place where it isn't likely to decay if you expect to be using it later. And "in the code" is about the best place to do it, but it'll take some extra effort in setting things up.
If it's a project I'll work on for a longer time but in short bursts, I'll use more locals.
The reason for is that if you use globals, only a small part of the project's structure is captured in your code; the rest of it is in your head. This is fine as long as you will complete the project before that structure has time to decay, but it's terrible if you want to work on the project for a longer time.
Using a lot of globals is kind of like working with an editor that will occasionally delete your comments and rename classes you haven't touched in a while; sure, everything will continue to work but it'll be confusing as hell after a while. You'll want to capture your structure in a place where it isn't likely to decay if you expect to be using it later. And "in the code" is about the best place to do it, but it'll take some extra effort in setting things up.
- kikito
- Inner party member
- Posts: 3153
- Joined: Sat Oct 03, 2009 5:22 pm
- Location: Madrid, Spain
- Contact:
Re: Organizing your project
D. No exceptions.
One particular example I can think on the bump.love demo: I wanted to make a screen shake effect when a grenade exploded.
This means that the grenades need to "know" the camera object - so they get it as a parameter in their constructor. And then it gets assigned to a member variable: self.camera = camera
This means that the entities which create grenades (Guardians) need to have a reference to the camera object, so they can pass it to the grenades they create. So they get it as a constructor and as an attribute.
The same thing happens with the Map, which creates the Guardias. It needs a reference to the camera, so it gets a constructor parameter and an attribute for the camera.
All that could have been removed if I had used a global variable. I would have typed much less.
And yet, I choose to write more here. Why?
For the same reason I return the classes as local variables require instead of making them global: Explicit dependencies make code easier to read.
Writing little code is important, but it is much more important to make that code readable. This is because we read code much more often. You can write code once, and then read it many, many times. The costs of the extra key presses to make the code easier to read is minimal compared to the effort it takes to understand the code by reading it many times.
Global variables make dependencies implicit: you have to remember in your head that they are there. You have to "allocate mental space" for them in your head while reading code.
Locals, parameters and requires are explicit: you must remember less things about them while reading code. You have some "extra mental space" in your head that you can dedicate to understanding the rest of the code. That is why I think they make code easier to read.
So, go D (and C - use local xxx = require 'xxx')
One particular example I can think on the bump.love demo: I wanted to make a screen shake effect when a grenade exploded.
This means that the grenades need to "know" the camera object - so they get it as a parameter in their constructor. And then it gets assigned to a member variable: self.camera = camera
This means that the entities which create grenades (Guardians) need to have a reference to the camera object, so they can pass it to the grenades they create. So they get it as a constructor and as an attribute.
The same thing happens with the Map, which creates the Guardias. It needs a reference to the camera, so it gets a constructor parameter and an attribute for the camera.
All that could have been removed if I had used a global variable. I would have typed much less.
I generally agree with this statement. The more code you write, the more you increase the possibility of making a mistake. The only infallible code is the code which doesn't exist.OttoRobba wrote:The less I have to type, the better
And yet, I choose to write more here. Why?
For the same reason I return the classes as local variables require instead of making them global: Explicit dependencies make code easier to read.
Writing little code is important, but it is much more important to make that code readable. This is because we read code much more often. You can write code once, and then read it many, many times. The costs of the extra key presses to make the code easier to read is minimal compared to the effort it takes to understand the code by reading it many times.
Global variables make dependencies implicit: you have to remember in your head that they are there. You have to "allocate mental space" for them in your head while reading code.
Locals, parameters and requires are explicit: you must remember less things about them while reading code. You have some "extra mental space" in your head that you can dedicate to understanding the rest of the code. That is why I think they make code easier to read.
So, go D (and C - use local xxx = require 'xxx')
When I write def I mean function.
Re: Organizing your project
I do like to type less, and to have less noisy code, because it's much easier to read. But I think as a project scales it's more and more difficult to understand and to extend it if I use globals.Kingdaro wrote:If you prefer to do less typing, A and B. If you prefer to have your scripts' dependencies more concise, C and D.
Have a look at the sources of Mair0 (http://stabyourself.net/mari0/), Project Hawkthrone (http://projecthawkthorne.com/), Mr Rescue (http://tangramgames.dk/games/mrrescue/) to name a few. I think they prepresent very different coding styles, and they are certainly not jam games.Reef wrote:What I would really like, is to see some high quality example projects that I can dig through well organized source code. I like this framework and the community because I can dig through .love files and everyone is willing to help. However, many of the projects people post here are incomplete or quick and dirty games done for jams. In some ways this is the nature of the framework (do it yourself / prototyping) and even Lua itself but I'd like some great, clean examples of how to structure and organize things. Wikis and tutorials are great for initial learning and understanding concepts but I'd like to work towards larger projects.
It's appealing, and I might end up with this one. Some people thinks globals are evil.Inny wrote:IMHO the best way to go is probably going with Globals for your classes, and locals for all of your instance variables. If you can pull off all-locals, just because you're that awesome, then go for it. But it's probably best to just load all of your code before or during love.load, and not have to worry about it.
This is also true!OttoRobba wrote:The less I have to type, the better. More lines of code means I'm more likely to make mistakes.
I also like to declare my variables explicitly.Xgoff wrote:if i see a variable and can't find its declaration in the same file, i ain't gonna be happy
Also if you go back to your project after one or two months, it's good to see immediately what's going on.kikito wrote:Locals, parameters and requires are explicit: you must remember less things about them while reading code. You have some "extra mental space" in your head that you can dedicate to understanding the rest of the code. That is why I think they make code easier to read.
Great, thanks for your suggestions and opinions!
It's not that I'm coding a very large game, I just want to develop good lua coding habits.
Re: Organizing your project
What kikito said with one exception.
Things I need often everywhere in my source code and which would increase the amount of characters in some lines drastically get declared global for me. Example : the screen width and height. I use them a lot to position stuff correctly and I dont want to type "love.graphics.getWidth()" every time nor do I want to write "local _width, _height = love.window.getDimensions()" in every single lua file I use them in. so I declare them one time in the main via "WIDTH, HEIGHT = love.window.getDimensions()".
But as you can see, if I create a global var I write it in uppercase letters so I don't get problems with any local width, height vars.
Besides that, I do everything local.
Things I need often everywhere in my source code and which would increase the amount of characters in some lines drastically get declared global for me. Example : the screen width and height. I use them a lot to position stuff correctly and I dont want to type "love.graphics.getWidth()" every time nor do I want to write "local _width, _height = love.window.getDimensions()" in every single lua file I use them in. so I declare them one time in the main via "WIDTH, HEIGHT = love.window.getDimensions()".
But as you can see, if I create a global var I write it in uppercase letters so I don't get problems with any local width, height vars.
Besides that, I do everything local.
Who is online
Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 2 guests