2
\$\begingroup\$

May sound funny, but that's what the question is about.

Suppose you have this situation: http://i48.tinypic.com/whl6pk.jpgGraph

Red squares are data types. Hexagons are methods and Journal is the main issue here. Or more exactly, how the link between Game and Journal should be interpreted. Basically, a lot of game components (instantiated inside of Game) will have to gain access to the Journal functionality. The two options that I have thought of myself are the following:

Journal could be a namespace w/ 'globally' used methods and fields. Something like:

journal::AddEntry(someEntry);

OR I could make Journal a Class, just like Game, and instantiate it inside of Game, and then pass a reference to Journal to each property of Game that needs to use the journal. Something like:

  GameProperty *property = new GameProperty(&ourJournal);
  //...inside of property:
  Journal *referenceToJournal;

But then, if I'd delete referenceToJournal, the actual ourJournal object would be deleted too, right? I'm pretty sure this just complicates things by a lot, and having lots of references towards the same object is a bad habit.

I'm kind of thinking that the:

namespace Journal
{
//Fields and methods go here
}
...
#include "Journal.h"
Journal::method();

is the best way to achieve global visibility for the Journal without much stress. But it kind of hurts, since I come from a C# & XNA background where everything is object oriented. Which is the main reason I have thought of a:

class Game()
{
void loadcontent();
void update();
void draw();
}

logic.

How would you attempt to achieve given the scenario? Instantiate a Journal inside of Game and then constantly pass a reference to Journal to all the other game components (instantiated inside of Game), or just treat everything (including Game) as namespaces to provide 'global' access to all required components? I'm also thinking about classes with public static fields and methods, but that'd be just like namespaces, at least from my point of view. Here is some more supportive code, to better describe my view on things (pseudocode for the namespace idea):

//FILE Journal.cpp (implementation of Journal.h)
namespace Journal
{
struct JournalEntry
{
    INT QuestID;
    String Description;
};

Vector<JournalEntry> Entries;


void AddEntry(JournalEntry theEntry)
{
    Entries.Add(Entry);
}

/*
* Used at getting all text that has to be shown on the
*/
Vector<String> GetDescriptionsForEntriesWithQuestID(INT theQuestID)
{
    Vector<String> Descriptions;
    foreach (JournalEntry jEntry in Entries)
    {
        if (jEntry.QuestID == theQuestID)
        {
            Descriptions.Add(jEntry.Description);
        }
    }
    return Descriptions;
}
};


//FILE GameComponent.cpp implementation of GameComponent.h
#include "Journal.h"
void GameComponent::ShowInfo()
{
Vector<String> info = Journal::GetDescriptionsForEntriesWithQuestID(3);
//go through the collection and do stuff
}

Thanks!

\$\endgroup\$

3 Answers 3

3
\$\begingroup\$

My first instinct is that you've got a mistaken assumption hiding in the sentence 'Basically, a lot of game components (instantiated inside of Game) will have to gain access to the Journal functionality.' Why will a lot of game components need access to the journal? The way you've laid it out, journal entries should only be coming from your 'story manager' or equivalent; information the player learns about NPCs can come from this, information they learn about objects can come from this, etc. At worst, it feels like you should hang the journal methods off of your core Player class somewhere and be making calls along the lines of Player->AddJournalEntry() (which will then call to the journal class behind the scenes, of course). Regardless, the upshot is that 'lots of game components' needing access to this fairly specialized entity is a good sign of a design error somewhere else in the chain.

\$\endgroup\$
7
  • \$\begingroup\$ Good point. Perhaps implementing an Observer design pattern? So that each game component that needs to add something to the journal sends an event that eventually propagates up to Game which then calls the this->Journal->AddJournalEntry()? \$\endgroup\$
    – user15805
    May 6, 2012 at 20:39
  • \$\begingroup\$ And because I did not answer to the first part: what I meant by a lot of game components is something that includes most objects on the map. Doing something, say, with a rock, adds something to the journal. \$\endgroup\$
    – user15805
    May 6, 2012 at 20:45
  • 1
    \$\begingroup\$ 'Doing something with a rock adds something to the journal' - that's fine, but that doesn't mean that it's the rock's responsibility to add the journal text. Instead, it could easily be the action that the player takes (or the player itself) that queries the rock and says 'do you have journal text to add? If so, what?'. That way the rock is responsible for its content (the journal text) but the player is still responsible for the activity. \$\endgroup\$ May 6, 2012 at 21:08
  • \$\begingroup\$ What's more, even the content responsibility can be taken away from the rock and given to a 'journal text manager' which knows about the objects in the world and keeps e.g. a table from objectID to JournalText. This has the advantage of leaving all your text in one place for proofreading and localization if need be. \$\endgroup\$ May 6, 2012 at 21:09
  • \$\begingroup\$ Makes sense. All text content will be found in separate files assigned to particular objects. Thanks for your answer, it was indeed enlightening in the matter. I'll mark it as good. \$\endgroup\$
    – user15805
    May 6, 2012 at 21:17
2
\$\begingroup\$

Well, namespace are more to organize code, and a journal seems to me better as a class. To help you out, you could use C++ references (Journal& myJournal) and also use the theory of "whoever creates, is the same one that deletes". That is, if your Game new'ed Journal, it should delete it too, no other object should bother about deleting it, Game should.

This works very well, and avoid memory leaks, since you know who should delete it, and you don't get confused about where to delete it.

And just one plus: Namespaces are just names. a static global and namespace members are just the same on compiled code.

\$\endgroup\$
2
  • \$\begingroup\$ delete Object; when the Object has a reference to Journal, then the reference won't be altered in any way (the reference would be a private member Journal *journal;, right? I won't be adding delete journal; in Object's destructor. \$\endgroup\$
    – user15805
    May 6, 2012 at 20:37
  • 1
    \$\begingroup\$ Yeah, the memory will still untouched unless you put a delete journal. And I'd really think you should take a look at the reference types. (just use & instead of *), this way, you have a reference that you dont have to bother about deleting or newing around. \$\endgroup\$ May 6, 2012 at 20:47
1
\$\begingroup\$

This started as a comment on Steven Stadnicki's comment, but grew too big so I figured might as well answer it fully.

As for your question on whether to use a class or module-type thing with a namespace, I don't think it actually matters. Rid yourself of this need to constantly be object-oriented! Whether you should use a namespace or some kind of global/singleton/dependency-injected class depends on how you're using namespaces in general.

If you'd have multiple journals loaded at one time with different state, then yes it should be a class. Otherwise, it might be unnecessary. I presume that the general flow for setting up and using the journal is something like this:

On game startup:
  Initialize Journal (either Player.Journal = new Journal(); or Journal::Initialize()).
    This should grab memory that the journal needs to do work
  On game startup, Journal load quest data from disk for lookup by ID
  On saved-game load, Journal loads quest data for the current player
  During gameplay, Journal accepts queries and operations to lookup quest info or add quests to current player's journal
  On saved-game unload, flag all player-specific data as "dirty" or reset on the spot
  On game quit, Journal cleans itself up

For games where you'd only have a single Journal (ie: no multiplayer, sync or async), the namespace approach is perfectly valid so long as it fits within your scheme of namespace usage. You could also make a class with entirely static members. The differences are subtle and not worth thinking about. There's fundamentally little difference between calling Journal* journalInstance = new Journal(); ... delete journalInstance; and Journal::Init(); ... Journal::DeInit();. You're just allocating and initializing memory, manipulating it, and then freeing it.

Now for my commentary on Steven's comment. I might have misinterpreted what you were getting at, I apologize and please let me know so I can edit this to make more sense. This approach doesn't seem to be that flexible in the long run. If I'm following your logic correctly, this means that for any sort of system like this related to any system that may be a part of the player, you'd need to add a check to the list of things that happens when the player interacts with an object. Say you wanted a rock that would add something to your journal and give you a buff. That's an extra conditional in the Interact() function. "Do you have journal text to add? Do you have buffs to apply to the player?" Now what if you have another rock that adds a buff to the player, but also triggers a cutscene. Now you have "Do you have journal text to add? Do you have any buffs to apply to the player? Do you have a cutscene to play?" This will get out of hand.

It would make more sense to me to make AddJournalEntryOnInteract a behavior/component which would be in charge of calling the Journal functions. That way you could add as many sorts of components as you'd like that could respond to a player interact. If there's only ever one Journal ever, then hiding Journal functions behind the player class seems like it's adding unnecessary overhead. This would make sense in multiplayer, since each player would have their own journal, but if this is single player, that's not really necessary to do and just adds one more set of functions to your player class that don't need to be there.

The implementation could be, that in your scene data, you give this rock a list of components that respond to Interact();. When the rock is Interact()ed with, it iterates through all of its components that respond to Interact(); and each has the opportunity to do their thing like adding content to the journal, buffing the player, triggering a cutscene, or whatever.

EDIT

One point I forgot to mention about the Journal namespace vs class thing, is that you'll likely find it easier to create and use mock journal objects if you want to do unit testing with a mocked Journal.

ANOTHER EDIT: But then again, what's so bad about include guards?

\$\endgroup\$
4
  • \$\begingroup\$ Thanks for the great reply! I was thinking about a pseudo-scripting language for the dialogues and stuff. Say, rock 1 will be assigned file dialogue_rock1.script which will have a format like say_player: Something; say_NPC: Something here too; play_cutscene: CutsceneID; etc. I am going to spend some more time planning the whole game logic. I'm also considering switching to C# and XNA, as C# is my main programming language. I was planning on making this game for about 5 years, and the point is to get it done, so the easier it is, the better. \$\endgroup\$
    – user15805
    May 7, 2012 at 12:51
  • \$\begingroup\$ I think that simplifying it down to just an Interact() function might be a little too tight, but I do agree with the notion that a component-based approach is an excellent plan here; having the rock implement a Journalizable component in some fashion. I think this makes the code a bit cleaner, but I would be worried about the data management aspects if there's a lot of text hanging around; as I suggested in my answer, my gut instinct is that you don't actually want the rock to be directly responsible for its text, but rather to have all the quest text in one place somewhere. \$\endgroup\$ May 7, 2012 at 16:04
  • \$\begingroup\$ But of course, the rock could load its text from the appropriate spot and be responsible for presenting it when asked. \$\endgroup\$ May 7, 2012 at 16:04
  • \$\begingroup\$ @SteveStadnicki I agree, I don't want to have some rock having responsibility for the data, so I'd want it to refer to a questID that's in some database-type-thing. I can agree see that Interact() is a bit too simplistic on its own. Bogdan Marginean: A design like this could still work if you had a scripting system for cutscenes. Scripts are just data, after all. \$\endgroup\$ May 8, 2012 at 3:12

You must log in to answer this question.