Game Development Community

BreakOut style Game functionality

by David Higgins · in Torque Game Builder · 08/21/2006 (8:39 pm) · 13 replies

I'm attempting to play around with both tile-layers and the physics engine at the same time, and a simple Break Out clone seemed appropriate for this ...

I have a fully working 'board' using a TileMap and Tile Layer with 'blocks' drawn onto the tilelayer. When the ball collides with a 'block', the block is destroyed and the ball reacts to the collision ... I've been toying with both BOUNCE and RIGID response mode's and neither seem to give a really 'nice' gameplay feel ... bounce appears to be more 'breakout' style, but it more or less responds with a very simple 'opposite angle' reaction, making it fairly difficult to 'aim' the ball in any direction.

I tried rigid, but when the paddle hits the ball, it responds fairly poorly and does not really go in the direction expected as rigid also takes into account the balls rotation, the paddles rotation, the angle the ball collided with the paddle and a number of other things ... this is fine for something like billiards (that demo's almost done btw), but for breakout, there's a general 'expectation' as to what will happen based on where the ball collides with the paddle.

If you collide with the paddle on any general angle and hit the paddle in the center, you more or less expect it to respond like 'BOUNCE' does, but when you hit the near to outter or outter edge of the paddle, you sort of expect the ball to just simply go 'backwards' ...

Anyone know how to implement this? I was thinking about possibly creating a 'ghost' paddle, and then mounting a series of paddle 'parts' (left, right, left center, right center, center) and having the ball respond to each portion differently ... if it hits the center of the paddle, just add/suibtract 180 degree's from the balls polar velocity ... if it hits the left/right center portion then add/subtract 90 degree's ... and if it hits the left or right portion, either send it back the way it came (add/subtract 360 degrees) or add/subtract a random amount between 45 and 90 degree's ... possibly even adding more sections, and inc/dec the degree by 10 for with each section ...


Just curious, how would 'you' implement this functionality? Keep in mind, I'm new to TGB and TorqueScript all together so I may be missing an obvious 'duh' response/function/attribute that might do this for me (I doubt it's built in though).

#1
08/23/2006 (8:51 pm)
That idea would probably work.

I'm just theorizing here, but when you got the collision callback couldn't you just check the difference between the X-center of your paddle, and the X-center of the ball and do a calculation for return angle based on the distance that the ball hit to the left or right of the center of the paddle?

Of course, you'd have to determine left side or right side based on a greater than, less than of the x positions of the paddle and the ball when calculating the return trajectory as positive or negative...


But a setup like this would reduce the amount of ghost paddles, as well as allow you to fine tune the angles, because you could split the distance up into a nearly infinite set of chunks.

For instance you could have the angle change every half of a unit, or every tenth of a unit.

Might be something to think about.
#2
08/23/2006 (8:53 pm)
@Mark, I'd need a little more info on how to do that, my math's not the greatest ... which would probably explain why doing that wasn't the first thing that popped into mind.

I was, however, a tad upset that the physics in the engine weren't capable of doing this -- but perhaps i was asking/expecting too much there ...
#3
08/23/2006 (9:09 pm)
Let me work on it a little and see what I can come back to you with.

One extra question, do you want the paddle's speed to be applied to the balls return velocity, or are you going to keep the ball speed a constant and just use the paddle to direct the angle?
#4
08/23/2006 (9:16 pm)
Mark,

Basically, I'd like to simulate what Arkanoid does ... for the most part.

I'll assume most of the odd stuff I am trying to do with object movement will require a developer that has decent to extensive math knowledge and logic skills (luckily, I have one). I, however, am not that developer, and am just toying around with the engine for the time being to see what I can do by myself ... so far, I'm extremely impressed and 99.9% positive I'll be licensing the engine before the end of the month (one or two more paychecks...!)
#5
08/23/2006 (11:48 pm)
Okay David, here's the code that will do as I said. It's pretty straightforward, requires no ghost paddles, etc... Not even any fancy math.

//Paddle.cs

function paddle::OnLevelLoaded(%this, %scenegraph)
{
$p=%this;
moveMap.bindCmd(keyboard, "a", "paddleLeft();", "paddleLeftStop();");
moveMap.bindCmd(keyboard, "d", "paddleRight();", "paddleRightStop();");
$p.hspeed=100;
}

function paddle::updateMovement(%this)
{
   if(%this.moveLeft)
   {
          $p.setLinearVelocityX( -$p.hSpeed );
   }
   if(%this.moveRight)
   {
          $p.setLinearVelocityX( $p.hSpeed );
   }   
   if(!%this.moveLeft && !%this.moveRight)
   {
      %this.setLinearVelocityX( 0 );
   } 
}


function paddleLeft()
{
   $p.moveLeft = true;
   $p.updateMovement();
}

function paddleRight()
{
   $p.moveRight = true;
   $p.updateMovement();
}

function paddleLeftStop()
{
   $p.moveLeft = false;
   $p.updateMovement();
}

function paddleRightStop()
{
   $p.moveRight = false;
   $p.updateMovement();
}

function paddle::onCollision(%srcObj, %dstObj, %srcRef, %dstRef, %time, %normal,
%contactCount, %contacts)
{
	if(%dstObj.class $= "ball")
{
 	 %b=%dstObj;
	%paddlepos=%srcObj.getPosition();
	%paddlex=getWord(%paddlepos,0);
	%ballpos=%dstObj.getPosition();
	%ballx=getword(%ballpos,0);
	%d=mAbs(%ballx-%paddlex);
	if(%ballx < %paddlex )
		{
			%anglemodifier=-1;}
	else
	{
	%anglemodifier=1;	 
	}
	if (%d <=2) %returnangle=0;
	if(%d>2 && %d<=4) %returnangle=00;
	if(%d>4 && %d<=6) %returnangle=15;
	if(%d>6 && %d<=8) %returnangle=30;
	if(%d>8 && %d<=9) %returnangle=45;
	if(%d>9 && %d<=10) %returnangle=60;
	if(%d>10) %returnangle=75;
	%returnangle=%returnangle * %anglemodifier;	
	%speed=$b.getLinearVelocityPolar();
	%b.setAtRest();
      %b.setLinearVelocityPolar(%returnangle,80);
}
}

and just for completeness, heres the script file for the ball as well.

//ball.cs

 function ball::OnLevelLoaded(%this, %scenegraph)
{
$b=%this;
$b.speed=100;
$b.spawn();

}

function ball::spawn(%this)
{
%this.setPosition(getRandom(-80, 80), getrandom(-40,-70));
%this.setLinearVelocityPolar(180,$b.speed);
}


function ball::onWorldLimit(%this, %mode, %limit)
{
%this.spawn();
}

Just add these two files into your game.cs and exec them, then throw together a level with walls on the sides and on the top. Add a rectangle and assign it to the 'paddle' class, and add a ball (I used the GG logo) and assign it to the 'ball' class.

The rest is a matter of tweaking the send/receive Physics and Collisions which I can step you through if you'd like. Also, you may have to adjust a few of the variables for speed and start positions to fit your level.

This method worked well, but every so often I'd get a weird bounce when at the extreme end of the paddle. But it only happens at higher speeds, and I figure it has to do with multiple collisions being trapped and the physics overriding the coding.

Mark
#6
08/26/2006 (5:45 pm)
@Mark, thanks I'll give that a try and see where I end up with -- appreciate the time.
#7
08/27/2006 (12:35 am)
@Mark, the code works great -- thank you alot for contributing it -- I'll assume giving you credit is OK?
#8
08/27/2006 (2:24 am)
New issue -- grrr!

After implementing the changes that Mark gave me, I attempted to actually make a 'game' out of this and am apparently not doing too well.

// Blocks.cs
function board::isBlock(%this, %x, %y)
{
   echo("X/Y:" SPC %x SPC %y);
   if($board.getTileCustomData(%x SPC %y) $= "brick") { echo("Brick"); return true; }
   else { echo("Not Brick"); return false; } 
}

function board::onLevelLoaded(%this, %scenegraph)
{
   $board = %this;
}

// Ball.cs
function ball::onCollision(%srcObj, %dstObj, %srcRef, %dstRef, %time, %normal, %contactCount, %contacts)
{
   echo("COLLIDE:" SPC %dstObj.Class);
   if(%dstObj.Class $= "board" && $board.isBlock(%dstRef))
   {
      $board.clearTile(%dstRef);
      %continue = false;
      for(%x = 0; %x = $board.getTileCountX(); %x++)
      {
         for(%y = 0; %y = $board.getTileCountY(); %y++)
         {
            echo("CUSTOM DATA:" SPC $board.getTileCustomData(%x, %y));
            if($board.getTileCustomData(%x, %y) $= "brick") %continue = true;
         }
      }
      $CONTINUE = %continue;      
   }
}

That's the code I have in my ball.cs, $board is initialized in board::onLevelLoaded(...) and the Tile Layer I have loaded has a class of 'board', $board.dump() shows all the Tile Layer methods. the isBlock method worked previously as:

function isBlock(%dstObj, %dstRef)
{
  if(%dstObj.Class $= "board")
  {
    if(%dstObj.getTileCustomData(%dstRef) $= "brick") { return true; }
    else { return false; }
  } else { return false; }
}

Now, when I run the code ... as soon as the ball collide's with anything, it appears to just lock up -- Task Manager in Windows shows TGB.exe going from <100mb of RAM to over 600mb of RAM (I have 1gb physical) and the CPU usage fluxuates between 0 and 100 (mostly at 100% for a minute or two, but calms down and settles around 20-30% for a while ... ).

I assume this ridiculous CPU usage is due to the for loops I wrote, as that's when CPU usage usually goes nuts, but I'm not 100% sure if it's the loop code (as it seems logical and clean -- at 2:30am) or perhaps I'm doing something I shouldn't be doing as far as TGB is concerned ...

Any idea's ?

EDIT: fixed code a bit -- still same issue
#9
08/27/2006 (12:56 pm)
Here's my brick code I used... Just assign the class to your tilelayer with the bricks that you want to disappear. If you want unbreakable bricks as well, use a second tilelayer overliad on the first and don't assign it to that class. Also remember with the AutoRotation field and pathing along with multiple layering you can get some challenging levels and some bizarre visuals effects.

function brick::OnLevelLoaded(%this, %scenegraph)
{
$br=%this;

}


function brick::onCollision(%srcObj, %dstObj, %srcRef, %dstRef, %time, %normal,
%contactCount, %contacts)
{

                 //This Line clears out a brick when hit... 	 
                 $br.clearTile(getword(%srcref,0),getword(%srcref,1));
 	 
}

Hope this helps. If not repost and I'll check back tomorrow after work.
#10
08/27/2006 (2:39 pm)
@Mark,

I had the clearTile code working ... my problem was determing whether or not there were anymore bricks/blocks left or if the level was completed.

I think my problem was with the logic I was using, don't know why I tried doing it the way I was -- I think I've got it now though

I'll post the results when I finish it ...
#11
08/28/2006 (12:54 pm)
I cheated to do mine. I put a dynamic variable on the tilelayer called 'BrickCnt', and then I created a global at the top part of the brick.cs file, and everytime I cleared a brick I incremented the counter variable and compared it to the dynamic variable, when they equaled they were all gone.

Not an elegant solution, but a quick one.

Of course to accomodate multiple tile layers of breakable bricks or the tally on different levels, and such would require some rethinking, but it could be done by having your level tally the 'BrickCnt' varaibles for all of your tile layers, and then you'd know the total bricks on each level. Or you could want to track the individual count on each tilelayer so that maybe certain layers would respawn so many seconds after they cleared making a harder challenge or such.
#12
08/28/2006 (3:06 pm)
@Mark, not bad ideas ...

I only went the direction I did because I wanted to more or less mess around with the tileScript and CustomData to see how it worked -- more so for another game idea that would rely heavily on custom tile data and defaults ..

thank you alot for the help ... I intend to post the work I've done as a demo for everyone to see, I'll give you credit in the source and the 'Credits' tab of the GUI ...

I hope to be able to make it into a tutorial, or useable learning resource such as an example with comments, etc ... may ask for your help when it comes to explaining the paddle collision, I think it's pretty straight forward now that I see it, but I may have trouble expressing it in words ;)
#13
08/29/2006 (4:04 pm)
Not a problem, if you need something just let me know.