Line Collision: Gap Logic

Before moving on to line collision for uneven terrain, I wanted to show you an extra tool you can use to optimize collision detection. It doesn’t work for platform games, but for uneven terrain games it is almost essential. I call it Gap Logic, and it is based on the tile engine, or grid collision logic.

Use Arrow keys to move, Space bar to jump

The idea is to divide the X axis of the screen into equal parts, or gaps, and use these X values for the various lines that will create the terrain. You can understand this a little better by looking at the next picture:

It is a lot like plotting a graph. The green lines represent the gaps, and the red line is made by varying the Y values as much as you want, but using the gaps for the X values.

So after pushing all these lines into a Vector or Array, I can quickly find the line the hero is about to land on, by dividing the hero's nextX by the Gap value. Like this:

1
2
3
4
 
var index:int = _hero.nextX/LINE_GAP;
 
var line:Line = _lines[index];

And so I get rid of the loop on lines collection.

For a game where you have a really long terrain, with hundreds of lines, this could help a great deal. Besides, it is a lot easier to create a level builder like this.

Just keep in mind that you can replace the following code with the loop from the previous tutorials.

How is it Done

I start establishing a value for GAP, creating a Starting Point and then creating an array of Y values for the terrain. This last array could be spit out from a level builder application.

1
2
3
4
5
6
7
8
9
10
 
private static const GAP:int = 50;
 
private var _start:Point = new Point(0,200);
 
private var _lineYValues:Array = [5,10,-10,2,10,0,2,5,50,-50,5,8,0,4,5,2,3,1,5,8,2,0,6,2,1,9,1,2,
 
3,4,5,6,7,0,2,6,0,-5,6,7,8,-8,5,10,-10,2,10,0,2,5,50,-50,5,8,0,4,
 
5,2,3,1,5,8,2,0,6,2,1,9,1,2,3,4,5,6,7,0,2,6,0,-5,6,7,8,-8];

You probably noticed something funny about the Y values, they are actually a reference to how much each Y value changes in relation to the starting point Y. But you can do it differently if you would like.

And here is the method that builds the lines:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
var cntX:int = _start.x + GAP;
 
 
 
var line:Line = new Line(new Point(_start.x, _start.y), new Point(cntX, _start.y - _lineYValues[0]));
 
for (var i:int = 1; i < _lineYValues.length; i++) {
 
	line = new Line(new Point(cntX - GAP, _start.y - _lineYValues[i-1]), new Point(cntX, _start.y - _lineYValues[i]));
 
	addChild(line);
 
	_lines.push(line);
 
	cntX += GAP;
 
}

The logic to close the lines and form the terrain graphic with bitmapFill or whatever, would be added here as well. Notice too that the line doesn't have to be added to the stage. In fact when I use this in games, I don't bother having any sprites at all for the lines.

Another option, if you must have the lines, is using Graphics.lineTo to draw the terrain with one single line. But still create the individual Line objects.

With these coordinates, you end up with something like the example above. Now I'll show you how simple it is to run collision, and show you a way to simplify collision for non-platform games even further.

The update method now simply updates nextX and nextY. I don't pass it the line anymore:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 
public function update ():void {
 
	if (!_inAir) {
 
		if (_moveLeft) {
 
			_vx -=  _acceleration;
 
		}
 
		if (_moveRight) {
 
			_vx +=  _acceleration;
 
		}
 
	}
 
 
 
	_vy +=  GRAVITY;
 
 
 
	if (! _moveRight && ! _moveLeft && !_inAir) {
 
		if (Math.abs(_vx) > MAX_SPEED_X / 10) {
 
			_vx *=  0.6;
 
		} else {
 
			_vx = 0;
 
		}
 
	}
 
 
 
	/*
 
	//if you want to add the slide logic
 
	if (_platform) {
 
		if (_platform.data.slope != 0) {
 
			var angle:Number = _platform.data.angle;
 
			_vx +=  2*angle;
 
		}
 
	}*/
 
 
 
	limitSpeeds();
 
 
 
	_nextX = x + _vx;
 
	_nextY = y + _vy;
 
}

The main loop causes the hero to run update, then grabs the nextX to determine which line should be checked for collision, using the Gap logic:

1
2
3
4
5
6
 
var index:int = hero.nextX/GAP;
 
hero.update();
 
hero.checkPosition(_lines[index]);

The checkPosition method is now a lot simpler than what I had used before, and here I do pass the line:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 
public function checkPosition (line:Line):void {
 
 
 
	var newY:Number = line.data.translateFromX(_nextX);
 
	if (_platform && _platform == line && ! _inAir) {
 
		_nextY = newY;
 
		_vy = 0;
 
		this.platform = line;
 
		return;
 
	}
 
	if (_nextY >= newY  ) {
 
		_nextY = newY;
 
 
 
		/*
 
		//if you want to add bouncing
 
		if (_vy > 4) {
 
			_vy *= -.6;
 
			this.platform = null;
 
			return;
 
		}else {
 
			_vy = 0;
 
		}
 
		*/
 
 
 
		_vy = 0;
 
		this.platform = line;
 
		return;
 
	}
 
	this.platform = null;
 
}

Again I check to see if the hero is already on a line, otherwise I track for collision.

Also, if you want the sprite to appear to be stuck to the line, you can rewrite the first conditional in the checkPosition method. Change from:

1
2
 
if (_platform && _platform == line && ! _inAir) {

To:

1
2
 
if (_platform && ! _inAir) {

It will reduce the "jumping off" when moving between lines.

This would be the result of that:

Of course, if you don't want the sprite to jump at all, you can make it stuck to the line just by changing the nextY value instantly upon calling checkPosition. Like this:

1
2
3
4
5
6
7
8
9
10
11
12
 
public function checkPosition (line:Line):void {	
 
	var newY:Number = line.data.translateFromX(_nextX);
 
	_nextY = newY;
 
	//this will set inAir to false, and rotate the sprite
 
	this.platform = line;
 
}

And this would be the result of that.

Of course, more changes could be easily applied just by altering the physics of the game. These are the base values I use, notice the low Gravity value:

1
2
3
4
5
6
7
8
9
10
 
private const GRAVITY:Number = 0.5;
 
private const JUMP:int = 8;
 
private const MAX_SPEED_X:int = 20;
 
private const MAX_SPEED_Y:int = 10;
 
private var _acceleration:int = 1;

Next, I will run the same logic but using wheels again, doing something similar to what is used in the classic arcade game Moon Patrol.

2 thoughts to “Line Collision: Gap Logic”

  1. hi,

    very nice tutorial good job

    where can i find the full source for this tutorial?

    i will appreciate if u help me with this

    thanks

    1. Hi Ahmad.

      I will post the source code for the gap logic sometime over the weekend and I can email you when I do so.

Comments are closed.