Game Development Community

Making Hidden Object game workflow

by Vlad I · in Torque Game Builder · 11/29/2011 (9:02 am) · 15 replies

I'm trying to make a hidden object game some thing like this :


Basically you click on the screen to find some objects, you collect some and use them to solve puzzles, and you walk from room to room.

I'm a bit confused with the workflow.
I understand making a shooter or a platformer , where you complete a level and never go back so in your script you have a trigger to load a next level. but in Hidden object type game you often walk back and forth.

So basically I will need to dump all my art I made into TGB editor and make it as one level? Make the inventory, screens/rooms, items etc. and make them invisible and only show them when they are triggered as the game is progressed?
O
h btw you need to have save and load option, keeping in mind that you might have some items in the inventory.

#1
11/29/2011 (12:46 pm)
A way you could make it is by having a certain amount of rooms per level, and you simply move the camera or switch the level to whatever room you need.
#2
11/29/2011 (12:54 pm)
I've only had one game with a *lot* of graphics, but it was just barely over. After I finished the game, I manually managed the t2dImageMapDatablocks by loading a common set at the beginning and then I new'd and deleted the other datablocks right before I loaded levels.

You should also read this thread which describes how to load image datablocks on a per scene file basis.
#3
11/29/2011 (1:28 pm)
Dont forget it is all dynamic, e.g. you walk back and forth, you pick some items into your inventory and use them later.

I've searched the entire forums. In one thread I read that you might actually save each scene/room as a level just like you do it with a shooter, but for the items and other objects you use globals?
And then you load your levels as the game is progressed.
Like level 01(room01) you click on a door with a trigger level 02(room02) loads with some items , you pick up an item(it pops up into your inventory) somehow you set it as global, so when you go back to level 01 (room01) the level loads with that item in your inventory?(and when you go back to level 02 (room02) you dont see the items you picked up previously.)

For now I have all my scenes as separate levels, so when I finish level 01, level 02 loads up and so on. But of coz when I want to load level 01 after level 02 is finished it is loaded all over again and you have to find the items all over again. If you know what I mean.
#4
11/29/2011 (2:38 pm)
Your approach will work. If you name all of the objects in the level editor, you can do something like the following:

function Player::getItem( %this, %item )
{
  %this.heldItem[%item] = true;
}

function Player::hasItem( %this, %item )
{
  return %this.heldItem[%item];
}

function Item::onClick( %this )
{
  Player.getItem( %this.getName() );
}

// Initialize this separately.  You can save and load it, too.
new ScriptObject(Player);

// Example of how to check for ownership.
if( Player.hasItem( "Clock" ) )
{
  ... Do Something ...
}

// Do this for each scene you've defined.
function Room01::onLevelLoaded( %this )
{
  %count = %this.getCount();
  for( %i = 0; %i < %count; %i++ )
  {
    %obj = %this.getObject( %i );
    if( Player.hasItem( %obj.getName() ) )
      %obj.delete(); // or setVisible(false)?
  }
}
#5
11/30/2011 (7:48 am)
Thanks for your help guys.
I've been trying to wrap my brains around it for a day.
I'm following your advice William, here is what I have in my .cs :
//////////////////////////////////////////////////////////////////////////////
//This is where we do level01 stuff

 function Box01::onMouseDown( %this, %modifier, %worldPosition, %clicks)  
  {  
    Star01.safedelete();  
    Parent::onMouseDown( %this, %modifier, %worldPosition, %clicks );  
  }
 function Box02::onMouseDown( %this, %modifier, %worldPosition, %clicks)  
  {  
    Star02.safedelete();  
    Parent::onMouseDown( %this, %modifier, %worldPosition, %clicks );  
  } 
 function Box03::onMouseDown( %this, %modifier, %worldPosition, %clicks)  
  {  
    Star03.safedelete();  
    Parent::onMouseDown( %this, %modifier, %worldPosition, %clicks );  
  }
$totalNumberOfBoxes = 0;
   
    // I'm assuming that each box has the class name "Boxes".  
    function Boxes::onAdd(%this)  
    {  
     %this.setUseMouseEvents( true ); // enabling mouse event  
     $totalNumberOfBoxes++;  
    }  
 
 function Boxes::onMouseDown( %this, %modifier, %worldPosition, %clicks)   
  {  
   %this.safeDelete();  
   $totalNumberOfBoxes--;  
   if( $totalNumberOfBoxes == 0 )  
     schedule( 0, 0, loadNextLevel );  
 }   
   
 function loadNextLevel()  
  {  
   sceneWindow2D.endLevel();  
   sceneWindow2D.loadLevel("game/data/levels/level02.t2d");  
  }  
    
//////////////////////////////////////////////////////////////////////////////
//This is where we do level02 stuff

function Circle01::onMouseDown( %this, %modifier, %worldPosition, %clicks)  
  {  
    Ball01.safedelete();  
    sceneWindow2D.loadLevel("game/data/levels/level01.t2d"); 
  }

/////////////////////////////////////////////////////////////////////////
//This is where we try what William suggested

function room01::onLevelLoaded(%this)
{
	%count = boxes.getCount();
	for( %i = 0; %i < %count; %i++ )  
  {  
   	 %obj= boxes.getObject( %i );  
   	 if( Player.hasItem( %obj.getName() ) )  
     	 %obj.delete(); // or setVisible(false)?  
  }  
}

It is just a test scene to make it work.What I have in level01 is 3 objects (boxes) and 3 other objects (stars), so when you click on box01 , it gets deleted with star 01, it goes on till you click on all 3 boxes objects and then level 02 is loaded, there you click on circle so the level 01 gets loaded, but it is loaded all over again all boxes and stars are back. I thought they would be deleted with what William suggested.
What am I doing wrong? the console doesnt show any error.
#6
11/30/2011 (8:41 am)
Make sure that the name of your scene (not the file name, but the actual t2dSceneGraph) is "room01". This is very important!

Then, do the following:
new ScriptObject(Player);

function Player::getItem( %this, %item )
{
  %this.heldItem[%item] = true;
}

function Player::hasItem( %this, %item )
{
  return %this.heldItem[%item];
}

// Level 1
function Box01::onMouseDown( %this, %modifier, %worldPosition, %clicks)  
{
  Player.getItem( "Star01" );
  Star01.safedelete();  
  Parent::onMouseDown( %this, %modifier, %worldPosition, %clicks );  
}
function Box02::onMouseDown( %this, %modifier, %worldPosition, %clicks)  
{  
  Player.getItem( "Star02" );
  Star02.safedelete();  
  Parent::onMouseDown( %this, %modifier, %worldPosition, %clicks );  
} 
function Box03::onMouseDown( %this, %modifier, %worldPosition, %clicks)  
{  
  Player.getItem( "Star03" );
  Star03.safedelete();  
  Parent::onMouseDown( %this, %modifier, %worldPosition, %clicks );  
}

$totalNumberOfBoxes = 0;
   
// I'm assuming that each box has the class name "Boxes".  
function Boxes::onAdd(%this)  
{  
  %this.setUseMouseEvents( true ); // enabling mouse event  
  $totalNumberOfBoxes++;  
}  
 
function Boxes::onMouseDown( %this, %modifier, %worldPosition, %clicks)   
{  
  Player.getItem( %this.getName() );
  %this.safeDelete();  
  $totalNumberOfBoxes--;  
  if( $totalNumberOfBoxes == 0 )  
    schedule( 0, 0, loadNextLevel );  
}   
   
function loadNextLevel()  
{  
  sceneWindow2D.endLevel();  
  sceneWindow2D.loadLevel("game/data/levels/level02.t2d");  
}  

// Level 2
function Circle01::onMouseDown( %this, %modifier, %worldPosition, %clicks)  
{  
  Ball01.safedelete();  
  sceneWindow2D.loadLevel("game/data/levels/level01.t2d"); 
}

function room01::onLevelLoaded(%this)
{
  %count = %this.getCount();
  for( %i = 0; %i < %count; %i++ )  
  {  
    %obj = %this.getObject( %i );  
    if( Player.hasItem( %obj.getName() ) )  
    {
      %obj.delete(); // or setVisible(false)?  
    }
  }  
}
#7
11/30/2011 (8:50 am)
I did exactly you guided me , still when level 01 loads after level 02 I have the boxes and stars. I can send you my project if you care to look into it?
#8
11/30/2011 (9:16 am)
Here is a link with the test project
www.filefactory.com/file/cf6b392/n/01HoTestLevels.zip
it is only 1mb
#9
11/30/2011 (10:26 am)
I'm getting it now... I'll advise very soon.
#10
11/30/2011 (10:43 am)
There's two things I had to do:

1) Make sure that the t2dSceneGraph name matches the name with the "::onLevelLoaded". You had "room1" as the t2dSceneGraph and "room01::onLevelLoaded". It doesn't matter which you change it to, but it has to be identical.

2) You have to delete the objects in backwards order (I *always* make this mistake).
function room1::onLevelLoaded(%this)  
{  
  %i = %this.getCount();
  while( %i-- >= 0 )
  {    
    %obj = %this.getObject( %i );    
    if( Player.hasItem( %obj.getName() ) )    
    {  
      %obj.delete(); // or setVisible(false)?    
    }  
  }    
}

With those two changes, when I return to the first level the balls and stars are gone.
#11
11/30/2011 (10:53 am)
Thank you William ! it works !
#12
11/30/2011 (11:10 am)
William, can you explain me please the last room1 function in short. I know it might sound noobish of me , but I believe people should learn instead of bothering others asking for help every time.
what you define as %i ?
I value your time it's ok if have something else to do.
#13
11/30/2011 (11:24 am)
ok I think I got it , it is on the permanent loop with looping construct while.
#14
11/30/2011 (12:57 pm)
When a t2dSceneGraph is loaded into a t2dSceneWindow, it calls the function "::onLevelLoaded" for that scene graph.

The scene graph is mostly a list of all of the objects that are in your scene (also known as your level file). Given that, look at the following code annotated with some comments.

function room1::onLevelLoaded( %this )
{
  // %this is the same as room1.
  
  // Get the number of objects in the scene
  %i = %this.getCount(); // could have been %i = room1.getCount();

  // Pretend there are 4 objects in the scene.  %i now equals 4.

  // There are 4 objects, Object[0..3], but %i is 4 right now.
  // We want to go through the objects backwards from 3 down to 0.
  // So first, we decrement 1 from %i.  As long as this is
  // greater than or equal to 0, keep repeating the code...

  while( %i-- >= 0 )
  {
    // Get object 3 (then 2, then 1, then 0)
    %obj = %this.getObject( %i );

    // This object has a name, like "Box01" or "Star03".
    // See if the global "Player" object has that item.
    if( Player.hasItem( %obj.getName() ) )
    {
      // If so, remove it from the scene graph
      %obj.delete();
    }    
  } // Go back, subtracting 1 from %i.
}

Why do we do this backwards from 3 to 0 instead of fowards from 0 to 3?

Start with our 4 objects...
array[0] = Box01
array[1] = Box02
array[2] = Star01
array[3] = Star02

If %i equals 0 and we delete it we have...
array[0] = Box02
array[1] = Star01
array[2] = Star02

And now we set %i = 1. You'll see we skipped Box02!

Going backwards is an easy way to make sure we don't skip any objects in the list if we delete some.

Hope that helps!
#15
12/01/2011 (4:49 am)
Thank you for the explanation! Nice and elegant just like e=mc2 :)