Global list of objects inside class.

Monkey Forums/Monkey Beginners/Global list of objects inside class.

Farflame(Posted 2014) [#1]
I'm returning to Monkey after a couple of years away and I'm struggling with something that gave me problems before. I'm trying to get as much stuff inside the classes as possible, including the list of objects of that class. In the past, I think I eventually just shirked and used an untidy approach that made my programs untidy and difficult to read. So here's my basic program so far. I'm trying to declare the 'Monsters' list inside the 'cMonster' class, as a global, but when I try to call the Draw method from the Game.OnRender method, it's giving an error (in JungleIDE), 'Identifier Monsters not found'.

I thought since it was declared globally, it should be available anywhere? I think it's a scope issue.

I realise that this is probably very basic but I'm having trouble getting my head around it.

Sorry, forgot how to show code and can't find link describing how to show code.

Strict
Import mojo

Function Main:Int()
New Game()
Return 0
End

Class Game Extends App

Field player:Player

Method OnCreate:Int()
SetUpdateRate(60)

cMonster.Initialize()
For Local i:Int = 0 To 100
Local c:cMonster = New cMonster(Rnd(500), Rnd(500))
c.x = 0 ' Just to suppress the editor warning that c isn't used.
Next
player = New Player(100, 100)

Return 0
End

Method OnUpdate:Int()
player.x = MouseX()
player.y = MouseY()
Return 0
End

Method OnRender:Int()
Cls(32, 64, 128)

player.Draw
Monsters.Draw ' This is the error.
Return 0
End

End

Class Player
Field x:Float, y:Float
Field image:Image


Method New(x:Float = 100, y:Float = 100)
image = LoadImage("greywizard.png")
Self.x = x
Self.y = y
End

Method Draw:Void()
DrawImage(image, x, y)
End
End

Class cMonster
Field x:Int
Field y:Int
Global image:Image
Global Monsters:List<cMonster>

Function Initialize:Void()
Monsters = New List<cMonster>
image = LoadImage("dragon.png")
End

Method New(NewX:Int, NewY:Int)
Local m:cMonster = New cMonster
m.x = x
m.y = y
Monsters.AddLast m
End Method

Method Draw:Void()
For Local Monster:= EachIn Monsters
DrawImage(image, x, y)
Next
End Method

End Class


ImmutableOctet(SKNG)(Posted 2014) [#2]
The 'Monsters' global variable isn't located in the global scope. It's in the 'cMonster' class, so you should write:


The global scope of a class is accessed through the "." operator. So, with the global variable in 'cMonster', we need to get it from the 'cMonster' class. Move the global variable outside of the class if you want to access it like your original code did. The class's global scope is different from the global scope of the module itself. And global variables are always available to other modules if they're public (And the module in question imports the module with the global variable).

Also, this isn't really the best structure for handling multiple monsters. You should probably just make that list a field in your 'Game' class. That's assuming you're not planning on completely restructuring this, which I'd also recommend. For an example, this is fine, but for a full game engine, this will get cluttered. Also, the forum codes are on the bottom right. For some reason they don't pop up when making a thread, though. You should use 'codebox' for large amounts of code, and 'code' for small amounts. These can be used via the usual BBCode symbols ([INSERTHERE]), and they should be ended with that, only with a slash before the code-type ([/INSERTHERE]).


Farflame(Posted 2014) [#3]
Thanks. It's giving the same error though. I didn't realise that a class's global scope was different from the module global scope, so that's good to know. This was only a test and bolted together from a few bits of code, so I'm happy to restructure it completely. Just need to play around with it a lot more until I understand the scope a bit better.


Farflame(Posted 2014) [#4]
I think I'm seeing the problem, I've put my classes outside of the game class, so there's no scope at all to 'see' anything declared inside them? If I put them inside the game class, then they'll be visible inside that. But the thing is, I was trying to make it as tidy as possible and have everything relevant to a class, declared and encapsulated inside the class itself - all methods, variables etc. In the past, I had loads of functions relevant to classes, inside the game class, so it was a real mess.


tiresius(Posted 2014) [#5]
I would recommend getting a book on Object Oriented Programming (Head First series was entertaining read) because even though the language lends itself to OOP there are still ways to make it really messy and cumbersome.

Also, there may be some example tuts out there of mostly complete games that you can take a look at and get ideas from on how to structure your classes. Because what you're trying to do is in practically every single game ... keeping track of a collection of "stuff".

Keep at it !


Farflame(Posted 2014) [#6]
It's ok, I've been using OOP since Blitz and I absolutely love it. I have no issues with OOP at all, just working with it in Monkey. I have made 2 complete (small) games with Monkey, but I did tended to skip bits (at least, understanding why stuff worked). I know what I'm trying to do, just not sure why Monkey doesn't like certain things.

In this case it's fairly simple, the Method needed to be a Function since I have no instance of the class. The list is inside the class, so just changing it to a function solves it. As far as I'm aware, this is fairly unique to Monkey, or I haven't encountered it before at least.


ImmutableOctet(SKNG)(Posted 2014) [#7]
Well, they aren't games, but you can check out the modules the community has made. They should be useful for structural references. Just keep in mind they're supposed to be somewhat self-contained. Here's my modules, for example.

As for game structure, you should probably stay far away from global variables, and instead use fields and constructors. Unless of course you're dealing with something shared, like an 'Image', then globals could work. I personally wrote an elaborate wrapper for Mojo's 'Image' functionality, which reuses the actual 'Image' objects automatically. From there I just extend my custom image class with my sprite class, and I have sprites held within game-objects.

My game-objects are held within other game-objects (Usually sharing a common ancestor), which all gets contained up to the scene, then the scene-manager (Both are still game-objects, mind you) holds each scene. From there, I can pretty easily implement synchronization routines for online multiplayer, as I just need to write standard serialization routines for the objects. And since the multiplayer-game is handled by the scene-manager, I can technically synchronize anything with a bit of code (If the base-class's implementation doesn't already do the job). And of course, I simply make a new scene-manager when starting the game, and handle that in my main 'Game' class (Which is based on Mojo's 'App' class).

So, that's a very basic look at the layout of my game engine(s). I also have somewhat complicated systems that deal with draw-order and cameras (Which use my sub-display system for split-screen and the like). There's also my 'inputmanager' module, which I'm planning on releasing soon. As well as other modules I've made (But haven't released yet), such as my argument-loading framework. And don't even get me started on the messes that are my ID-system, collision routines, and relational quad-trees.

The only problem I have with this structure is that my menus are scene and scene-manager based. This means I have to create a new scene manager for the menus, then create a new manager for gameplay. It's really not that big of a deal, though.

Anyway, what I described is more or less my engine in a nutshell. Needless to say, I like writing everything from scratch (Other than a few small things here and there), as well as have things containing similar things inside of them for the sake of standardization. Not to mention object synchronization.


impixi(Posted 2014) [#8]
Your cMonster.Draw method should be a function and modified like so:

Function Draw:Void()
  For Local Monster:= Eachin Monsters
     DrawImage(image, Monster.x, Monster.y)
  Next
End


PS. Insert code in code .. /code tags surrounded by square brackets.

EDIT: Ah, I see you already solved it:


In this case it's fairly simple, the Method needed to be a Function since I have no instance of the class. The list is inside the class, so just changing it to a function solves it. As far as I'm aware, this is fairly unique to Monkey, or I haven't encountered it before at least.




Farflame(Posted 2014) [#9]
Thanks guys. I am able to put together a game fairly quickly I think, it's just that I'm using messy techniques and want to tidy up my code. I'll follow your advice and keep following examples to try to find the best methods. This has already taught me a few things, it all helps :)


impixi(Posted 2014) [#10]
Depending on your requirements, another "object-oriented" approach is to create a "monster manager" class that extends Monkey's List class. That way you could implement List's Compare function and provide a sorting mechanism if necessary (eg for z-ordering, layering, etc). Something like (compiles, but untested):



PS: For posting large code blocks use codebox .. /codebox tags surrounded by square brackets.


Farflame(Posted 2014) [#11]
Thanks :)


Gerry Quinn(Posted 2014) [#12]
Another option is to have something like this: