Game Development Community

Help with loading a level

by DeVry Instructors (#0020) · in Torque Game Builder · 10/09/2008 (6:21 pm) · 9 replies

Hello every one I am kind of new to TGB and I am having trouble with transitions of levels. What I have are a few question could any of you guys help me.

1. When you want your first level to end when the player get a certain amount of point what function do I call and in what folder?I am working off of the Shooter tutorial.

2. When you change levels it a level transition or a scene transition?


Here is my code under the enemy.cs file that I used to initialize the level transition


//This function takes the initial postion of enemy object
function EnemyShip::onLevelLoaded(%this, %scenegraph)
{
// Save the initial X value so we can use it when we respawn this ship
%this.startX = %this.getPositionX();

// Missile speed
%this.missileSpeed = -300;

// Spawn this ship
%this.scenegraph = %scenegraph; // Make sure the scenegraph is available for the fireMissile() function
%this.spawn();
}


//This fuction sets the spawn point of enemy object at random
function EnemyShip::spawn(%this)


{
//When enemy spawns fires one missle
%this.fireMissile();
// Set the minimum and maximum Y positions to be the
// world limits, accounting for the size of the ship
%this.minY = getword(%this.getWorldLimit(), 2) + (%this.getHeight() / 2);
%this.maxY = getword(%this.getWorldLimit(), 4) - (%this.getHeight() / 2);

// Set the speed limits
%this.minSpeed = -50;
%this.maxSpeed = -100;

// Set the speed and position of this ship
%this.setLinearVelocityX(getRandom(%this.minSpeed, %this.maxSpeed));
%this.setPositionY(getRandom(%this.minY, %this.maxY));
%this.setPositionX(%this.startX);
//When enemy spawns fires one missle
%this.fireMissile();
}

//This fuction recal the enemy ship once it pass the world limit and set it back at the spawn point
function EnemyShip::onWorldLimit(%this, %mode, %limit)
{
// When the ship hits the left side of the world limits, then respawn the ship
if (%limit $= "left") {
%this.spawn();
}
}
//Enables enemy to shoot missles
function EnemyShip::fireMissile(%this)
{
%this.enemyMissile = new t2dStaticSprite()
{
scenegraph = %this.scenegraph;
class = EnemyMissile;
missileSpeed = %this.missileSpeed;
enemy = %this;
};

%this.enemyMissile.fire();
}


//Enemy ship explode on collison with missle
function EnemyShip::explode(%this)
{
%explosion = new t2dParticleEffect()
{
scenegraph = %this.scenegraph;

};

%explosion.loadEffect("~/data/particles/big_explosion.eff");
%explosion.setEffectLifeMode("KILL", 1);

%this.scenegraph.incScore();
//Object to go to next level
if (scoreObject.text > 3)

{
%obj.clearScene( true );
%status = %obj.loadScene("~/data/levels/level2.t2");

}


%explosion.setPosition(%this.getPosition());
%explosion.playEffect();

%this.spawn();
}



function loadlevel()

{


sceneWindow2D.loadLevel("~/data/levels/level2.t2d");
}

#1
10/09/2008 (7:05 pm)
As far as I can tell, the above code won't work because you're calling the 'clearScene' and 'loadScene' functions with the local variable '%obj' which is never defined (set equal to any value). If you look in the console, you should be getting errors like: "Cannot find object 0, trying to call function 'clearScene'" whenever %obj.clearScene is executed (and a similar one for the 'loadScene' function).

I believe this would do what you want:
if(scoreObjectText.text > 3)
{
    %this.scenegraph.endLevel(); //Use the "end/load Level" functions to load levels you've made in TGB
    %this.scenegraph.loadLevel( [filename] ); //"loadScene" loads files saved by the "saveScene" function
}

Hope that helps!!

If it doesn't work, tell me.
#2
10/10/2008 (4:54 am)
I am still having problems loading level thanks with endLevel and load level function. Maybe it is a problem in where I place the function? Wouldn't you place this function in the .cs file that is affecting the score?

The following are my file names in my gamescripts folder with a short description:

bossEnemy.cs & enemy.cw - initialize and puts enemy on the screen also calculates kills of enemy and boss
bossenemyMissile & enemyMissile.cs - takes missile and causes damage to player
game.cs - starts game and level, also stores the global variable for score
Player.cs - initialize player and calculates collision with enemy and missiles
playerMissile.cs - creates missiles and damages the enemy and boss


The following are my files with in the behaviors folders:

displayScore.cs - displays score for my text item "Score:"
genericButtons.cs - used for main menu
level1.cs & level2.cs - used to keep trak of score on level // this may be the problem here is the coode

function level1::onLevelLoaded(%this){
%this.gamescore = 0 ;
scoreObject.text = "score:" SPC %gamescore;
}
function level1::incScore(%this){
%this.gamescore++;
scoreObject.text = "score:" SPC %this.gamescore;
}


Please help if possible

Thanks a million
Jenney
#3
10/10/2008 (5:29 am)
I am still having problems loading level thanks with endLevel and load level function. Maybe it is a problem in where I place the function? Wouldn't you place this function in the .cs file that is affecting the score?

The following are my file names in my gamescripts folder with a short description:

bossEnemy.cs & enemy.cw - initialize and puts enemy on the screen also calculates kills of enemy and boss
bossenemyMissile & enemyMissile.cs - takes missile and causes damage to player
game.cs - starts game and level, also stores the global variable for score
Player.cs - initialize player and calculates collision with enemy and missiles
playerMissile.cs - creates missiles and damages the enemy and boss


The following are my files with in the behaviors folders:

displayScore.cs - displays score for my text item "Score:"
genericButtons.cs - used for main menu
level1.cs & level2.cs - used to keep trak of score on level // this may be the problem here is the coode

function level1::onLevelLoaded(%this){
%this.gamescore = 0 ;
scoreObject.text = "score:" SPC %gamescore;
}
function level1::incScore(%this){
%this.gamescore++;
scoreObject.text = "score:" SPC %this.gamescore;
}


Please help if possible

Thanks a million
Jenney
#4
10/10/2008 (6:10 pm)
Okay, this might be the problem. In your above code block, you're testing to see if the game score is greater than 3 like this:

if(scoreObject.text > 3)
{
   ...go to next level
}

However, in the [level]incScore() function you're setting the scoreObject.text equal to "score: [current score]" So, the test would look like:

if("score: [a number]" > 3)
{
   ...will never execute
}

To resolve this you could parse the scoreObject.text to get just the number and then test that number, like this:

%score = getWord(scoreObject.text,1); //get the second (zero-based) value in  a space-separated string

if(%score > 3)
{
    ...this will excecute
}

You can put something like "echo("I'm in the level change function!");" within the 'if' statement, so you know for sure the "end/load" level functions are even being called.

You can also put "echo("score value:" SPC %score);" before the 'if' statement to make certain the score is actually doing what you want.

As far as where you put the 'loadLevel' function, for clarity, are you asking if it matters where in script you call "[scenegraph].endLevel()" and "[scenegraph].loadLevel()"? If so, I don't believe it should.

If you're asking if it matters where you put your custom "loadLevel()" function, I don't think it would matter, but I would probably put it in the .cs that calls it for organization reasons.

Again, I would suggest using the 'echo' function to make certain everything's working right as far as when the score is greater than three, the level change functions are called. If it still doesn't work, what exactly happens? Nothing? Blank screen? If the "endLevel()" is executed, there should be a blank screen.

I hope that helps!
#5
10/11/2008 (11:42 am)
First of all I would like to say thanks for all the help you've given me thus far. I don't know if I would have made it this far with out you. The code that you've sent look a whole lot better; however, the game crashes once the player achieves a score hire than 3. Here the current code I have for my enemy.cs

//This function takes the initial postion of enemy object
function EnemyShip::onLevelLoaded(%this, %scenegraph)
{
   // Save the initial X value so we can use it when we respawn this ship
   %this.startX = %this.getPositionX();

   // Missile speed
   %this.missileSpeed = -300;
   
   // Spawn this ship 
   %this.scenegraph = %scenegraph; // Make sure the scenegraph is available for the fireMissile() function
   %this.spawn();
}


//This fuction sets the spawn point of enemy object at random
function EnemyShip::spawn(%this)


{
   //When enemy spawns fires one missle
   %this.fireMissile();
   // Set the minimum and maximum Y positions to be the
   // world limits, accounting for the size of the ship
   %this.minY = getword(%this.getWorldLimit(), 2) + (%this.getHeight() / 2);
   %this.maxY = getword(%this.getWorldLimit(), 4) - (%this.getHeight() / 2);
   
   // Set the speed limits
   %this.minSpeed = -50;
   %this.maxSpeed = -100;
   
   // Set the speed and position of this ship
   %this.setLinearVelocityX(getRandom(%this.minSpeed, %this.maxSpeed));
   %this.setPositionY(getRandom(%this.minY, %this.maxY));
   %this.setPositionX(%this.startX);   
   //When enemy spawns fires one missle
   %this.fireMissile();
}

//This fuction recal the enemy ship once it pass the world limit and set it back at the spawn point
function EnemyShip::onWorldLimit(%this, %mode, %limit)
{
    // When the ship hits the left side of the world limits, then respawn the ship
    if (%limit $= "left") {
       %this.spawn();
    }
}
//Enables enemy to shoot missles
function EnemyShip::fireMissile(%this)
{
   %this.enemyMissile = new t2dStaticSprite()
   {
      scenegraph = %this.scenegraph;
      class = EnemyMissile;
      missileSpeed = %this.missileSpeed;
      enemy = %this;
   };
   
   %this.enemyMissile.fire();
}


//Enemy ship explode on collison with missle
function EnemyShip::explode(%this)
{
   %explosion = new t2dParticleEffect() 
   {
      scenegraph = %this.scenegraph; 
    
   };
   
   %explosion.loadEffect("~/data/particles/big_explosion.eff");
   %explosion.setEffectLifeMode("KILL", 1);

   %this.scenegraph.incScore();
//Object to go to next level

echo("score:" SPC %score);
%score = getWord(scoreObject.text,1); //get the second (zero-based) value in  a space-separated string

if(%score > 3)
 {
   
    %this.scenegraph.endLevel("~/data/levels/level1.t2d"); //Use the "end/load Level" functions to load levels you've made in TGB
    %this.scenegraph.loadLevel("~/data/levels/level2.t2d"); //"loadScene" loads files saved by the "saveScene" function

 }

    {
     
    
   %explosion.setPosition(%this.getPosition());
   %explosion.playEffect();

   %this.spawn();
}


Here are two other files I have that affect the score that maybe the cause of the problem

game.cs

//---------------------------------------------------------------------------------------------
// Torque Game Builder
// Copyright (C) GarageGames.com, Inc.
//---------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------
// startGame
// All game logic should be set up here. This will be called by the level builder when you
// select "Run Game" or by the startup process of your game to load the first level.
//---------------------------------------------------------------------------------------------
function startGame(%level)
{
   Canvas.setContent(mainScreenGui);
   Canvas.setCursor(DefaultCursor);
   
   new ActionMap(moveMap);   
   moveMap.push();
   
   $enableDirectInput = true;
   activateDirectInput();
   enableJoystick();
   
   sceneWindow2D.loadLevel(%level);

   //init score to zero
   %enemy.UpdateScore(%this, -%client.score);
   //%gamescore = 0;
}

//---------------------------------------------------------------------------------------------
// endGame
// Game cleanup should be done here.
//---------------------------------------------------------------------------------------------
function endGame()
{
   sceneWindow2D.endLevel();
   moveMap.pop();
   moveMap.delete();
}
function newGame()
{
   %level = "game/data/levels/level1.t2d";
   
   if (isFile(%level))
   {
      sceneWindow2D.loadLevel(%level);
   }
}


level1.cs

function level1::onLevelLoaded(%this){
	%this.gamescore = 0 ;
	scoreObject.text = "score:" SPC %gamescore;
}
function level1::incScore(%this){
    %this.gamescore++;
    scoreObject.text = "score:" SPC %this.gamescore;

}



Again I really appreciate the help

Jenney
#6
10/11/2008 (1:31 pm)
I think the problem is in this code block from enemy.cs:

//Object to go to next level
echo("score:" SPC %score);
%score = getWord(scoreObject.text,1); //get the second (zero-based) value in  a space-separated string
if(%score > 3) 
{       
    %this.scenegraph.endLevel("~/data/levels/level1.t2d"); 
    %this.scenegraph.loadLevel("~/data/levels/level2.t2d"); 
}    
{            
%explosion.setPosition(%this.getPosition());   
%explosion.playEffect();   
%this.spawn();
}

First off, the 'echo' statement should be after the "%score = getWord(scoreObject.text,1);" line. However, as the program is crashing everytime the score is greater than three, the 'if' statement seems to be evaulating everything correctly, and so you no longer really need the 'echo' statement at all. That's not why it's crashing, but it's something to note.

I believe the problem is in this snippet:
if(%score > 3) 
{       
    %this.scenegraph.endLevel("~/data/levels/level1.t2d"); 
    %this.scenegraph.loadLevel("~/data/levels/level2.t2d"); 
}    
[b]{[/b]          
%explosion.setPosition(%this.getPosition());   
%explosion.playEffect();   
%this.spawn();
[i]}[/i]

This is what I believe is happening: The score is evaulated by the 'if' statement. If it is less than or equal to three, the functions that clear and load a new level are skipped, the explosion is played at the ship's position, and the ship is respawned. However, if the score is greater than three, the clear and load functions are called, but after they are called, the functions to play the effect and respawn the ship are still called. That's probably where the program crashes because, after you just cleared the scene (destroying all objects within it), you're trying to have a no longer existent effect play and a void ship respawn.

To resolve this, you can do one of two things: 1) place a 'return;' statement after the "loadLevel" function at the end of the 'if' statement and delete the bracket in bold, or 2) type "else" before the bracket in bold and add an ending bracket ( '}' ) before the bracket in italiacs. Doing either of these will cause the play effect and respawn functions to be skipped once the calls to clear and load the level are made.

So, here is what it should look like using the first technique:
if(%score > 3) 
{       
    %this.scenegraph.endLevel("~/data/levels/level1.t2d"); 
    %this.scenegraph.loadLevel("~/data/levels/level2.t2d");
    return; //have the function return before it gets to the lines below
}    
   //bracket that was here has been deleted  
%explosion.setPosition(%this.getPosition());   
%explosion.playEffect();   
%this.spawn();
}

Here is what it should look like using the second:
if(%score > 3) 
{       
    %this.scenegraph.endLevel("~/data/levels/level1.t2d"); 
    %this.scenegraph.loadLevel("~/data/levels/level2.t2d");
}    
else  //an 'else' statement skips the code between the brackets if the above 'if' evaluates to 'true'
{
   %explosion.setPosition(%this.getPosition());   
   %explosion.playEffect();   
   %this.spawn();
}  //add another bracket to signify the end of the 'else' statement
}

I'm not sure if that will solve the problem, but it's one less thing that could be causing it :)

There are also some other things I saw that might cause the program to do things other than you want. I'm not sure if they're a result of posting the code (i.e. they're not present within the actual code). If so, or what I think might be a problem is actually how you want it to work, please bare with me.

1) The "fireMissile" function in the "EnemyShip::spawn" function is called twice: once at the top, and again at the bottom. This will create two missiles everytime the ship is [re]spawned. The second, will be referenced by the "EnemyShip::enemyMissile" variable, while the first won't be referenced by anything.

2) There are several undefined variables in the "startGame" function: %enemy, %this, and %client. As they are undefined, it's useless to try and call any functions or pass them as values to other functions. The "%" sign signifies the variables are local to the function, and, therefore they must be defined before use.

I hope some of all that made sense. :)
#7
10/11/2008 (2:12 pm)
One way to prevent crashing while loading the next level is to schedule the load level function for the next frame.

// %sceneGraph.loadLevel(%level);
%sceneGraph.schedule(32, "loadLevel", %level);
#8
10/12/2008 (11:01 am)
Thanks for the help guys : )
I took your advise and got rid of the useless code in the game.cs, even though it didn't change much. The game still crashes when the player scores three. So I got to thinking maybe changing the level according to the score is not a good idea. Which lead me to try to create a timer. If any of you have any idea of how I should start please reply. What I would like to do with the timer is to creat a GUI of a timer in the middle of the screen that would tell the player how much time they have left to survive before they begin the next level. If possible would still like to figure out how to change level by score here is the latest code
//This function takes the initial postion of enemy object
function EnemyShip::onLevelLoaded(%this, %scenegraph)
{
   // Save the initial X value so we can use it when we respawn this ship
   %this.startX = %this.getPositionX();

   // Missile speed
   %this.missileSpeed = -300;
   
   // Spawn this ship 
   %this.scenegraph = %scenegraph; // Make sure the scenegraph is available for the fireMissile() function
   %this.spawn();
}


//This fuction sets the spawn point of enemy object at random
function EnemyShip::spawn(%this)


{
   //When enemy spawns fires one missle
   //%this.fireMissile();
   // Set the minimum and maximum Y positions to be the
   // world limits, accounting for the size of the ship
   %this.minY = getword(%this.getWorldLimit(), 2) + (%this.getHeight() / 2);
   %this.maxY = getword(%this.getWorldLimit(), 4) - (%this.getHeight() / 2);
   
   // Set the speed limits
   %this.minSpeed = -50;
   %this.maxSpeed = -100;
   
   // Set the speed and position of this ship
   %this.setLinearVelocityX(getRandom(%this.minSpeed, %this.maxSpeed));
   %this.setPositionY(getRandom(%this.minY, %this.maxY));
   %this.setPositionX(%this.startX);   
   //When enemy spawns fires one missle
   %this.fireMissile();
}

//This fuction recal the enemy ship once it pass the world limit and set it back at the spawn point
function EnemyShip::onWorldLimit(%this, %mode, %limit)
{
    // When the ship hits the left side of the world limits, then respawn the ship
    if (%limit $= "left") {
       %this.spawn();
    }
}
//Enables enemy to shoot missles
function EnemyShip::fireMissile(%this)
{
   %this.enemyMissile = new t2dStaticSprite()
   {
      scenegraph = %this.scenegraph;
      class = EnemyMissile;
      missileSpeed = %this.missileSpeed;
      enemy = %this;
   };
   
   %this.enemyMissile.fire();
}


//Enemy ship explode on collison with missle
function EnemyShip::explode(%this)
{
   %explosion = new t2dParticleEffect() 
   {
      scenegraph = %this.scenegraph; 
    
   };
   
   %explosion.loadEffect("~/data/particles/big_explosion.eff");
   %explosion.setEffectLifeMode("KILL", 1);

   %this.scenegraph.incScore();
//Object to go to next level

echo("score:" SPC %score);
%score = getWord(scoreObject.text,1); //get the second (zero-based) value in  a space-separated string

if(%score > 3) 
{       
    %this.scenegraph.endLevel("~/data/levels/level1.t2d"); 
    %sceneGraph.schedule(32, "loadLevel", %level1);
    %this.scenegraph.loadLevel("~/data/levels/level2.t2d");
}    
else  //an 'else' statement skips the code between the brackets if the above 'if' evaluates to 'true'
{
   %explosion.setPosition(%this.getPosition());   
   %explosion.playEffect();   
   %this.spawn();
}  //add another bracket to signify the end of the 'else' statement
}

thanks,
Jenney : )
#9
10/12/2008 (7:49 pm)
One thing, the "%scenegraph.schedule(32, "loadLevel", %level)" replaces the "%scenegraph.loadLevel" below it. Also, you'll want to set the %level variable equal to the name of the level you're loading before you schedule the scene change. So, with that, here's what the code should look like:

function EnemyShip::explode(%this)
{
   %explosion = new t2dParticleEffect() 
   {      scenegraph = %this.scenegraph;
   };   

   %explosion.loadEffect("~/data/particles/big_explosion.eff");   
   %explosion.setEffectLifeMode("KILL", 1);   
   %this.scenegraph.incScore();//Object to go to next level
   
   %score = getWord(scoreObject.text,1); //get the second (zero-based) value in  a space-separated string
   if(%score > 3) 
  {           
      %this.scenegraph.endLevel("~/data/levels/level1.t2d");  
      %nextLevel = "~/data/levels/level2.t2d";  //set this to the name of the next level
      %sceneGraph.schedule(32, "loadLevel", %nextLevel); 
   }    
   else  //an 'else' statement skips the code between the brackets if the above 'if' evaluates to 'true'
  {
       %explosion.setPosition(%this.getPosition());  
       %explosion.playEffect();
       %this.spawn();
   }  //add another bracket to signify the end of the 'else' statement
}

As far as a timer is concerned, all I usually do is create a text object, let's call it 'timerVal', and then give it a class(Timer) which updates its text in second intervals. Something like:

function Timer::onAdd(%this)
{
     %sec = 0;
     %min = 0;

     %update = %this.schedule(1000,increaseTime);  //updates time every second
}

function Timer::increaseTime(%this)
{
    if(isEventPending(%this.update))
         return;

    %this.sec += 1;

    if(%this.sec == 60)
    {
        %this.sec = 0;
        %this.min += 1;
     }
    
    if(%this.sec < 10)
        %secText = ("0" SPC %this.sec);
    else
        %secText = %this.sec

    if(%this.min < 10)
      %minText = ("0" SPC %this.min);
   else
      %minText = %this.min;

    %this.owner.text = (%minText SPC ":" SPC %secText);
    %update = %this.schedule(1000,increaseTime);  //updates time every second
}

That's something like how I'd do it, although I just typed it up right now so that code hasn't exactly been tested.

Hope it helps!