Local / Global Variables - Why?

BlitzMax Forums/BlitzMax Beginners Area/Local / Global Variables - Why?

Matt Vinyl(Posted 2007) [#1]
From various reading over the last few days, I've come to learn that Global variables, should, where possible, be avoided - I assume mainly down to the memory that they require. However, I'm struggling to get my head around how to use mainly local variables.

I tend to have my programmes set up in the following order:

-General Setup & Variable / Media Declarations
-Main Loop
-Function / Type Declarations

The trouble is, at the moment, most of my code ends up like this:
Global mvinylrotate
Global introbackrotate:Float
Global mvinylscale:Float=0
Global copyrightscale:Float=4
Global introfadeout
Global introfadecounter
Global introalpha:Float=1
Global yearalpha:Float=0
Global introplaysound
Global intromvinylsound
Global titlefadein:Float=0
Global titlestaralpha:Float=0
Global titlestarrotate
Global titlecreditsticker=800
Global titlescale:Float=1
Global titlescaleflag
Global titlerotate:Float=0
Global titlerotateflag
Global s1flag
Global s2flag
Global s3flag
Global titlevolume:Float=0
Global gamestate=1


Whereas, I can't recal a time I've seen this in anyone elses code. :(

Here's the current code I'm working on:

' Slider v0.1 (alpha) - By Matt Vinyl 2007
'-----------------------------------------------------------

' General Setup---------------------------------------------

AppTitle="Slider - By Matt Vinyl 2007"

Graphics(1024,768)

SetMaskColor 255,0,255
SetBlend(ALPHABLEND)
SetAlpha 0

SeedRnd MilliSecs()

Global GAMEMODE=2 'Global flag for master game flow
'0=Titlescreen
'1=Start game
'2=Main gameplay

Global GFX_L1BACK=LoadImage("gfx\back.png")

Global FADEIN:Float

' Main Loop-------------------------------------------------

Repeat
	Cls
	If GAMEMODE=0 
	
	End If
	If GAMEMODE=1
	
	End If
	If GAMEMODE=2
		DRAWGAME()
	End If
	Flip
Until KeyHit(KEY_ESCAPE)

' Functions-------------------------------------------------

Function DRAWGAME()
	DrawImage GFX_L1BACK,0,0
	If FADEIN<1
		FADEIN:+0.01
		SetAlpha FADEIN
	End If
End Function


This works as expected, but relies on global variables again. I tried:

Function DRAWGAME()
	DrawImage GFX_L1BACK,0,0
        Local FADEIN:Float
	If FADEIN<1
		FADEIN:+0.01
		SetAlpha FADEIN
	End If
End Function


and removed the Global declaration, but it doesn't fade in as expected. I'm guessing this is because the local variable is being 'reset' every time the 'Local' line is read.

Could anyone provide me with some general guidelines on how to go about using mainly local variables?

Pwweeaassee! ;)


Matt Vinyl(Posted 2007) [#2]
Whoops, sorry about the length of those lines, forgot my res is set at 1920 x 1200...!


H&K(Posted 2007) [#3]
1) GO and delete the -------- from the post
2) Just make one type TGlobal, and stick all your globals in that.
The main reason ppl moan, is Namespace, not speed.

Dont get me wrong, in OOP the Global should really belong to some object, (As a Global of that object), so if you find that you are only using a certain global with a certain Object.
Then you should accociate that global with that object by moveing it inside (Notice "Object/Type" not "Instace")


Czar Flavius(Posted 2007) [#4]
That's a tough one. Personally I'd try keeping fadin as a global because it's just simpler that way.

Why the capitals? Traditionally only constants are in capitals. Although BMax isn't case-sensitive it doesn't matter, caps is just an odd style for variables and functions.

gamemode is a variable that could be local, as it is not used in any functions. You don't even need gamemode. Just make three repeat loops for each stage, using exit to get out.

Images can be made local variables and sent as parameters to functions. However for simple games it's not so important.

If you give some code of a more complete program it would be easier to see how you are using the variables. I think you might need to change your way of design.

Global variables are bad for two reasons. They encourage bad design, as you can end up with parts of the program depending upon other parts. Then if you change something, you could create errors spreading unexpectadly throughout the program. Secondly, they are slower to access than local variables (I've heard). But they use the same amount of memory.

Hope this helps.


Matt Vinyl(Posted 2007) [#5]
(Code lines tidied!)

Thanks for the suggestions. I just use CAPS for vars and functions as I find they 'stand out' easier for me. I'm more than open for suggestions as to change my approach - it's just always worked fine for me, but to be fair, I've only really ever made small programs (and I'm much more used to B3D!)

Cheers,
M.


GfK(Posted 2007) [#6]
I use quite a lot of globals. Why? Because its far easier when variables are regularly needed by a range of different functions. You have two choices. Pass variables from function to function, or make them global.

For temporary variables (Such as 'For X = 1 to 10') - I would define the X as Local within a function, for instance. Reason being, I may use that variable in many different functions, but its a generic variable that I would use for many purposes.

What I DO tend to do, is have, for example, a tScore type. Within that type, I can add various fields pertaining to the game scoring system. Then at the start, all I need to do is create one tScore object, and I can access all the score variables from there. But I digress.


H&K(Posted 2007) [#7]
What I DO tend to do, is have, for example, a tScore type. Within that type, I can add various fields pertaining to the game scoring system. Then at the start, all I need to do is create one tScore object, and I can access all the score variables from there. But I digress
I dont think that is a digresstion, I think thats what I did/whould recomend doing. (Mostly cos Im rubish at remembering names, and Blide/protean's code completion helps


CS_TBL(Posted 2007) [#8]
namespace, in combination with multiple instances of your stuff.

From your list of globals there's a lot more to tell however as your variables seem related to visualisation/timing of, say, a titlescreen. Whether or not these are globals is not even relevant, the fact that you use named variables like these for this kind of stuff is equally limiting, namespace-wise.

What you *really* want is an animation system where you define animating... 'things', let's call them "entities" to stick a bit to Blitz. Each entity could have fields like these: (and I'll start simple, but I'll pull some strings later)
x
y
z
imagehandle
sound
rotatex
rotatey
rotatez
fadeinspeed
fadeoutspeed
alpha

etc.

What you then need to do is build up your titlescreen using these entities, meaning that you'll end up with this:

title:TAnimEntity=New TAnimEntity
title.x=5
title.rotatex=5
[..]

copyright:TAnimEntity=New TAnimEntity
copyright.alpha=.5
copyright.rotatey=45
[..]


By using this, the only variable you're really using is these entity names 'title', 'copyright', etc. With this you already wiped most of your initial vars.

So, I announced to pull some strings:

You don't want title:TAnimEntity and copyright:TAnimEntity at all, because with those names you're actually hardcoding something, and hardcoding sucks! You want an array of TAnimEntity's, and just fill it with all these entities, preferably using an external scriptfile.. perhaps XML even..

Why's that? With this array you can actually design your whole titlescreen outside your sourcecode using that scriptfile in notepad. Each entity the parser sees in your script reslices your array, and so your titlescreen with all its things can become really big -without bloating your code!-.

More strings to pull:

You don't want fadein and fadeout variables in your TAnimEntity type, as that's more or less hardcoding again. What you want is a TEnvelope, and readout an envelope-value based on a phase-index. This TEnvelope is a seperate object again which can be as complex as anything.. a simple attack, attack-decay, or decay.. but also a multisstage/segment envelope, or perhaps you could even use beziers to readout such a multistage/segment envelope. If you have this, this whole TEnvelope is *at once* perfectly suitable to move *anything*, and not just coordinates, but also alpha or soundvolume for all I care. Normally you'd have tons and tons of volumefadein, volumefadeout -like variables for all that, and fadein and fadeout are just mere attack-decay movements, nothing like the bezier-goodies you could have.

So, back to this TAnimEntity array.. from your whole list we just reduced it to one single array instance. Want to get rid of that one too? Make it a field in your TGame, and in the end, one game:TGame is everything you have!


The real problems here are not the difficulties of a game, but the opportunities and possibilities! Once you get the hang of it you keep studying possible futures and you keep splitting things up. Eventually the work will pay itself back, but until that time you'll have to accept that small-scope/oneshot procedural coders are running circles around you.


Otus(Posted 2007) [#9]
What I'd do with the fading is have a local fadein variable in the main loop and pass it to the DrawGame() function as Float Var. This way DrawGame can be used by other functions, with different fade variables.

If the program were more complicated, I would consider using some kind of type structure (CS_TBL explained it well), but for something that simple it's unnecessary.

By the way, I always have the main loop in a function, and call it after initialization. I don't know if it's a good idea in a simple program, but at least it makes the code clearer to me.